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 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 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, 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, 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}