mu_pi 7.0.0

Platform Initialization (PI) Specification definitions and support code in Rust.
Documentation
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
//! Firmware Filesystem
//!
//! Exports services used to interact with the Firmware Filesystem (FFS).
//!
//! See <https://uefi.org/specs/PI/1.8A/V3_Design_Discussion.html#firmware-storage-introduction>.
//!
//! ## License
//!
//! Copyright (C) Microsoft Corporation. All rights reserved.
//!
//! SPDX-License-Identifier: BSD-2-Clause-Patent
//!

extern crate alloc;

use core::{fmt, mem, num::Wrapping, slice};

pub mod ffs;
pub mod fv;
pub mod fvb;

use ffs::{attributes::raw::LARGE_FILE, file, section};
pub use ffs::{
    attributes::{Attribute as FfsAttribute, raw as FfsRawAttribute},
    file::{
        State as FfsFileState, Type as FfsFileType,
        raw::{state as FfsFileRawState, r#type as FfsFileRawType},
    },
    section::{
        EfiSectionType, Type as FfsSectionType, header as FfsSectionHeader, raw_type as FfsSectionRawType,
        raw_type::encapsulated as FfsEncapsulatedSectionRawType,
    },
};
pub use fv::{
    EfiFvFileType, WritePolicy,
    attributes::{EfiFvAttributes, Fv2 as Fv2Attributes, raw::fv2 as Fv2RawAttributes},
    file::{Attribute as FvFileAttribute, EfiFvFileAttributes, raw::attribute as FvFileRawAttribute},
};
pub use fvb::attributes::{EfiFvbAttributes2, Fvb2 as Fvb2Attributes, raw::fvb2 as Fvb2RawAttributes};

use alloc::{boxed::Box, collections::VecDeque, vec::Vec};
use num_traits::WrappingSub;
use r_efi::efi;

use crate::address_helper::align_up;

pub mod guid {
    use r_efi::efi;

    pub const BROTLI_SECTION: efi::Guid =
        efi::Guid::from_fields(0x3D532050, 0x5CDA, 0x4FD0, 0x87, 0x9E, &[0x0F, 0x7F, 0x63, 0x0D, 0x5A, 0xFB]);
    pub const CRC32_SECTION: efi::Guid =
        efi::Guid::from_fields(0xFC1BCDB0, 0x7D31, 0x49aa, 0x93, 0x6A, &[0xA4, 0x60, 0x0D, 0x9D, 0xD0, 0x83]);
    pub const LZMA_SECTION: efi::Guid =
        efi::Guid::from_fields(0xEE4E5898, 0x3914, 0x4259, 0x9D, 0x6E, &[0xDC, 0x7B, 0xD7, 0x94, 0x03, 0xCF]);
    pub const LZMA_F86_SECTION: efi::Guid =
        efi::Guid::from_fields(0xD42AE6BD, 0x1352, 0x4BFB, 0x90, 0x9A, &[0xCA, 0x72, 0xA6, 0xEA, 0xE8, 0x89]);
    pub const LZMA_PARALLEL_SECTION: efi::Guid =
        efi::Guid::from_fields(0xBD9921EA, 0xED91, 0x404A, 0x8B, 0x2F, &[0xB4, 0xD7, 0x24, 0x74, 0x7C, 0x8C]);
    pub const TIANO_DECOMPRESS_SECTION: efi::Guid =
        efi::Guid::from_fields(0xA31280AD, 0x481E, 0x41B6, 0x95, 0xE8, &[0x12, 0x7F, 0x4C, 0x98, 0x47, 0x79]);
}

/// Defines an interface that can be implemented to provide extraction logic for encapsulation sections.
///
/// ## Example
///```
/// # use std::{env, fs, path::Path, error::Error};
/// use mu_pi::fw_fs::{FirmwareVolume, Section, SectionExtractor};
/// use r_efi::efi;
///
/// struct ExampleSectionExtractor {}
/// impl SectionExtractor for ExampleSectionExtractor {
///   fn extract(&self, section: &Section) -> Result<Box<[u8]>, efi::Status> {
///     println!("Encapsulated section: {:?}", section);
///     Ok(Box::new([0u8; 0])) //A real section extractor would provide the extracted buffer on return.
///   }
/// }
///
/// # fn main() -> Result<(), Box<dyn Error>> {
/// # let root = Path::new(&env::var("CARGO_MANIFEST_DIR")?).join("test_resources");
/// # let fv_bytes = fs::read(root.join("GIGANTOR.Fv"))?;
/// let mut fv = FirmwareVolume::new(&fv_bytes).expect("Firmware Volume Corrupt");
/// for file in fv.file_iter() {
///   let file = file.map_err(|_|"parse error".to_string())?;
///   for (idx, section) in file.section_iter_with_extractor(&ExampleSectionExtractor {}).enumerate() {
///     let section = section.map_err(|_|"parse error".to_string())?;
///     println!("file: {:?}, section: {:?} type: {:?}", file.name(), idx, section.section_type());
///   }
/// }
/// # Ok(())
/// # }
///```
pub trait SectionExtractor {
    /// Extracts the given section and returns the resulting buffer.
    ///
    /// This will be called for every encapsulation section when supplied as an argument to
    /// [`File::section_iter_with_extractor`].
    ///
    /// If section extraction is successful, then the resulting buffer is returned.
    ///
    /// If the section extraction implementation supports extracting the section, but there is an error extracting it,
    /// then an error should be returned.
    ///
    /// If the section extraction implementation does not support the encapsulations type used in this section, it can
    /// return a successful extraction with a zero-size buffer - this will allow parsing the rest of the FFS while only
    /// exposing the encapsulation section as a whole (without exposing sections it contains that cannot be extracted).
    fn extract(&self, section: &Section) -> Result<Box<[u8]>, efi::Status>;
}

// Null implementation of SectionExtractor used by [`FirmwareVolume::new`] and [`File::new`] when no extraction is
// desired.
struct NullSectionExtractor {}

impl SectionExtractor for NullSectionExtractor {
    fn extract(&self, _section: &Section) -> Result<Box<[u8]>, efi::Status> {
        Ok(Box::new([0u8; 0]))
    }
}

#[derive(Clone)]
pub struct FirmwareVolumeExtHeader<'a> {
    header: fv::ExtHeader,
    data: &'a [u8],
}

impl fmt::Debug for FirmwareVolumeExtHeader<'_> {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        f.debug_struct("FirmwareVolumeExtHeader")
            .field("header", &self.header)
            .field("data.len()", &self.data.len())
            .finish_non_exhaustive()
    }
}

