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 }