github.com/mvdan/u-root-coreutils@v0.0.0-20230122170626-c2eef2898555/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  	"io"
    13  	"reflect"
    14  	"unsafe"
    15  
    16  	"github.com/mvdan/u-root-coreutils/pkg/ubinary"
    17  )
    18  
    19  const (
    20  	trampolineEntry = "u-root-entry-long"
    21  	trampolineInfo  = "u-root-info-long"
    22  	trampolineMagic = "u-root-mb-magic"
    23  )
    24  
    25  // In Go 1.17+, Go references to assembly functions resolve to an ABIInternal
    26  // wrapper function rather than the function itself. We must reference from
    27  // assembly to get the ABI0 (i.e., primary) address (this way of doing things
    28  // will work for both 1.17+ and versions prior to 1.17). Note for posterity:
    29  // runtime.funcPC (used previously) is going away in 1.18+.
    30  //
    31  // Each of the functions below of form 'addrOfXXX' return the starting PC
    32  // of the assembl routine XXX.
    33  
    34  func addrOfStart() uintptr
    35  func addrOfEnd() uintptr
    36  func addrOfInfo() uintptr
    37  func addrOfMagic() uintptr
    38  func addrOfEntry() uintptr
    39  
    40  // Setup scans file for trampoline code and sets
    41  // values for multiboot info address and kernel entry point.
    42  func Setup(path string, magic, infoAddr, entryPoint uintptr) ([]byte, error) {
    43  	trampolineStart, d, err := extract(path)
    44  	if err != nil {
    45  		return nil, err
    46  	}
    47  	return patch(trampolineStart, d, magic, infoAddr, entryPoint)
    48  }
    49  
    50  // extract extracts trampoline segment from file.
    51  // trampoline segment begins after "u-root-trampoline-begin" byte sequence + padding,
    52  // and ends at "u-root-trampoline-end" byte sequence.
    53  func extract(path string) (uintptr, []byte, error) {
    54  	// TODO(https://github.com/golang/go/issues/35055): deal with
    55  	// potentially non-contiguous trampoline. Rather than locating start
    56  	// and end, we should locate start,boot,farjump{32,64},gdt,info,entry
    57  	// individually and return one potentially really big trampoline slice.
    58  	tbegin := addrOfStart()
    59  	tend := addrOfEnd()
    60  	if tend <= tbegin {
    61  		return 0, nil, io.ErrUnexpectedEOF
    62  	}
    63  	tramp := ptrToSlice(tbegin, int(tend-tbegin))
    64  
    65  	// tramp is read-only executable memory. So we gotta copy it to a
    66  	// slice. Gotta modify it later.
    67  	cp := append([]byte(nil), tramp...)
    68  	return tbegin, cp, nil
    69  }
    70  
    71  func ptrToSlice(ptr uintptr, size int) []byte {
    72  	var data []byte
    73  
    74  	sh := (*reflect.SliceHeader)(unsafe.Pointer(&data))
    75  	sh.Data = ptr
    76  	sh.Len = size
    77  	sh.Cap = size
    78  
    79  	return data
    80  }
    81  
    82  // patch patches the trampoline code to store value for multiboot info address,
    83  // entry point, and boot magic value.
    84  //
    85  // All 3 are determined by pretending they are functions, and finding their PC
    86  // within our own address space.
    87  func patch(trampolineStart uintptr, trampoline []byte, magicVal, infoAddr, entryPoint uintptr) ([]byte, error) {
    88  	replace := func(start uintptr, d []byte, fPC uintptr, val uint32) error {
    89  		buf := make([]byte, 4)
    90  		ubinary.NativeEndian.PutUint32(buf, val)
    91  
    92  		offset := fPC - start
    93  		if int(offset+4) > len(d) {
    94  			return io.ErrUnexpectedEOF
    95  		}
    96  		copy(d[int(offset):], buf)
    97  		return nil
    98  	}
    99  
   100  	if err := replace(trampolineStart, trampoline, addrOfInfo(), uint32(infoAddr)); err != nil {
   101  		return nil, err
   102  	}
   103  	if err := replace(trampolineStart, trampoline, addrOfEntry(), uint32(entryPoint)); err != nil {
   104  		return nil, err
   105  	}
   106  	if err := replace(trampolineStart, trampoline, addrOfMagic(), uint32(magicVal)); err != nil {
   107  		return nil, err
   108  	}
   109  	return trampoline, nil
   110  }