/// Firmware Volume access support
///
/// Provides access to firmware volume contents.
///
/// ## Example
///```
/// # use std::{env, fs, path::Path, error::Error};
/// use mu_pi::fw_fs::FirmwareVolume;
/// # fn main() -> Result<(), Box<dyn Error>> {
/// # let root = Path::new(&env::var("CARGO_MANIFEST_DIR")?).join("test_resources");
/// # let fv_bytes = fs::read(root.join("GIGANTOR.Fv"))?;
/// let fv = FirmwareVolume::new(&fv_bytes).expect("Firmware Volume Corrupt");
/// println!("{:#x?}", fv);
/// # Ok(())
/// # }
///```
#[derive(Clone)]
pub struct FirmwareVolume<'a> {
    data: &'a [u8],
    attributes: EfiFvbAttributes2,
    block_map: Vec<fv::BlockMapEntry>,
    ext_header: Option<FirmwareVolumeExtHeader<'a>>,
    data_offset: usize,
    erase_byte: u8,
}

impl<'a> FirmwareVolume<'a> {
    /// Instantiate a new FirmwareVolume.
    ///
    /// Contents of the FirmwareVolume will be cached in this instance.
    pub fn new(buffer: &'a [u8]) -> Result<Self, efi::Status> {
        //buffer must be large enough to hold the header structure.
        if buffer.len() < mem::size_of::<fv::Header>() {
            Err(efi::Status::INVALID_PARAMETER)?;
        }

        //Safety: buffer is large enough to contain the header, so can cast to a ref.
        let fv_header = unsafe { &*(buffer.as_ptr() as *const fv::Header) };

        // signature: must be ASCII '_FVH'
        if fv_header.signature != u32::from_le_bytes(*b"_FVH") {
            Err(efi::Status::VOLUME_CORRUPTED)?;
        }

        // header_length: must be large enough to hold the header.
        if (fv_header.header_length as usize) < mem::size_of::<fv::Header>() {
            Err(efi::Status::VOLUME_CORRUPTED)?;
        }

        // header_length: buffer must be large enough to hold the header.
        if (fv_header.header_length as usize) > buffer.len() {
            Err(efi::Status::VOLUME_CORRUPTED)?;
        }

        // checksum: fv header must sum to zero (and must be multiple of 2 bytes)
        if fv_header.header_length & 0x01 != 0 {
            Err(efi::Status::VOLUME_CORRUPTED)?;
        }

        let header_slice = &buffer[..fv_header.header_length as usize];
        let sum: Wrapping<u16> =
            header_slice.chunks_exact(2).map(|x| Wrapping(u16::from_le_bytes(x.try_into().unwrap()))).sum();

        if sum != Wrapping(0u16) {
            Err(efi::Status::VOLUME_CORRUPTED)?;
        }

        // revision: must be at least 2. Assumes that if later specs bump the rev they will maintain
        // backwards compat with existing header definition.
        if fv_header.revision < 2 {
            Err(efi::Status::VOLUME_CORRUPTED)?;
        }

        // file_system_guid: must be EFI_FIRMWARE_FILE_SYSTEM2_GUID or EFI_FIRMWARE_FILE_SYSTEM3_GUID.
        if fv_header.file_system_guid != ffs::guid::EFI_FIRMWARE_FILE_SYSTEM2_GUID
            && fv_header.file_system_guid != ffs::guid::EFI_FIRMWARE_FILE_SYSTEM3_GUID
        {
            Err(efi::Status::INVALID_PARAMETER)?;
        }

        // fv_length: must be large enough to hold the header.
        if fv_header.fv_length < fv_header.header_length as u64 {
            Err(efi::Status::VOLUME_CORRUPTED)?;
        }

        // fv_length: must be less than or equal to fv_data buffer length
        if fv_header.fv_length > buffer.len() as u64 {
            Err(efi::Status::VOLUME_CORRUPTED)?;
        }

        //ext_header_offset: must be inside the fv
        if fv_header.ext_header_offset as u64 > fv_header.fv_length {
            Err(efi::Status::VOLUME_CORRUPTED)?;
        }

        //if ext_header is present, its size must fit inside the FV.
        let ext_header = {
            if fv_header.ext_header_offset != 0 {
                let ext_header_offset = fv_header.ext_header_offset as usize;
                if ext_header_offset + mem::size_of::<fv::ExtHeader>() > buffer.len() {
                    Err(efi::Status::VOLUME_CORRUPTED)?;
                }

                //Safety: previous check ensures that fv_data is large enough to contain the ext_header
                let ext_header = unsafe { &*(buffer[ext_header_offset..].as_ptr() as *const fv::ExtHeader) };
                let ext_header_end = ext_header_offset + ext_header.ext_header_size as usize;
                if ext_header_end > buffer.len() {
                    Err(efi::Status::VOLUME_CORRUPTED)?;
                }
                Some(FirmwareVolumeExtHeader { header: *ext_header, data: &buffer[ext_header_offset..ext_header_end] })
            } else {
                None
            }
        };

        //block map must fit within the fv header (which is checked above to guarantee it is within the fv_data buffer).
        let block_map = &buffer[mem::size_of::<fv::Header>()..fv_header.header_length as usize];

        //block map should be a multiple of 8 in size
        if block_map.len() & 0x7 != 0 {
            Err(efi::Status::VOLUME_CORRUPTED)?;
        }

        let mut block_map = block_map
            .chunks_exact(8)
            .map(|x| fv::BlockMapEntry {
                num_blocks: u32::from_le_bytes(x[..4].try_into().unwrap()),
                length: u32::from_le_bytes(x[4..].try_into().unwrap()),
            })
            .collect::<Vec<_>>();

        //block map should terminate with zero entry
        if block_map.last() != Some(&fv::BlockMapEntry { num_blocks: 0, length: 0 }) {
            Err(efi::Status::VOLUME_CORRUPTED)?;
        }

        //remove the terminator.
        block_map.pop();

        //thre must be at least one valid entry in the block map.
        if block_map.is_empty() {
            Err(efi::Status::VOLUME_CORRUPTED)?;
        }

        //other entries in block map must be non-zero.
        if block_map.iter().any(|x| x == &fv::BlockMapEntry { num_blocks: 0, length: 0 }) {
            Err(efi::Status::VOLUME_CORRUPTED)?;
        }

        let data_offset = {
            if let Some(ext_header) = &ext_header {
                // if ext header exists, then data starts after ext header
                fv_header.ext_header_offset as usize + ext_header.header.ext_header_size as usize
            } else {
                // otherwise data starts after the fv_header.
                fv_header.header_length as usize
            }
        };

        let data_offset = align_up(data_offset as u64, 8) as usize;
        let erase_byte = if fv_header.attributes & Fvb2RawAttributes::ERASE_POLARITY != 0 { 0xff } else { 0 };

        Ok(Self { data: buffer, attributes: fv_header.attributes, block_map, ext_header, data_offset, erase_byte })
    }

