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 }