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