github.com/shaardie/u-root@v4.0.1-0.20190127173353-f24a1c26aa2e+incompatible/pkg/multiboot/internal/trampoline/trampoline_linux_amd64.go (about)

     1  // Copyright 2018 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  // Trampoline sets machine to a specific state defined
     6  // by multiboot v1 spec and boots the final kernel.
     7  // https://www.gnu.org/software/grub/manual/multiboot/multiboot.html#Machine-state.
     8  package trampoline
     9  
    10  import (
    11  	"bytes"
    12  	"fmt"
    13  	"io"
    14  	"io/ioutil"
    15  
    16  	"github.com/u-root/u-root/pkg/ubinary"
    17  )
    18  
    19  const (
    20  	trampolineBeginLow  = "u-root-trampoline"
    21  	trampolineBeginHigh = "-begin"
    22  	trampolineEnd       = "u-root-trampoline-end"
    23  
    24  	trampolineEntry = "u-root-entry-long"
    25  	trampolineInfo  = "u-root-info-long"
    26  )
    27  
    28  var trampolineBegin []byte
    29  
    30  var alwaysFalse bool
    31  
    32  func start()
    33  
    34  func init() {
    35  	// Cannot use "u-root-trampoline-begin" directly, because then there
    36  	// would be two locations of "u-root-trampoline-begin" sequence.
    37  	trampolineBegin = append([]byte(trampolineBeginLow), []byte(trampolineBeginHigh)...)
    38  
    39  	if alwaysFalse {
    40  		// Can never happen, but still need it for linker to include assembly to a binary.
    41  		start()
    42  	}
    43  
    44  	// Hope Go compiler will never get smart enough to optimize this function.
    45  }
    46  
    47  // alignUp aligns x to a 0x10 bytes boundary.
    48  // go compiler aligns TEXT parts at 0x10 bytes boundary.
    49  func alignUp(x int) int {
    50  	const mask = 0x10 - 1
    51  	return (x + mask) & ^mask
    52  }
    53  
    54  // Setup scans file for trampoline code and sets
    55  // values for multiboot info address and kernel entry point.
    56  func Setup(path string, infoAddr, entryPoint uintptr) ([]byte, error) {
    57  	d, err := extract(path)
    58  	if err != nil {
    59  		return nil, err
    60  	}
    61  	return patch(d, infoAddr, entryPoint)
    62  }
    63  
    64  // extract extracts trampoline segment from file.
    65  // trampoline segment begins after "u-root-trampoline-begin" byte sequence + padding,
    66  // and ends at "u-root-trampoline-end" byte sequence.
    67  func extract(path string) ([]byte, error) {
    68  	d, err := ioutil.ReadFile(path)
    69  	if err != nil {
    70  		return nil, fmt.Errorf("cannot read trampoline file: %v", err)
    71  	}
    72  
    73  	begin := bytes.Index(d, trampolineBegin)
    74  	if begin == -1 {
    75  		return nil, io.ErrUnexpectedEOF
    76  	}
    77  	if begin = alignUp(begin + len(trampolineBegin)); begin > len(d) {
    78  		return nil, io.ErrUnexpectedEOF
    79  	}
    80  	if ind := bytes.Index(d[begin:], trampolineBegin); ind != -1 {
    81  		return nil, fmt.Errorf("multiple definition of %q label", trampolineBegin)
    82  	}
    83  
    84  	end := bytes.Index(d[begin:], []byte(trampolineEnd))
    85  	if end == -1 {
    86  		return nil, io.ErrUnexpectedEOF
    87  	}
    88  	return d[begin : begin+end], nil
    89  }
    90  
    91  // patch patches the trampoline code to store value for multiboot info address
    92  // after "u-root-header-long" byte sequence + padding and value
    93  // for kernel entry point, after "u-root-entry-long" byte sequence + padding.
    94  func patch(trampoline []byte, infoAddr, entryPoint uintptr) ([]byte, error) {
    95  	replace := func(d, label []byte, val uint32) error {
    96  		buf := make([]byte, 4)
    97  		ubinary.NativeEndian.PutUint32(buf, val)
    98  
    99  		ind := bytes.Index(d, label)
   100  		if ind == -1 {
   101  			return fmt.Errorf("%q label not found in file", label)
   102  		}
   103  		ind = alignUp(ind + len(label))
   104  		if len(d) < ind+len(buf) {
   105  			return io.ErrUnexpectedEOF
   106  		}
   107  		copy(d[ind:], buf)
   108  		return nil
   109  	}
   110  
   111  	if err := replace(trampoline, []byte(trampolineInfo), uint32(infoAddr)); err != nil {
   112  		return nil, err
   113  	}
   114  	if err := replace(trampoline, []byte(trampolineEntry), uint32(entryPoint)); err != nil {
   115  		return nil, err
   116  	}
   117  	return trampoline, nil
   118  }