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 }