    /// Instantiate a new FirmwareVolume from a base address.
    ///
    /// ## Safety
    /// Caller must ensure that base_address is the address of the start of a firmware volume.
    ///
    /// Contents of the FirmwareVolume will be cached in this instance.
    pub unsafe fn new_from_address(base_address: u64) -> Result<Self, efi::Status> {
        // SAFETY: per function safety contract, base_address is the start of a firmware volume. The firmware volume
        // header signature is checked for a new firmware volume is returned.
        let fv_header = unsafe { &*(base_address as *const fv::Header) };
        if fv_header.signature != u32::from_le_bytes(*b"_FVH") {
            // base_address is not the start of a firmware volume.
            return Err(efi::Status::VOLUME_CORRUPTED);
        }

        // SAFETY: The slice creation is considered safe if the base address for the FV header has a valid
        // firmware volume header signature.
        let fv_buffer = unsafe { slice::from_raw_parts(base_address as *const u8, fv_header.fv_length as usize) };
        Self::new(fv_buffer)
    }

    /// Returns the block map for the FV
    pub fn block_map(&self) -> &Vec<fv::BlockMapEntry> {
        &self.block_map
    }

    /// Returns the GUID name of the FV, if any.
    pub fn fv_name(&self) -> Option<efi::Guid> {
        self.ext_header.as_ref().map(|ext_header| ext_header.header.fv_name)
    }

    /// Returns an iterator of the files in this FV.
    pub fn file_iter(&self) -> impl Iterator<Item = Result<File<'a>, efi::Status>> {
        FvFileIterator::new(&self.data[self.data_offset..], self.erase_byte)
    }

    /// returns the (linear block offset from FV base, block_size, remaining_blocks) given an LBA.
    pub fn lba_info(&self, lba: u32) -> Result<(u32, u32, u32), efi::Status> {
        let block_map = self.block_map();

        let mut total_blocks = 0;
        let mut offset = 0;
        let mut block_size = 0;

        for entry in block_map {
            total_blocks += entry.num_blocks;
            block_size = entry.length;
            if lba < total_blocks {
                break;
            }
            offset += entry.num_blocks * entry.length;
        }

        if lba >= total_blocks {
            return Err(efi::Status::INVALID_PARAMETER); //lba out of range.
        }

        let remaining_blocks = total_blocks - lba;
        Ok((offset + lba * block_size, block_size, remaining_blocks))
    }

    /// Returns the attributes for the FirmwareVolume
    pub fn attributes(&self) -> EfiFvbAttributes2 {
        self.attributes
    }

    /// Returns the size in bytes of the FV data + header.
    pub fn size(&self) -> u64 {
        self.data.len() as u64
    }

    /// Returns the FV data.
    pub fn data(&self) -> &[u8] {
        self.data
    }
}

impl fmt::Debug for FirmwareVolume<'_> {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        f.debug_struct("FirmwareVolume")
            .field("attributes", &self.attributes)
            .field("block_map", &self.block_map)
            .field("ext_header", &self.ext_header)
            .field("data_offset", &self.data_offset)
            .field("erase_byte", &self.erase_byte)
            .field("data.len()", &self.data.len())
            .finish_non_exhaustive()
    }
}

/// File access support
///
/// Provides access to file contents.
///
/// ## Example
///```
/// # use std::{env, fs, path::Path, error::Error};
/// use mu_pi::fw_fs::FirmwareVolume;
/// # fn main() -> Result<(), Box<dyn Error>> {
/// # let root = Path::new(&env::var("CARGO_MANIFEST_DIR")?).join("test_resources");
/// # let fv_bytes = fs::read(root.join("GIGANTOR.Fv"))?;
/// let fv = FirmwareVolume::new(&fv_bytes).expect("Firmware Volume Corrupt");
/// for file in fv.file_iter() {
///   println!("{:#x?}", file);
/// }
/// # Ok(())
/// # }
///```
#[derive(Clone)]
pub struct File<'a> {
    data: &'a [u8],
    name: efi::Guid,
    file_type: u8,
    attributes: u8,
    header_size: usize,
    size: u64,
}

impl<'a> File<'a> {
    /// Instantiates a new File by parsing the given buffer.
    ///
    /// The normal way to obtain a File instance would be through the [`FirmwareVolume::file_iter()`] method, but
    /// a constructor is provided here to enable independent instantiation of a file.
    pub fn new(buffer: &'a [u8]) -> Result<Self, efi::Status> {
        // verify that buffer has enough storage for a file header.
        if buffer.len() < mem::size_of::<file::Header>() {
            Err(efi::Status::INVALID_PARAMETER)?;
        }

        //Safety: buffer is large enough to contain the header, so can cast to a ref.
        let file_header = unsafe { &*(buffer.as_ptr() as *const file::Header) };

        // determine size and data offset
        let (header_size, size) = {
            let header_size = mem::size_of::<file::Header>();
            if (file_header.attributes & LARGE_FILE) == 0 {
                //standard header with 24-bit size
                let mut size_vec = file_header.size.to_vec();
                size_vec.push(0);
                let size = u32::from_le_bytes(size_vec.try_into().unwrap());
                (header_size, size as u64)
            } else {
                //extended header with 64-bit size
                let extended_size_length = mem::size_of::<u64>();
                if buffer[header_size..].len() < extended_size_length {
                    Err(efi::Status::VOLUME_CORRUPTED)?;
                }
                let size =
                    u64::from_le_bytes(buffer[header_size..header_size + extended_size_length].try_into().unwrap());
                (header_size + extended_size_length, size as u64)
            }
        };

        // Verify that the total size of the file fits within the buffer.
        if size as usize > buffer.len() {
            Err(efi::Status::VOLUME_CORRUPTED)?;
        }

        // Interpreting the state field requires knowledge of the EFI_FVB_ERASE_POLARITY from the FV header, which is not
        // available here unless the constructor API is modified to specify it. So it is inferred based on the state of
        // the reserved bits in the EFI_FFS_FILE_STATE which spec requires to be set to EFI_FVB_ERASE_POLARITY.
        // This implementation does not support FV modification, so the only valid state is EFI_FILE_DATA_VALID.
        if (file_header.state & 0x80) == 0 {
            //erase polarity = 0. Verify DATA_VALID is set, and no higher-order bits are set.
            if file_header.state & 0xFC != ffs::file::raw::state::DATA_VALID {
                //file is not in EFI_FILE_DATA_VALID state.
                Err(efi::Status::VOLUME_CORRUPTED)?;
            }
        } else {
            //erase polarity = 1. Verify DATA_VALID is clear, and no higher-order bits are clear.
            if (!file_header.state) & 0xFC != ffs::file::raw::state::DATA_VALID {
                //file is not in EFI_FILE_DATA_VALID state.
                Err(efi::Status::VOLUME_CORRUPTED)?;
            }
        }

        //Verify the header checksum.
        let header_sum: Wrapping<u8> = buffer[..header_size].iter().map(|&x| Wrapping(x)).sum();
        // integrity_check_file and state are assumed to be zero for checksum, so subtract them here.
        let header_sum = header_sum.wrapping_sub(&Wrapping(file_header.integrity_check_file));
        let header_sum = header_sum.wrapping_sub(&Wrapping(file_header.state));
        if header_sum != Wrapping(0u8) {
            Err(efi::Status::VOLUME_CORRUPTED)?;
        }

        //Verify the file data checksum.
        if file_header.attributes & ffs::attributes::raw::CHECKSUM != 0 {
            let data_sum: Wrapping<u8> = buffer[header_size..size as usize].iter().map(|&x| Wrapping(x)).sum();
            if data_sum != Wrapping(0u8) {
                Err(efi::Status::VOLUME_CORRUPTED)?;
            }
        } else {
            // Verify that the checksum is initialized to 0xAA per spec requirements when CHECKSUM attribute is cleared.
            if file_header.integrity_check_file != 0xAA {
                Err(efi::Status::VOLUME_CORRUPTED)?;
            }
        }

        Ok(Self {
            data: &buffer[..size as usize],
            name: file_header.name,
            file_type: file_header.file_type,
            attributes: file_header.attributes,
            header_size,
            size,
        })
    }

