github.com/linuxboot/fiano@v1.2.0/pkg/uefi/meregion.go (about)

     1  // Copyright 2019 the LinuxBoot 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  	"encoding/hex"
    11  	"fmt"
    12  
    13  	"github.com/linuxboot/fiano/pkg/log"
    14  )
    15  
    16  // ME Partition parsing, the goal is to spot a padding in the ME Region
    17  // after the ME partitions so that this region can be shrunk in the IFD.
    18  //
    19  // ME partition informations from http://me.bios.io/ME_blob_format
    20  
    21  // MEFTPSignature is the sequence of bytes that an ME Flash Partition
    22  // Table is expected to start with ie "$FPT".
    23  var (
    24  	MEFTPSignature = []byte{0x24, 0x46, 0x50, 0x54}
    25  )
    26  
    27  const (
    28  	// MEPartitionDescriptorMinLength is the min size of the descriptor
    29  	MEPartitionDescriptorMinLength = 28
    30  	// MEPartitionTableEntryLength is the size of a partition table entry
    31  	MEPartitionTableEntryLength = 32
    32  )
    33  
    34  // MEFPT is the main structure that represents an ME Flash Partition Table.
    35  type MEFPT struct {
    36  	// Holds the raw buffer
    37  	buf []byte
    38  
    39  	PartitionCount    uint32
    40  	PartitionMapStart int
    41  	Entries           []MEPartitionEntry
    42  	// Metadata for extraction and recovery
    43  	ExtractPath string
    44  }
    45  
    46  // MEPartitionEntry is an entry in FTP
    47  type MEPartitionEntry struct {
    48  	Name     MEName
    49  	Owner    [4]byte
    50  	Offset   uint32
    51  	Length   uint32
    52  	Reserved [3]uint32
    53  	Flags    uint32
    54  }
    55  
    56  // MEName represent 4 bytes with JSON string support
    57  type MEName [4]byte
    58  
    59  // MarshalText converts MEName to a byte range (for JSON)
    60  func (n MEName) MarshalText() ([]byte, error) {
    61  	return bytes.TrimRight(n[:], "\x00"), nil
    62  }
    63  
    64  // UnmarshalText converts a byte range to MEName (for JSON)
    65  func (n *MEName) UnmarshalText(b []byte) error {
    66  	var m MEName
    67  	copy(m[:], b)
    68  	*n = m
    69  	if len(b) > len(m) {
    70  		return fmt.Errorf("can’t unmarshal %q to MEName, %d > %d", b, len(b), len(m))
    71  	}
    72  	return nil
    73  }
    74  
    75  func (n MEName) String() string {
    76  	b, _ := n.MarshalText()
    77  	return string(b)
    78  }
    79  
    80  // OffsetIsValid returns true if the entry has a valid offset
    81  func (e MEPartitionEntry) OffsetIsValid() bool {
    82  	return e.Offset != 0 && e.Offset != 0xffffffff
    83  }
    84  
    85  var mePartitionEntryTypeNames = map[byte]string{0: "Code", 1: "Data", 2: "NVRAM", 3: "Generic", 4: "EFFS", 5: "ROM"}
    86  
    87  // Type returns the type of the entry
    88  func (e MEPartitionEntry) Type() string {
    89  	t := byte(e.Flags & 0x7f)
    90  	if s, ok := mePartitionEntryTypeNames[t]; ok {
    91  		return s
    92  	}
    93  	return fmt.Sprintf("Unknown (%d)", t)
    94  }
    95  
    96  // FindMEDescriptor searches for an Intel ME FTP signature
    97  func FindMEDescriptor(buf []byte) (int, error) {
    98  	if bytes.Equal(buf[16:16+len(MEFTPSignature)], MEFTPSignature) {
    99  		// 16 + 4 since the descriptor starts after the signature
   100  		return 16 + len(MEFTPSignature), nil
   101  	}
   102  	if bytes.Equal(buf[:len(MEFTPSignature)], MEFTPSignature) {
   103  		// + 4 since the descriptor starts after the signature
   104  		return len(MEFTPSignature), nil
   105  	}
   106  	return -1, fmt.Errorf("ME Flash Partition Table signature %#02x not found: first 20 bytes are:\n%s", MEFTPSignature, hex.Dump(buf[:20]))
   107  }
   108  
   109  // Buf returns the buffer.
   110  // Used mostly for things interacting with the Firmware interface.
   111  func (fp *MEFPT) Buf() []byte {
   112  	return fp.buf
   113  }
   114  
   115  // SetBuf sets the buffer.
   116  // Used mostly for things interacting with the Firmware interface.
   117  func (fp *MEFPT) SetBuf(buf []byte) {
   118  	fp.buf = buf
   119  }
   120  
   121  // Apply calls the visitor on the MEFPT.
   122  func (fp *MEFPT) Apply(v Visitor) error {
   123  	return v.Visit(fp)
   124  }
   125  
   126  // ApplyChildren calls the visitor on each child node of MEFPT.
   127  func (fp *MEFPT) ApplyChildren(v Visitor) error {
   128  	return nil
   129  }
   130  
   131  // NewMEFPT tries to create a MEFPT
   132  func NewMEFPT(buf []byte) (*MEFPT, error) {
   133  	o, err := FindMEDescriptor(buf)
   134  	if err != nil {
   135  		return nil, err
   136  	}
   137  	if len(buf) < o+MEPartitionDescriptorMinLength {
   138  		return nil, fmt.Errorf("ME section (%#x) too small for ME Flash Partition Table (%#x)", len(buf), o+MEPartitionDescriptorMinLength)
   139  	}
   140  	fp := &MEFPT{PartitionMapStart: o + MEPartitionDescriptorMinLength}
   141  	r := bytes.NewReader(buf[o:])
   142  	if err := binary.Read(r, binary.LittleEndian, &fp.PartitionCount); err != nil {
   143  		return nil, err
   144  	}
   145  	l := fp.PartitionMapStart + MEPartitionTableEntryLength*int(fp.PartitionCount)
   146  	if len(buf) < l {
   147  		return nil, fmt.Errorf("ME section (%#x) too small for %d entries in ME Flash Partition Table (%#x)", len(buf), fp.PartitionCount, l)
   148  	}
   149  
   150  	fp.buf = make([]byte, l)
   151  	copy(fp.buf, buf[:l])
   152  	if err := fp.parsePartitions(); err != nil {
   153  		return nil, err
   154  	}
   155  	return fp, nil
   156  }
   157  
   158  func (fp *MEFPT) parsePartitions() error {
   159  	fp.Entries = make([]MEPartitionEntry, fp.PartitionCount)
   160  	r := bytes.NewReader(fp.buf[fp.PartitionMapStart:])
   161  	return binary.Read(r, binary.LittleEndian, fp.Entries)
   162  }
   163  
   164  // MERegion implements Region for a raw chunk of bytes in the firmware image.
   165  type MERegion struct {
   166  	FPT *MEFPT
   167  	// holds the raw data
   168  	buf []byte
   169  	// Metadata for extraction and recovery
   170  	ExtractPath string
   171  	// This is a pointer to the FlashRegion struct laid out in the ifd.
   172  	FRegion *FlashRegion
   173  	// Region Type as per the IFD
   174  	RegionType FlashRegionType
   175  	// Computed free space after parsing the partition table
   176  	FreeSpaceOffset uint64
   177  }
   178  
   179  // SetFlashRegion sets the flash region.
   180  func (rr *MERegion) SetFlashRegion(fr *FlashRegion) {
   181  	rr.FRegion = fr
   182  }
   183  
   184  // FlashRegion gets the flash region.
   185  func (rr *MERegion) FlashRegion() (fr *FlashRegion) {
   186  	return rr.FRegion
   187  }
   188  
   189  // NewMERegion creates a new region.
   190  func NewMERegion(buf []byte, r *FlashRegion, rt FlashRegionType) (Region, error) {
   191  	rr := &MERegion{FRegion: r, RegionType: rt}
   192  	rr.buf = make([]byte, len(buf))
   193  	copy(rr.buf, buf)
   194  	fp, err := NewMEFPT(buf)
   195  	if err != nil {
   196  		log.Errorf("error parsing ME Flash Partition Table: %v", err)
   197  		return rr, nil
   198  	}
   199  	rr.FPT = fp
   200  	// Compute FreeSpaceOffset
   201  	for _, p := range fp.Entries {
   202  		if p.OffsetIsValid() {
   203  			endOffset := uint64(p.Offset) + uint64(p.Length)
   204  			if endOffset > rr.FreeSpaceOffset {
   205  				rr.FreeSpaceOffset = endOffset
   206  			}
   207  		}
   208  	}
   209  
   210  	return rr, nil
   211  }
   212  
   213  // Type returns the flash region type.
   214  func (rr *MERegion) Type() FlashRegionType {
   215  	return RegionTypeME
   216  }
   217  
   218  // Buf returns the buffer.
   219  // Used mostly for things interacting with the Firmware interface.
   220  func (rr *MERegion) Buf() []byte {
   221  	return rr.buf
   222  }
   223  
   224  // SetBuf sets the buffer.
   225  // Used mostly for things interacting with the Firmware interface.
   226  func (rr *MERegion) SetBuf(buf []byte) {
   227  	rr.buf = buf
   228  }
   229  
   230  // Apply calls the visitor on the MERegion.
   231  func (rr *MERegion) Apply(v Visitor) error {
   232  	return v.Visit(rr)
   233  }
   234  
   235  // ApplyChildren calls the visitor on each child node of MERegion.
   236  func (rr *MERegion) ApplyChildren(v Visitor) error {
   237  	if rr.FPT == nil {
   238  		return nil
   239  	}
   240  	return rr.FPT.Apply(v)
   241  }