github.com/andrewsun2898/u-root@v6.0.1-0.20200616011413-4b2895c1b815+incompatible/pkg/boot/multiboot/multiboot.go (about)

     1  // Copyright 2018-2019 the u-root Authors. All rights reserved
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  // Package multiboot implements bootloading multiboot kernels as defined by
     6  // https://www.gnu.org/software/grub/manual/multiboot/multiboot.html.
     7  //
     8  // Package multiboot crafts kexec segments that can be used with the kexec_load
     9  // system call.
    10  package multiboot
    11  
    12  import (
    13  	"bytes"
    14  	"debug/elf"
    15  	"encoding/binary"
    16  	"fmt"
    17  	"io"
    18  	"log"
    19  	"os"
    20  	"path/filepath"
    21  	"strings"
    22  
    23  	"github.com/u-root/u-root/pkg/boot/ibft"
    24  	"github.com/u-root/u-root/pkg/boot/kexec"
    25  	"github.com/u-root/u-root/pkg/boot/multiboot/internal/trampoline"
    26  	"github.com/u-root/u-root/pkg/ubinary"
    27  	"github.com/u-root/u-root/pkg/uio"
    28  )
    29  
    30  const bootloader = "u-root kexec"
    31  
    32  // Module describe a module by a ReaderAt and a `CmdLine`
    33  type Module struct {
    34  	Module  io.ReaderAt
    35  	Name    string
    36  	CmdLine string
    37  }
    38  
    39  // Modules is a range of module with a Closer interface
    40  type Modules []Module
    41  
    42  // multiboot defines parameters for working with multiboot kernels.
    43  type multiboot struct {
    44  	mem kexec.Memory
    45  
    46  	kernel  io.ReaderAt
    47  	modules []Module
    48  
    49  	cmdLine    string
    50  	bootloader string
    51  
    52  	// trampoline is a path to an executable blob, which contains a trampoline segment.
    53  	// Trampoline sets machine to a specific state defined by multiboot v1 spec.
    54  	// https://www.gnu.org/software/grub/manual/multiboot/multiboot.html#Machine-state.
    55  	trampoline string
    56  
    57  	// EntryPoint is a pointer to trampoline.
    58  	entryPoint uintptr
    59  
    60  	info          info
    61  	loadedModules modules
    62  }
    63  
    64  var (
    65  	rangeTypes = map[kexec.RangeType]uint32{
    66  		kexec.RangeRAM:      1,
    67  		kexec.RangeDefault:  2,
    68  		kexec.RangeACPI:     3,
    69  		kexec.RangeNVS:      4,
    70  		kexec.RangeReserved: 2,
    71  	}
    72  )
    73  
    74  var sizeofMemoryMap = uint(binary.Size(MemoryMap{}))
    75  
    76  // MemoryMap represents a reserved range of memory passed via the multiboot Info header.
    77  type MemoryMap struct {
    78  	// Size is the size of the associated structure in bytes.
    79  	Size uint32
    80  	// BaseAddr is the starting address.
    81  	BaseAddr uint64
    82  	// Length is the size of the memory region in bytes.
    83  	Length uint64
    84  	// Type is the variety of address range represented.
    85  	Type uint32
    86  }
    87  
    88  // String returns a readable representation of a MemoryMap entry.
    89  func (m MemoryMap) String() string {
    90  	return fmt.Sprintf("[0x%x, 0x%x) (len: 0x%x, size: 0x%x, type: %d)", m.BaseAddr, m.BaseAddr+m.Length, m.Length, m.Size, m.Type)
    91  }
    92  
    93  type memoryMaps []MemoryMap
    94  
    95  // marshal writes out the exact bytes expected by the multiboot info header
    96  // specified in
    97  // https://www.gnu.org/software/grub/manual/multiboot/multiboot.html#Boot-information-format.
    98  func (m memoryMaps) marshal() ([]byte, error) {
    99  	buf := bytes.Buffer{}
   100  	err := binary.Write(&buf, ubinary.NativeEndian, m)
   101  	return buf.Bytes(), err
   102  }
   103  
   104  // elems adds mutiboot info elements describing the memory map of the system.
   105  func (m memoryMaps) elems() []elem {
   106  	var e []elem
   107  	for _, mm := range m {
   108  		e = append(e, &mutibootMemRange{
   109  			startAddr: mm.BaseAddr,
   110  			length:    mm.Length,
   111  			memType:   mm.Type,
   112  		})
   113  	}
   114  	return e
   115  }
   116  
   117  // String returns a new-line-separated representation of the entire memory map.
   118  func (m memoryMaps) String() string {
   119  	var s []string
   120  	for _, mm := range m {
   121  		s = append(s, mm.String())
   122  	}
   123  	return strings.Join(s, "\n")
   124  }
   125  
   126  // Probe checks if `kernel` is multiboot v1 or mutiboot kernel.
   127  func Probe(kernel io.ReaderAt) error {
   128  	r := tryGzipFilter(kernel)
   129  	_, err := parseHeader(uio.Reader(r))
   130  	if err == ErrHeaderNotFound {
   131  		_, err = parseMutiHeader(uio.Reader(r))
   132  	}
   133  	return err
   134  }
   135  
   136  // newMB returns a new multiboot instance.
   137  func newMB(kernel io.ReaderAt, cmdLine string, modules []Module) (*multiboot, error) {
   138  	// Trampoline should be a part of current binary.
   139  	p, err := os.Executable()
   140  	if err != nil {
   141  		return nil, fmt.Errorf("cannot find current executable path: %v", err)
   142  	}
   143  	trampoline, err := filepath.EvalSymlinks(p)
   144  	if err != nil {
   145  		return nil, fmt.Errorf("cannot eval symlinks for %v: %v", p, err)
   146  	}
   147  
   148  	return &multiboot{
   149  		kernel:     kernel,
   150  		modules:    modules,
   151  		cmdLine:    cmdLine,
   152  		trampoline: trampoline,
   153  		bootloader: bootloader,
   154  		mem:        kexec.Memory{},
   155  	}, nil
   156  }
   157  
   158  // Load parses and loads a multiboot `kernel` using kexec_load.
   159  //
   160  // debug turns on debug logging.
   161  //
   162  // Load can set up an arbitrary number of modules, and takes care of the
   163  // multiboot info structure, including the memory map.
   164  //
   165  // After Load is called, kexec.Reboot() is ready to be called any time to stop
   166  // Linux and execute the loaded kernel.
   167  func Load(debug bool, kernel io.ReaderAt, cmdline string, modules []Module, ibft *ibft.IBFT) error {
   168  	kernel = tryGzipFilter(kernel)
   169  	for i, mod := range modules {
   170  		modules[i].Module = tryGzipFilter(mod.Module)
   171  	}
   172  
   173  	m, err := newMB(kernel, cmdline, modules)
   174  	if err != nil {
   175  		return err
   176  	}
   177  	if err := m.load(debug, ibft); err != nil {
   178  		return err
   179  	}
   180  	if err := kexec.Load(m.entryPoint, m.mem.Segments, 0); err != nil {
   181  		return fmt.Errorf("kexec.Load() error: %v", err)
   182  	}
   183  	return nil
   184  }
   185  
   186  // OpenModules open modules as files and fill a range of `Module` struct
   187  //
   188  // Each module is a path followed by optional command-line arguments, e.g.
   189  // []string{"./module arg1 arg2", "./module2 arg3 arg4"}.
   190  func OpenModules(cmds []string) (Modules, error) {
   191  	modules := make([]Module, len(cmds))
   192  	for i, cmd := range cmds {
   193  		modules[i].CmdLine = cmd
   194  		name := strings.Fields(cmd)[0]
   195  		modules[i].Name = name
   196  		f, err := os.Open(name)
   197  		if err != nil {
   198  			// TODO close already open files
   199  			return nil, fmt.Errorf("error opening module %v: %v", name, err)
   200  		}
   201  		modules[i].Module = f
   202  	}
   203  	return modules, nil
   204  }
   205  
   206  // LazyOpenModules assigns modules to be opened as files.
   207  //
   208  // Each module is a path followed by optional command-line arguments, e.g.
   209  // []string{"./module arg1 arg2", "./module2 arg3 arg4"}.
   210  func LazyOpenModules(cmds []string) Modules {
   211  	modules := make([]Module, 0, len(cmds))
   212  	for _, cmd := range cmds {
   213  		name := strings.Fields(cmd)[0]
   214  		modules = append(modules, Module{
   215  			CmdLine: cmd,
   216  			Name:    name,
   217  			Module:  uio.NewLazyFile(name),
   218  		})
   219  	}
   220  	return modules
   221  }
   222  
   223  // Close closes all Modules ReaderAt implementing the io.Closer interface
   224  func (m Modules) Close() error {
   225  	// poor error handling inspired from uio.multiCloser
   226  	var allErr error
   227  	for _, mod := range m {
   228  		if c, ok := mod.Module.(io.Closer); ok {
   229  			if err := c.Close(); err != nil {
   230  				allErr = err
   231  			}
   232  		}
   233  	}
   234  	return allErr
   235  }
   236  
   237  // load loads and parses multiboot information from m.kernel.
   238  func (m *multiboot) load(debug bool, ibft *ibft.IBFT) error {
   239  	var err error
   240  	log.Println("Parsing multiboot header")
   241  	// TODO: the kernel is opened like 4 separate times here. Just open it
   242  	// once and pass it around.
   243  
   244  	var header imageType
   245  	multibootHeader, err := parseHeader(uio.Reader(m.kernel))
   246  	if err == nil {
   247  		header = multibootHeader
   248  	} else if err == ErrHeaderNotFound {
   249  		var mutibootHeader *mutibootHeader
   250  		// We don't even need the header at the moment. Just need to
   251  		// know it's there. Everything that matters is in the ELF.
   252  		mutibootHeader, err = parseMutiHeader(uio.Reader(m.kernel))
   253  		header = mutibootHeader
   254  	}
   255  	if err != nil {
   256  		return fmt.Errorf("error parsing headers: %v", err)
   257  	}
   258  	log.Printf("Found %s image", header.name())
   259  
   260  	log.Printf("Getting kernel entry point")
   261  	kernelEntry, err := getEntryPoint(m.kernel)
   262  	if err != nil {
   263  		return fmt.Errorf("error getting kernel entry point: %v", err)
   264  	}
   265  	log.Printf("Kernel entry point at %#x", kernelEntry)
   266  
   267  	log.Printf("Parsing ELF segments")
   268  	if err := m.mem.LoadElfSegments(m.kernel); err != nil {
   269  		return fmt.Errorf("error loading ELF segments: %v", err)
   270  	}
   271  
   272  	log.Printf("Parsing memory map")
   273  	if err := m.mem.ParseMemoryMap(); err != nil {
   274  		return fmt.Errorf("error parsing memory map: %v", err)
   275  	}
   276  
   277  	// Insert the iBFT now, since nothing else has been allocated and this
   278  	// is the most restricted allocation we're gonna have to make.
   279  	if ibft != nil {
   280  		ibuf := ibft.Marshal()
   281  
   282  		// The iBFT may sit between 512K and 1M in physical memory. The
   283  		// loaded OS finds it by scanning this region.
   284  		allowedRange := kexec.Range{
   285  			Start: 0x80000,
   286  			Size:  0x80000,
   287  		}
   288  		r, err := m.mem.ReservePhys(uint(len(ibuf)), allowedRange)
   289  		if err != nil {
   290  			return fmt.Errorf("reserving space for the iBFT in %s failed: %v", allowedRange, err)
   291  		}
   292  		log.Printf("iBFT was allocated at %s: %#v", r, ibft)
   293  		m.mem.Segments.Insert(kexec.NewSegment(ibuf, r))
   294  	}
   295  
   296  	log.Printf("Preparing %s info", header.name())
   297  	infoAddr, err := header.addInfo(m)
   298  	if err != nil {
   299  		return fmt.Errorf("error preparing %s info: %v", header.name(), err)
   300  	}
   301  
   302  	log.Printf("Adding trampoline")
   303  	if m.entryPoint, err = m.addTrampoline(header.bootMagic(), infoAddr, kernelEntry); err != nil {
   304  		return fmt.Errorf("error adding trampoline: %v", err)
   305  	}
   306  	log.Printf("Trampoline entry point at %#x", m.entryPoint)
   307  
   308  	if debug {
   309  		info, err := m.description()
   310  		if err != nil {
   311  			log.Printf("%v cannot create debug info: %v", DebugPrefix, err)
   312  		}
   313  		log.Printf("%v %v", DebugPrefix, info)
   314  	}
   315  
   316  	return nil
   317  }
   318  
   319  func getEntryPoint(r io.ReaderAt) (uintptr, error) {
   320  	f, err := elf.NewFile(r)
   321  	if err != nil {
   322  		return 0, err
   323  	}
   324  	return uintptr(f.Entry), err
   325  }
   326  
   327  // addInfo collects and adds multiboot info into the relocations/segments.
   328  //
   329  // addInfo marshals out everything required for
   330  // https://www.gnu.org/software/grub/manual/multiboot/multiboot.html#Boot-information-format
   331  // which is a memory map; a list of module structures, pointed to by mods_addr
   332  // and mods_count; and the multiboot info structure itself.
   333  func (h *header) addInfo(m *multiboot) (addr uintptr, err error) {
   334  	iw, err := h.newMultibootInfo(m)
   335  	if err != nil {
   336  		return 0, err
   337  	}
   338  	infoSize, err := iw.size()
   339  	if err != nil {
   340  		return 0, err
   341  	}
   342  
   343  	r, err := m.mem.FindSpace(infoSize)
   344  	if err != nil {
   345  		return 0, err
   346  	}
   347  
   348  	d, err := iw.marshal(r.Start)
   349  	if err != nil {
   350  		return 0, err
   351  	}
   352  	m.info = iw.info
   353  
   354  	m.mem.Segments.Insert(kexec.NewSegment(d, r))
   355  	return r.Start, nil
   356  }
   357  
   358  // addInfo collects and adds mutiboot (without L!) into the segments.
   359  //
   360  // The format is described in the structs in
   361  // https://github.com/vmware/esx-boot/blob/master/include/mutiboot.h
   362  //
   363  // It includes a memory map and a list of modules.
   364  func (*mutibootHeader) addInfo(m *multiboot) (addr uintptr, err error) {
   365  	var mi mutibootInfo
   366  
   367  	mi.elems = append(mi.elems, m.memoryMap().elems()...)
   368  	mods, err := m.loadModules()
   369  	if err != nil {
   370  		return 0, err
   371  	}
   372  	mi.elems = append(mi.elems, mods.elems()...)
   373  
   374  	// This marshals the mutiboot info with cmdline = 0. We're gonna append
   375  	// the cmdline, so we must know the size of the marshaled stuff first
   376  	// to be able to point to it.
   377  	//
   378  	// TODO: find a better place to put the cmdline so we don't do this
   379  	// bullshit.
   380  	b := mi.marshal()
   381  
   382  	// string + null-terminator
   383  	cmdlineLen := len(m.cmdLine) + 1
   384  
   385  	memRange, err := m.mem.FindSpace(uint(len(b) + cmdlineLen))
   386  	if err != nil {
   387  		return 0, err
   388  	}
   389  	mi.cmdline = uint64(memRange.Start + uintptr(len(b)))
   390  
   391  	// Re-marshal, now that the cmdline is set.
   392  	b = mi.marshal()
   393  	b = append(b, []byte(m.cmdLine)...)
   394  	b = append(b, 0)
   395  	m.mem.Segments.Insert(kexec.NewSegment(b, memRange))
   396  	return memRange.Start, nil
   397  }
   398  
   399  func (m multiboot) memoryMap() memoryMaps {
   400  	var ret memoryMaps
   401  	for _, r := range m.mem.Phys {
   402  		typ, ok := rangeTypes[r.Type]
   403  		if !ok {
   404  			typ = rangeTypes[kexec.RangeDefault]
   405  		}
   406  		v := MemoryMap{
   407  			// Size is really used for skipping to the next pair.
   408  			Size:     uint32(sizeofMemoryMap) - 4,
   409  			BaseAddr: uint64(r.Start),
   410  			Length:   uint64(r.Size),
   411  			Type:     typ,
   412  		}
   413  		ret = append(ret, v)
   414  	}
   415  	log.Printf("Memory map: %v", ret)
   416  	return ret
   417  }
   418  
   419  // addMmap adds a multiboot-marshaled memory map in memory.
   420  func (m *multiboot) addMmap() (addr uintptr, size uint, err error) {
   421  	mmap := m.memoryMap()
   422  	d, err := mmap.marshal()
   423  	if err != nil {
   424  		return 0, 0, err
   425  	}
   426  	r, err := m.mem.AddKexecSegment(d)
   427  	if err != nil {
   428  		return 0, 0, err
   429  	}
   430  	return r.Start, uint(len(mmap)) * sizeofMemoryMap, nil
   431  }
   432  
   433  func (m multiboot) memoryBoundaries() (lower, upper uint32) {
   434  	const M1 = 1048576
   435  	const K640 = 640 * 1024
   436  	for _, r := range m.mem.Phys {
   437  		if r.Type != kexec.RangeRAM {
   438  			continue
   439  		}
   440  		end := uint32(r.Start) + uint32(r.Size)
   441  		// Lower memory starts at address 0, and upper memory starts at address 1 megabyte.
   442  		// The maximum possible value for lower memory is 640 kilobytes.
   443  		// The value returned for upper memory is maximally the address of the first upper memory hole minus 1 megabyte.
   444  		// It is not guaranteed to be this value.
   445  		if r.Start <= K640 && end > lower {
   446  			lower = end
   447  		}
   448  		if r.Start <= M1 && end > upper+M1 {
   449  			upper = end - M1
   450  		}
   451  	}
   452  	return
   453  }
   454  
   455  func min(a, b uint32) uint32 {
   456  	if a < b {
   457  		return a
   458  	}
   459  	return b
   460  }
   461  
   462  func (h *header) newMultibootInfo(m *multiboot) (*infoWrapper, error) {
   463  	mmapAddr, mmapSize, err := m.addMmap()
   464  	if err != nil {
   465  		return nil, err
   466  	}
   467  	var inf info
   468  	if h.Flags&flagHeaderMemoryInfo != 0 {
   469  		lower, upper := m.memoryBoundaries()
   470  		inf = info{
   471  			Flags:      flagInfoMemMap | flagInfoMemory,
   472  			MemLower:   min(uint32(lower>>10), 0xFFFFFFFF),
   473  			MemUpper:   min(uint32(upper>>10), 0xFFFFFFFF),
   474  			MmapLength: uint32(mmapSize),
   475  			MmapAddr:   uint32(mmapAddr),
   476  		}
   477  	}
   478  
   479  	if len(m.modules) > 0 {
   480  		modAddr, err := m.addMultibootModules()
   481  		if err != nil {
   482  			return nil, err
   483  		}
   484  		inf.Flags |= flagInfoMods
   485  		inf.ModsAddr = uint32(modAddr)
   486  		inf.ModsCount = uint32(len(m.modules))
   487  	}
   488  
   489  	return &infoWrapper{
   490  		info:           inf,
   491  		CmdLine:        m.cmdLine,
   492  		BootLoaderName: m.bootloader,
   493  	}, nil
   494  }
   495  
   496  func (m *multiboot) addTrampoline(magic, infoAddr, kernelEntry uintptr) (entry uintptr, err error) {
   497  	// Trampoline setups the machine registers to desired state
   498  	// and executes the loaded kernel.
   499  	d, err := trampoline.Setup(m.trampoline, magic, infoAddr, kernelEntry)
   500  	if err != nil {
   501  		return 0, err
   502  	}
   503  
   504  	r, err := m.mem.AddKexecSegment(d)
   505  	if err != nil {
   506  		return 0, err
   507  	}
   508  	return r.Start, nil
   509  }