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  }