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