github.com/system-transparency/u-root@v6.0.1-0.20190919065413-ed07a650de4c+incompatible/pkg/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  // Module defines modules to be loaded along with the kernel.
     6  // https://www.gnu.org/software/grub/manual/multiboot/multiboot.html#Boot-information-format.
     7  
     8  package multiboot
     9  
    10  import (
    11  	"bytes"
    12  	"encoding/binary"
    13  	"fmt"
    14  	"log"
    15  	"os"
    16  	"strings"
    17  
    18  	"github.com/u-root/u-root/pkg/ubinary"
    19  )
    20  
    21  // A module represents a module to be loaded along with the kernel.
    22  type module struct {
    23  	// Start is the inclusive start of the Module memory location
    24  	Start uint32
    25  
    26  	// End is the exclusive end of the Module memory location.
    27  	End uint32
    28  
    29  	// CmdLine is a pointer to a null-terminated ASCII string.
    30  	CmdLine uint32
    31  
    32  	// Reserved is always zero.
    33  	Reserved uint32
    34  }
    35  
    36  type modules []module
    37  
    38  func (m *multiboot) addModules() (uintptr, error) {
    39  	loaded, data, err := loadModules(m.modules)
    40  	if err != nil {
    41  		return 0, err
    42  	}
    43  
    44  	cmdlineRange, err := m.mem.AddKexecSegment(data)
    45  	if err != nil {
    46  		return 0, err
    47  	}
    48  
    49  	loaded.fix(uint32(cmdlineRange.Start))
    50  
    51  	m.loadedModules = loaded
    52  
    53  	b, err := loaded.marshal()
    54  	if err != nil {
    55  		return 0, err
    56  	}
    57  	modRange, err := m.mem.AddKexecSegment(b)
    58  	if err != nil {
    59  		return 0, err
    60  	}
    61  	return modRange.Start, nil
    62  }
    63  
    64  // loadModules loads module files.
    65  // Returns loaded modules description and buffer storing loaded modules.
    66  // Memory layout of the loaded modules is following:
    67  //			cmdLine_1
    68  //			cmdLine_2
    69  //			...
    70  //			cmdLine_n
    71  //			<padding>
    72  //			modules_1
    73  //			<padding>
    74  //			modules_2
    75  //			...
    76  //			<padding>
    77  //			modules_n
    78  //
    79  // <padding> aligns the start of each module to a page beginning.
    80  func loadModules(cmds []string) (loaded modules, data []byte, err error) {
    81  	loaded = make(modules, len(cmds))
    82  	buf := bytes.Buffer{}
    83  
    84  	for i, cmd := range cmds {
    85  		if err := loaded[i].setCmdLine(&buf, cmd); err != nil {
    86  			return nil, nil, err
    87  		}
    88  	}
    89  
    90  	for i, cmd := range cmds {
    91  		name := strings.Fields(cmd)[0]
    92  		if err := loaded[i].loadModule(&buf, name); err != nil {
    93  			return nil, nil, fmt.Errorf("error adding module %v: %v", name, err)
    94  		}
    95  	}
    96  
    97  	return loaded, buf.Bytes(), nil
    98  }
    99  
   100  // alignUp pads buf to a page boundary.
   101  func alignUp(buf *bytes.Buffer) error {
   102  	mask := (os.Getpagesize() - 1)
   103  	size := (buf.Len() + mask) &^ mask
   104  	_, err := buf.Write(bytes.Repeat([]byte{0}, size-buf.Len()))
   105  	return err
   106  }
   107  
   108  func (m *module) loadModule(buf *bytes.Buffer, name string) error {
   109  	log.Printf("Adding module %v", name)
   110  
   111  	b, err := readFile(name)
   112  	if err != nil {
   113  		return err
   114  	}
   115  
   116  	// place start of each module to a beginning of a page.
   117  	if err := alignUp(buf); err != nil {
   118  		return err
   119  	}
   120  
   121  	m.Start = uint32(buf.Len())
   122  	if _, err := buf.Write(b); err != nil {
   123  		return err
   124  	}
   125  	m.End = uint32(buf.Len())
   126  
   127  	return nil
   128  }
   129  
   130  func (m *module) setCmdLine(buf *bytes.Buffer, cmdLine string) error {
   131  	m.CmdLine = uint32(buf.Len())
   132  	if _, err := buf.WriteString(cmdLine); err != nil {
   133  		return err
   134  	}
   135  	return buf.WriteByte(0)
   136  }
   137  
   138  // fix fixes pointers converting relative values to absolute values.
   139  func (m modules) fix(base uint32) {
   140  	for i := range m {
   141  		m[i].Start += base
   142  		m[i].End += base
   143  		m[i].CmdLine += base
   144  	}
   145  }
   146  
   147  // marshal writes out the exact bytes of modules to be loaded
   148  // along with the kernel.
   149  func (m modules) marshal() ([]byte, error) {
   150  	buf := bytes.Buffer{}
   151  	err := binary.Write(&buf, ubinary.NativeEndian, m)
   152  	return buf.Bytes(), err
   153  }