    /// Returns the file type.
    pub fn file_type(&self) -> Option<FfsFileType> {
        match self.file_type {
            FfsFileRawType::RAW => Some(FfsFileType::Raw),
            FfsFileRawType::FREEFORM => Some(FfsFileType::FreeForm),
            FfsFileRawType::SECURITY_CORE => Some(FfsFileType::SecurityCore),
            FfsFileRawType::PEI_CORE => Some(FfsFileType::PeiCore),
            FfsFileRawType::DXE_CORE => Some(FfsFileType::DxeCore),
            FfsFileRawType::PEIM => Some(FfsFileType::Peim),
            FfsFileRawType::DRIVER => Some(FfsFileType::Driver),
            FfsFileRawType::COMBINED_PEIM_DRIVER => Some(FfsFileType::CombinedPeimDriver),
            FfsFileRawType::APPLICATION => Some(FfsFileType::Application),
            FfsFileRawType::MM => Some(FfsFileType::Mm),
            FfsFileRawType::FIRMWARE_VOLUME_IMAGE => Some(FfsFileType::FirmwareVolumeImage),
            FfsFileRawType::COMBINED_MM_DXE => Some(FfsFileType::CombinedMmDxe),
            FfsFileRawType::MM_CORE => Some(FfsFileType::MmCore),
            FfsFileRawType::MM_STANDALONE => Some(FfsFileType::MmStandalone),
            FfsFileRawType::MM_CORE_STANDALONE => Some(FfsFileType::MmCoreStandalone),
            FfsFileRawType::OEM_MIN..=FfsFileRawType::OEM_MAX => Some(FfsFileType::OemMin),
            FfsFileRawType::DEBUG_MIN..=FfsFileRawType::DEBUG_MAX => Some(FfsFileType::DebugMin),
            FfsFileRawType::FFS_PAD => Some(FfsFileType::FfsPad),
            FfsFileRawType::FFS_MIN..=FfsFileRawType::FFS_MAX => Some(FfsFileType::FfsUnknown),
            _ => None,
        }
    }

    /// Returns the file type as a raw u8.
    pub fn file_type_raw(&self) -> u8 {
        self.file_type
    }

    /// Returns the FV attributes for the file.
    pub fn fv_attributes(&self) -> EfiFvFileAttributes {
        let attributes = self.attributes;
        let data_alignment = (attributes & FfsRawAttribute::DATA_ALIGNMENT) >> 3;
        // decode alignment per Table 3.3 in PI spec 1.8 Part III.
        let mut file_attributes: u32 = match (
            data_alignment,
            (attributes & FfsRawAttribute::DATA_ALIGNMENT_2) == FfsRawAttribute::DATA_ALIGNMENT_2,
        ) {
            (0, false) => 0,
            (1, false) => 4,
            (2, false) => 7,
            (3, false) => 9,
            (4, false) => 10,
            (5, false) => 12,
            (6, false) => 15,
            (7, false) => 16,
            (x @ 0..=7, true) => (17 + x) as u32,
            (_, _) => panic!("Invalid data_alignment!"),
        };
        if attributes & FfsRawAttribute::FIXED != 0 {
            file_attributes |= FvFileRawAttribute::FIXED;
        }
        file_attributes as EfiFvFileAttributes
    }

    /// Returns the file attributes as a raw u8
    pub fn attributes_raw(&self) -> u8 {
        self.attributes
    }

    /// Returns the file name GUID.
    pub fn name(&self) -> efi::Guid {
        self.name
    }

    /// Returns the size in bytes of the whole file, including the header.
    pub fn size(&self) -> u64 {
        self.size
    }

    /// Returns the raw data from the file (without extracting any sections), not including the header.
    pub fn content(&self) -> &[u8] {
        &self.data[self.header_size..self.size as usize]
    }

    /// Returns the raw data for the file, including the header.
    pub fn data(&self) -> &[u8] {
        self.data
    }

    // Returns an iterator over the sections of this file (without extracting encapsulation sections).
    pub fn section_iter(&self) -> impl Iterator<Item = Result<Section, efi::Status>> + '_ {
        self.section_iter_with_extractor(&NullSectionExtractor {})
    }

    // Returns an iterator over the sections of this file, extracting encapsulation sections with the given extractor.
    pub fn section_iter_with_extractor<'b>(
        &'b self,
        extractor: &'b dyn SectionExtractor,
    ) -> impl Iterator<Item = Result<Section, efi::Status>> + 'b {
        FileSectionIterator::new(&self.data[self.header_size..self.size as usize], extractor)
    }
}

impl fmt::Debug for File<'_> {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        f.debug_struct("File")
            .field("name", &self.name)
            .field("file_type", &self.file_type)
            .field("attributes", &self.attributes)
            .field("header_size", &self.header_size)
            .field("size", &self.size)
            .field("data.len()", &self.data.len())
            .finish_non_exhaustive()
    }
}

/// Section Metadata
///
/// Describes the meta data in the section header (if any - most section types do not have metadata).
#[derive(Debug, Clone)]
pub enum SectionMetaData {
    None,
    Compression(FfsSectionHeader::Compression),
    GuidDefined(FfsSectionHeader::GuidDefined, Box<[u8]>),
    Version(FfsSectionHeader::Version),
    FreeformSubtypeGuid(FfsSectionHeader::FreeformSubtypeGuid),
}

