github.com/linuxboot/fiano@v1.2.0/pkg/visitors/assemble.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 visitors
     6  
     7  import (
     8  	"bytes"
     9  	"encoding/binary"
    10  	"fmt"
    11  	"sort"
    12  
    13  	"github.com/linuxboot/fiano/pkg/compression"
    14  	"github.com/linuxboot/fiano/pkg/guid"
    15  	"github.com/linuxboot/fiano/pkg/log"
    16  	"github.com/linuxboot/fiano/pkg/uefi"
    17  	"github.com/linuxboot/fiano/pkg/unicode"
    18  )
    19  
    20  // Assemble reconstitutes the firmware tree assuming that the leaf node buffers are accurate
    21  type Assemble struct {
    22  	// This is set when a file or section >=16MiB is encountered during assembly.
    23  	// This tells the enclosing FV to use the FFSV3 GUID instead of the FFSV2 GUID,
    24  	// and the enclosing FV resets it.
    25  	// TODO: figure out if, in the case where the FVs are triply nested, must the FVs further up
    26  	// also use the FFSV3 GUID? In that case we should fix this since only the innermost
    27  	// enclosing FV changes to FFSV3
    28  	useFFS3 bool
    29  }
    30  
    31  // Run just applies the visitor.
    32  func (v *Assemble) Run(f uefi.Firmware) error {
    33  	return f.Apply(v)
    34  }
    35  
    36  // Visit applies the Assemble visitor to any Firmware type.
    37  func (v *Assemble) Visit(f uefi.Firmware) error {
    38  	var err error
    39  
    40  	// Get the damn Erase Polarity
    41  	if f, ok := f.(*uefi.FirmwareVolume); ok {
    42  		// Set Erase Polarity
    43  		if err = uefi.SetErasePolarity(f.GetErasePolarity()); err != nil {
    44  			return err
    45  		}
    46  	}
    47  
    48  	// We first assemble the children.
    49  	// Sounds horrible but has to be done =(
    50  	if err = f.ApplyChildren(v); err != nil {
    51  		return err
    52  	}
    53  
    54  	switch f := f.(type) {
    55  
    56  	case *uefi.FirmwareVolume:
    57  		if len(f.Files) == 0 {
    58  			// No children, buffer should already contain data.
    59  			return nil
    60  		}
    61  		// We assume the buffer already contains the header. We repopulate the header from the buffer
    62  		// Construct the full buffer.
    63  		// The FV header is the only thing we've read in so far.
    64  		fBuf := f.Buf()
    65  		fBufLen := uint64(len(fBuf))
    66  		// The reason I check against f.Length and fBuf instead of the min size is that the volume could
    67  		// have extended headers.
    68  		if f.Length < fBufLen {
    69  			return fmt.Errorf("buffer read in bigger than FV length!, expected %v got %v bytes",
    70  				f.Length, fBufLen)
    71  		}
    72  
    73  		fileOffset := f.DataOffset
    74  		if f.DataOffset != fBufLen {
    75  			// remove all old file data
    76  			fBuf = fBuf[:f.DataOffset]
    77  			f.SetBuf(fBuf)
    78  		}
    79  
    80  		for _, file := range f.Files {
    81  			fileBuf := file.Buf()
    82  			fileLen := uint64(len(fileBuf))
    83  			if fileLen == 0 {
    84  				log.Fatalf("%v", file.Header.GUID)
    85  			}
    86  
    87  			// Pad to the 8 byte alignments.
    88  			alignedOffset := uefi.Align8(fileOffset)
    89  			// Read out the file alignment requirements
    90  			if alignBase := file.Header.Attributes.GetAlignment(); alignBase != 1 {
    91  				hl := file.HeaderLen()
    92  				// We need to align the data, not the header. This is so terrible.
    93  				fileDataOffset := uefi.Align(alignedOffset+hl, alignBase)
    94  				// Calculate the starting offset of the file
    95  				newOffset := fileDataOffset - hl
    96  				if gap := (newOffset - alignedOffset); gap >= 8 && gap < uefi.FileHeaderMinLength {
    97  					// We need to re align to the next boundary cause we can't put a pad file in here.
    98  					// Who thought this was a good idea?
    99  					fileDataOffset = uefi.Align(fileDataOffset+1, alignBase)
   100  					newOffset = fileDataOffset - hl
   101  				}
   102  				if newOffset != alignedOffset {
   103  					// Add a pad file starting from alignedOffset to newOffset
   104  					pfile, err := uefi.CreatePadFile(newOffset - alignedOffset)
   105  					if err != nil {
   106  						return err
   107  					}
   108  					if err = f.InsertFile(alignedOffset, pfile.Buf()); err != nil {
   109  						return fmt.Errorf("file %s: %v", pfile.Header.GUID, err)
   110  					}
   111  				}
   112  				alignedOffset = newOffset
   113  			}
   114  			if err = f.InsertFile(alignedOffset, fileBuf); err != nil {
   115  				return fmt.Errorf("file %s: %v", file.Header.GUID, err)
   116  			}
   117  			fileOffset = alignedOffset + fileLen
   118  		}
   119  
   120  		// Check if we're out of space.
   121  		newFVLen := uint64(len(f.Buf()))
   122  		if f.Length < newFVLen && !f.Resizable {
   123  			return fmt.Errorf("out of space in firmware volume. space available: %v bytes, new size: %v, reduce size by %v bytes", f.Length, newFVLen, newFVLen-f.Length)
   124  		}
   125  
   126  		if f.Length < newFVLen {
   127  			// We've expanded the FV, resize
   128  			if f.Blocks[0].Size == 0 {
   129  				return fmt.Errorf("first block in FV has zero size! block was %v", f.Blocks[0])
   130  			}
   131  			// Align to the next block boundary
   132  			// Make sure there are enough blocks for the length
   133  			f.Length = uefi.Align(newFVLen, uint64(f.Blocks[0].Size))
   134  			// Right now we assume there's only one block entry
   135  			// TODO: handle multiple block entries
   136  			f.Blocks[0].Count = uint32(f.Length / uint64(f.Blocks[0].Size))
   137  		}
   138  		if f.Length > newFVLen {
   139  			// If the buffer is not long enough, pad ErasePolarity
   140  			extLen := f.Length - newFVLen
   141  			emptyBuf := make([]byte, extLen)
   142  			uefi.Erase(emptyBuf, uefi.Attributes.ErasePolarity)
   143  			f.SetBuf(append(f.Buf(), emptyBuf...))
   144  		}
   145  
   146  		f.FreeSpace = f.Length - uefi.Align8(newFVLen)
   147  		fBuf = f.Buf()
   148  
   149  		// Write the length to the correct spot
   150  		// TODO: handle the whole header instead of doing this
   151  		binary.LittleEndian.PutUint64(fBuf[32:], f.Length)
   152  
   153  		// Write the correct GUID to the correct spot
   154  		// Refer to EFI_FIRMWARE_FILE_SYSTEM3_GUID in section 3.2.2, volume 3 in
   155  		// the UEFI PI Specification version 1.6
   156  		if v.useFFS3 && f.FileSystemGUID == *uefi.FFS2 {
   157  			// There is a large file or section, we need to swap to FFSV3
   158  			f.FileSystemGUID = *uefi.FFS3
   159  			// Write it out
   160  			copy(fBuf[16:32], f.FileSystemGUID[:])
   161  		}
   162  		v.useFFS3 = false
   163  
   164  		// Write the block map count
   165  		binary.LittleEndian.PutUint32(fBuf[56:], f.Blocks[0].Count)
   166  		// Checksum the header again
   167  		// TODO: handle the whole header instead of doing this
   168  		// First we zero out the original checksum
   169  		binary.LittleEndian.PutUint16(fBuf[50:], 0)
   170  		sum, err := uefi.Checksum16(fBuf[:f.HeaderLen])
   171  		if err != nil {
   172  			return err
   173  		}
   174  		newSum := 0 - sum
   175  		binary.LittleEndian.PutUint16(fBuf[50:], newSum)
   176  
   177  		// Save the buffer
   178  		f.SetBuf(fBuf)
   179  
   180  	case *uefi.File:
   181  		if len(f.Sections) == 0 && f.NVarStore == nil {
   182  			// No children, buffer should already contain data.
   183  			// we don't support this file type, just return the raw buffer.
   184  			// Or we've removed the sections and just want to replace the file directly
   185  			// We have to make sure the state is correct, so we still need to write out
   186  			// the file header.
   187  
   188  			// TODO: Not setting to valid used to cause some failures on some bioses, verify that it no longer fails.
   189  			// There are some bioses that don't set the valid bits correctly,
   190  			// fh.State = 0x07 ^ uefi.Attributes.ErasePolarity
   191  			return nil
   192  		}
   193  
   194  		// Otherwise, we reconstruct the entire file from the sections and the
   195  		// file header using data from the JSON/existing header struct. This means
   196  		// that some JSON values are now respected, including GUID changes.
   197  		// However file lengths and checksums will be recalculated.
   198  
   199  		// Assemble all sections so we know the final file size. We need to do this
   200  		// to know if we need to use the extended header.
   201  		fileData := []byte{}
   202  		dLen := uint64(0)
   203  		if f.NVarStore != nil {
   204  			fileData = f.NVarStore.Buf()
   205  			dLen = f.NVarStore.Length
   206  		} else {
   207  			for _, s := range f.Sections {
   208  				// Align to 4 bytes and extend with 00s
   209  				// Why is it 00s? I don't know. Everything else has been extended with FFs
   210  				// but somehow in between sections alignment is done with 0s. What the heck.
   211  				for count := uefi.Align4(dLen) - dLen; count > 0; count-- {
   212  					fileData = append(fileData, 0x00)
   213  				}
   214  				dLen = uefi.Align4(dLen)
   215  
   216  				// Append the section
   217  				sData := s.Buf()
   218  				dLen += uint64(len(sData))
   219  				fileData = append(fileData, sData...)
   220  			}
   221  		}
   222  
   223  		f.SetSize(uefi.FileHeaderMinLength+dLen, true)
   224  		// We need to use FFSV3
   225  		if f.Header.ExtendedSize > 0xFFFFFF {
   226  			v.useFFS3 = true
   227  		}
   228  
   229  		// TODO: Not setting to valid used to cause some failures on some bioses, verify that it no longer fails.
   230  		// There are some bioses that don't set the valid bits correctly,
   231  		// fh.SetState(uefi.FileStateValid)
   232  
   233  		if err = f.ChecksumAndAssemble(fileData); err != nil {
   234  			return err
   235  		}
   236  		return nil
   237  
   238  	case *uefi.Section:
   239  		if len(f.Encapsulated) == 0 {
   240  			// No children, buffer should already contain data.
   241  			// We allow for some modifications like UI sections and version
   242  			// sections
   243  			switch f.Header.Type {
   244  			default:
   245  				return nil
   246  			case uefi.SectionTypeUserInterface:
   247  				f.SetBuf(unicode.UTF8ToUCS2(f.Name))
   248  			case uefi.SectionTypeVersion:
   249  				newBuf := make([]byte, 2)
   250  				binary.LittleEndian.PutUint16(newBuf, f.BuildNumber)
   251  				newBuf = append(newBuf, unicode.UTF8ToUCS2(f.Version)...)
   252  				f.SetBuf(newBuf)
   253  			case uefi.SectionTypeDXEDepEx, uefi.SectionTypePEIDepEx,
   254  				uefi.SectionMMDepEx:
   255  				// Assemble dependency sections.
   256  				// TODO: handle differences in opcode support between different dependency sections.
   257  				newBuf := []byte{}
   258  				for _, d := range f.DepEx {
   259  					opcode, ok := uefi.DepExNamesToOpCodes[d.OpCode]
   260  					if !ok {
   261  						return fmt.Errorf("unable to map depex opcode string to opcode, string was: %v",
   262  							d.OpCode)
   263  					}
   264  					newBuf = append(newBuf, opcode)
   265  					// Sanity checks.
   266  					switch d.OpCode {
   267  					case "BEFORE", "AFTER", "PUSH":
   268  						// GUID must not be nil.
   269  						if d.GUID == nil {
   270  							return fmt.Errorf("depex opcode %v must not have nil guid",
   271  								d.OpCode)
   272  						}
   273  						newBuf = append(newBuf, d.GUID[:]...)
   274  					default:
   275  						// GUID must be nil.
   276  						if d.GUID != nil {
   277  							return fmt.Errorf("depex opcode %v must not have a guid! got %v",
   278  								d.OpCode, *d.GUID)
   279  						}
   280  					}
   281  				}
   282  				f.SetBuf(newBuf)
   283  			}
   284  
   285  			// We've got the data in the section buffer, now regenerate the header.
   286  			err = f.GenSecHeader()
   287  			if f.Header.ExtendedSize > 0xFFFFFF {
   288  				v.useFFS3 = true
   289  			}
   290  			return err
   291  		}
   292  
   293  		// Construct the section data
   294  		secData := []byte{}
   295  		dLen := uint64(0)
   296  		for _, es := range f.Encapsulated {
   297  			// Align to 4 bytes and extend with 00s
   298  			for count := uefi.Align4(dLen) - dLen; count > 0; count-- {
   299  				secData = append(secData, 0x00)
   300  			}
   301  			dLen = uefi.Align4(dLen)
   302  
   303  			esData := es.Value.Buf()
   304  			dLen += uint64(len(esData))
   305  			secData = append(secData, esData...)
   306  		}
   307  
   308  		// Special processing for some section types
   309  		switch f.Header.Type {
   310  		case uefi.SectionTypeGUIDDefined:
   311  			ts := f.TypeSpecific.Header.(*uefi.SectionGUIDDefined)
   312  			if ts.Attributes&uint16(uefi.GUIDEDSectionProcessingRequired) != 0 {
   313  				compressor := compression.CompressorFromGUID(&ts.GUID)
   314  				if compressor == nil {
   315  					return fmt.Errorf("unknown guid defined from section %v, should not have encapsulated sections", f)
   316  				}
   317  				if fBuf, err := compressor.Encode(secData); err == nil {
   318  					f.SetBuf(fBuf)
   319  				} else {
   320  					return err
   321  				}
   322  			}
   323  		default:
   324  			f.SetBuf(secData)
   325  		}
   326  
   327  		// Fix up the header
   328  		err = f.GenSecHeader()
   329  		if f.Header.ExtendedSize > 0xFFFFFF {
   330  			v.useFFS3 = true
   331  		}
   332  
   333  	case *uefi.NVarStore:
   334  		nvData := []byte{}
   335  		nvLen := uint64(0)
   336  		// NVAR
   337  		for _, v := range f.Entries {
   338  			// Append the NVar
   339  			vData := v.Buf()
   340  			nvLen += uint64(len(vData))
   341  			nvData = append(nvData, vData...)
   342  		}
   343  
   344  		// update offsets
   345  		f.FreeSpaceOffset = nvLen
   346  		f.GUIDStoreOffset = f.Length - uint64(binary.Size(guid.GUID{}))*uint64(len(f.GUIDStore))
   347  		// Erase Empty space
   348  		erased := make([]byte, f.GUIDStoreOffset-f.FreeSpaceOffset)
   349  		uefi.Erase(erased, uefi.Attributes.ErasePolarity)
   350  		nvData = append(nvData, erased...)
   351  
   352  		// Copy the GUID store
   353  		var guidStoreBuf []byte
   354  		guidStoreBuf, err = f.GetGUIDStoreBuf()
   355  		if err != nil {
   356  			return err
   357  		}
   358  		nvData = append(nvData, guidStoreBuf...)
   359  
   360  		f.SetBuf(nvData)
   361  
   362  	case *uefi.NVar:
   363  		// Only rebuild Valid NVAR
   364  		if f.IsValid() {
   365  			var content []byte
   366  			if f.NVarStore == nil {
   367  				content = f.Buf()[f.DataOffset:]
   368  			} else {
   369  				content = f.NVarStore.Buf()
   370  			}
   371  
   372  			err = f.Assemble(content, true)
   373  		}
   374  
   375  	case *uefi.FlashDescriptor:
   376  		// We only parse Descriptor, Region and Master, so regenerate only that and keep the rest of the buffer.
   377  		// We assume the location in the Flash Descriptor sector have not changed.
   378  		// TODO: verify the integrity of the Start offsets
   379  		fBuf := f.Buf()
   380  		// Regenerate DescriptorMap
   381  		desc := new(bytes.Buffer)
   382  		err = binary.Write(desc, binary.LittleEndian, f.DescriptorMap)
   383  		if err != nil {
   384  			return fmt.Errorf("unable to construct binary DescriptorMap of IFD: got %v", err)
   385  		}
   386  		copy(fBuf[f.DescriptorMapStart:f.DescriptorMapStart+uint(uefi.FlashDescriptorMapSize)], desc.Bytes())
   387  		// Regenerate Region
   388  		region := new(bytes.Buffer)
   389  		err = binary.Write(region, binary.LittleEndian, f.Region)
   390  		if err != nil {
   391  			return fmt.Errorf("unable to construct binary Region of IFD: got %v", err)
   392  		}
   393  		copy(fBuf[f.RegionStart:f.RegionStart+uint(uefi.FlashRegionSectionSize)], region.Bytes())
   394  		// Regenerate Master
   395  		master := new(bytes.Buffer)
   396  		err = binary.Write(master, binary.LittleEndian, f.Master)
   397  		if err != nil {
   398  			return fmt.Errorf("unable to construct binary Master of IFD: got %v", err)
   399  		}
   400  		copy(fBuf[f.MasterStart:f.MasterStart+uint(uefi.FlashMasterSectionSize)], master.Bytes())
   401  
   402  		// Set the buffer
   403  		f.SetBuf(fBuf)
   404  
   405  		return nil
   406  
   407  	case *uefi.BIOSRegion:
   408  		fBuf := make([]byte, f.Length)
   409  		firstFV, err := f.FirstFV()
   410  		if err != nil {
   411  			return err
   412  		}
   413  		if err = uefi.SetErasePolarity(firstFV.GetErasePolarity()); err != nil {
   414  			return err
   415  		}
   416  		uefi.Erase(fBuf, uefi.Attributes.ErasePolarity)
   417  		// Put the elements together
   418  		offset := uint64(0)
   419  		for _, e := range f.Elements {
   420  			// copy the fv over the original
   421  			// TODO: handle different sizes.
   422  			// We'll have to FF out the new regions/ check for clashes
   423  			ebuf := e.Value.Buf()
   424  			copy(fBuf[offset:offset+uint64(len(ebuf))], ebuf)
   425  			offset += uint64(len(ebuf))
   426  		}
   427  		// Set the buffer
   428  		f.SetBuf(fBuf)
   429  
   430  		return nil
   431  
   432  	case *uefi.FlashImage:
   433  		ifdbuf := f.IFD.Buf()
   434  		// Assemble regions.
   435  		// We need to sort them since a) we don't really know the order until we parse the block numbers
   436  		// and b) the order may have changed anyway.
   437  		if !f.IFD.Region.FlashRegions[uefi.RegionTypeBIOS].Valid() {
   438  			return fmt.Errorf("no BIOS region: invalid region parameters %v",
   439  				f.IFD.Region.FlashRegions[uefi.RegionTypeBIOS])
   440  		}
   441  
   442  		// Point FlashRegion to struct read from IFD rather than json.
   443  		nr := int(f.IFD.DescriptorMap.NumberOfRegions)
   444  		for _, t := range f.Regions {
   445  			r := t.Value.(uefi.Region)
   446  
   447  			if r.Type() == uefi.RegionTypeUnknown {
   448  				continue
   449  			}
   450  			if nr != 0 && int(r.Type()) > nr {
   451  				// Region exceeds original number of regions.
   452  				// TODO: handle this in some way by increasing the number of regions.
   453  				continue
   454  			}
   455  			if int(r.Type()) >= len(f.IFD.Region.FlashRegions) {
   456  				// This is some new unknown region, there's no IFD entry
   457  				continue
   458  			}
   459  			r.SetFlashRegion(&f.IFD.Region.FlashRegions[r.Type()])
   460  		}
   461  
   462  		// Sort Regions, prepare to set flash buffer
   463  		sort.Slice(f.Regions, func(i, j int) bool {
   464  			ri := f.Regions[i].Value.(uefi.Region)
   465  			rj := f.Regions[j].Value.(uefi.Region)
   466  			return ri.FlashRegion().Base < rj.FlashRegion().Base
   467  		})
   468  
   469  		// Search for gaps
   470  		// if there are gaps or overlaps, fail immediately
   471  		offset := uint64(uefi.FlashDescriptorLength)
   472  		fBuf := make([]byte, 0)
   473  		fBuf = append(fBuf, ifdbuf...)
   474  		for _, t := range f.Regions {
   475  			r := t.Value.(uefi.Region)
   476  			nextBase := uint64(r.FlashRegion().BaseOffset())
   477  			if nextBase < offset {
   478  				// Something is wrong, overlapping regions
   479  				// TODO: print a better error message describing what it overlaps with
   480  				return fmt.Errorf("overlapping regions! region %v overlaps with the previous region", r)
   481  			}
   482  			if nextBase > offset {
   483  				// There is a gap
   484  				return fmt.Errorf("gap between regions from %v to %v", offset, nextBase)
   485  			}
   486  			offset = uint64(r.FlashRegion().EndOffset())
   487  			fBuf = append(fBuf, r.Buf()...)
   488  		}
   489  		// check for the last region
   490  		if offset != f.FlashSize {
   491  			return fmt.Errorf("gap between at end of flash from %v to %v", offset, f.FlashSize)
   492  		}
   493  
   494  		f.SetBuf(fBuf)
   495  		return nil
   496  
   497  	}
   498  
   499  	return err
   500  
   501  }