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