github.com/u-root/u-root@v7.0.1-0.20200915234505-ad7babab0a8e+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 "io" 13 "reflect" 14 "unsafe" 15 16 "github.com/u-root/u-root/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 func start() 26 func end() 27 func info() 28 func magic() 29 func entry() 30 31 // funcPC gives the program counter of the given function. 32 // 33 //go:linkname funcPC runtime.funcPC 34 func funcPC(f interface{}) uintptr 35 36 // Setup scans file for trampoline code and sets 37 // values for multiboot info address and kernel entry point. 38 func Setup(path string, magic, infoAddr, entryPoint uintptr) ([]byte, error) { 39 trampolineStart, d, err := extract(path) 40 if err != nil { 41 return nil, err 42 } 43 return patch(trampolineStart, d, magic, infoAddr, entryPoint) 44 } 45 46 // extract extracts trampoline segment from file. 47 // trampoline segment begins after "u-root-trampoline-begin" byte sequence + padding, 48 // and ends at "u-root-trampoline-end" byte sequence. 49 func extract(path string) (uintptr, []byte, error) { 50 // TODO(https://github.com/golang/go/issues/35055): deal with 51 // potentially non-contiguous trampoline. Rather than locating start 52 // and end, we should locate start,boot,farjump{32,64},gdt,info,entry 53 // individually and return one potentially really big trampoline slice. 54 tbegin := funcPC(start) 55 tend := funcPC(end) 56 if tend <= tbegin { 57 return 0, nil, io.ErrUnexpectedEOF 58 } 59 tramp := ptrToSlice(tbegin, int(tend-tbegin)) 60 61 // tramp is read-only executable memory. So we gotta copy it to a 62 // slice. Gotta modify it later. 63 cp := append([]byte(nil), tramp...) 64 return tbegin, cp, nil 65 } 66 67 func ptrToSlice(ptr uintptr, size int) []byte { 68 var data []byte 69 70 sh := (*reflect.SliceHeader)(unsafe.Pointer(&data)) 71 sh.Data = ptr 72 sh.Len = size 73 sh.Cap = size 74 75 return data 76 } 77 78 // patch patches the trampoline code to store value for multiboot info address, 79 // entry point, and boot magic value. 80 // 81 // All 3 are determined by pretending they are functions, and finding their PC 82 // within our own address space. 83 func patch(trampolineStart uintptr, trampoline []byte, magicVal, infoAddr, entryPoint uintptr) ([]byte, error) { 84 replace := func(start uintptr, d []byte, f func(), val uint32) error { 85 buf := make([]byte, 4) 86 ubinary.NativeEndian.PutUint32(buf, val) 87 88 offset := funcPC(f) - start 89 if int(offset+4) > len(d) { 90 return io.ErrUnexpectedEOF 91 } 92 copy(d[int(offset):], buf) 93 return nil 94 } 95 96 if err := replace(trampolineStart, trampoline, info, uint32(infoAddr)); err != nil { 97 return nil, err 98 } 99 if err := replace(trampolineStart, trampoline, entry, uint32(entryPoint)); err != nil { 100 return nil, err 101 } 102 if err := replace(trampolineStart, trampoline, magic, uint32(magicVal)); err != nil { 103 return nil, err 104 } 105 return trampoline, nil 106 }