github.com/shaardie/u-root@v4.0.1-0.20190127173353-f24a1c26aa2e+incompatible/pkg/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 basic primitives
     6  // to load multiboot kernels as defined in
     7  // https://www.gnu.org/software/grub/manual/multiboot/multiboot.html.
     8  package multiboot
     9  
    10  import (
    11  	"bytes"
    12  	"debug/elf"
    13  	"encoding/binary"
    14  	"fmt"
    15  	"io"
    16  	"log"
    17  
    18  	"github.com/u-root/u-root/pkg/kexec"
    19  	"github.com/u-root/u-root/pkg/multiboot/internal/trampoline"
    20  	"github.com/u-root/u-root/pkg/ubinary"
    21  )
    22  
    23  const bootloader = "u-root kexec"
    24  
    25  // Multiboot defines parameters for working with multiboot kernels.
    26  type Multiboot struct {
    27  	mem kexec.Memory
    28  
    29  	file    string
    30  	modules []string
    31  
    32  	cmdLine    string
    33  	bootloader string
    34  
    35  	// trampoline is a path to an executable blob, which contains a trampoline segment.
    36  	// Trampoline sets machine to a specific state defined by multiboot v1 spec.
    37  	// https://www.gnu.org/software/grub/manual/multiboot/multiboot.html#Machine-state.
    38  	trampoline string
    39  
    40  	header Header
    41  
    42  	// infoAddr is a pointer to multiboot info.
    43  	infoAddr uintptr
    44  	// kernelEntry is a pointer to entry point of kernel.
    45  	kernelEntry uintptr
    46  	// EntryPoint is a pointer to trampoline.
    47  	EntryPoint uintptr
    48  
    49  	info          Info
    50  	loadedModules []Module
    51  }
    52  
    53  var rangeTypes = map[kexec.RangeType]uint32{
    54  	kexec.RangeRAM:     1,
    55  	kexec.RangeDefault: 2,
    56  	kexec.RangeNVACPI:  3,
    57  	kexec.RangeACPI:    3,
    58  	kexec.RangeNVS:     4,
    59  }
    60  
    61  var sizeofMemoryMap = uint(binary.Size(MemoryMap{}))
    62  
    63  // MemoryMap represents a reserved range of memory passed via the Multiboot Info header.
    64  type MemoryMap struct {
    65  	// Size is the size of the associated structure in bytes.
    66  	Size uint32
    67  	// BaseAddr is the starting address.
    68  	BaseAddr uint64
    69  	// Length is the size of the memory region in bytes.
    70  	Length uint64
    71  	// Type is the variety of address range represented.
    72  	Type uint32
    73  }
    74  
    75  type memoryMaps []MemoryMap
    76  
    77  // Probe checks if file is multiboot v1 kernel.
    78  func Probe(file string) error {
    79  	b, err := readFile(file)
    80  	if err != nil {
    81  		return err
    82  	}
    83  	kernel := &kernelReader{buf: b}
    84  	_, err = parseHeader(kernel)
    85  	return err
    86  }
    87  
    88  // New returns a new Multiboot instance.
    89  func New(file, cmdLine, trampoline string, modules []string) *Multiboot {
    90  	return &Multiboot{
    91  		file:       file,
    92  		modules:    modules,
    93  		cmdLine:    cmdLine,
    94  		trampoline: trampoline,
    95  		bootloader: bootloader,
    96  		mem:        kexec.Memory{},
    97  	}
    98  }
    99  
   100  // Load loads and parses multiboot information from m.file.
   101  func (m *Multiboot) Load(debug bool) error {
   102  	log.Printf("Parsing file %v", m.file)
   103  	b, err := readFile(m.file)
   104  	if err != nil {
   105  		return err
   106  	}
   107  	kernel := kernelReader{buf: b}
   108  	log.Println("Parsing Multiboot Header")
   109  	if m.header, err = parseHeader(&kernel); err != nil {
   110  		return fmt.Errorf("Error parsing headers: %v", err)
   111  	}
   112  
   113  	log.Printf("Getting kernel entry point")
   114  	if m.kernelEntry, err = getEntryPoint(kernel); err != nil {
   115  		return fmt.Errorf("Error getting kernel entry point: %v", err)
   116  	}
   117  
   118  	log.Printf("Parsing ELF segments")
   119  	if err := m.mem.LoadElfSegments(kernel); err != nil {
   120  		return fmt.Errorf("Error loading ELF segments: %v", err)
   121  	}
   122  
   123  	log.Printf("Parsing memory map")
   124  	if err := m.mem.ParseMemoryMap(); err != nil {
   125  		return fmt.Errorf("Error parsing memory map: %v", err)
   126  	}
   127  
   128  	log.Printf("Preparing Multiboot Info")
   129  	if m.infoAddr, err = m.addInfo(); err != nil {
   130  		return fmt.Errorf("Error preparing Multiboot Info: %v", err)
   131  	}
   132  
   133  	log.Printf("Adding trampoline")
   134  	if m.EntryPoint, err = m.addTrampoline(); err != nil {
   135  		return fmt.Errorf("Error adding trampoline: %v", err)
   136  	}
   137  
   138  	if debug {
   139  		info, err := m.Description()
   140  		if err != nil {
   141  			log.Printf("%v cannot create debug info: %v", DebugPrefix, err)
   142  		}
   143  		log.Printf("%v %v", DebugPrefix, info)
   144  	}
   145  
   146  	return nil
   147  }
   148  
   149  func getEntryPoint(r io.ReaderAt) (uintptr, error) {
   150  	f, err := elf.NewFile(r)
   151  	if err != nil {
   152  		return 0, err
   153  	}
   154  	return uintptr(f.Entry), err
   155  }
   156  
   157  func (m *Multiboot) addInfo() (addr uintptr, err error) {
   158  	iw, err := m.newMultibootInfo()
   159  	if err != nil {
   160  		return 0, err
   161  	}
   162  	infoSize, err := iw.size()
   163  	if err != nil {
   164  		return 0, err
   165  	}
   166  
   167  	addr, err = m.mem.FindSpace(infoSize)
   168  	if err != nil {
   169  		return 0, err
   170  	}
   171  
   172  	d, err := iw.marshal(addr)
   173  	if err != nil {
   174  		return 0, err
   175  	}
   176  	m.info = iw.Info
   177  
   178  	addr, err = m.mem.AddKexecSegment(d)
   179  	if err != nil {
   180  		return 0, err
   181  	}
   182  	return addr, nil
   183  }
   184  
   185  func (m Multiboot) memoryMap() memoryMaps {
   186  	var ret memoryMaps
   187  	for _, r := range m.mem.Phys {
   188  		typ, ok := rangeTypes[r.Type]
   189  		if !ok {
   190  			typ = rangeTypes[kexec.RangeDefault]
   191  		}
   192  		v := MemoryMap{
   193  			// Size is really used for skipping to the next pair.
   194  			Size:     uint32(sizeofMemoryMap) - 4,
   195  			BaseAddr: uint64(r.Start),
   196  			Length:   uint64(r.Size) + 1,
   197  			Type:     typ,
   198  		}
   199  		ret = append(ret, v)
   200  	}
   201  	return ret
   202  }
   203  
   204  func (m *Multiboot) addMmap() (addr uintptr, size uint, err error) {
   205  	mmap := m.memoryMap()
   206  	d, err := mmap.marshal()
   207  	if err != nil {
   208  		return 0, 0, err
   209  	}
   210  	addr, err = m.mem.AddKexecSegment(d)
   211  	if err != nil {
   212  		return 0, 0, err
   213  	}
   214  	return addr, uint(len(mmap)) * sizeofMemoryMap, nil
   215  }
   216  
   217  func (m Multiboot) memoryBoundaries() (lower, upper uint32) {
   218  	const M1 = 1048576
   219  	const K640 = 640 * 1024
   220  	for _, r := range m.mem.Phys {
   221  		if r.Type != kexec.RangeRAM {
   222  			continue
   223  		}
   224  		end := uint32(r.Start) + uint32(r.Size)
   225  		// Lower memory starts at address 0, and upper memory starts at address 1 megabyte.
   226  		// The maximum possible value for lower memory is 640 kilobytes.
   227  		// The value returned for upper memory is maximally the address of the first upper memory hole minus 1 megabyte.
   228  		// It is not guaranteed to be this value.
   229  		if r.Start <= K640 && end > lower {
   230  			lower = end
   231  		}
   232  		if r.Start <= M1 && end > upper+M1 {
   233  			upper = end - M1
   234  		}
   235  	}
   236  	return
   237  }
   238  
   239  func min(a, b uint32) uint32 {
   240  	if a < b {
   241  		return a
   242  	}
   243  	return b
   244  }
   245  
   246  func (m *Multiboot) newMultibootInfo() (*infoWrapper, error) {
   247  	mmapAddr, mmapSize, err := m.addMmap()
   248  	if err != nil {
   249  		return nil, err
   250  	}
   251  	var info Info
   252  	if m.header.Flags&flagHeaderMemoryInfo != 0 {
   253  		lower, upper := m.memoryBoundaries()
   254  		info = Info{
   255  			Flags:      flagInfoMemMap | flagInfoMemory,
   256  			MemLower:   min(uint32(lower>>10), 0xFFFFFFFF),
   257  			MemUpper:   min(uint32(upper>>10), 0xFFFFFFFF),
   258  			MmapLength: uint32(mmapSize),
   259  			MmapAddr:   uint32(mmapAddr),
   260  		}
   261  	}
   262  
   263  	if len(m.modules) > 0 {
   264  		modAddr, err := m.addModules()
   265  		if err != nil {
   266  			return nil, err
   267  		}
   268  		info.Flags |= flagInfoMods
   269  		info.ModsAddr = uint32(modAddr)
   270  		info.ModsCount = uint32(len(m.modules))
   271  	}
   272  
   273  	info.CmdLine = sizeofInfo
   274  	info.BootLoaderName = sizeofInfo + uint32(len(m.cmdLine)) + 1
   275  	info.Flags |= flagInfoCmdLine | flagInfoBootLoaderName
   276  	return &infoWrapper{
   277  		Info:           info,
   278  		CmdLine:        m.cmdLine,
   279  		BootLoaderName: m.bootloader,
   280  	}, nil
   281  }
   282  
   283  // Segments returns kexec.Segments, where all the multiboot related
   284  // information is stored.
   285  func (m Multiboot) Segments() []kexec.Segment {
   286  	return m.mem.Segments
   287  }
   288  
   289  // marshal writes out the exact bytes expected by the multiboot info header
   290  // specified in
   291  // https://www.gnu.org/software/grub/manual/multiboot/multiboot.html#Boot-information-format.
   292  func (m memoryMaps) marshal() ([]byte, error) {
   293  	buf := bytes.Buffer{}
   294  	err := binary.Write(&buf, ubinary.NativeEndian, m)
   295  	return buf.Bytes(), err
   296  }
   297  
   298  func (m *Multiboot) addTrampoline() (entry uintptr, err error) {
   299  	// Trampoline setups the machine registers to desired state
   300  	// and executes the loaded kernel.
   301  	d, err := trampoline.Setup(m.trampoline, m.infoAddr, m.kernelEntry)
   302  	if err != nil {
   303  		return 0, err
   304  	}
   305  
   306  	addr, err := m.mem.AddKexecSegment(d)
   307  	if err != nil {
   308  		return 0, err
   309  	}
   310  
   311  	return addr, nil
   312  }