/// Section access support
///
/// Provides access to section contents.
///
/// ## Example
///```
/// # use std::{env, fs, path::Path, error::Error};
/// use mu_pi::fw_fs::FirmwareVolume;
/// # fn main() -> Result<(), Box<dyn Error>> {
/// # let root = Path::new(&env::var("CARGO_MANIFEST_DIR")?).join("test_resources");
/// # let fv_bytes = fs::read(root.join("GIGANTOR.Fv"))?;
/// let fv = FirmwareVolume::new(&fv_bytes).expect("Firmware Volume Corrupt");
/// for file in fv.file_iter() {
///   let file = file.unwrap();
///   for section in file.section_iter() {
///     println!("{:#x?}", section);
///   }
/// }
/// # Ok(())
/// # }
///```
#[derive(Clone)]
pub struct Section {
    section_type: u8,
    meta_data: SectionMetaData,
    data: Box<[u8]>,
    section_size: usize,
}

impl Section {
    /// Instantiates a new Section by parsing the given buffer.
    ///
    /// The normal way to obtain a Section instance would be through the [`File::section_iter()`] method, but
    /// a constructor is provided here to enable independent instantiation of a section.
    pub fn new(buffer: &[u8]) -> Result<Self, efi::Status> {
        // verify that buffer has enough storage for a section header.
        if buffer.len() < mem::size_of::<section::Header>() {
            Err(efi::Status::INVALID_PARAMETER)?;
        }

        //Safety: buffer is large enough to contain the header, so can cast to a ref.
        let section_header = unsafe { &*(buffer.as_ptr() as *const section::Header) };

        //determine section_size and start of section content based on whether extended size field is present.
        let header_end = mem::size_of::<section::Header>();
        let (section_size, content_offset) = {
            if section_header.size.iter().all(|&x| x == 0xff) {
                //extended header - confirm there is space for extended size
                if buffer.len() < header_end + mem::size_of::<u32>() {
                    Err(efi::Status::VOLUME_CORRUPTED)?;
                }
                let size =
                    u32::from_le_bytes(buffer[header_end..header_end + mem::size_of::<u32>()].try_into().unwrap());
                (size as usize, header_end + mem::size_of::<u32>())
            } else {
                //standard header
                let mut size_vec = section_header.size.to_vec();
                size_vec.push(0);
                let size = u32::from_le_bytes(size_vec.try_into().unwrap());
                (size as usize, header_end)
            }
        };

        let (meta_data, data) = match section_header.section_type {
            FfsSectionRawType::encapsulated::COMPRESSION => {
                let compression_header_size = mem::size_of::<section::header::Compression>();
                //verify that buffer has enough storage for a compression header.
                if buffer.len() < content_offset + compression_header_size {
                    Err(efi::Status::VOLUME_CORRUPTED)?;
                }
                //Safety: buffer is large enough to hold compression header
                let compression_header =
                    unsafe { &*(buffer[content_offset..].as_ptr() as *const section::header::Compression) };
                let data: Box<[u8]> = Box::from(&buffer[content_offset + compression_header_size..section_size]);
                (SectionMetaData::Compression(*compression_header), data)
            }
            FfsSectionRawType::encapsulated::GUID_DEFINED => {
                let guid_defined_header_size = mem::size_of::<section::header::GuidDefined>();
                //verify that buffer has enough storage for a guid_defined header.
                if buffer.len() < content_offset + guid_defined_header_size {
                    Err(efi::Status::VOLUME_CORRUPTED)?;
                }
                //Safety: buffer is large enough to hold guid_defined header
                let guid_defined =
                    unsafe { &*(buffer[content_offset..].as_ptr() as *const section::header::GuidDefined) };

                //verify that buffer has enough storage for guid-specific fields.
                let data_offset = guid_defined.data_offset as usize;
                if buffer.len() < data_offset {
                    Err(efi::Status::VOLUME_CORRUPTED)?;
                }

                let guid_specific_header_fields: Box<[u8]> =
                    Box::from(&buffer[content_offset + guid_defined_header_size..data_offset]);
                let data: Box<[u8]> = Box::from(&buffer[data_offset..section_size]);

                (SectionMetaData::GuidDefined(*guid_defined, guid_specific_header_fields), data)
            }
            FfsSectionRawType::VERSION => {
                let version_header_size = mem::size_of::<section::header::Version>();
                //verify that buffer has enough storage for a version header.
                if buffer.len() < content_offset + version_header_size {
                    Err(efi::Status::VOLUME_CORRUPTED)?;
                }
                //Safety: buffer is large enough to hold version header
                let version_header =
                    unsafe { &*(buffer[content_offset..].as_ptr() as *const section::header::Version) };
                let data: Box<[u8]> = Box::from(&buffer[content_offset + version_header_size..section_size]);
                (SectionMetaData::Version(*version_header), data)
            }
            FfsSectionRawType::FREEFORM_SUBTYPE_GUID => {
                let freeform_header_size = mem::size_of::<section::header::FreeformSubtypeGuid>();
                //verify that buffer has enough storage for a freeform header.
                if buffer.len() < content_offset + freeform_header_size {
                    Err(efi::Status::VOLUME_CORRUPTED)?;
                }
                //Safety: buffer is large enough to hold freeform header
                let freeform_header =
                    unsafe { &*(buffer[content_offset..].as_ptr() as *const section::header::FreeformSubtypeGuid) };
                let data: Box<[u8]> = Box::from(&buffer[content_offset + freeform_header_size..section_size]);
                (SectionMetaData::FreeformSubtypeGuid(*freeform_header), data)
            }
            FfsSectionRawType::OEM_MIN..=FfsSectionRawType::FFS_MAX => {
                //these section types do not have a defined header. So set metadata to none, and set data to the entire section buffer.
                let data: Box<[u8]> = Box::from(buffer);
                (SectionMetaData::None, data)
            }
            _ => {
                let data: Box<[u8]> = Box::from(&buffer[content_offset..section_size]);
                (SectionMetaData::None, data)
            }
        };

        Ok(Self { section_type: section_header.section_type, meta_data, data, section_size })
    }

    /// Returns the section type.
    pub fn section_type(&self) -> Option<FfsSectionType> {
        match self.section_type {
            FfsSectionRawType::encapsulated::COMPRESSION => Some(FfsSectionType::Compression),
            FfsSectionRawType::encapsulated::GUID_DEFINED => Some(FfsSectionType::GuidDefined),
            FfsSectionRawType::encapsulated::DISPOSABLE => Some(FfsSectionType::Disposable),
            FfsSectionRawType::PE32 => Some(FfsSectionType::Pe32),
            FfsSectionRawType::PIC => Some(FfsSectionType::Pic),
            FfsSectionRawType::TE => Some(FfsSectionType::Te),
            FfsSectionRawType::DXE_DEPEX => Some(FfsSectionType::DxeDepex),
            FfsSectionRawType::VERSION => Some(FfsSectionType::Version),
            FfsSectionRawType::USER_INTERFACE => Some(FfsSectionType::UserInterface),
            FfsSectionRawType::COMPATIBILITY16 => Some(FfsSectionType::Compatibility16),
            FfsSectionRawType::FIRMWARE_VOLUME_IMAGE => Some(FfsSectionType::FirmwareVolumeImage),
            FfsSectionRawType::FREEFORM_SUBTYPE_GUID => Some(FfsSectionType::FreeformSubtypeGuid),
            FfsSectionRawType::RAW => Some(FfsSectionType::Raw),
            FfsSectionRawType::PEI_DEPEX => Some(FfsSectionType::PeiDepex),
            FfsSectionRawType::MM_DEPEX => Some(FfsSectionType::MmDepex),
            _ => None,
        }
    }

