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  }