github.com/insomniacslk/u-root@v0.0.0-20200717035308-96b791510d76/pkg/boot/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  // Package trampoline sets machine to a specific state defined by multiboot v1
     6  // spec and jumps to the intended kernel.
     7  //
     8  // https://www.gnu.org/software/grub/manual/multiboot/multiboot.html#Machine-state.
     9  package trampoline
    10  
    11  import (
    12  	"bytes"
    13  	"fmt"
    14  	"io"
    15  	"reflect"
    16  	"unsafe"
    17  
    18  	"github.com/u-root/u-root/pkg/ubinary"
    19  )
    20  
    21  const (
    22  	trampolineEntry = "u-root-entry-long"
    23  	trampolineInfo  = "u-root-info-long"
    24  	trampolineMagic = "u-root-mb-magic"
    25  )
    26  
    27  func start()
    28  func end()
    29  
    30  // funcPC gives the program counter of the given function.
    31  //
    32  //go:linkname funcPC runtime.funcPC
    33  func funcPC(f interface{}) uintptr
    34  
    35  // alignUp aligns x to a 0x10 bytes boundary.
    36  // go compiler aligns TEXT parts at 0x10 bytes boundary.
    37  func alignUp(x int) int {
    38  	const mask = 0x10 - 1
    39  	return (x + mask) & ^mask
    40  }
    41  
    42  // Setup scans file for trampoline code and sets
    43  // values for multiboot info address and kernel entry point.
    44  func Setup(path string, magic, infoAddr, entryPoint uintptr) ([]byte, error) {
    45  	d, err := extract(path)
    46  	if err != nil {
    47  		return nil, err
    48  	}
    49  	return patch(d, magic, infoAddr, entryPoint)
    50  }
    51  
    52  // extract extracts trampoline segment from file.
    53  // trampoline segment begins after "u-root-trampoline-begin" byte sequence + padding,
    54  // and ends at "u-root-trampoline-end" byte sequence.
    55  func extract(path string) ([]byte, error) {
    56  	// TODO(https://github.com/golang/go/issues/35055): deal with
    57  	// potentially non-contiguous trampoline. Rather than locating start
    58  	// and end, we should locate start,boot,farjump{32,64},gdt,info,entry
    59  	// individually and return one potentially really big trampoline slice.
    60  	tbegin := funcPC(start)
    61  	tend := funcPC(end)
    62  	if tend <= tbegin {
    63  		return nil, io.ErrUnexpectedEOF
    64  	}
    65  	tramp := ptrToSlice(tbegin, int(tend-tbegin))
    66  
    67  	// tramp is read-only executable memory. So we gotta copy it to a
    68  	// slice. Gotta modify it later.
    69  	cp := append([]byte(nil), tramp...)
    70  	return cp, nil
    71  }
    72  
    73  func ptrToSlice(ptr uintptr, size int) []byte {
    74  	var data []byte
    75  
    76  	sh := (*reflect.SliceHeader)(unsafe.Pointer(&data))
    77  	sh.Data = ptr
    78  	sh.Len = size
    79  	sh.Cap = size
    80  
    81  	return data
    82  }
    83  
    84  // patch patches the trampoline code to store value for multiboot info address
    85  // after "u-root-header-long" byte sequence + padding and value
    86  // for kernel entry point, after "u-root-entry-long" byte sequence + padding.
    87  func patch(trampoline []byte, magic, infoAddr, entryPoint uintptr) ([]byte, error) {
    88  	replace := func(d, label []byte, val uint32) error {
    89  		buf := make([]byte, 4)
    90  		ubinary.NativeEndian.PutUint32(buf, val)
    91  
    92  		ind := bytes.Index(d, label)
    93  		if ind == -1 {
    94  			return fmt.Errorf("%q label not found in file", label)
    95  		}
    96  		ind = alignUp(ind + len(label))
    97  		if len(d) < ind+len(buf) {
    98  			return io.ErrUnexpectedEOF
    99  		}
   100  		copy(d[ind:], buf)
   101  		return nil
   102  	}
   103  
   104  	if err := replace(trampoline, []byte(trampolineInfo), uint32(infoAddr)); err != nil {
   105  		return nil, err
   106  	}
   107  	if err := replace(trampoline, []byte(trampolineEntry), uint32(entryPoint)); err != nil {
   108  		return nil, err
   109  	}
   110  	if err := replace(trampoline, []byte(trampolineMagic), uint32(magic)); err != nil {
   111  		return nil, err
   112  	}
   113  	return trampoline, nil
   114  }