    /// Returns the section type as a raw u8.
    pub fn section_type_raw(&self) -> u8 {
        self.section_type
    }

    /// Indicates whether this section is an encapsulation section (i.e. can be expended with a SectionExtractor).
    pub fn is_encapsulation(&self) -> bool {
        self.section_type() == Some(FfsSectionType::Compression)
            || self.section_type() == Some(FfsSectionType::GuidDefined)
    }

    /// Returns the section metadata.
    pub fn meta_data(&self) -> &SectionMetaData {
        &self.meta_data
    }

    /// Returns the section data.
    pub fn section_data(&self) -> &[u8] {
        &self.data
    }
    pub fn section_size(&self) -> usize {
        self.section_size
    }
}

impl fmt::Debug for Section {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        f.debug_struct("Section")
            .field("section_type", &self.section_type)
            .field("meta_data", &self.meta_data)
            .field("data.len()", &self.data.len())
            .finish_non_exhaustive()
    }
}

struct FvFileIterator<'a> {
    buffer: &'a [u8],
    erase_byte: u8,
    next_offset: usize,
    error: bool,
}

impl<'a> FvFileIterator<'a> {
    pub fn new(buffer: &'a [u8], erase_byte: u8) -> Self {
        FvFileIterator { buffer, erase_byte, next_offset: 0, error: false }
    }
}

impl<'a> Iterator for FvFileIterator<'a> {
    type Item = Result<File<'a>, efi::Status>;

    fn next(&mut self) -> Option<Self::Item> {
        if self.error {
            return None;
        }
        if self.next_offset > self.buffer.len() {
            return None;
        }
        if self.buffer[self.next_offset..].len() < mem::size_of::<file::Header>() {
            return None;
        }
        if self.buffer[self.next_offset..self.next_offset + mem::size_of::<file::Header>()]
            .iter()
            .all(|&x| x == self.erase_byte)
        {
            return None;
        }
        let result = File::new(&self.buffer[self.next_offset..]);
        if let Ok(ref file) = result {
            // per the PI spec, "Given a file F, the next file FvHeader is located at the next 8-byte aligned firmware volume
            // offset following the last byte the file F"
            self.next_offset = align_up(self.next_offset as u64 + file.size(), 8) as usize;
        } else {
            self.error = true;
        }

        Some(result)
    }
}

struct FileSectionIterator<'a> {
    buffer: &'a [u8],
    extractor: &'a dyn SectionExtractor,
    next_offset: usize,
    error: bool,
    pending_extracted_sections: VecDeque<Result<Section, efi::Status>>,
}

impl<'a> FileSectionIterator<'a> {
    pub fn new(buffer: &'a [u8], extractor: &'a dyn SectionExtractor) -> Self {
        FileSectionIterator {
            buffer,
            extractor,
            next_offset: 0,
            error: false,
            pending_extracted_sections: VecDeque::new(),
        }
    }
}

impl Iterator for FileSectionIterator<'_> {
    type Item = Result<Section, efi::Status>;

    fn next(&mut self) -> Option<Self::Item> {
        if self.error {
            return None;
        }

        if let Some(result) = self.pending_extracted_sections.pop_front() {
            if result.is_err() {
                self.error = true;
            }
            return Some(result);
        }

        if self.next_offset > self.buffer.len() {
            return None;
        }

        if self.buffer[self.next_offset..].len() < mem::size_of::<ffs::section::Header>() {
            return None;
        }
        let result = Section::new(&self.buffer[self.next_offset..]);
        if let Ok(ref section) = result {
            if section.is_encapsulation() {
                // attempt to extract the encapsulated section.
                match self.extractor.extract(section) {
                    Ok(extracted_buffer) => {
                        for section in FileSectionIterator::new(&extracted_buffer, self.extractor) {
                            self.pending_extracted_sections.push_back(section);
                        }
                    }
                    Err(err) => {
                        // on error, push the error on pending sections. This encapsulation section will be returned, and on the
                        // next iteration, the error will be returned.
                        self.pending_extracted_sections.push_back(Err(err));
                    }
                }
            }
            self.next_offset += align_up(section.section_size() as u64, 4) as usize;
        } else {
            self.error = true;
        }
        Some(result)
    }
}

#[cfg(test)]
mod unit_tests {
    use std::{
        collections::HashMap,
        env,
        error::Error,
        fs::{self, File},
        path::Path,
    };

    use core::{mem, sync::atomic::AtomicBool};
    use r_efi::efi;
    use serde::Deserialize;
    use uuid::Uuid;

    use crate::fw_fs::{SectionMetaData, guid};

    use super::{FfsSectionType, FirmwareVolume, NullSectionExtractor, Section, SectionExtractor, fv};

    #[derive(Debug, Deserialize)]
    struct TargetValues {
        total_number_of_files: u32,
        files_to_test: HashMap<String, FfsFileTargetValues>,
    }

    #[derive(Debug, Deserialize)]
    struct FfsFileTargetValues {
        file_type: u8,
        attributes: u8,
        size: u64,
        number_of_sections: usize,
        sections: HashMap<usize, FfsSectionTargetValues>,
    }

    #[derive(Debug, Deserialize)]
    struct FfsSectionTargetValues {
        section_type: Option<FfsSectionType>,
        size: u64,
        text: Option<String>,
    }

    fn stringify(error: efi::Status) -> String {
        format!("efi error: {:x?}", error).to_string()
    }

