Skip to main content

bias_loader_uefi/
lib.rs

1use std::borrow::Cow;
2use std::ffi::CStr;
3use std::fmt::Display;
4use std::os::raw::c_char;
5use std::ptr::slice_from_raw_parts;
6
7use self_cell::self_cell;
8use thiserror::Error;
9use uuid::Uuid;
10
11use serde_with::{As, BorrowCow};
12
13pub mod depex;
14use depex::DepExOpcode;
15
16pub mod secureboot;
17
18pub mod parsers;
19pub use parsers::{try_unpack, ParsedImage};
20
21pub const INVALID_GUID: &'static str = "00000000-0000-0000-0000-000000000000";
22
23#[cxx::bridge(namespace = "efiloader")]
24mod ffi {
25    unsafe extern "C++" {
26        include!("bias-loader-uefi/cxx/uefitool.h");
27
28        type Uefitool<'a>;
29
30        unsafe fn uefitool_new<'a>(
31            buffer: *const u8,
32            size: usize,
33        ) -> Result<UniquePtr<Uefitool<'a>>>;
34
35        fn uefitool_dump<'a>(x: &mut UniquePtr<Uefitool<'a>>) -> Result<()>;
36
37        unsafe fn uefitool_for_each_module<'a>(
38            x: &UniquePtr<Uefitool<'a>>,
39            cb: *mut c_char,
40            ud: *mut c_char,
41        );
42        unsafe fn uefitool_count_modules<'a>(x: &UniquePtr<Uefitool<'a>>) -> usize;
43
44        unsafe fn uefitool_for_each_raw_section<'a>(
45            x: &UniquePtr<Uefitool<'a>>,
46            cb: *mut c_char,
47            ud: *mut c_char,
48        );
49        unsafe fn uefitool_count_raw_sections<'a>(x: &UniquePtr<Uefitool<'a>>) -> usize;
50
51        unsafe fn uefitool_for_each_nvram<'a>(
52            x: &UniquePtr<Uefitool<'a>>,
53            cb: *mut c_char,
54            ud: *mut c_char,
55        );
56        unsafe fn uefitool_count_nvram<'a>(x: &UniquePtr<Uefitool<'a>>) -> usize;
57
58        unsafe fn uefitool_for_each_microcode<'a>(
59            x: &UniquePtr<Uefitool<'a>>,
60            cb: *mut c_char,
61            ud: *mut c_char,
62        );
63        unsafe fn uefitool_count_microcode<'a>(x: &UniquePtr<Uefitool<'a>>) -> usize;
64
65        unsafe fn uefitool_for_each_guid_defined_section<'a>(
66            x: &UniquePtr<Uefitool<'a>>,
67            cb: *mut c_char,
68            ud: *mut c_char,
69        );
70        unsafe fn uefitool_count_guid_defined_sections<'a>(x: &UniquePtr<Uefitool<'a>>) -> usize;
71    }
72}
73
74#[derive(
75    Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, serde::Deserialize, serde::Serialize,
76)]
77pub enum UefiModuleType {
78    #[serde(alias = "raw")]
79    Raw,
80    #[serde(alias = "freeform")]
81    Freeform,
82    #[serde(alias = "sec.core", alias = "sec-core")]
83    SecCore,
84    #[serde(alias = "pei.core", alias = "pei-core")]
85    PeiCore,
86    #[serde(alias = "dxe.core", alias = "dxe-core")]
87    DxeCore,
88    #[serde(alias = "pei.module", alias = "pei-module")]
89    PeiModule,
90    #[serde(
91        alias = "dxe.driver",
92        alias = "dxe-driver",
93        alias = "dxe.module",
94        alias = "dxe-module"
95    )]
96    DxeDriver,
97    #[serde(alias = "combined.pei.dxe", alias = "combined-pei-dxe")]
98    CombinedPeiDxe,
99    #[serde(alias = "application")]
100    Application,
101    #[serde(alias = "smm.module", alias = "smm-module")]
102    SmmModule,
103    #[serde(alias = "volume.image", alias = "volume-image")]
104    VolumeImage,
105    #[serde(alias = "combined.smm.dxe", alias = "combined-smm-dxe")]
106    CombinedSmmDxe,
107    #[serde(alias = "smm.core", alias = "smm-core")]
108    SmmCore,
109    #[serde(
110        alias = "smm.standalone",
111        alias = "smm-standalone",
112        alias = "smm.standalone.module",
113        alias = "smm-standalone-module"
114    )]
115    SmmStandaloneModule,
116    #[serde(alias = "smm.standalone.core", alias = "smm-standalone-core")]
117    SmmStandaloneCore,
118    #[serde(alias = "oem")]
119    Oem(u8),
120    #[serde(alias = "debug")]
121    Debug(u8),
122    #[serde(alias = "pad")]
123    Pad,
124    #[serde(alias = "ffs")]
125    Ffs(u8),
126    #[serde(alias = "unknown")]
127    Unknown,
128}
129
130impl UefiModuleType {
131    pub fn is_pei(&self) -> bool {
132        matches!(self, Self::PeiCore | Self::PeiModule | Self::CombinedPeiDxe)
133    }
134
135    pub fn is_dxe(&self) -> bool {
136        matches!(
137            self,
138            Self::DxeCore
139                | Self::CombinedPeiDxe
140                | Self::CombinedSmmDxe
141                | Self::DxeDriver
142                | Self::Application
143        )
144    }
145
146    pub fn is_smm(&self) -> bool {
147        matches!(
148            self,
149            Self::SmmModule
150                | Self::CombinedSmmDxe
151                | Self::SmmCore
152                | Self::SmmStandaloneModule
153                | Self::SmmStandaloneCore
154        )
155    }
156}
157
158impl Default for UefiModuleType {
159    fn default() -> Self {
160        Self::Unknown
161    }
162}
163
164impl From<u8> for UefiModuleType {
165    fn from(ft: u8) -> Self {
166        match ft {
167            0x01 => Self::Raw,
168            0x02 => Self::Freeform,
169            0x03 => Self::SecCore,
170            0x04 => Self::PeiCore,
171            0x05 => Self::DxeCore,
172            0x06 => Self::PeiModule,
173            0x07 => Self::DxeDriver,
174            0x08 => Self::CombinedPeiDxe,
175            0x09 => Self::Application,
176            0x0a => Self::SmmModule,
177            0x0b => Self::VolumeImage,
178            0x0c => Self::CombinedSmmDxe,
179            0x0d => Self::SmmCore,
180            0x0e => Self::SmmStandaloneModule,
181            0x0f => Self::SmmStandaloneCore,
182            0xc0..=0xdf => Self::Oem(ft),
183            0xe0..=0xef => Self::Debug(ft),
184            0xf0 => Self::Pad,
185            0xf1..=0xff => Self::Ffs(ft),
186            _ => Self::Unknown,
187        }
188    }
189}
190
191impl From<UefiModuleType> for u8 {
192    fn from(t: UefiModuleType) -> Self {
193        match t {
194            UefiModuleType::Raw => 0x01,
195            UefiModuleType::Freeform => 0x02,
196            UefiModuleType::SecCore => 0x03,
197            UefiModuleType::PeiCore => 0x04,
198            UefiModuleType::DxeCore => 0x05,
199            UefiModuleType::PeiModule => 0x06,
200            UefiModuleType::DxeDriver => 0x07,
201            UefiModuleType::CombinedPeiDxe => 0x08,
202            UefiModuleType::Application => 0x09,
203            UefiModuleType::SmmModule => 0x0a,
204            UefiModuleType::VolumeImage => 0x0b,
205            UefiModuleType::CombinedSmmDxe => 0x0c,
206            UefiModuleType::SmmCore => 0x0d,
207            UefiModuleType::SmmStandaloneModule => 0x0e,
208            UefiModuleType::SmmStandaloneCore => 0x0f,
209            UefiModuleType::Oem(ft) => ft,
210            UefiModuleType::Debug(ft) => ft,
211            UefiModuleType::Pad => 0xf0,
212            UefiModuleType::Ffs(ft) => ft,
213            UefiModuleType::Unknown => 0x00,
214        }
215    }
216}
217
218#[derive(Debug, Clone, PartialEq, Eq, Hash, serde::Serialize, serde::Deserialize)]
219pub struct UefiModule<'a> {
220    #[serde(borrow)]
221    #[serde(with = "As::<BorrowCow>")]
222    name: Cow<'a, str>,
223    #[serde(borrow)]
224    #[serde(with = "As::<BorrowCow>")]
225    real_name: Cow<'a, str>,
226    #[serde(borrow)]
227    #[serde(with = "As::<Option<BorrowCow>>")]
228    guid: Option<Cow<'a, str>>,
229    module_type: UefiModuleType,
230    is_pe: bool,
231    is_te: bool,
232    has_ui: bool,
233    is_duplicate: bool,
234    depex: Vec<DepExOpcode>,
235    #[serde(borrow)]
236    #[serde(with = "As::<BorrowCow>")]
237    bytes: Cow<'a, [u8]>,
238}
239
240pub type UefiData<'a> = UefiModule<'a>;
241pub type UefiSectionType = UefiModuleType;
242
243impl<'a> UefiModule<'a> {
244    pub fn new(
245        name: impl Into<Cow<'a, str>>,
246        guid: Option<impl Into<Cow<'a, str>>>,
247        module_type: impl Into<UefiModuleType>,
248        is_pe: bool,
249        is_te: bool,
250        has_ui: bool,
251        depex: Vec<DepExOpcode>,
252        bytes: impl Into<Cow<'a, [u8]>>,
253    ) -> Self {
254        let name = name.into();
255        Self::new_with(
256            name.clone(),
257            name,
258            guid,
259            module_type,
260            is_pe,
261            is_te,
262            has_ui,
263            false,
264            depex,
265            bytes,
266        )
267    }
268
269    pub fn new_with(
270        name: impl Into<Cow<'a, str>>,
271        real_name: impl Into<Cow<'a, str>>,
272        guid: Option<impl Into<Cow<'a, str>>>,
273        module_type: impl Into<UefiModuleType>,
274        is_pe: bool,
275        is_te: bool,
276        has_ui: bool,
277        is_duplicate: bool,
278        depex: Vec<DepExOpcode>,
279        bytes: impl Into<Cow<'a, [u8]>>,
280    ) -> Self {
281        Self {
282            name: name.into(),
283            real_name: real_name.into(),
284            guid: guid.map(|g| g.into()),
285            module_type: module_type.into(),
286            is_pe,
287            is_te,
288            has_ui,
289            is_duplicate,
290            depex,
291            bytes: bytes.into(),
292        }
293    }
294
295    pub fn name(&self) -> &str {
296        &*self.name
297    }
298
299    pub fn real_name(&self) -> &str {
300        &*self.real_name
301    }
302
303    pub fn guid(&self) -> &str {
304        self.guid.as_deref().unwrap_or(INVALID_GUID)
305    }
306
307    pub fn has_valid_guid(&self) -> bool {
308        self.guid.is_some()
309    }
310
311    pub fn module_type(&self) -> UefiModuleType {
312        self.module_type
313    }
314
315    pub fn section_type(&self) -> UefiSectionType {
316        self.module_type
317    }
318
319    pub fn is_pe(&self) -> bool {
320        self.is_pe
321    }
322
323    pub fn is_te(&self) -> bool {
324        self.is_te
325    }
326
327    pub fn has_ui(&self) -> bool {
328        self.has_ui
329    }
330
331    pub fn is_duplicate(&self) -> bool {
332        self.is_duplicate
333    }
334
335    pub fn depex(&self) -> &[DepExOpcode] {
336        &*self.depex
337    }
338
339    pub fn bytes(&self) -> &[u8] {
340        &*self.bytes
341    }
342
343    pub fn into_owned(self) -> UefiModule<'static> {
344        UefiModule {
345            name: self.name.into_owned().into(),
346            real_name: self.real_name.into_owned().into(),
347            guid: self.guid.map(|g| g.into_owned().into()),
348            module_type: self.module_type,
349            is_pe: self.is_pe,
350            is_te: self.is_te,
351            has_ui: self.has_ui,
352            is_duplicate: self.is_duplicate,
353            depex: self.depex,
354            bytes: self.bytes.into_owned().into(),
355        }
356    }
357}
358
359#[derive(
360    Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, serde::Serialize, serde::Deserialize,
361)]
362pub enum UefiNvramVarType {
363    Nvar,
364    Vss,
365    Evsa,
366    Unknown,
367}
368
369#[derive(
370    Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, serde::Serialize, serde::Deserialize,
371)]
372pub enum UefiNvramVarSubType {
373    InvalidNvar,
374    InvalidNvarLink,
375    LinkNvar,
376    DataNvar,
377    FullNvar,
378
379    InvalidVss,
380    StandardVss,
381    AppleVss,
382    AuthVss,
383    IntelVss,
384
385    InvalidEvsa,
386    UnknownEvsa,
387    GuidEvsa,
388    NameEvsa,
389    DataEvsa,
390
391    Unknown,
392}
393
394impl From<u8> for UefiNvramVarType {
395    fn from(t: u8) -> Self {
396        match t {
397            0x00 => Self::Nvar,
398            0x01 => Self::Vss,
399            0x02 => Self::Evsa,
400            _ => Self::Unknown,
401        }
402    }
403}
404
405impl From<u8> for UefiNvramVarSubType {
406    fn from(t: u8) -> Self {
407        match t {
408            130 => Self::InvalidNvar,
409            131 => Self::InvalidNvarLink,
410            132 => Self::LinkNvar,
411            133 => Self::DataNvar,
412            134 => Self::FullNvar,
413
414            140 => Self::InvalidVss,
415            141 => Self::StandardVss,
416            142 => Self::AppleVss,
417            143 => Self::AuthVss,
418            144 => Self::IntelVss,
419
420            160 => Self::InvalidEvsa,
421            161 => Self::UnknownEvsa,
422            162 => Self::GuidEvsa,
423            163 => Self::NameEvsa,
424            164 => Self::DataEvsa,
425
426            _ => Self::Unknown,
427        }
428    }
429}
430
431impl From<UefiNvramVarType> for u8 {
432    fn from(t: UefiNvramVarType) -> u8 {
433        match t {
434            UefiNvramVarType::Nvar => 0x00,
435            UefiNvramVarType::Vss => 0x01,
436            UefiNvramVarType::Evsa => 0x02,
437            UefiNvramVarType::Unknown => 0xff,
438        }
439    }
440}
441
442impl From<UefiNvramVarSubType> for u8 {
443    fn from(t: UefiNvramVarSubType) -> u8 {
444        match t {
445            UefiNvramVarSubType::InvalidNvar => 130,
446            UefiNvramVarSubType::InvalidNvarLink => 131,
447            UefiNvramVarSubType::LinkNvar => 132,
448            UefiNvramVarSubType::DataNvar => 133,
449            UefiNvramVarSubType::FullNvar => 134,
450
451            UefiNvramVarSubType::InvalidVss => 140,
452            UefiNvramVarSubType::StandardVss => 141,
453            UefiNvramVarSubType::AppleVss => 142,
454            UefiNvramVarSubType::AuthVss => 143,
455            UefiNvramVarSubType::IntelVss => 144,
456
457            UefiNvramVarSubType::InvalidEvsa => 160,
458            UefiNvramVarSubType::UnknownEvsa => 161,
459            UefiNvramVarSubType::GuidEvsa => 162,
460            UefiNvramVarSubType::NameEvsa => 163,
461            UefiNvramVarSubType::DataEvsa => 164,
462
463            UefiNvramVarSubType::Unknown => 0,
464        }
465    }
466}
467
468#[derive(Debug, Clone, PartialEq, Eq, Hash, serde::Serialize, serde::Deserialize)]
469pub struct UefiNvramVar<'a> {
470    #[serde(borrow)]
471    #[serde(with = "As::<BorrowCow>")]
472    name: Cow<'a, str>,
473    #[serde(borrow)]
474    #[serde(with = "As::<Option<BorrowCow>>")]
475    guid: Option<Cow<'a, str>>,
476    var_type: UefiNvramVarType,
477    var_subtype: UefiNvramVarSubType,
478    attrs: u32,
479    ext_attrs: u8,
480    state: u8,
481    #[serde(borrow)]
482    #[serde(with = "As::<BorrowCow>")]
483    data: Cow<'a, [u8]>,
484}
485
486impl<'a> UefiNvramVar<'a> {
487    pub fn new(
488        name: impl Into<Cow<'a, str>>,
489        guid: Option<impl Into<Cow<'a, str>>>,
490        var_type: impl Into<UefiNvramVarType>,
491        var_subtype: u8,
492        attrs: u32,
493        ext_attrs: u8,
494        state: u8,
495        data: impl Into<Cow<'a, [u8]>>,
496    ) -> Self {
497        Self {
498            name: name.into(),
499            guid: guid.map(|g| g.into()),
500            var_type: var_type.into(),
501            var_subtype: var_subtype.into(),
502            attrs,
503            ext_attrs,
504            state,
505            data: data.into(),
506        }
507    }
508
509    pub fn name(&self) -> &str {
510        &*self.name
511    }
512
513    pub fn guid(&self) -> &str {
514        self.guid.as_deref().unwrap_or(INVALID_GUID)
515    }
516
517    pub fn has_valid_guid(&self) -> bool {
518        self.guid.is_some()
519    }
520
521    pub fn var_type(&self) -> UefiNvramVarType {
522        self.var_type
523    }
524
525    pub fn var_subtype(&self) -> UefiNvramVarSubType {
526        self.var_subtype
527    }
528
529    pub fn attributes(&self) -> u32 {
530        self.attrs
531    }
532
533    pub fn is_runtime(&self) -> bool {
534        match self.var_type {
535            UefiNvramVarType::Nvar => (self.attrs & 0x01) != 0,
536            UefiNvramVarType::Vss => (self.attrs & 0x04) != 0,
537            UefiNvramVarType::Evsa => (self.attrs & 0x04) != 0,
538            _ => false,
539        }
540    }
541
542    pub fn is_boot_service(&self) -> bool {
543        match self.var_type {
544            UefiNvramVarType::Vss => (self.attrs & 0x02) != 0,
545            UefiNvramVarType::Evsa => (self.attrs & 0x02) != 0,
546            _ => false,
547        }
548    }
549
550    pub fn is_non_volatile(&self) -> bool {
551        match self.var_type {
552            UefiNvramVarType::Vss => (self.attrs & 0x01) != 0,
553            UefiNvramVarType::Evsa => (self.attrs & 0x01) != 0,
554            _ => false,
555        }
556    }
557
558    pub fn is_auth_write(&self) -> bool {
559        match self.var_type {
560            UefiNvramVarType::Nvar => (self.attrs & 0x40) != 0 || (self.ext_attrs & 0x10) != 0,
561            UefiNvramVarType::Vss => (self.attrs & 0x10) != 0,
562            UefiNvramVarType::Evsa => (self.attrs & 0x10) != 0,
563            _ => false,
564        }
565    }
566
567    pub fn is_time_based_auth_write(&self) -> bool {
568        match self.var_type {
569            UefiNvramVarType::Nvar => (self.ext_attrs & 0x20) != 0,
570            UefiNvramVarType::Vss => (self.attrs & 0x20) != 0,
571            UefiNvramVarType::Evsa => (self.attrs & 0x20) != 0,
572            _ => false,
573        }
574    }
575
576    pub fn is_invalid(&self) -> bool {
577        match self.var_subtype {
578            UefiNvramVarSubType::InvalidNvar
579            | UefiNvramVarSubType::InvalidNvarLink
580            | UefiNvramVarSubType::InvalidVss
581            | UefiNvramVarSubType::InvalidEvsa => true,
582            _ => false,
583        }
584    }
585
586    pub fn is_valid(&self) -> bool {
587        !self.is_invalid()
588    }
589
590    pub fn is_added(&self) -> bool {
591        self.var_type == UefiNvramVarType::Vss && (self.state & 0x3f) != 0
592    }
593
594    pub fn is_deleted(&self) -> bool {
595        self.var_type == UefiNvramVarType::Vss && (self.state & 0xfd) != 0
596    }
597
598    pub fn is_in_deleted_transition(&self) -> bool {
599        self.var_type == UefiNvramVarType::Vss && (self.state & 0xfe) != 0
600    }
601
602    pub fn data(&self) -> &[u8] {
603        &*self.data
604    }
605
606    pub fn into_owned(self) -> UefiNvramVar<'static> {
607        UefiNvramVar {
608            name: self.name.into_owned().into(),
609            guid: self.guid.map(|g| g.into_owned().into()),
610            var_type: self.var_type,
611            var_subtype: self.var_subtype,
612            attrs: self.attrs,
613            ext_attrs: self.ext_attrs,
614            state: self.state,
615            data: self.data.into_owned().into(),
616        }
617    }
618}
619
620#[derive(
621    Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, serde::Serialize, serde::Deserialize,
622)]
623#[non_exhaustive]
624pub enum UefiMicrocodeVendor {
625    Intel,
626    Amd,
627}
628
629impl UefiMicrocodeVendor {
630    pub fn is_intel(&self) -> bool {
631        matches!(self, Self::Intel)
632    }
633
634    pub fn is_amd(&self) -> bool {
635        matches!(self, Self::Amd)
636    }
637}
638
639impl TryFrom<u8> for UefiMicrocodeVendor {
640    type Error = UefiError;
641
642    fn try_from(v: u8) -> Result<Self, Self::Error> {
643        match v {
644            0x00 => Ok(Self::Intel),
645            0x01 => Ok(Self::Amd),
646            _ => Err(UefiError::Parse),
647        }
648    }
649}
650
651impl Display for UefiMicrocodeVendor {
652    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
653        match *self {
654            UefiMicrocodeVendor::Intel => write!(f, "Intel"),
655            UefiMicrocodeVendor::Amd => write!(f, "AMD"),
656        }
657    }
658}
659
660#[derive(Debug, Clone, PartialEq, Eq, Hash, serde::Serialize, serde::Deserialize)]
661pub struct MicrocodeInfo<'a> {
662    #[serde(borrow)]
663    #[serde(with = "As::<BorrowCow>")]
664    date: Cow<'a, str>,
665    cpu_signature: u32,
666    update_revision: u32,
667    processor_flags: u8,
668    vendor: UefiMicrocodeVendor,
669}
670
671impl<'a> MicrocodeInfo<'a> {
672    fn new(
673        date: impl Into<Cow<'a, str>>,
674        cpu_signature: u32,
675        update_revision: u32,
676        processor_flags: u8,
677        vendor: UefiMicrocodeVendor,
678    ) -> Self {
679        Self {
680            date: date.into(),
681            cpu_signature,
682            update_revision,
683            processor_flags,
684            vendor,
685        }
686    }
687
688    pub fn new_amd(
689        date: impl Into<Cow<'a, str>>,
690        cpu_signature: u32,
691        update_revision: u32,
692    ) -> Self {
693        Self::new(
694            date,
695            cpu_signature,
696            update_revision,
697            0,
698            UefiMicrocodeVendor::Amd,
699        )
700    }
701
702    pub fn new_intel(
703        date: impl Into<Cow<'a, str>>,
704        cpu_signature: u32,
705        update_revision: u32,
706        processor_flags: u8,
707    ) -> Self {
708        Self::new(
709            date,
710            cpu_signature,
711            update_revision,
712            processor_flags,
713            UefiMicrocodeVendor::Intel,
714        )
715    }
716
717    pub fn vendor(&self) -> UefiMicrocodeVendor {
718        self.vendor
719    }
720
721    pub fn date(&self) -> &str {
722        &*self.date
723    }
724
725    pub fn cpu_signature(&self) -> u32 {
726        self.cpu_signature
727    }
728
729    pub fn update_revision(&self) -> u32 {
730        self.update_revision
731    }
732
733    pub fn processor_flags(&self) -> u8 {
734        self.processor_flags
735    }
736
737    pub fn into_owned(self) -> MicrocodeInfo<'static> {
738        MicrocodeInfo {
739            date: self.date.into_owned().into(),
740            cpu_signature: self.cpu_signature,
741            update_revision: self.update_revision,
742            processor_flags: self.processor_flags,
743            vendor: self.vendor,
744        }
745    }
746}
747
748#[derive(Debug, Clone, PartialEq, Eq, Hash, serde::Serialize, serde::Deserialize)]
749pub struct UefiSection<'a> {
750    #[serde(borrow)]
751    #[serde(with = "As::<Option<BorrowCow>>")]
752    guid: Option<Cow<'a, str>>,
753}
754
755impl<'a> UefiSection<'a> {
756    pub fn new(guid: Option<impl Into<Cow<'a, str>>>) -> Self {
757        Self {
758            guid: guid.map(|g| g.into()),
759        }
760    }
761
762    pub fn guid(&self) -> &str {
763        self.guid.as_deref().unwrap_or(INVALID_GUID)
764    }
765
766    pub fn has_valid_guid(&self) -> bool {
767        self.guid.is_some()
768    }
769
770    pub fn into_owned(self) -> UefiSection<'static> {
771        UefiSection {
772            guid: self.guid.map(|g| g.into_owned().into()),
773        }
774    }
775}
776
777#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
778#[repr(C)]
779pub enum ContinueOrStop {
780    Continue = 0,
781    Stop = 1,
782}
783
784unsafe fn guid_from_ptr<'a>(guid: *const c_char) -> Option<Cow<'a, str>> {
785    let guid = CStr::from_ptr(guid).to_string_lossy();
786    if Uuid::parse_str(&guid).is_ok() {
787        Some(guid)
788    } else {
789        None
790    }
791}
792
793unsafe extern "C" fn module_trampoline<F>(
794    closure: *mut c_char,
795    name: *const c_char,
796    real_name: *const c_char,
797    guid: *const c_char,
798    module_type: u8,
799    is_pe: bool,
800    is_te: bool,
801    has_ui: bool,
802    is_duplicate: bool,
803    depex: *const u8,
804    depex_size: usize,
805    buffer: *const u8,
806    buffer_size: usize,
807) -> ContinueOrStop
808where
809    F: FnMut(UefiModule) -> ContinueOrStop,
810{
811    let name = CStr::from_ptr(name);
812    let real_name = CStr::from_ptr(real_name);
813    let guid = guid_from_ptr(guid);
814    let bytes = slice_from_raw_parts(buffer, buffer_size).as_ref().unwrap();
815
816    let depex_bytes = slice_from_raw_parts(depex, depex_size).as_ref().unwrap();
817    let depex = DepExOpcode::parse_all(&depex_bytes).unwrap_or_default();
818
819    let closure = &mut *(closure as *mut F);
820
821    closure(UefiModule::new_with(
822        name.to_string_lossy(),
823        real_name.to_string_lossy(),
824        guid,
825        module_type,
826        is_pe,
827        is_te,
828        has_ui,
829        is_duplicate,
830        depex,
831        bytes,
832    ))
833}
834
835fn get_module_trampoline<F>(
836    _closure: &F,
837) -> unsafe extern "C" fn(
838    *mut c_char,
839    *const c_char,
840    *const c_char,
841    *const c_char,
842    u8,
843    bool,
844    bool,
845    bool,
846    bool,
847    *const u8,
848    usize,
849    *const u8,
850    usize,
851) -> ContinueOrStop
852where
853    F: FnMut(UefiModule) -> ContinueOrStop,
854{
855    module_trampoline::<F>
856}
857
858unsafe extern "C" fn var_trampoline<F>(
859    closure: *mut c_char,
860    var_type: u8,
861    var_subtype: u8,
862    attrs: u32,
863    ext_attrs: u8,
864    state: u8,
865    guid: *const c_char,
866    name: *const c_char,
867    data: *const u8,
868    data_size: usize,
869) -> ContinueOrStop
870where
871    F: FnMut(UefiNvramVar) -> ContinueOrStop,
872{
873    let name = CStr::from_ptr(name);
874    let guid = guid_from_ptr(guid);
875    let bytes = slice_from_raw_parts(data, data_size).as_ref().unwrap();
876    let closure = &mut *(closure as *mut F);
877
878    closure(UefiNvramVar::new(
879        name.to_string_lossy(),
880        guid,
881        var_type,
882        var_subtype,
883        attrs,
884        ext_attrs,
885        state,
886        bytes,
887    ))
888}
889
890fn get_var_trampoline<F>(
891    _closure: &F,
892) -> unsafe extern "C" fn(
893    *mut c_char,
894    u8,
895    u8,
896    u32,
897    u8,
898    u8,
899    *const c_char,
900    *const c_char,
901    *const u8,
902    usize,
903) -> ContinueOrStop
904where
905    F: FnMut(UefiNvramVar) -> ContinueOrStop,
906{
907    var_trampoline::<F>
908}
909
910unsafe extern "C" fn microcode_trampoline<F>(
911    closure: *mut c_char,
912    date: *const c_char,
913    cpu_signature: u32,
914    update_revision: u32,
915    processor_flags: u8,
916    vendor: u8,
917) -> ContinueOrStop
918where
919    F: FnMut(MicrocodeInfo) -> ContinueOrStop,
920{
921    let date = CStr::from_ptr(date);
922    let closure = &mut *(closure as *mut F);
923
924    match UefiMicrocodeVendor::try_from(vendor) {
925        Ok(UefiMicrocodeVendor::Amd) => closure(MicrocodeInfo::new_amd(
926            date.to_string_lossy(),
927            cpu_signature,
928            update_revision,
929        )),
930        Ok(UefiMicrocodeVendor::Intel) => closure(MicrocodeInfo::new_intel(
931            date.to_string_lossy(),
932            cpu_signature,
933            update_revision,
934            processor_flags,
935        )),
936        _ => unreachable!(),
937    }
938}
939
940fn get_microcode_trampoline<F>(
941    _closure: &F,
942) -> unsafe extern "C" fn(*mut c_char, *const c_char, u32, u32, u8, u8) -> ContinueOrStop
943where
944    F: FnMut(MicrocodeInfo) -> ContinueOrStop,
945{
946    microcode_trampoline::<F>
947}
948
949unsafe extern "C" fn guid_defined_section_trampoline<F>(
950    closure: *mut c_char,
951    guid: *const c_char,
952) -> ContinueOrStop
953where
954    F: FnMut(UefiSection) -> ContinueOrStop,
955{
956    let guid = guid_from_ptr(guid);
957    let closure = &mut *(closure as *mut F);
958
959    closure(UefiSection::new(guid))
960}
961
962fn get_guid_defined_section_tampiline<F>(
963    _closure: &F,
964) -> unsafe extern "C" fn(*mut c_char, guid: *const c_char) -> ContinueOrStop
965where
966    F: FnMut(UefiSection) -> ContinueOrStop,
967{
968    guid_defined_section_trampoline::<F>
969}
970
971#[derive(Debug, Error)]
972pub enum UefiError {
973    #[error("cannot dump firmware buffer")]
974    Dump,
975    #[error("cannot parse firmware buffer")]
976    Parse,
977    #[error("cannot unpack firmware buffer")]
978    Unpack(#[from] parsers::UnpackError),
979}
980
981pub type Error = UefiError;
982
983pub struct Uefi<'a> {
984    inner: cxx::UniquePtr<ffi::Uefitool<'a>>,
985}
986
987impl<'a> Uefi<'a> {
988    pub fn new(buffer: &'a [u8]) -> Result<Self, Error> {
989        let inner = unsafe {
990            let bptr = buffer.as_ptr();
991            let size = buffer.len();
992
993            ffi::uefitool_new(bptr, size)
994        }
995        .map_err(|_| Error::Parse)?;
996
997        let mut slf = Self { inner };
998
999        slf.dump()?;
1000
1001        Ok(slf)
1002    }
1003
1004    fn dump(&mut self) -> Result<(), Error> {
1005        ffi::uefitool_dump(&mut self.inner).map_err(|_| Error::Dump)
1006    }
1007
1008    pub fn for_each<F>(&self, mut f: F)
1009    where
1010        F: for<'m> FnMut(UefiModule<'m>),
1011    {
1012        self.for_each_until(move |v| {
1013            f(v);
1014            ContinueOrStop::Continue
1015        })
1016    }
1017
1018    pub fn for_each_until<F>(&self, f: F)
1019    where
1020        F: for<'m> FnMut(UefiModule<'m>) -> ContinueOrStop,
1021    {
1022        unsafe {
1023            let mut closure = f;
1024            let callback = get_module_trampoline(&closure);
1025
1026            ffi::uefitool_for_each_module(
1027                &self.inner,
1028                callback as *mut c_char,
1029                &mut closure as *mut _ as *mut c_char,
1030            )
1031        }
1032    }
1033
1034    pub fn for_each_raw_section<F>(&self, mut f: F)
1035    where
1036        F: for<'m> FnMut(UefiData<'m>),
1037    {
1038        self.for_each_raw_section_until(move |v| {
1039            f(v);
1040            ContinueOrStop::Continue
1041        })
1042    }
1043
1044    pub fn for_each_raw_section_until<F>(&self, f: F)
1045    where
1046        F: for<'m> FnMut(UefiData<'m>) -> ContinueOrStop,
1047    {
1048        unsafe {
1049            let mut closure = f;
1050            let callback = get_module_trampoline(&closure);
1051
1052            ffi::uefitool_for_each_raw_section(
1053                &self.inner,
1054                callback as *mut c_char,
1055                &mut closure as *mut _ as *mut c_char,
1056            )
1057        }
1058    }
1059
1060    pub fn for_each_var<F>(&self, mut f: F)
1061    where
1062        F: for<'v> FnMut(UefiNvramVar<'v>),
1063    {
1064        self.for_each_var_until(move |v| {
1065            f(v);
1066            ContinueOrStop::Continue
1067        })
1068    }
1069
1070    pub fn for_each_var_until<F>(&self, f: F)
1071    where
1072        F: for<'v> FnMut(UefiNvramVar<'v>) -> ContinueOrStop,
1073    {
1074        unsafe {
1075            let mut closure = f;
1076            let callback = get_var_trampoline(&closure);
1077
1078            ffi::uefitool_for_each_nvram(
1079                &self.inner,
1080                callback as *mut c_char,
1081                &mut closure as *mut _ as *mut c_char,
1082            )
1083        }
1084    }
1085
1086    pub fn for_each_microcode<F>(&self, mut f: F)
1087    where
1088        F: for<'v> FnMut(MicrocodeInfo<'v>),
1089    {
1090        self.for_each_microcode_until(move |v| {
1091            f(v);
1092            ContinueOrStop::Continue
1093        })
1094    }
1095
1096    pub fn for_each_microcode_until<F>(&self, f: F)
1097    where
1098        F: for<'v> FnMut(MicrocodeInfo<'v>) -> ContinueOrStop,
1099    {
1100        unsafe {
1101            let mut closure = f;
1102            let callback = get_microcode_trampoline(&closure);
1103
1104            ffi::uefitool_for_each_microcode(
1105                &self.inner,
1106                callback as *mut c_char,
1107                &mut closure as *mut _ as *mut c_char,
1108            )
1109        }
1110    }
1111
1112    pub fn for_each_guid_defined_section<F>(&self, mut f: F)
1113    where
1114        F: for<'v> FnMut(UefiSection<'v>),
1115    {
1116        self.for_each_guid_defined_section_until(move |v| {
1117            f(v);
1118            ContinueOrStop::Continue
1119        })
1120    }
1121
1122    pub fn for_each_guid_defined_section_until<F>(&self, f: F)
1123    where
1124        F: for<'v> FnMut(UefiSection<'v>) -> ContinueOrStop,
1125    {
1126        unsafe {
1127            let mut closure = f;
1128            let callback = get_guid_defined_section_tampiline(&closure);
1129
1130            ffi::uefitool_for_each_guid_defined_section(
1131                &self.inner,
1132                callback as *mut c_char,
1133                &mut closure as *mut _ as *mut c_char,
1134            )
1135        }
1136    }
1137
1138    pub fn count_modules(&self) -> usize {
1139        unsafe { ffi::uefitool_count_modules(&self.inner) }
1140    }
1141
1142    pub fn count_vars(&self) -> usize {
1143        unsafe { ffi::uefitool_count_nvram(&self.inner) }
1144    }
1145
1146    pub fn count_raw_sections(&self) -> usize {
1147        unsafe { ffi::uefitool_count_raw_sections(&self.inner) }
1148    }
1149
1150    pub fn count_microcode(&self) -> usize {
1151        unsafe { ffi::uefitool_count_microcode(&self.inner) }
1152    }
1153
1154    pub fn count_guid_defined_sections(&self) -> usize {
1155        unsafe { ffi::uefitool_count_guid_defined_sections(&self.inner) }
1156    }
1157}
1158
1159type UefiVec<'a> = Vec<Uefi<'a>>;
1160
1161self_cell! {
1162    struct UefiMultiInner<'a> {
1163        owner: Vec<ParsedImage<'a>>,
1164
1165        #[covariant]
1166        dependent: UefiVec,
1167    }
1168}
1169
1170pub struct UefiMulti<'a> {
1171    inner: UefiMultiInner<'a>,
1172}
1173
1174impl<'a> UefiMulti<'a> {
1175    pub fn new(buffer: &'a [u8]) -> Result<Self, Error> {
1176        let parsed = ParsedImage::from_bytes(buffer)?;
1177
1178        Ok(Self {
1179            inner: UefiMultiInner::try_new(parsed, |loaded| {
1180                loaded
1181                    .iter()
1182                    .map(|image| Uefi::new(image.bytes()))
1183                    .collect::<Result<Vec<_>, _>>()
1184            })?,
1185        })
1186    }
1187
1188    #[inline]
1189    pub fn loaded(&self) -> &[Uefi] {
1190        &self.inner.borrow_dependent()
1191    }
1192
1193    #[inline]
1194    pub fn parsed(&self) -> &[ParsedImage] {
1195        &self.inner.borrow_owner()
1196    }
1197
1198    #[inline]
1199    pub fn iter(&self) -> impl ExactSizeIterator<Item = &Uefi> {
1200        self.inner.borrow_dependent().iter()
1201    }
1202
1203    #[inline]
1204    pub fn iter_full(&self) -> impl ExactSizeIterator<Item = (&Uefi, &ParsedImage)> {
1205        self.inner
1206            .borrow_dependent()
1207            .iter()
1208            .zip(self.inner.borrow_owner().iter())
1209    }
1210}
1211
1212#[cfg(test)]
1213mod test {
1214    use std::fs::{self, File};
1215    use std::io::{BufReader, BufWriter, Read, Write};
1216    use std::path::PathBuf;
1217
1218    use super::*;
1219
1220    #[test]
1221    fn test_intel_ami() -> Result<(), Box<dyn std::error::Error>> {
1222        let mut bytes = Vec::default();
1223        let mut f = BufReader::new(File::open("./tests/fw71.bin")?);
1224        f.read_to_end(&mut bytes)?;
1225
1226        let fw = Uefi::new(&bytes)?;
1227        let mut found1 = false;
1228        let mut found2 = false;
1229
1230        fw.for_each(|m| {
1231            found1 |= m.name() == "AMITSE" && m.guid() == "B1DA0ADF-4F77-4070-A88E-BFFE1C60529A";
1232            found2 |= m.guid() == "A2DF5376-C2ED-49C0-90FF-8B173B0FD066";
1233        });
1234
1235        assert_eq!(found1, true);
1236        assert_eq!(found2, true);
1237
1238        Ok(())
1239    }
1240
1241    #[test]
1242    fn test_names() -> Result<(), Box<dyn std::error::Error>> {
1243        let mut bytes = Vec::default();
1244        let mut f = BufReader::new(File::open("./tests/fw71.bin")?);
1245        f.read_to_end(&mut bytes)?;
1246
1247        let fw = Uefi::new(&bytes)?;
1248        let mut found = false;
1249        let mut count = 0;
1250
1251        fw.for_each(|m| {
1252            found |= m.name() == "RamDiskDxe";
1253            count += 1;
1254        });
1255
1256        assert_eq!(count, 511);
1257        assert_eq!(found, true);
1258
1259        Ok(())
1260    }
1261
1262    #[test]
1263    fn test_unpack() -> Result<(), Box<dyn std::error::Error>> {
1264        let mut bytes = Vec::default();
1265        let mut f = BufReader::new(File::open("./tests/M2TKT2DA.cap")?);
1266        f.read_to_end(&mut bytes)?;
1267
1268        let pfat_bytes = try_unpack(&bytes)?.into_owned();
1269
1270        // unpacked already
1271        let mut f = BufReader::new(File::open("./tests/M2TKT2DA.bin")?);
1272
1273        bytes.clear();
1274        f.read_to_end(&mut bytes)?;
1275
1276        assert_eq!(bytes, pfat_bytes);
1277
1278        let mut f = BufReader::new(File::open("./tests/DellG3_3579_3779_1220.cap")?);
1279        let mut bytes = Vec::default();
1280        f.read_to_end(&mut bytes)?;
1281
1282        let pfs_bytes = try_unpack(&bytes)?.into_owned();
1283
1284        // unpacked already
1285        let mut f = BufReader::new(File::open("./tests/DellG3_3579_3779_1220.bin")?);
1286
1287        bytes.clear();
1288        f.read_to_end(&mut bytes)?;
1289
1290        let dell = Uefi::new(&bytes)?;
1291        let mut count = 0;
1292
1293        dell.for_each(|_| count += 1);
1294
1295        assert_eq!(count, dell.count_modules());
1296        // assert_eq!(count, 1033);
1297        assert_eq!(count, 524);
1298
1299        assert_eq!(bytes, pfs_bytes);
1300
1301        let mut f = BufReader::new(File::open("./tests/fw.bin")?);
1302        let mut bytes = Vec::default();
1303        f.read_to_end(&mut bytes)?;
1304
1305        let unk_bytes = try_unpack(&bytes)?;
1306
1307        assert_eq!(bytes, &*unk_bytes);
1308
1309        let mut f = BufReader::new(File::open("./tests/jscn23ww.cap")?);
1310        let mut bytes = Vec::default();
1311        f.read_to_end(&mut bytes)?;
1312
1313        let arm_bytes = try_unpack(&bytes)?;
1314
1315        assert_eq!(bytes, &*arm_bytes);
1316
1317        let arm = Uefi::new(&arm_bytes)?;
1318        let mut count = 0;
1319
1320        fs::create_dir_all("/tmp/extracted")?;
1321
1322        arm.for_each(|m| {
1323            let mut out = BufWriter::new(
1324                File::create(PathBuf::from_iter(["/tmp", "extracted", m.name()])).unwrap(),
1325            );
1326            out.write_all(m.bytes()).ok();
1327            count += 1;
1328        });
1329
1330        // assert_eq!(count, 371);
1331        assert_eq!(count, 186);
1332
1333        let mut f = BufReader::new(File::open(
1334            "./tests/d4d7014f4b0c4b1c3d34c18e8b92205a35b47aed87af359a068957da423053ff.bin",
1335        )?);
1336        let mut bytes = Vec::default();
1337        f.read_to_end(&mut bytes)?;
1338
1339        let mut count = 0;
1340        let load = Uefi::new(&bytes)?;
1341
1342        load.for_each(|_| count += 1);
1343
1344        assert_eq!(count, 2);
1345
1346        Ok(())
1347    }
1348
1349    #[test]
1350    fn test_serde() -> Result<(), Box<dyn std::error::Error>> {
1351        let t1 = "\"dxe.driver\"";
1352        let t2 = "\"DxeDriver\"";
1353        let t3 = "{\"ffs\": 16}";
1354        let t4 = "{\"Ffs\": 16}";
1355
1356        assert_eq!(
1357            serde_json::from_str::<UefiModuleType>(t1)?,
1358            UefiModuleType::DxeDriver
1359        );
1360
1361        assert_eq!(
1362            serde_json::from_str::<UefiModuleType>(t2)?,
1363            UefiModuleType::DxeDriver
1364        );
1365
1366        assert_eq!(
1367            serde_json::from_str::<UefiModuleType>(t3)?,
1368            UefiModuleType::Ffs(16)
1369        );
1370
1371        assert_eq!(
1372            serde_json::from_str::<UefiModuleType>(t4)?,
1373            UefiModuleType::Ffs(16)
1374        );
1375
1376        Ok(())
1377    }
1378}