github.com/shaardie/u-root@v4.0.1-0.20190127173353-f24a1c26aa2e+incompatible/pkg/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 // Trampoline sets machine to a specific state defined 6 // by multiboot v1 spec and boots the final kernel. 7 // https://www.gnu.org/software/grub/manual/multiboot/multiboot.html#Machine-state. 8 package trampoline 9 10 import ( 11 "bytes" 12 "fmt" 13 "io" 14 "io/ioutil" 15 16 "github.com/u-root/u-root/pkg/ubinary" 17 ) 18 19 const ( 20 trampolineBeginLow = "u-root-trampoline" 21 trampolineBeginHigh = "-begin" 22 trampolineEnd = "u-root-trampoline-end" 23 24 trampolineEntry = "u-root-entry-long" 25 trampolineInfo = "u-root-info-long" 26 ) 27 28 var trampolineBegin []byte 29 30 var alwaysFalse bool 31 32 func start() 33 34 func init() { 35 // Cannot use "u-root-trampoline-begin" directly, because then there 36 // would be two locations of "u-root-trampoline-begin" sequence. 37 trampolineBegin = append([]byte(trampolineBeginLow), []byte(trampolineBeginHigh)...) 38 39 if alwaysFalse { 40 // Can never happen, but still need it for linker to include assembly to a binary. 41 start() 42 } 43 44 // Hope Go compiler will never get smart enough to optimize this function. 45 } 46 47 // alignUp aligns x to a 0x10 bytes boundary. 48 // go compiler aligns TEXT parts at 0x10 bytes boundary. 49 func alignUp(x int) int { 50 const mask = 0x10 - 1 51 return (x + mask) & ^mask 52 } 53 54 // Setup scans file for trampoline code and sets 55 // values for multiboot info address and kernel entry point. 56 func Setup(path string, infoAddr, entryPoint uintptr) ([]byte, error) { 57 d, err := extract(path) 58 if err != nil { 59 return nil, err 60 } 61 return patch(d, infoAddr, entryPoint) 62 } 63 64 // extract extracts trampoline segment from file. 65 // trampoline segment begins after "u-root-trampoline-begin" byte sequence + padding, 66 // and ends at "u-root-trampoline-end" byte sequence. 67 func extract(path string) ([]byte, error) { 68 d, err := ioutil.ReadFile(path) 69 if err != nil { 70 return nil, fmt.Errorf("cannot read trampoline file: %v", err) 71 } 72 73 begin := bytes.Index(d, trampolineBegin) 74 if begin == -1 { 75 return nil, io.ErrUnexpectedEOF 76 } 77 if begin = alignUp(begin + len(trampolineBegin)); begin > len(d) { 78 return nil, io.ErrUnexpectedEOF 79 } 80 if ind := bytes.Index(d[begin:], trampolineBegin); ind != -1 { 81 return nil, fmt.Errorf("multiple definition of %q label", trampolineBegin) 82 } 83 84 end := bytes.Index(d[begin:], []byte(trampolineEnd)) 85 if end == -1 { 86 return nil, io.ErrUnexpectedEOF 87 } 88 return d[begin : begin+end], nil 89 } 90 91 // patch patches the trampoline code to store value for multiboot info address 92 // after "u-root-header-long" byte sequence + padding and value 93 // for kernel entry point, after "u-root-entry-long" byte sequence + padding. 94 func patch(trampoline []byte, infoAddr, entryPoint uintptr) ([]byte, error) { 95 replace := func(d, label []byte, val uint32) error { 96 buf := make([]byte, 4) 97 ubinary.NativeEndian.PutUint32(buf, val) 98 99 ind := bytes.Index(d, label) 100 if ind == -1 { 101 return fmt.Errorf("%q label not found in file", label) 102 } 103 ind = alignUp(ind + len(label)) 104 if len(d) < ind+len(buf) { 105 return io.ErrUnexpectedEOF 106 } 107 copy(d[ind:], buf) 108 return nil 109 } 110 111 if err := replace(trampoline, []byte(trampolineInfo), uint32(infoAddr)); err != nil { 112 return nil, err 113 } 114 if err := replace(trampoline, []byte(trampolineEntry), uint32(entryPoint)); err != nil { 115 return nil, err 116 } 117 return trampoline, nil 118 }