    fn test_firmware_volume_worker(
        fv: FirmwareVolume,
        mut expected_values: TargetValues,
        extractor: &dyn SectionExtractor,
    ) -> Result<(), Box<dyn Error>> {
        let mut count = 0;
        for ffs_file in fv.file_iter() {
            let ffs_file = ffs_file.map_err(stringify)?;
            count += 1;
            let file_name = Uuid::from_bytes_le(*ffs_file.name().as_bytes()).to_string().to_uppercase();
            if let Some(mut target) = expected_values.files_to_test.remove(&file_name) {
                assert_eq!(target.file_type, ffs_file.file_type_raw(), "[{file_name}] Error with the file type.");
                assert_eq!(
                    target.attributes,
                    ffs_file.attributes_raw(),
                    "[{file_name}] Error with the file attributes."
                );
                assert_eq!(target.size, ffs_file.size(), "[{file_name}] Error with the file size (Full size).");
                let sections: Result<Vec<Section>, efi::Status> =
                    ffs_file.section_iter_with_extractor(extractor).collect();
                let sections = sections.map_err(stringify)?;
                for section in sections.iter().enumerate() {
                    println!("{:x?}", section);
                }
                assert_eq!(
                    target.number_of_sections,
                    sections.len(),
                    "[{file_name}] Error with the number of section in the File"
                );

                for (idx, section) in sections.iter().enumerate() {
                    if let Some(target) = target.sections.remove(&idx) {
                        assert_eq!(
                            target.section_type,
                            section.section_type(),
                            "[{file_name}, section: {idx}] Error with the section Type"
                        );
                        assert_eq!(
                            target.size,
                            section.section_data().len() as u64,
                            "[{file_name}, section: {idx}] Error with the section Size"
                        );
                        assert_eq!(
                            target.text,
                            extract_text_from_section(section),
                            "[{file_name}, section: {idx}] Error with the section Text"
                        );
                    }
                }

                assert!(target.sections.is_empty(), "Some section use case has not been run.");
            }
        }
        assert_eq!(
            expected_values.total_number_of_files, count,
            "The number of file found does not match the expected one."
        );
        assert!(expected_values.files_to_test.is_empty(), "Some file use case has not been run.");
        Ok(())
    }

    fn extract_text_from_section(section: &Section) -> Option<String> {
        if section.section_type() == Some(FfsSectionType::UserInterface) {
            let display_name_chars: Vec<u16> =
                section.section_data().chunks(2).map(|x| u16::from_le_bytes(x.try_into().unwrap())).collect();
            Some(String::from_utf16_lossy(&display_name_chars).trim_end_matches(char::from(0)).to_string())
        } else {
            None
        }
    }

    #[test]
    fn test_firmware_volume() -> Result<(), Box<dyn Error>> {
        let root = Path::new(&env::var("CARGO_MANIFEST_DIR")?).join("test_resources");

        let fv_bytes = fs::read(root.join("DXEFV.Fv"))?;
        let fv = FirmwareVolume::new(&fv_bytes).unwrap();

        let expected_values =
            serde_yml::from_reader::<File, TargetValues>(File::open(root.join("DXEFV_expected_values.yml"))?)?;

        test_firmware_volume_worker(fv, expected_values, &NullSectionExtractor {})
    }

    #[test]
    fn test_giant_firmware_volume() -> Result<(), Box<dyn Error>> {
        let root = Path::new(&env::var("CARGO_MANIFEST_DIR")?).join("test_resources");

        let fv_bytes = fs::read(root.join("GIGANTOR.Fv"))?;
        let fv = FirmwareVolume::new(&fv_bytes).unwrap();

        let expected_values =
            serde_yml::from_reader::<File, TargetValues>(File::open(root.join("GIGANTOR_expected_values.yml"))?)?;

        test_firmware_volume_worker(fv, expected_values, &NullSectionExtractor {})
    }

    #[test]
    fn test_section_extraction() -> Result<(), Box<dyn Error>> {
        let root = Path::new(&env::var("CARGO_MANIFEST_DIR")?).join("test_resources");

        let fv_bytes = fs::read(root.join("FVMAIN_COMPACT.Fv"))?;

        let expected_values =
            serde_yml::from_reader::<File, TargetValues>(File::open(root.join("FVMAIN_COMPACT_expected_values.yml"))?)?;

        struct TestExtractor {
            invoked: AtomicBool,
        }

        impl SectionExtractor for TestExtractor {
            fn extract(&self, section: &Section) -> Result<Box<[u8]>, efi::Status> {
                let SectionMetaData::GuidDefined(metadata, _guid_specific) = section.meta_data() else {
                    panic!("Unexpected section metadata");
                };
                assert_eq!(metadata.section_definition_guid, guid::BROTLI_SECTION);
                self.invoked.store(true, core::sync::atomic::Ordering::SeqCst);
                Ok(Box::new([0u8; 0]))
            }
        }

        let test_extractor = TestExtractor { invoked: AtomicBool::new(false) };

        let fv = FirmwareVolume::new(&fv_bytes).unwrap();

        test_firmware_volume_worker(fv, expected_values, &test_extractor)?;

        assert!(test_extractor.invoked.load(core::sync::atomic::Ordering::SeqCst));

        Ok(())
    }

    #[test]
    fn test_malformed_firmware_volume() -> Result<(), Box<dyn Error>> {
        let root = Path::new(&env::var("CARGO_MANIFEST_DIR")?).join("test_resources");

        // bogus signature.
        let mut fv_bytes = fs::read(root.join("DXEFV.Fv"))?;
        let fv_header = fv_bytes.as_mut_ptr() as *mut fv::Header;
        unsafe {
            (*fv_header).signature ^= 0xdeadbeef;
        };
        assert_eq!(FirmwareVolume::new(&fv_bytes).unwrap_err(), efi::Status::VOLUME_CORRUPTED);

        // bogus header_length.
        let mut fv_bytes = fs::read(root.join("DXEFV.Fv"))?;
        let fv_header = fv_bytes.as_mut_ptr() as *mut fv::Header;
        unsafe {
            (*fv_header).header_length = 0;
        };
        assert_eq!(FirmwareVolume::new(&fv_bytes).unwrap_err(), efi::Status::VOLUME_CORRUPTED);

        // bogus checksum.
        let mut fv_bytes = fs::read(root.join("DXEFV.Fv"))?;
        let fv_header = fv_bytes.as_mut_ptr() as *mut fv::Header;
        unsafe {
            (*fv_header).checksum ^= 0xbeef;
        };
        assert_eq!(FirmwareVolume::new(&fv_bytes).unwrap_err(), efi::Status::VOLUME_CORRUPTED);

        // bogus revision.
        let mut fv_bytes = fs::read(root.join("DXEFV.Fv"))?;
        let fv_header = fv_bytes.as_mut_ptr() as *mut fv::Header;
        unsafe {
            (*fv_header).revision = 1;
        };
        assert_eq!(FirmwareVolume::new(&fv_bytes).unwrap_err(), efi::Status::VOLUME_CORRUPTED);

        // bogus filesystem guid.
        let mut fv_bytes = fs::read(root.join("DXEFV.Fv"))?;
        let fv_header = fv_bytes.as_mut_ptr() as *mut fv::Header;
        unsafe {
            (*fv_header).file_system_guid = efi::Guid::from_bytes(&[0xa5; 16]);
        };
        assert_eq!(FirmwareVolume::new(&fv_bytes).unwrap_err(), efi::Status::VOLUME_CORRUPTED);

        // bogus fv length.
        let mut fv_bytes = fs::read(root.join("DXEFV.Fv"))?;
        let fv_header = fv_bytes.as_mut_ptr() as *mut fv::Header;
        unsafe {
            (*fv_header).fv_length = 0;
        };
        assert_eq!(FirmwareVolume::new(&fv_bytes).unwrap_err(), efi::Status::VOLUME_CORRUPTED);

        // bogus ext header offset.
        let mut fv_bytes = fs::read(root.join("DXEFV.Fv"))?;
        let fv_header = fv_bytes.as_mut_ptr() as *mut fv::Header;
        unsafe {
            (*fv_header).fv_length = ((*fv_header).ext_header_offset - 1) as u64;
        };
        assert_eq!(FirmwareVolume::new(&fv_bytes).unwrap_err(), efi::Status::VOLUME_CORRUPTED);

        Ok(())
    }

