github.com/mvdan/u-root-coreutils@v0.0.0-20230122170626-c2eef2898555/pkg/boot/uefi/fv.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  	"encoding/binary"
    10  	"fmt"
    11  )
    12  
    13  // These headers are coming from EDK2
    14  // MdePkg/Include/Pi/PiFirmwareVolume.h
    15  // MdePkg/Include/Pi/PiFirmwareFile.h
    16  
    17  type EFIFirmwareVolumeHeader struct {
    18  	ZeroVector      [16]uint8
    19  	FileSystemGUID  [16]uint8
    20  	FvLength        uint64
    21  	Signature       [4]uint8
    22  	Attributes      uint32
    23  	HeaderLength    uint16
    24  	Checksum        uint16
    25  	ExtHeaderOffset uint16
    26  	Reserved        uint8
    27  	Revision        uint8
    28  }
    29  
    30  type EFIFFSFileHeader struct {
    31  	Name           [16]uint8
    32  	HeaderChecksum uint8
    33  	FileChecksum   uint8
    34  	Type           uint8
    35  	Attributes     uint8
    36  	Size           [3]uint8
    37  	State          uint8
    38  }
    39  
    40  const (
    41  	EFIFFSAttribLargeFile       uint8 = 0x01
    42  	EFICommonSectionHeaderSize  int   = 4
    43  	EFICommonSectionHeader2Size int   = 8
    44  	EFIFFSFileHeaderSize        int   = 24
    45  	EFIFFSFileHeader2Size       int   = 32
    46  )
    47  
    48  const (
    49  	EFISectionTypePE32    uint8 = 0x10
    50  	EFISectionTypeFVImage uint8 = 0x17
    51  )
    52  
    53  const (
    54  	EFIFVFileTypeSecurityCore        = 0x03
    55  	EFIFVFileTypeFirmwareVolumeImage = 0x0b
    56  )
    57  
    58  // UnmarshalBinary unmarshals the FiwmreareVolumeHeader from binary data.
    59  func (e *EFIFirmwareVolumeHeader) UnmarshalBinary(data []byte) error {
    60  	if len(data) < 0x38 {
    61  		return fmt.Errorf("invalid entry point stucture length %d", len(data))
    62  	}
    63  	if err := binary.Read(bytes.NewReader(data), binary.LittleEndian, e); err != nil {
    64  		return err
    65  	}
    66  	if !bytes.Equal(e.Signature[:], []byte("_FVH")) {
    67  		return fmt.Errorf("invalid Signature string %q", string(e.Signature[:]))
    68  	}
    69  	return nil
    70  }
    71  
    72  // UnmarshalBinary unmarshals the EFIFFSFileHeader from binary data.
    73  func (e *EFIFFSFileHeader) UnmarshalBinary(data []byte) error {
    74  	if len(data) < EFIFFSFileHeaderSize {
    75  		return fmt.Errorf("invalid entry point stucture length %d", len(data))
    76  	}
    77  	if err := binary.Read(bytes.NewReader(data), binary.LittleEndian, e); err != nil {
    78  		return err
    79  	}
    80  	return nil
    81  }
    82  
    83  // findSecurityCorePEEntry finds SEC PE entry in Firmware Volume
    84  func findSecurityCorePEEntry(data []byte) (offset int, err error) {
    85  	var fvh EFIFirmwareVolumeHeader
    86  	var ffs EFIFFSFileHeader
    87  	if err = fvh.UnmarshalBinary(data); err != nil {
    88  		return 0, err
    89  	}
    90  	offset += int(fvh.HeaderLength)
    91  	for offset < int(fvh.FvLength) {
    92  		if err = ffs.UnmarshalBinary(data[offset:]); err != nil {
    93  			break
    94  		}
    95  		fs := int(ffs.Size[0]) + int(ffs.Size[1])<<8 + int(ffs.Size[2])<<16
    96  		large := ffs.Attributes&EFIFFSAttribLargeFile != 0
    97  		// file size should not be 0
    98  		if fs == 0 {
    99  			return 0, fmt.Errorf("file is corrupt")
   100  		}
   101  		switch ffs.Type {
   102  		case EFIFVFileTypeSecurityCore:
   103  			peo, err := findSectionInFFS(EFISectionTypePE32, data[offset:offset+fs], large)
   104  			if err == nil {
   105  				return offset + peo, nil
   106  			}
   107  		case EFIFVFileTypeFirmwareVolumeImage:
   108  			fvo, err := findSectionInFFS(EFISectionTypeFVImage, data[offset:offset+fs], large)
   109  			if err == nil {
   110  				offset2, err := findSecurityCorePEEntry(data[offset+fvo:])
   111  				if err == nil {
   112  					return offset + fvo + offset2, nil
   113  				}
   114  			}
   115  		}
   116  
   117  		// next FFS needs to be aligned with 8 bytes.
   118  		if fs&7 != 0 {
   119  			fs &= ^7
   120  			fs += 8
   121  		}
   122  		offset += fs
   123  	}
   124  	return 0, fmt.Errorf("unable to find SEC ffs in this file")
   125  }
   126  
   127  // findSectionInFFS finds given first Given SectionType's entry in FFS
   128  func findSectionInFFS(sectionType uint8, data []byte, isLargeFile bool) (cursor int, err error) {
   129  	fhs := EFIFFSFileHeaderSize
   130  	shs := EFICommonSectionHeaderSize
   131  	if isLargeFile {
   132  		fhs = EFIFFSFileHeader2Size
   133  		shs = EFICommonSectionHeader2Size
   134  	}
   135  	cursor += fhs
   136  	for cursor < len(data) {
   137  		if data[cursor+3] == sectionType {
   138  			return cursor + shs, nil
   139  		}
   140  		ss := int(data[cursor])
   141  		ss += int(data[cursor+1]) << 8
   142  		ss += int(data[cursor+2]) << 16
   143  		if ss == 0 {
   144  			return 0, fmt.Errorf("unable to parse FFS")
   145  		}
   146  		cursor += ss
   147  	}
   148  	return cursor, fmt.Errorf("cannot find PE32 entry in FFS")
   149  }