github.com/mvdan/u-root-coreutils@v0.0.0-20230122170626-c2eef2898555/pkg/boot/uefi/uefi.go (about) 1 // Copyright 2021 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 uefi 6 7 import ( 8 "bytes" 9 "debug/pe" 10 "encoding/binary" 11 "fmt" 12 "io" 13 "log" 14 "math" 15 "os" 16 17 "github.com/mvdan/u-root-coreutils/pkg/acpi" 18 "github.com/mvdan/u-root-coreutils/pkg/boot/kexec" 19 "github.com/mvdan/u-root-coreutils/pkg/smbios" 20 ) 21 22 var ( 23 kexecLoad = kexec.Load 24 kexecParseMemoryMap = kexec.ParseMemoryMap 25 getRSDP = acpi.GetRSDP 26 getSMBIOSBase = smbios.SMBIOSBase 27 ) 28 29 // SerialPortConfig defines debug port configuration 30 // This struct will be used to initialize SERIAL_PORT_INFO 31 // in payload (UefiPayloadPkg/Include/Guid/SerialPortInfoGuid.h) 32 type SerialPortConfig struct { 33 Type uint32 34 BaseAddr uint32 35 Baud uint32 36 RegWidth uint32 37 InputHertz uint32 38 UartPciAddr uint32 39 } 40 41 // Serial port type for Type in UefiPayloadPkg's SERIAL_PORT_INFO 42 const ( 43 SerialPortTypeIO = 1 44 SerialPortTypeMMIO = 2 45 ) 46 47 // Current Config Version: 1 48 const PayloadConfigVersion = 1 49 50 type payloadConfig struct { 51 Version uint64 52 ACPIBase uint64 53 ACPISize uint64 54 SMBIOSBase uint64 55 SMBIOSSize uint64 56 SerialConfig SerialPortConfig 57 NumMemoryMapEntries uint32 58 } 59 60 // FVImage is a structure for loading a firmware volume 61 type FVImage struct { 62 name string 63 mem kexec.Memory 64 entryAddress uintptr 65 ImageBase uintptr 66 SerialConfig SerialPortConfig 67 } 68 69 func checkFVAndGetEntryPoint(name string) (uintptr, error) { 70 // Parse entire firmware volume to find SEC's PE32. Since payload will 71 // be only few MBs, it should be fine to load entire image for parsing. 72 dat, err := os.ReadFile(name) 73 if err != nil { 74 return 0, err 75 } 76 77 secEntry, err := findSecurityCorePEEntry(dat) 78 if err != nil { 79 return 0, err 80 } 81 82 f, err := os.Open(name) 83 if err != nil { 84 return 0, err 85 } 86 defer f.Close() 87 88 pef := io.NewSectionReader(f, int64(secEntry), math.MaxInt64) 89 pf, err := pe.NewFile(pef) 90 if err != nil { 91 return 0, err 92 } 93 op64, ok := pf.OptionalHeader.(*pe.OptionalHeader64) 94 if !ok { 95 return 0, fmt.Errorf("it is not OptionalHeader64") 96 } 97 return uintptr(secEntry) + uintptr(op64.AddressOfEntryPoint), nil 98 } 99 100 // New loads the file and return FVImage stucture if entry image is found 101 func New(n string) (*FVImage, error) { 102 entry, err := checkFVAndGetEntryPoint(n) 103 if err != nil { 104 return nil, err 105 } 106 return &FVImage{name: n, mem: kexec.Memory{}, entryAddress: entry}, nil 107 } 108 109 // Reserved 64kb for passing params 110 const uefiPayloadConfigSize = 0x10000 111 112 // Load loads fimware volume payload and boot the the payload 113 func (fv *FVImage) Load(verbose bool) error { 114 // Install payload 115 dat, err := os.ReadFile(fv.name) 116 if err != nil { 117 return err 118 } 119 fv.mem.Segments.Insert(kexec.NewSegment(dat, kexec.Range{Start: fv.ImageBase, Size: uint(len(dat))})) 120 121 // Install payload config & its memory map: 64 kb below the image 122 // We cannot use the memory above the image base because it may be used by HOB 123 configAddr := fv.ImageBase - uintptr(uefiPayloadConfigSize) 124 125 // Get MemoryMap 126 mm, err := kexecParseMemoryMap() 127 if err != nil { 128 return err 129 } 130 131 // Get Acpi Basc (RSDP) 132 rsdp, err := getRSDP() 133 if err != nil { 134 return err 135 } 136 137 smbiosBase, smbiosSize, err := getSMBIOSBase() 138 if err != nil { 139 return err 140 } 141 142 pc := payloadConfig{ 143 Version: PayloadConfigVersion, 144 ACPIBase: uint64(rsdp.RSDPAddr()), 145 ACPISize: uint64(rsdp.Len()), 146 SMBIOSBase: uint64(smbiosBase), 147 SMBIOSSize: uint64(smbiosSize), 148 SerialConfig: fv.SerialConfig, 149 NumMemoryMapEntries: uint32(len(mm)), 150 } 151 152 pcbuf := &bytes.Buffer{} 153 if err := binary.Write(pcbuf, binary.LittleEndian, pc); err != nil { 154 return err 155 } 156 157 if err := binary.Write(pcbuf, binary.LittleEndian, mm.AsPayloadParam()); err != nil { 158 return err 159 } 160 161 if len(pcbuf.Bytes()) > uefiPayloadConfigSize { 162 return fmt.Errorf("Config/Memmap size is greater than reserved size: %d bytes", len(pcbuf.Bytes())) 163 } 164 165 fv.mem.Segments.Insert(kexec.NewSegment(pcbuf.Bytes(), kexec.Range{Start: configAddr, Size: uint(len(pcbuf.Bytes()))})) 166 167 if verbose { 168 log.Printf("segments cmdline %v", fv.mem.Segments) 169 } 170 171 if err := kexecLoad(fv.ImageBase+uintptr(fv.entryAddress), fv.mem.Segments, 0); err != nil { 172 return fmt.Errorf("kexec.Load() error: %v", err) 173 } 174 175 return nil 176 }