    #[test]
    fn zero_size_block_map_gives_same_offset_as_no_block_map() {
        //code in FirmwareVolume::new() assumes that the size of a struct that ends in a zero-size array is the same
        //as an identical struct that doesn't have the array at all. This unit test validates that assumption.
        #[repr(C)]
        struct A {
            foo: usize,
            bar: u32,
            baz: u32,
            block_map: [fv::BlockMapEntry; 0],
        }

        #[repr(C)]
        struct B {
            foo: usize,
            bar: u32,
            baz: u32,
        }
        assert_eq!(mem::size_of::<A>(), mem::size_of::<B>());

        let a = A { foo: 0, bar: 0, baz: 0, block_map: [fv::BlockMapEntry { length: 0, num_blocks: 0 }; 0] };

        let a_ptr = &a as *const A;

        unsafe {
            assert_eq!((*a_ptr).block_map.as_ptr(), a_ptr.offset(1) as *const fv::BlockMapEntry);
        }
    }

    struct ExampleSectionExtractor {}
    impl SectionExtractor for ExampleSectionExtractor {
        fn extract(&self, section: &Section) -> Result<Box<[u8]>, efi::Status> {
            println!("Encapsulated section: {:?}", section);
            Ok(Box::new([0u8; 0])) //A real section extractor would provide the extracted buffer on return.
        }
    }

    #[test]
    fn section_extract_should_extract() -> Result<(), Box<dyn Error>> {
        let root = Path::new(&env::var("CARGO_MANIFEST_DIR")?).join("test_resources");
        let fv_bytes = fs::read(root.join("GIGANTOR.Fv"))?;
        let fv = FirmwareVolume::new(&fv_bytes).expect("Firmware Volume Corrupt");
        for file in fv.file_iter() {
            let file = file.map_err(|_| "parse error".to_string())?;
            for (idx, section) in file.section_iter_with_extractor(&ExampleSectionExtractor {}).enumerate() {
                let section = section.map_err(|_| "parse error".to_string())?;
                println!("file: {:?}, section: {:?} type: {:?}", file.name(), idx, section.section_type());
            }
        }
        Ok(())
    }

    #[test]
    fn section_should_have_correct_metadata() -> Result<(), Box<dyn Error>> {
        let empty_pe32: [u8; 4] = [0x04, 0x00, 0x00, 0x10];
        let section = Section::new(&empty_pe32).unwrap();
        assert!(matches!(section.meta_data(), SectionMetaData::None));

        let empty_compression: [u8; 0x11] =
            [0x11, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00];
        let section = Section::new(&empty_compression).unwrap();
        match section.meta_data() {
            SectionMetaData::Compression(header) => {
                let length = header.uncompressed_length;
                assert_eq!(length, 0);
                assert_eq!(header.compression_type, 1);
            }
            otherwise_bad => panic!("invalid section: {:x?}", otherwise_bad),
        }

        let empty_guid_defined: [u8; 32] = [
            0x20, 0x00, 0x00, 0x02, //Header
            0x01, 0x23, 0x45, 0x67, 0x89, 0xAB, 0xCD, 0xEF, 0x01, 0x23, 0x45, 0x67, 0x89, 0xAB, 0xCD, 0xEF, //GUID
            0x1C, 0x00, //Data offset
            0x12, 0x34, //Attributes
            0x00, 0x01, 0x02, 0x03, //GUID-specific fields
            0x04, 0x15, 0x19, 0x80, //Data
        ];
        let section = Section::new(&empty_guid_defined).unwrap();
        match section.meta_data() {
            SectionMetaData::GuidDefined(header, guid_data) => {
                assert_eq!(
                    header.section_definition_guid,
                    efi::Guid::from_bytes(&[
                        0x01, 0x23, 0x45, 0x67, 0x89, 0xAB, 0xCD, 0xEF, 0x01, 0x23, 0x45, 0x67, 0x89, 0xAB, 0xCD, 0xEF
                    ])
                );
                assert_eq!(header.data_offset, 0x1C);
                assert_eq!(header.attributes, 0x3412);
                assert_eq!(guid_data.to_vec(), &[0x00u8, 0x01, 0x02, 0x03]);
                assert_eq!(section.section_data(), &[0x04, 0x15, 0x19, 0x80]);
            }
            otherwise_bad => panic!("invalid section: {:x?}", otherwise_bad),
        }

        let empty_version: [u8; 14] =
            [0x0E, 0x00, 0x00, 0x14, 0x00, 0x00, 0x31, 0x00, 0x2E, 0x00, 0x30, 0x00, 0x00, 0x00];
        let section = Section::new(&empty_version).unwrap();
        match section.meta_data() {
            SectionMetaData::Version(version) => {
                assert_eq!(version.build_number, 0);
                assert_eq!(section.section_data(), &[0x31, 0x00, 0x2E, 0x00, 0x30, 0x00, 0x00, 0x00]);
            }
            otherwise_bad => panic!("invalid section: {:x?}", otherwise_bad),
        }

        let empty_freeform_subtype: [u8; 24] = [
            0x18, 0x00, 0x00, 0x18, //Header
            0x01, 0x23, 0x45, 0x67, 0x89, 0xAB, 0xCD, 0xEF, 0x01, 0x23, 0x45, 0x67, 0x89, 0xAB, 0xCD, 0xEF, //GUID
            0x04, 0x15, 0x19, 0x80, //Data
        ];
        let section = Section::new(&empty_freeform_subtype).unwrap();
        match section.meta_data() {
            SectionMetaData::FreeformSubtypeGuid(ffst_header) => {
                assert_eq!(
                    ffst_header.sub_type_guid,
                    efi::Guid::from_bytes(&[
                        0x01, 0x23, 0x45, 0x67, 0x89, 0xAB, 0xCD, 0xEF, 0x01, 0x23, 0x45, 0x67, 0x89, 0xAB, 0xCD, 0xEF
                    ])
                );
                assert_eq!(section.section_data(), &[0x04, 0x15, 0x19, 0x80]);
            }
            otherwise_bad => panic!("invalid section: {:x?}", otherwise_bad),
        }

        Ok(())
    }
}