github.com/linuxboot/fiano@v1.2.0/pkg/uefi/section.go (about) 1 // Copyright 2018 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/json" 11 "errors" 12 "fmt" 13 "unsafe" 14 15 "github.com/linuxboot/fiano/pkg/compression" 16 "github.com/linuxboot/fiano/pkg/guid" 17 "github.com/linuxboot/fiano/pkg/log" 18 "github.com/linuxboot/fiano/pkg/unicode" 19 ) 20 21 const ( 22 // SectionMinLength is the minimum length of a file section header. 23 SectionMinLength = 0x04 24 // SectionExtMinLength is the minimum length of an extended file section header. 25 SectionExtMinLength = 0x08 26 ) 27 28 // SectionType holds a section type value 29 type SectionType uint8 30 31 // UEFI Section types 32 const ( 33 SectionTypeAll SectionType = 0x00 34 SectionTypeCompression SectionType = 0x01 35 SectionTypeGUIDDefined SectionType = 0x02 36 SectionTypeDisposable SectionType = 0x03 37 SectionTypePE32 SectionType = 0x10 38 SectionTypePIC SectionType = 0x11 39 SectionTypeTE SectionType = 0x12 40 SectionTypeDXEDepEx SectionType = 0x13 41 SectionTypeVersion SectionType = 0x14 42 SectionTypeUserInterface SectionType = 0x15 43 SectionTypeCompatibility16 SectionType = 0x16 44 SectionTypeFirmwareVolumeImage SectionType = 0x17 45 SectionTypeFreeformSubtypeGUID SectionType = 0x18 46 SectionTypeRaw SectionType = 0x19 47 SectionTypePEIDepEx SectionType = 0x1b 48 SectionMMDepEx SectionType = 0x1c 49 ) 50 51 var sectionTypeNames = map[SectionType]string{ 52 SectionTypeCompression: "EFI_SECTION_COMPRESSION", 53 SectionTypeGUIDDefined: "EFI_SECTION_GUID_DEFINED", 54 SectionTypeDisposable: "EFI_SECTION_DISPOSABLE", 55 SectionTypePE32: "EFI_SECTION_PE32", 56 SectionTypePIC: "EFI_SECTION_PIC", 57 SectionTypeTE: "EFI_SECTION_TE", 58 SectionTypeDXEDepEx: "EFI_SECTION_DXE_DEPEX", 59 SectionTypeVersion: "EFI_SECTION_VERSION", 60 SectionTypeUserInterface: "EFI_SECTION_USER_INTERFACE", 61 SectionTypeCompatibility16: "EFI_SECTION_COMPATIBILITY16", 62 SectionTypeFirmwareVolumeImage: "EFI_SECTION_FIRMWARE_VOLUME_IMAGE", 63 SectionTypeFreeformSubtypeGUID: "EFI_SECTION_FREEFORM_SUBTYPE_GUID", 64 SectionTypeRaw: "EFI_SECTION_RAW", 65 SectionTypePEIDepEx: "EFI_SECTION_PEI_DEPEX", 66 SectionMMDepEx: "EFI_SECTION_MM_DEPEX", 67 } 68 69 // String creates a string representation for the section type. 70 func (s SectionType) String() string { 71 if t, ok := sectionTypeNames[s]; ok { 72 return t 73 } 74 return "UNKNOWN" 75 } 76 77 // GUIDEDSectionAttribute holds a GUIDED section attribute bitfield 78 type GUIDEDSectionAttribute uint16 79 80 // UEFI GUIDED Section Attributes 81 const ( 82 GUIDEDSectionProcessingRequired GUIDEDSectionAttribute = 0x01 83 GUIDEDSectionAuthStatusValid GUIDEDSectionAttribute = 0x02 84 ) 85 86 // SectionHeader represents an EFI_COMMON_SECTION_HEADER as specified in 87 // UEFI PI Spec 3.2.4 Firmware File Section 88 type SectionHeader struct { 89 Size [3]uint8 `json:"-"` 90 Type SectionType 91 } 92 93 // SectionExtHeader represents an EFI_COMMON_SECTION_HEADER2 as specified in 94 // UEFI PI Spec 3.2.4 Firmware File Section 95 type SectionExtHeader struct { 96 SectionHeader 97 ExtendedSize uint32 `json:"-"` 98 } 99 100 // SectionGUIDDefinedHeader contains the fields for a EFI_SECTION_GUID_DEFINED 101 // encapsulated section header. 102 type SectionGUIDDefinedHeader struct { 103 GUID guid.GUID 104 DataOffset uint16 105 Attributes uint16 106 } 107 108 // SectionGUIDDefined contains the type specific fields for a 109 // EFI_SECTION_GUID_DEFINED section. 110 type SectionGUIDDefined struct { 111 SectionGUIDDefinedHeader 112 113 // Metadata 114 Compression string 115 } 116 117 // GetBinHeaderLen returns the length of the binary typ specific header 118 func (s *SectionGUIDDefined) GetBinHeaderLen() uint32 { 119 return uint32(unsafe.Sizeof(s.SectionGUIDDefinedHeader)) 120 } 121 122 // TypeHeader interface forces type specific headers to report their length 123 type TypeHeader interface { 124 GetBinHeaderLen() uint32 125 } 126 127 // TypeSpecificHeader is used for marshalling and unmarshalling from JSON 128 type TypeSpecificHeader struct { 129 Type SectionType 130 Header TypeHeader 131 } 132 133 var headerTypes = map[SectionType]func() TypeHeader{ 134 SectionTypeGUIDDefined: func() TypeHeader { return &SectionGUIDDefined{} }, 135 } 136 137 // UnmarshalJSON unmarshals a TypeSpecificHeader struct and correctly deduces the 138 // type of the interface. 139 func (t *TypeSpecificHeader) UnmarshalJSON(b []byte) error { 140 var getType struct { 141 Type SectionType 142 Header json.RawMessage 143 } 144 if err := json.Unmarshal(b, &getType); err != nil { 145 return err 146 } 147 factory, ok := headerTypes[getType.Type] 148 if !ok { 149 return fmt.Errorf("unknown TypeSpecificHeader type '%v', unable to unmarshal", getType.Type) 150 } 151 t.Type = SectionType(getType.Type) 152 t.Header = factory() 153 return json.Unmarshal(getType.Header, &t.Header) 154 } 155 156 // DepExOpCode is one opcode for the dependency expression section. 157 type DepExOpCode string 158 159 // DepExOpCodes maps the numeric code to the string. 160 var DepExOpCodes = map[byte]DepExOpCode{ 161 0x0: "BEFORE", 162 0x1: "AFTER", 163 0x2: "PUSH", 164 0x3: "AND", 165 0x4: "OR", 166 0x5: "NOT", 167 0x6: "TRUE", 168 0x7: "FALSE", 169 0x8: "END", 170 0x9: "SOR", 171 } 172 173 // DepExNamesToOpCodes maps the operation back to the code. 174 var DepExNamesToOpCodes = map[DepExOpCode]byte{} 175 176 func init() { 177 for k, v := range DepExOpCodes { 178 DepExNamesToOpCodes[v] = k 179 } 180 } 181 182 // DepExOp contains one operation for the dependency expression. 183 type DepExOp struct { 184 OpCode DepExOpCode 185 GUID *guid.GUID `json:",omitempty"` 186 } 187 188 // Section represents a Firmware File Section 189 type Section struct { 190 Header SectionExtHeader 191 Type string 192 buf []byte 193 194 // Metadata for extraction and recovery 195 ExtractPath string 196 FileOrder int `json:"-"` 197 198 // Type specific fields 199 // TODO: It will be simpler if this was not an interface 200 TypeSpecific *TypeSpecificHeader `json:",omitempty"` 201 202 // For EFI_SECTION_USER_INTERFACE 203 Name string `json:",omitempty"` 204 205 // For EFI_SECTION_VERSION 206 BuildNumber uint16 `json:",omitempty"` 207 Version string `json:",omitempty"` 208 209 // For EFI_SECTION_DXE_DEPEX, EFI_SECTION_PEI_DEPEX, and EFI_SECTION_MM_DEPEX 210 DepEx []DepExOp `json:",omitempty"` 211 212 // Encapsulated firmware 213 Encapsulated []*TypedFirmware `json:",omitempty"` 214 } 215 216 // String returns the String value of the section if it makes sense, 217 // such as the name or the version string. 218 func (s *Section) String() string { 219 switch s.Header.Type { 220 case SectionTypeUserInterface: 221 return s.Name 222 case SectionTypeVersion: 223 return "Version " + s.Version 224 } 225 return "" 226 } 227 228 // SetType sets the section type in the header and updates the string name. 229 func (s *Section) SetType(t SectionType) { 230 s.Header.Type = t 231 s.Type = t.String() 232 } 233 234 // Buf returns the buffer. 235 // Used mostly for things interacting with the Firmware interface. 236 func (s *Section) Buf() []byte { 237 return s.buf 238 } 239 240 // SetBuf sets the buffer. 241 // Used mostly for things interacting with the Firmware interface. 242 func (s *Section) SetBuf(buf []byte) { 243 s.buf = buf 244 } 245 246 // Apply calls the visitor on the Section. 247 func (s *Section) Apply(v Visitor) error { 248 return v.Visit(s) 249 } 250 251 // ApplyChildren calls the visitor on each child node of Section. 252 func (s *Section) ApplyChildren(v Visitor) error { 253 for _, f := range s.Encapsulated { 254 if err := f.Value.Apply(v); err != nil { 255 return err 256 } 257 } 258 return nil 259 } 260 261 // CreateSection creates a new section from minimal components. 262 // The guid is only used in the case of a GUID Defined section type. 263 func CreateSection(t SectionType, buf []byte, encap []Firmware, g *guid.GUID) (*Section, error) { 264 s := &Section{} 265 266 s.Header.Type = t 267 // Map type to string. 268 s.Type = s.Header.Type.String() 269 270 s.buf = append([]byte{}, buf...) // Copy out buffer. 271 272 for _, e := range encap { 273 s.Encapsulated = append(s.Encapsulated, MakeTyped(e)) 274 } 275 276 // Create type section header 277 switch s.Header.Type { 278 case SectionTypeGUIDDefined: 279 if g == nil { 280 return nil, errors.New("guid was nil, can't make guid defined section") 281 } 282 guidDefHeader := &SectionGUIDDefined{} 283 guidDefHeader.GUID = *g 284 switch *g { 285 case compression.LZMAGUID: 286 guidDefHeader.Compression = "LZMA" 287 case compression.LZMAX86GUID: 288 guidDefHeader.Compression = "LZMAX86" 289 default: 290 guidDefHeader.Compression = "UNKNOWN" 291 } 292 guidDefHeader.Attributes = uint16(GUIDEDSectionProcessingRequired) 293 s.TypeSpecific = &TypeSpecificHeader{SectionTypeGUIDDefined, guidDefHeader} 294 } 295 296 return s, nil 297 } 298 299 // GenSecHeader generates a full binary header for the section data. 300 // It assumes that the passed in section struct already contains section data in the buffer, 301 // the section type in the Type field, and the type specific header in the TypeSpecific field. 302 // It modifies the calling Section. 303 func (s *Section) GenSecHeader() error { 304 var err error 305 // Calculate size 306 headerLen := uint32(SectionMinLength) 307 if s.TypeSpecific != nil && s.TypeSpecific.Header != nil { 308 headerLen += s.TypeSpecific.Header.GetBinHeaderLen() 309 } 310 s.Header.ExtendedSize = uint32(len(s.buf)) + headerLen // TS header lengths are part of headerLen at this point 311 if s.Header.ExtendedSize >= 0xFFFFFF { 312 headerLen += 4 // Add space for the extended header. 313 s.Header.ExtendedSize += 4 314 } 315 316 // Set the correct data offset for GUID Defined headers. 317 // This is terrible 318 if s.Header.Type == SectionTypeGUIDDefined { 319 gd := s.TypeSpecific.Header.(*SectionGUIDDefined) 320 gd.DataOffset = uint16(headerLen) 321 // append type specific header in front of data 322 tsh := new(bytes.Buffer) 323 if err = binary.Write(tsh, binary.LittleEndian, &gd.SectionGUIDDefinedHeader); err != nil { 324 return err 325 } 326 s.buf = append(tsh.Bytes(), s.buf...) 327 } 328 329 // Append common header 330 s.Header.Size = Write3Size(uint64(s.Header.ExtendedSize)) 331 h := new(bytes.Buffer) 332 if s.Header.ExtendedSize >= 0xFFFFFF { 333 err = binary.Write(h, binary.LittleEndian, &s.Header) 334 } else { 335 err = binary.Write(h, binary.LittleEndian, &s.Header.SectionHeader) 336 } 337 if err != nil { 338 return err 339 } 340 s.buf = append(h.Bytes(), s.buf...) 341 return nil 342 } 343 344 // NewSection parses a sequence of bytes and returns a Section 345 // object, if a valid one is passed, or an error. 346 func NewSection(buf []byte, fileOrder int) (*Section, error) { 347 s := Section{FileOrder: fileOrder} 348 // Read in standard header. 349 r := bytes.NewReader(buf) 350 if err := binary.Read(r, binary.LittleEndian, &s.Header.SectionHeader); err != nil { 351 return nil, err 352 } 353 354 // Map type to string. 355 s.Type = s.Header.Type.String() 356 357 headerSize := unsafe.Sizeof(SectionHeader{}) 358 switch s.Header.Type { 359 case SectionTypeAll, SectionTypeCompression, SectionTypeGUIDDefined, SectionTypeDisposable, 360 SectionTypePE32, SectionTypePIC, SectionTypeTE, SectionTypeDXEDepEx, SectionTypeVersion, 361 SectionTypeUserInterface, SectionTypeCompatibility16, SectionTypeFirmwareVolumeImage, 362 SectionTypeFreeformSubtypeGUID, SectionTypeRaw, SectionTypePEIDepEx, SectionMMDepEx: 363 if s.Header.Size == [3]uint8{0xFF, 0xFF, 0xFF} { 364 // Extended Header 365 if err := binary.Read(r, binary.LittleEndian, &s.Header.ExtendedSize); err != nil { 366 return nil, err 367 } 368 if s.Header.ExtendedSize == 0xFFFFFFFF { 369 return nil, errors.New("section size and extended size are all FFs! there should not be free space inside a file") 370 } 371 headerSize = unsafe.Sizeof(SectionExtHeader{}) 372 } else { 373 // Copy small size into big for easier handling. 374 // Section's extended size is 32 bits unlike file's 375 s.Header.ExtendedSize = uint32(Read3Size(s.Header.Size)) 376 } 377 default: 378 s.Header.ExtendedSize = uint32(Read3Size(s.Header.Size)) 379 if buflen := len(buf); int(s.Header.ExtendedSize) > buflen { 380 s.Header.ExtendedSize = uint32(buflen) 381 } 382 } 383 384 if buflen := len(buf); int(s.Header.ExtendedSize) > buflen { 385 return nil, fmt.Errorf("section size mismatch! Section has size %v, but buffer is %v bytes big", 386 s.Header.ExtendedSize, buflen) 387 } 388 389 if ReadOnly { 390 s.buf = buf[:s.Header.ExtendedSize] 391 } else { 392 // Copy out the buffer. 393 newBuf := buf[:s.Header.ExtendedSize] 394 s.buf = make([]byte, s.Header.ExtendedSize) 395 copy(s.buf, newBuf) 396 } 397 398 // Section type specific data 399 switch s.Header.Type { 400 case SectionTypeGUIDDefined: 401 typeSpec := &SectionGUIDDefined{} 402 if err := binary.Read(r, binary.LittleEndian, &typeSpec.SectionGUIDDefinedHeader); err != nil { 403 return nil, err 404 } 405 s.TypeSpecific = &TypeSpecificHeader{Type: SectionTypeGUIDDefined, Header: typeSpec} 406 407 // Determine how to interpret the section based on the GUID. 408 var encapBuf []byte 409 if typeSpec.Attributes&uint16(GUIDEDSectionProcessingRequired) != 0 && !DisableDecompression { 410 if compressor := compression.CompressorFromGUID(&typeSpec.GUID); compressor != nil { 411 typeSpec.Compression = compressor.Name() 412 var err error 413 encapBuf, err = compressor.Decode(buf[typeSpec.DataOffset:]) 414 if err != nil { 415 log.Errorf("%v", err) 416 typeSpec.Compression = "UNKNOWN" 417 encapBuf = []byte{} 418 } 419 } else { 420 typeSpec.Compression = "UNKNOWN" 421 } 422 } 423 424 for i, offset := 0, uint64(0); offset < uint64(len(encapBuf)); i++ { 425 encapS, err := NewSection(encapBuf[offset:], i) 426 if err != nil { 427 return nil, fmt.Errorf("error parsing encapsulated section #%d at offset %d: %v", 428 i, offset, err) 429 } 430 // Align to 4 bytes for now. The PI Spec doesn't say what alignment it should be 431 // but UEFITool aligns to 4 bytes, and this seems to work on everything I have. 432 offset = Align4(offset + uint64(encapS.Header.ExtendedSize)) 433 s.Encapsulated = append(s.Encapsulated, MakeTyped(encapS)) 434 } 435 436 case SectionTypeUserInterface: 437 s.Name = unicode.UCS2ToUTF8(s.buf[headerSize:]) 438 439 case SectionTypeVersion: 440 s.BuildNumber = binary.LittleEndian.Uint16(s.buf[headerSize : headerSize+2]) 441 s.Version = unicode.UCS2ToUTF8(s.buf[headerSize+2:]) 442 443 case SectionTypeFirmwareVolumeImage: 444 fv, err := NewFirmwareVolume(s.buf[headerSize:], 0, true) 445 if err != nil { 446 return nil, err 447 } 448 s.Encapsulated = []*TypedFirmware{MakeTyped(fv)} 449 450 case SectionTypeDXEDepEx, SectionTypePEIDepEx, SectionMMDepEx: 451 var err error 452 if s.DepEx, err = parseDepEx(s.buf[headerSize:]); err != nil { 453 log.Warnf("%v", err) 454 } 455 } 456 457 return &s, nil 458 } 459 460 func parseDepEx(b []byte) ([]DepExOp, error) { 461 depEx := []DepExOp{} 462 r := bytes.NewBuffer(b) 463 for { 464 opCodeByte, err := r.ReadByte() 465 if err != nil { 466 return nil, errors.New("invalid DEPEX, no END") 467 } 468 if opCodeStr, ok := DepExOpCodes[opCodeByte]; ok { 469 op := DepExOp{OpCode: opCodeStr} 470 if opCodeStr == "BEFORE" || opCodeStr == "AFTER" || opCodeStr == "PUSH" { 471 op.GUID = &guid.GUID{} 472 if err := binary.Read(r, binary.LittleEndian, op.GUID); err != nil { 473 return nil, fmt.Errorf("invalid DEPEX, could not read GUID: %v", err) 474 } 475 } 476 depEx = append(depEx, op) 477 if opCodeStr == "END" { 478 break 479 } 480 } else { 481 return nil, fmt.Errorf("invalid DEPEX opcode, %#v", opCodeByte) 482 } 483 } 484 return depEx, nil 485 }