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 }