github.com/linuxboot/fiano@v1.2.0/pkg/uefi/flash.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/hex"
    10  	"fmt"
    11  	"sort"
    12  )
    13  
    14  // FlashSignature is the sequence of bytes that a Flash image is expected to
    15  // start with.
    16  var (
    17  	FlashSignature = []byte{0x5a, 0xa5, 0xf0, 0x0f}
    18  )
    19  
    20  const (
    21  	// FlashDescriptorLength represents the size of the descriptor region.
    22  	FlashDescriptorLength = 0x1000
    23  )
    24  
    25  // FlashDescriptor is the main structure that represents an Intel Flash Descriptor.
    26  type FlashDescriptor struct {
    27  	// Holds the raw buffer
    28  	buf                []byte
    29  	DescriptorMapStart uint
    30  	RegionStart        uint
    31  	MasterStart        uint
    32  	DescriptorMap      *FlashDescriptorMap
    33  	Region             *FlashRegionSection
    34  	Master             *FlashMasterSection
    35  
    36  	//Metadata for extraction and recovery
    37  	ExtractPath string
    38  }
    39  
    40  // FindSignature searches for an Intel flash signature.
    41  func FindSignature(buf []byte) (int, error) {
    42  	if len(buf) >= 16+len(FlashSignature) && bytes.Equal(buf[16:16+len(FlashSignature)], FlashSignature) {
    43  		// 16 + 4 since the descriptor starts after the signature
    44  		return 20, nil
    45  	}
    46  	if len(buf) >= len(FlashSignature) && bytes.Equal(buf[:len(FlashSignature)], FlashSignature) {
    47  		// + 4 since the descriptor starts after the signature
    48  		return len(FlashSignature), nil
    49  	}
    50  
    51  	firstBytesCnt := 20
    52  	if len(buf) < firstBytesCnt {
    53  		firstBytesCnt = len(buf)
    54  	}
    55  	return -1, fmt.Errorf("flash signature not found: first %d bytes are:\n%s",
    56  		firstBytesCnt, hex.Dump(buf[:firstBytesCnt]))
    57  }
    58  
    59  // Buf returns the buffer.
    60  // Used mostly for things interacting with the Firmware interface.
    61  func (fd *FlashDescriptor) Buf() []byte {
    62  	return fd.buf
    63  }
    64  
    65  // SetBuf sets the buffer.
    66  // Used mostly for things interacting with the Firmware interface.
    67  func (fd *FlashDescriptor) SetBuf(buf []byte) {
    68  	fd.buf = buf
    69  }
    70  
    71  // Apply calls the visitor on the FlashDescriptor.
    72  func (fd *FlashDescriptor) Apply(v Visitor) error {
    73  	return v.Visit(fd)
    74  }
    75  
    76  // ApplyChildren calls the visitor on each child node of FlashDescriptor.
    77  func (fd *FlashDescriptor) ApplyChildren(v Visitor) error {
    78  	return nil
    79  }
    80  
    81  // ParseFlashDescriptor parses the ifd from the buffer
    82  func (fd *FlashDescriptor) ParseFlashDescriptor() error {
    83  	if buflen := len(fd.buf); buflen != FlashDescriptorLength {
    84  		return fmt.Errorf("flash descriptor length not %#x, was %#x", FlashDescriptorLength, buflen)
    85  	}
    86  
    87  	descriptorMapStart, err := FindSignature(fd.buf)
    88  	if err != nil {
    89  		return err
    90  	}
    91  	fd.DescriptorMapStart = uint(descriptorMapStart)
    92  
    93  	// Descriptor Map
    94  	desc, err := NewFlashDescriptorMap(fd.buf[fd.DescriptorMapStart:])
    95  	if err != nil {
    96  		return err
    97  	}
    98  	fd.DescriptorMap = desc
    99  
   100  	// Region
   101  	fd.RegionStart = uint(fd.DescriptorMap.RegionBase) * 0x10
   102  	region, err := NewFlashRegionSection(fd.buf[fd.RegionStart : fd.RegionStart+uint(FlashRegionSectionSize)])
   103  	if err != nil {
   104  		return err
   105  	}
   106  	fd.Region = region
   107  
   108  	// Master
   109  	fd.MasterStart = uint(fd.DescriptorMap.MasterBase) * 0x10
   110  	master, err := NewFlashMasterSection(fd.buf[fd.MasterStart : fd.MasterStart+uint(FlashMasterSectionSize)])
   111  	if err != nil {
   112  		return err
   113  	}
   114  	fd.Master = master
   115  
   116  	return nil
   117  }
   118  
   119  // FlashImage is the main structure that represents an Intel Flash image. It
   120  // implements the Firmware interface.
   121  type FlashImage struct {
   122  	// Holds the raw buffer
   123  	buf []byte
   124  	// Holds the Flash Descriptor
   125  	IFD FlashDescriptor
   126  	// Actual regions
   127  	Regions []*TypedFirmware `json:",omitempty"`
   128  
   129  	// Metadata for extraction and recovery
   130  	ExtractPath string
   131  	FlashSize   uint64
   132  }
   133  
   134  // Buf returns the buffer.
   135  // Used mostly for things interacting with the Firmware interface.
   136  func (f *FlashImage) Buf() []byte {
   137  	return f.buf
   138  }
   139  
   140  // SetBuf sets the buffer.
   141  // Used mostly for things interacting with the Firmware interface.
   142  func (f *FlashImage) SetBuf(buf []byte) {
   143  	f.buf = buf
   144  }
   145  
   146  // Apply calls the visitor on the FlashImage.
   147  func (f *FlashImage) Apply(v Visitor) error {
   148  	return v.Visit(f)
   149  }
   150  
   151  // ApplyChildren calls the visitor on each child node of FlashImage.
   152  func (f *FlashImage) ApplyChildren(v Visitor) error {
   153  	if err := f.IFD.Apply(v); err != nil {
   154  		return err
   155  	}
   156  	for _, r := range f.Regions {
   157  		if err := r.Value.Apply(v); err != nil {
   158  			return err
   159  		}
   160  	}
   161  	return nil
   162  }
   163  
   164  // IsPCH returns whether the flash image has the more recent PCH format, or not.
   165  // PCH images have the first 16 bytes reserved, and the 4-bytes signature starts
   166  // immediately after. Older images (ICH8/9/10) have the signature at the
   167  // beginning.
   168  // TODO: Check this. What if we have the signature in both places? I feel like the check
   169  // should be IsICH because I expect the ICH to override PCH if the signature exists in 0:4
   170  // since in that case 16:20 should be data. If that's the case, FindSignature needs to
   171  // be fixed as well
   172  func (f *FlashImage) IsPCH() bool {
   173  	return bytes.Equal(f.buf[16:16+len(FlashSignature)], FlashSignature)
   174  }
   175  
   176  // FindSignature looks for the Intel flash signature, and returns its offset
   177  // from the start of the image. The PCH images are located at offset 16, while
   178  // in ICH8/9/10 they start at 0. If no signature is found, it returns -1.
   179  func (f *FlashImage) FindSignature() (int, error) {
   180  	return FindSignature(f.buf)
   181  }
   182  
   183  func (f *FlashImage) String() string {
   184  	return fmt.Sprintf("FlashImage{Size=%v, Descriptor=%v, Region=%v, Master=%v}",
   185  		len(f.buf),
   186  		f.IFD.DescriptorMap.String(),
   187  		f.IFD.Region.String(),
   188  		f.IFD.Master.String(),
   189  	)
   190  }
   191  
   192  func (f *FlashImage) fillRegionGaps() error {
   193  	// Search for gaps and fill in with unknown regions
   194  	offset := uint64(FlashDescriptorLength)
   195  	var newRegions []*TypedFirmware
   196  	for _, t := range f.Regions {
   197  		r, _ := t.Value.(Region)
   198  		nextBase := uint64(r.FlashRegion().BaseOffset())
   199  		if nextBase < offset {
   200  			// Something is wrong, overlapping regions
   201  			// TODO: print a better error message describing what it overlaps with
   202  			return fmt.Errorf("overlapping regions! region type %s overlaps with the previous region",
   203  				r.Type().String())
   204  		}
   205  		if nextBase > offset {
   206  			// There is a gap, create an unknown region
   207  			tempFR := &FlashRegion{Base: uint16(offset / RegionBlockSize),
   208  				Limit: uint16(nextBase/RegionBlockSize) - 1}
   209  			newRegions = append(newRegions, MakeTyped(&RawRegion{buf: f.buf[offset:nextBase],
   210  				FRegion:    tempFR,
   211  				RegionType: RegionTypeUnknown}))
   212  		}
   213  		offset = uint64(r.FlashRegion().EndOffset())
   214  		newRegions = append(newRegions, MakeTyped(r))
   215  	}
   216  	// check for the last region
   217  	if offset != f.FlashSize {
   218  		tempFR := &FlashRegion{Base: uint16(offset / RegionBlockSize),
   219  			Limit: uint16(f.FlashSize/RegionBlockSize) - 1}
   220  		newRegions = append(newRegions, MakeTyped(&RawRegion{buf: f.buf[offset:f.FlashSize],
   221  			FRegion:    tempFR,
   222  			RegionType: RegionTypeUnknown}))
   223  	}
   224  	f.Regions = newRegions
   225  	return nil
   226  }
   227  
   228  // NewFlashImage tries to create a FlashImage structure, and returns a FlashImage
   229  // and an error if any. This only works with images that operate in Descriptor
   230  // mode.
   231  func NewFlashImage(buf []byte) (*FlashImage, error) {
   232  	if len(buf) < FlashDescriptorLength {
   233  		return nil, fmt.Errorf("flash Descriptor Map size too small: expected %v bytes, got %v",
   234  			FlashDescriptorLength,
   235  			len(buf),
   236  		)
   237  	}
   238  	f := FlashImage{FlashSize: uint64(len(buf))}
   239  
   240  	// Copy out buffers
   241  	f.buf = make([]byte, len(buf))
   242  	copy(f.buf, buf)
   243  	f.IFD.buf = make([]byte, FlashDescriptorLength)
   244  	copy(f.IFD.buf, buf[:FlashDescriptorLength])
   245  
   246  	if err := f.IFD.ParseFlashDescriptor(); err != nil {
   247  		return nil, err
   248  	}
   249  
   250  	// FlashRegions is an array, make a slice to keep reference to it's content
   251  	frs := f.IFD.Region.FlashRegions[:]
   252  
   253  	// BIOS region has to be valid
   254  	if !frs[RegionTypeBIOS].Valid() {
   255  		return nil, fmt.Errorf("no BIOS region: invalid region parameters %v", frs[RegionTypeBIOS])
   256  	}
   257  
   258  	nr := int(f.IFD.DescriptorMap.NumberOfRegions)
   259  	// Parse all the regions
   260  	for i, fr := range frs {
   261  		// Parse only a smaller number of regions if number of regions isn't 0
   262  		// Number of regions is deprecated in newer IFDs and is just 0, older IFDs report
   263  		// the number of regions and have falsely "valid" regions after that number.
   264  		if nr != 0 && i >= nr {
   265  			break
   266  		}
   267  		if !fr.Valid() {
   268  			continue
   269  		}
   270  		if o := uint64(fr.BaseOffset()); o >= f.FlashSize {
   271  			fmt.Printf("region %s (%d, %v) out of bounds: BaseOffset %#x, Flash size %#x, skipping...\n",
   272  				flashRegionTypeNames[FlashRegionType(i)], i, fr, o, f.FlashSize)
   273  			continue
   274  		}
   275  		if o := uint64(fr.EndOffset()); o > f.FlashSize {
   276  			fmt.Printf("region %s (%d, %v) out of bounds: EndOffset %#x, Flash size %#x, skipping...\n",
   277  				flashRegionTypeNames[FlashRegionType(i)], i, fr, o, f.FlashSize)
   278  			continue
   279  		}
   280  		if c, ok := regionConstructors[FlashRegionType(i)]; ok {
   281  			r, err := c(buf[fr.BaseOffset():fr.EndOffset()], &frs[i], FlashRegionType(i))
   282  			if err != nil {
   283  				return nil, err
   284  			}
   285  			f.Regions = append(f.Regions, MakeTyped(r))
   286  		}
   287  	}
   288  
   289  	// Sort the regions by offset so we can look for gaps
   290  	sort.Slice(f.Regions, func(i, j int) bool {
   291  		ri := f.Regions[i].Value.(Region)
   292  		rj := f.Regions[j].Value.(Region)
   293  		return ri.FlashRegion().Base < rj.FlashRegion().Base
   294  	})
   295  
   296  	if err := f.fillRegionGaps(); err != nil {
   297  		return nil, err
   298  	}
   299  	return &f, nil
   300  }