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 }