github.com/u-root/u-root@v7.0.1-0.20200915234505-ad7babab0a8e+incompatible/pkg/boot/multiboot/module.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
     6  
     7  import (
     8  	"bytes"
     9  	"encoding/binary"
    10  	"fmt"
    11  	"io"
    12  	"log"
    13  	"os"
    14  
    15  	"github.com/u-root/u-root/pkg/ubinary"
    16  	"github.com/u-root/u-root/pkg/uio"
    17  )
    18  
    19  // A module represents a module to be loaded along with the kernel.
    20  type module struct {
    21  	// Start is the inclusive start of the Module memory location
    22  	Start uint32
    23  
    24  	// End is the exclusive end of the Module memory location.
    25  	End uint32
    26  
    27  	// Cmdline is a pointer to a null-terminated ASCII string.
    28  	Cmdline uint32
    29  
    30  	// Reserved is always zero.
    31  	Reserved uint32
    32  }
    33  
    34  type modules []module
    35  
    36  func (m *multiboot) loadModules() (modules, error) {
    37  	loaded, data, err := loadModules(m.modules)
    38  	if err != nil {
    39  		return nil, err
    40  	}
    41  
    42  	cmdlineRange, err := m.mem.AddKexecSegment(data)
    43  	if err != nil {
    44  		return nil, err
    45  	}
    46  
    47  	loaded.fix(uint32(cmdlineRange.Start))
    48  	m.loadedModules = loaded
    49  
    50  	for i, mod := range loaded {
    51  		log.Printf("Added module %s at [%#x, %#x)", m.modules[i].Name(), mod.Start, mod.End)
    52  	}
    53  
    54  	return loaded, nil
    55  }
    56  
    57  func (m *multiboot) addMultibootModules() (uintptr, error) {
    58  	loaded, err := m.loadModules()
    59  	if err != nil {
    60  		return 0, err
    61  	}
    62  	b, err := loaded.marshal()
    63  	if err != nil {
    64  		return 0, err
    65  	}
    66  	modRange, err := m.mem.AddKexecSegment(b)
    67  	if err != nil {
    68  		return 0, err
    69  	}
    70  	return modRange.Start, nil
    71  }
    72  
    73  // loadModules loads module files.
    74  // Returns loaded modules description and buffer storing loaded modules.
    75  // Memory layout of the loaded modules is following:
    76  //			cmdLine_1
    77  //			cmdLine_2
    78  //			...
    79  //			cmdLine_n
    80  //			<padding>
    81  //			modules_1
    82  //			<padding>
    83  //			modules_2
    84  //			...
    85  //			<padding>
    86  //			modules_n
    87  //
    88  // <padding> aligns the start of each module to a page beginning.
    89  func loadModules(rmods []Module) (loaded modules, data []byte, err error) {
    90  	loaded = make(modules, len(rmods))
    91  	var buf bytes.Buffer
    92  
    93  	for i, rmod := range rmods {
    94  		if err := loaded[i].setCmdline(&buf, rmod.Cmdline); err != nil {
    95  			return nil, nil, err
    96  		}
    97  	}
    98  
    99  	for i, rmod := range rmods {
   100  		if err := loaded[i].loadModule(&buf, rmod.Module); err != nil {
   101  			return nil, nil, fmt.Errorf("error adding module %v: %v", rmod.Name(), err)
   102  		}
   103  	}
   104  	return loaded, buf.Bytes(), nil
   105  }
   106  
   107  func pageAlign(val uint32) uint32 {
   108  	mask := uint32(os.Getpagesize() - 1)
   109  	return (val + mask) &^ mask
   110  }
   111  
   112  // pageAlignBuf pads buf to a page boundary.
   113  func pageAlignBuf(buf *bytes.Buffer) error {
   114  	mask := (os.Getpagesize() - 1)
   115  	size := (buf.Len() + mask) &^ mask
   116  	_, err := buf.Write(bytes.Repeat([]byte{0}, size-buf.Len()))
   117  	return err
   118  }
   119  
   120  func (m *module) loadModule(buf *bytes.Buffer, r io.ReaderAt) error {
   121  	// place start of each module to a beginning of a page.
   122  	if err := pageAlignBuf(buf); err != nil {
   123  		return err
   124  	}
   125  
   126  	m.Start = uint32(buf.Len())
   127  
   128  	if _, err := io.Copy(buf, uio.Reader(r)); err != nil {
   129  		return err
   130  	}
   131  
   132  	m.End = uint32(buf.Len())
   133  	return nil
   134  }
   135  
   136  func (m *module) setCmdline(buf *bytes.Buffer, cmdLine string) error {
   137  	m.Cmdline = uint32(buf.Len())
   138  	if _, err := buf.WriteString(cmdLine); err != nil {
   139  		return err
   140  	}
   141  	return buf.WriteByte(0)
   142  }
   143  
   144  // fix fixes pointers converting relative values to absolute values.
   145  func (m modules) fix(base uint32) {
   146  	for i := range m {
   147  		m[i].Start += base
   148  		m[i].End += base
   149  		m[i].Cmdline += base
   150  	}
   151  }
   152  
   153  // marshal writes out the module list in multiboot info format, as described in
   154  // https://www.gnu.org/software/grub/manual/multiboot/multiboot.html#Boot-information-format
   155  func (m modules) marshal() ([]byte, error) {
   156  	buf := bytes.Buffer{}
   157  	err := binary.Write(&buf, ubinary.NativeEndian, m)
   158  	return buf.Bytes(), err
   159  }
   160  
   161  // elems adds mutiboot info elements describing where to find each module and
   162  // its cmdline.
   163  func (m modules) elems() []elem {
   164  	var e []elem
   165  	for _, mm := range m {
   166  		e = append(e, &mutibootModule{
   167  			cmdline:    uint64(mm.Cmdline),
   168  			moduleSize: uint64(mm.End - mm.Start),
   169  			ranges: []mutibootModuleRange{
   170  				{
   171  					startPageNum: uint64(mm.Start / uint32(os.Getpagesize())),
   172  					numPages:     pageAlign(mm.End-mm.Start) / uint32(os.Getpagesize()),
   173  				},
   174  			},
   175  		})
   176  	}
   177  	return e
   178  }