1#![no_std]
6#![feature(fn_traits)]
7#![warn(clippy::pedantic)]
8#![allow(clippy::must_use_candidate)]
9#![allow(clippy::non_ascii_literal)]
10#![allow(clippy::upper_case_acronyms)]
11
12extern crate alloc;
13
14use ::alloc::vec::Vec;
15use core::ffi::c_void;
16use core::fmt;
17use core::mem::{align_of, size_of};
18use core::slice;
19use crc_any::CRCu32;
20use tartan_bitfield::bitfield;
21use tartan_c_enum::c_enum;
22
23pub mod allocator;
24pub mod global;
25#[macro_use]
26pub mod io;
27
28
29pub const PAGE_SIZE: usize = 4096;
31
32
33#[repr(transparent)]
34#[derive(Debug, Clone, Copy, PartialEq, Eq)]
35pub struct Handle(usize);
36
37impl Handle {
38 pub const NULL: Handle = Handle(0);
39}
40
41
42c_enum! {
43 pub enum Revision(u32) {
44 V2_80 = (2 << 16) | 80,
45 V2_70 = (2 << 16) | 70,
46 V2_60 = (2 << 16) | 60,
47 V2_50 = (2 << 16) | 50,
48 V2_40 = (2 << 16) | 40,
49 V2_31 = (2 << 16) | 31,
50 V2_30 = (2 << 16) | 30,
51 V2_20 = (2 << 16) | 20,
52 V2_10 = (2 << 16) | 10,
53 V2_00 = 2 << 16 ,
54 V1_10 = (1 << 16) | 10,
55 V1_02 = (1 << 16) | 2,
56 }
57}
58
59impl Revision {
60 pub const LATEST: Revision = Revision::V2_80;
61
62 pub fn major_version(self) -> u16 {
63 #![allow(clippy::cast_possible_truncation)]
64 (self.0 >> 16) as u16
65 }
66
67 pub fn minor_version(self) -> u16 {
68 #![allow(clippy::cast_possible_truncation)]
69 (self.0 as u16) / 10
70 }
71
72 pub fn fix_version(self) -> u16 {
73 #![allow(clippy::cast_possible_truncation)]
74 (self.0 as u16) % 10
75 }
76}
77
78impl fmt::Display for Revision {
79 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
80 write!(
81 f,
82 "{}.{}.{}",
83 self.major_version(),
84 self.minor_version(),
85 self.fix_version(),
86 )
87 }
88}
89
90#[cfg(test)]
91mod test_revision {
92 use super::*;
93
94 #[test]
95 fn test_components() {
96 assert_eq!(Revision::V1_02.major_version(), 1);
97 assert_eq!(Revision::V1_02.minor_version(), 0);
98 assert_eq!(Revision::V1_02.fix_version(), 2);
99
100 assert_eq!(Revision::V2_31.major_version(), 2);
101 assert_eq!(Revision::V2_31.minor_version(), 3);
102 assert_eq!(Revision::V2_31.fix_version(), 1);
103 }
104}
105
106pub type Result = core::result::Result<Status, Status>;
107
108c_enum! {
109 pub enum Status(usize) {
110 Success = 0,
112
113 WarnUnknownGlyph = 1,
114 WarnDeleteFailure = 2,
115 WarnWriteFailure = 3,
116 WarnBufferTooSmall = 4,
117 WarnStaleData = 5,
118 WarnFileSystem = 6,
119 WarnResetRequired = 7,
120
121 LoadError = Status::ERROR_BIT | 1,
122 InvalidParameter = Status::ERROR_BIT | 2,
123 Unsupported = Status::ERROR_BIT | 3,
124 BadBufferSize = Status::ERROR_BIT | 4,
125 BufferTooSmall = Status::ERROR_BIT | 5,
126 NotReady = Status::ERROR_BIT | 6,
127 DeviceError = Status::ERROR_BIT | 7,
128 WriteProtected = Status::ERROR_BIT | 8,
129 OutOfResources = Status::ERROR_BIT | 9,
130 VolumeCorrupted = Status::ERROR_BIT | 10,
131 VolumeFull = Status::ERROR_BIT | 11,
132 NoMedia = Status::ERROR_BIT | 12,
133 MediaChanged = Status::ERROR_BIT | 13,
134 NotFound = Status::ERROR_BIT | 14,
135 AccessDenied = Status::ERROR_BIT | 15,
136 NoResponse = Status::ERROR_BIT | 16,
137 NoMapping = Status::ERROR_BIT | 17,
138 Timeout = Status::ERROR_BIT | 18,
139 NotStarted = Status::ERROR_BIT | 19,
140 AlreadyStarted = Status::ERROR_BIT | 20,
141 Aborted = Status::ERROR_BIT | 21,
142 ICMPError = Status::ERROR_BIT | 22,
143 TFTPError = Status::ERROR_BIT | 23,
144 ProtocolError = Status::ERROR_BIT | 24,
145 IncompatibleVersion = Status::ERROR_BIT | 25,
146 SecurityViolation = Status::ERROR_BIT | 26,
147 CRCError = Status::ERROR_BIT | 27,
148 EndOfMedia = Status::ERROR_BIT | 28,
149 EndOfFile = Status::ERROR_BIT | 31,
150 InvalidLanguage = Status::ERROR_BIT | 32,
151 CompromisedData = Status::ERROR_BIT | 33,
152 HTTPError = Status::ERROR_BIT | 35,
153 }
154}
155
156impl Status {
157 pub const ERROR_BIT: usize = 0x1_usize.reverse_bits(); pub fn is_error(self) -> bool {
160 (self.0 & Status::ERROR_BIT) != 0
161 }
162
163 pub fn is_warning(self) -> bool {
164 self != Status::Success && !self.is_error()
165 }
166
167 #[allow(clippy::missing_errors_doc)]
169 pub fn into_result(self) -> Result {
170 if self.is_error() {
171 Err(self)
172 } else {
173 Ok(self)
174 }
175 }
176}
177
178impl From<Status> for Result {
179 fn from(status: Status) -> Self {
180 status.into_result()
181 }
182}
183
184#[cfg(test)]
185mod test_status {
186 use super::*;
187
188 #[test]
189 fn test_equality() {
190 let status = Status(1);
191 assert_eq!(status, Status::WarnUnknownGlyph);
192 assert_ne!(status, Status::Success);
193 assert_ne!(status, Status::LoadError);
194 }
195
196 #[test]
197 #[allow(clippy::bool_assert_comparison)]
198 #[allow(clippy::branches_sharing_code)]
199 fn test_is_error() {
200 assert_eq!(false, Status::Success.is_error());
201
202 assert_eq!(false, Status::WarnUnknownGlyph.is_error());
203 assert_eq!(false, Status::WarnFileSystem.is_error());
204 assert_eq!(false, Status::WarnResetRequired.is_error());
205
206 assert_eq!(true, Status::LoadError.is_error());
207 assert_eq!(true, Status::AccessDenied.is_error());
208 assert_eq!(true, Status::HTTPError.is_error());
209
210 if cfg!(target_pointer_width = "32") {
211 assert_eq!(Status(0x0000_0000).is_error(), false);
212 assert_eq!(Status(0x0000_0001).is_error(), false);
213 assert_eq!(Status(0x7fff_ffff).is_error(), false);
214 assert_eq!(Status(0x8000_0000).is_error(), true);
215 assert_eq!(Status(0xffff_ffff).is_error(), true);
216 } else {
217 assert_eq!(Status(0x0000_0000_0000_0000).is_error(), false);
218 assert_eq!(Status(0x0000_0000_0000_0001).is_error(), false);
219 assert_eq!(Status(0x7fff_ffff_ffff_ffff).is_error(), false);
220 assert_eq!(Status(0x8000_0000_0000_0000).is_error(), true);
221 assert_eq!(Status(0xffff_ffff_ffff_ffff).is_error(), true);
222 }
223 }
224
225 #[test]
226 #[allow(clippy::bool_assert_comparison)]
227 #[allow(clippy::branches_sharing_code)]
228 fn test_is_warning() {
229 assert_eq!(false, Status::Success.is_warning());
230
231 assert_eq!(true, Status::WarnUnknownGlyph.is_warning());
232 assert_eq!(true, Status::WarnFileSystem.is_warning());
233 assert_eq!(true, Status::WarnResetRequired.is_warning());
234
235 assert_eq!(false, Status::LoadError.is_warning());
236 assert_eq!(false, Status::AccessDenied.is_warning());
237 assert_eq!(false, Status::HTTPError.is_warning());
238
239 if cfg!(target_pointer_width = "32") {
240 assert_eq!(Status(0x0000_0000).is_warning(), false);
241 assert_eq!(Status(0x0000_0001).is_warning(), true);
242 assert_eq!(Status(0x7fff_ffff).is_warning(), true);
243 assert_eq!(Status(0x8000_0000).is_warning(), false);
244 assert_eq!(Status(0xffff_ffff).is_warning(), false);
245 } else {
246 assert_eq!(Status(0x0000_0000_0000_0000).is_warning(), false);
247 assert_eq!(Status(0x0000_0000_0000_0001).is_warning(), true);
248 assert_eq!(Status(0x7fff_ffff_ffff_ffff).is_warning(), true);
249 assert_eq!(Status(0x8000_0000_0000_0000).is_warning(), false);
250 assert_eq!(Status(0xffff_ffff_ffff_ffff).is_warning(), false);
251 }
252 }
253
254 #[test]
255 fn test_into_result() {
256 assert_eq!(Status::Success.into_result(), Ok(Status::Success));
257 assert_eq!(Status::WarnFileSystem.into_result(), Ok(Status::WarnFileSystem));
258 assert_eq!(Status::LoadError.into_result(), Err(Status::LoadError));
259 assert_eq!(Status::HTTPError.into_result(), Err(Status::HTTPError));
260 }
261}
262
263
264pub trait Table {
265 const SIGNATURE: u64;
266 const MIN_REVISION: Revision;
267
268 fn header(&self) -> &TableHeader;
269
270 fn verify(&self)
271 where
272 Self: Sized,
273 {
274 self.verify_signature();
275 self.verify_revision();
276 self.verify_size();
277 self.verify_crc32();
278 }
279
280 fn verify_signature(&self) {
281 let actual_signature = self.header().signature;
282 assert!(
283 actual_signature == Self::SIGNATURE,
284 "Signature mismatch. Expected {:x}, received {:x}",
285 actual_signature,
286 Self::SIGNATURE,
287 );
288 }
289
290 fn verify_revision(&self) {
291 let actual_revision = self.header().revision;
292 assert!(
293 actual_revision >= Self::MIN_REVISION,
294 "Revision {} older than minimum supported revision {}",
295 actual_revision,
296 Self::MIN_REVISION,
297 );
298 }
299
300 fn verify_size(&self)
301 where
302 Self: Sized,
303 {
304 let actual_size = self.header().header_size as usize;
305 assert!(
306 actual_size >= size_of::<Self>(),
307 "Header size {} was less than expected {} bytes",
308 actual_size,
309 size_of::<Self>(),
310 );
311 }
312
313 fn verify_crc32(&self) {
314 const CRC_FIELD_LENGTH: usize = size_of::<u32>();
315
316 let header = self.header();
317 let size = header.header_size as usize;
318 let start_address = core::ptr::from_ref(self).cast::<u8>();
319 let crc_field_address = core::ptr::addr_of!(header.crc32).cast::<u8>();
320
321 let mut crc = CRCu32::crc32();
322 unsafe {
323 crc.digest(slice::from_raw_parts(
325 start_address,
326 crc_field_address.offset_from(start_address).try_into().unwrap(),
327 ));
328
329 crc.digest(&[0_u8; CRC_FIELD_LENGTH]);
331
332 let crc_field_end = crc_field_address.add(CRC_FIELD_LENGTH);
334 let end_address = start_address.add(size);
335 crc.digest(slice::from_raw_parts(
336 crc_field_end,
337 end_address.offset_from(crc_field_end).try_into().unwrap(),
338 ));
339 }
340
341 let orig_remainder = header.crc32;
342 let actual_remainder = crc.get_crc();
343 assert!(
344 actual_remainder == orig_remainder,
345 "Calculated CRC {actual_remainder:x} does not match listed value \
346 {orig_remainder:x}"
347 );
348 }
349}
350
351
352#[repr(C)]
353pub struct TableHeader {
354 pub signature: u64,
355 pub revision: Revision,
356 pub header_size: u32,
357 pub crc32: u32,
358 reserved: u32,
359}
360
361
362#[repr(C)]
363pub struct SystemTable<'a> {
364 pub header: TableHeader,
365 pub firmware_vendor: *const u16,
366 pub firmware_revision: u32,
367 pub console_in_handle: Handle,
368 pub console_in: Option<&'a proto::SimpleTextInput>,
369 pub console_out_handle: Handle,
370 pub console_out: Option<&'a proto::SimpleTextOutput>,
371 pub std_err_handle: Handle,
372 pub std_err: Option<&'a proto::SimpleTextOutput>,
373 pub runtime_services: &'a RuntimeServices,
374 pub boot_services: Option<&'a BootServices>,
375 pub config_entry_count: usize,
376 pub config_table: *const ConfigurationTable,
377}
378
379impl Table for SystemTable<'_> {
380 const SIGNATURE: u64 = 0x5453_5953_2049_4249;
381 const MIN_REVISION: Revision = Revision::V2_00;
382 fn header(&self) -> &TableHeader {
383 &self.header
384 }
385}
386
387impl SystemTable<'_> {
388 pub unsafe fn exit_boot_services(&mut self, image_handle: Handle) -> MemoryMap {
399 #![allow(clippy::missing_panics_doc)]
400 let boot_services = self.boot_services.unwrap();
401 let memory_map = boot_services.get_memory_map();
402 boot_services.exit_boot_services(image_handle, memory_map.key).unwrap();
404
405 self.boot_services = None;
407 self.console_in_handle = Handle::NULL;
408 self.console_in = None;
409 self.console_out_handle = Handle::NULL;
410 self.console_out = None;
411 self.std_err_handle = Handle::NULL;
412 self.std_err = None;
413
414 memory_map
415 }
416}
417
418
419#[repr(C)]
420pub struct RuntimeServices {
421 pub header: TableHeader,
422
423 get_time: usize,
425 set_time: usize,
426 get_wakeup_time: usize,
427 set_wakeup_time: usize,
428
429 set_virtual_address_map: usize,
431 convert_pointer: usize,
432
433 get_variable: usize,
435 get_next_variable_name: usize,
436 set_variable: usize,
437
438 get_next_high_monotonic_count: usize,
440 reset_system: usize,
441
442 update_capsule: usize,
444 query_capsule_capabilities: usize,
445
446 query_variable_info: usize,
448}
449
450impl Table for RuntimeServices {
451 const SIGNATURE: u64 = 0x5652_4553_544e_5552;
452 const MIN_REVISION: Revision = Revision::V2_00;
453 fn header(&self) -> &TableHeader {
454 &self.header
455 }
456}
457
458
459#[repr(C)]
460pub struct BootServices {
461 pub header: TableHeader,
462
463 raise_tpl: usize,
465 restore_tpl: usize,
466
467 allocate_pages_: unsafe extern "C" fn(
470 allocate_type: AllocateType,
471 memory_type: MemoryType,
472 page_count: usize,
473 physical_address: *mut u64,
474 ) -> Status,
475 pub free_pages:
476 unsafe extern "C" fn(physical_address: u64, page_count: usize) -> Status,
477 get_memory_map_: unsafe extern "C" fn(
478 map_size: &mut usize,
479 map: *mut c_void,
480 map_key: &mut usize,
481 descriptor_size: &mut usize,
482 descriptor_version: &mut u32,
483 ) -> Status,
484 pub allocate_pool: unsafe extern "C" fn(
485 pool_type: MemoryType,
486 size: usize,
487 buffer: *mut *mut c_void,
488 ) -> Status,
489 pub free_pool: unsafe extern "C" fn(buffer: *mut c_void) -> Status,
490
491 create_event: usize,
493 set_timer: usize,
494 wait_for_event: usize,
495 signal_event: usize,
496 close_event: usize,
497 check_event: usize,
498
499 install_protocol_interface: usize,
501 reinstall_protocol_interface: usize,
502 uninstall_protocol_interface: usize,
503 pub handle_protocol: unsafe extern "C" fn(
504 handle: Handle,
505 protocol: &GUID,
506 interface: *mut *const c_void,
507 ) -> Status,
508 reserved: usize,
509 register_protocol_notify: usize,
510 locate_handle: usize,
511 locate_device_path: usize,
512 install_configuration_table: usize,
513
514 load_image: usize,
516 start_image: usize,
517 exit: usize,
518 unload_image: usize,
519 exit_boot_services_:
520 unsafe extern "C" fn(image_handle: Handle, memory_map_key: usize) -> Status,
521
522 get_next_monotonic_count: usize,
524 stall: usize,
525 set_watchdog_timer: usize,
526
527 connect_controller: usize,
529 disconnect_controller: usize,
530
531 open_protocol_: unsafe extern "C" fn(
533 handle: Handle,
534 guid: &GUID,
535 interface: *mut *const c_void,
536 agent_handle: Handle,
537 controller_handle: Handle,
538 attributes: OpenProtocolAttributes,
539 ) -> Status,
540 close_protocol: usize,
541 open_protocol_information: usize,
542
543 protocols_per_handle: usize,
545 locate_handle_buffer: usize,
546 locate_protocol: usize,
547 install_multiple_protocol_interfaces: usize,
548 uninstall_multiple_protocol_interfaces: usize,
549
550 calculate_crc32: usize,
552
553 copy_mem: usize,
555 set_mem: usize,
556 create_event_ex: usize,
557}
558
559impl Table for BootServices {
560 const SIGNATURE: u64 = 0x5652_4553_544f_4f42;
561 const MIN_REVISION: Revision = Revision::V2_00;
562 fn header(&self) -> &TableHeader {
563 &self.header
564 }
565}
566
567impl BootServices {
568 pub fn allocate_pages(
589 &self,
590 allocate_type: AllocateType,
591 memory_type: MemoryType,
592 page_count: usize,
593 reference_address: Option<u64>,
594 ) -> core::result::Result<u64, Status> {
595 let mut physical_address = reference_address.unwrap_or_default();
596 unsafe {
597 (self.allocate_pages_)(
598 allocate_type,
599 memory_type,
600 page_count,
601 &raw mut physical_address,
602 )
603 .into_result()?;
604 }
605 Ok(physical_address)
606 }
607
608
609 pub fn get_memory_map(&self) -> MemoryMap {
614 let mut memory_map_size = 0_usize;
615 let mut memory_map = MemoryMap::new();
616
617 loop {
618 memory_map.raw_map.resize(memory_map_size, 0);
619
620 let result = unsafe {
621 (self.get_memory_map_)(
622 &mut memory_map_size,
623 memory_map.raw_map.as_mut_ptr().cast(),
626 &mut memory_map.key,
627 &mut memory_map.descriptor_size,
628 &mut memory_map.descriptor_version,
629 )
630 .into_result()
631 };
632 match result {
633 Ok(_) => break,
634 Err(Status::BufferTooSmall) => {
635 memory_map_size += memory_map.descriptor_size;
637 }
638 Err(e) => panic!("Unexpected error from get_memory_map: {:?}", e),
640 }
641 }
642
643 memory_map.raw_map.resize(memory_map_size, 0);
645
646 memory_map.verify();
647 memory_map
648 }
649
650
651 pub fn get_protocol<T: proto::Protocol>(
662 &self,
663 handle: Handle,
664 agent_handle: Handle,
665 ) -> core::result::Result<&T, Status> {
666 let mut protocol = core::ptr::null::<T>();
667 unsafe {
668 (self.open_protocol_)(
669 handle,
670 &T::PROTOCOL_ID,
671 core::ptr::addr_of_mut!(protocol).cast(),
672 agent_handle,
673 Handle::NULL,
674 OpenProtocolAttributes::Get,
675 )
676 .into_result()?;
677 Ok(protocol.as_ref().unwrap())
678 }
679 }
680
681 pub unsafe fn exit_boot_services(
706 &self,
707 image_handle: Handle,
708 memory_map_key: usize,
709 ) -> Result {
710 (self.exit_boot_services_)(image_handle, memory_map_key).into_result()
711 }
712}
713
714
715#[repr(C)]
716#[derive(Debug, Clone, Copy, PartialEq, Eq)]
717pub struct GUID(u32, u16, u16, [u8; 8]);
718
719impl GUID {
720 pub const fn from(n: u128) -> Self {
721 #[allow(clippy::cast_possible_truncation)]
722 GUID(
723 (n >> 96) as u32,
724 (n >> 80) as u16,
725 (n >> 64) as u16,
726 (n as u64).to_be_bytes(),
727 )
728 }
729}
730
731impl From<u128> for GUID {
732 fn from(n: u128) -> Self {
733 GUID::from(n)
734 }
735}
736
737#[cfg(test)]
738mod test_guid {
739 use super::*;
740
741 #[test]
742 fn test_from_u128() {
743 let guid = GUID::from(0x01020304_0506_0708_0910_111213141516);
744 assert_eq!(guid.0, 0x0102_0304);
745 assert_eq!(guid.1, 0x0506);
746 assert_eq!(guid.2, 0x0708);
747 assert_eq!(guid.3, *b"\x09\x10\x11\x12\x13\x14\x15\x16");
748 }
749
750 #[test]
751 fn test_equality() {
752 let guid_a = GUID::from(0xcf04d973_15f7_400b_b53b_82929911d09c);
753 let guid_b = GUID::from(0xcf04d973_15f7_400b_b53b_82929911d09c);
754 let guid_c = GUID::from(0x028c338c_0b14_4687_9ad7_14cba520b645);
755 assert_eq!(guid_a, guid_b);
756 assert_ne!(guid_a, guid_c);
757 assert_ne!(guid_b, guid_c);
758 }
759}
760
761#[repr(C)]
762pub struct ConfigurationTable {
763 pub vendor_guid: GUID,
764 vendor_table: usize,
765}
766
767#[rustfmt::skip]
768impl ConfigurationTable {
769 pub const ACPI_20_GUID: GUID = GUID::from(0x8868e871_e4f1_11d3_bc22_0080c73c8881);
770 pub const ACPI_GUID: GUID = GUID::from(0xeb9d2d30_2d88_11d3_9a16_0090273fc14d);
771 pub const SAL_SYSTEM_GUID: GUID = GUID::from(0xeb9d2d32_2d88_11d3_9a16_0090273fc14d);
772 pub const SMBIOS_GUID: GUID = GUID::from(0xeb9d2d31_2d88_11d3_9a16_0090273fc14d);
773 pub const SMBIOS3_GUID: GUID = GUID::from(0xf2fd1544_9794_4a2c_992e_e5bbcf20e394);
774 pub const MPS_GUID: GUID = GUID::from(0xeb9d2d2f_2d88_11d3_9a16_0090273fc14d);
775 }
777
778#[repr(C)]
779pub struct MemoryDescriptor {
780 pub memory_type: MemoryType,
781 pub physical_start: u64,
783 pub virtual_start: u64,
784 pub page_count: u64,
785 pub attributes: MemoryAttributes,
786}
787
788pub struct MemoryMap {
790 pub raw_map: Vec<u8>,
791 pub key: usize,
792 pub descriptor_size: usize,
793 pub descriptor_version: u32,
794}
795
796impl MemoryMap {
797 pub const MIN_VERSION: u32 = 1;
798
799 pub fn new() -> Self {
800 MemoryMap {
801 raw_map: Vec::new(),
802 key: 0,
803 descriptor_size: 0,
804 descriptor_version: 0,
805 }
806 }
807
808 pub fn verify(&self) {
809 self.verify_version();
810 self.verify_descriptor_size();
811 self.verify_map();
812 }
813
814 #[allow(clippy::missing_panics_doc)]
815 pub fn verify_version(&self) {
816 assert!(
817 self.descriptor_version >= Self::MIN_VERSION,
818 "Descriptor version {} less than required version {}",
819 self.descriptor_version,
820 Self::MIN_VERSION,
821 );
822 }
823
824 #[allow(clippy::missing_panics_doc)]
825 pub fn verify_descriptor_size(&self) {
826 assert!(
827 self.descriptor_size >= size_of::<MemoryDescriptor>(),
828 "Descriptor size {} shorter than required {} bytes",
829 self.descriptor_size,
830 size_of::<MemoryDescriptor>(),
831 );
832
833 assert!(
834 self.descriptor_size.is_multiple_of(align_of::<MemoryDescriptor>()),
835 "Descriptor size {} not a multiple of the MemoryDescriptor struct alignment \
836 {}",
837 self.descriptor_size,
838 align_of::<MemoryDescriptor>(),
839 );
840 }
841
842 #[allow(clippy::missing_panics_doc)]
843 pub fn verify_map(&self) {
844 assert!(
845 self.raw_map.len().is_multiple_of(self.descriptor_size),
846 "Memory map total size {} is not a multiple of descriptor size {}",
847 self.raw_map.len(),
848 self.descriptor_size,
849 );
850
851 assert!(
852 self.raw_map.as_ptr().align_offset(align_of::<MemoryDescriptor>()) == 0,
853 "Memory map at {:p} not aligned properly for dereferencing. Required: {:x}",
854 self.raw_map.as_ptr(),
855 align_of::<MemoryDescriptor>(),
856 );
857 }
858
859 pub fn iter(&self) -> impl Iterator<Item = &MemoryDescriptor> {
861 #![allow(clippy::cast_ptr_alignment)]
862
863 self.verify();
867 self.raw_map
868 .as_slice()
869 .chunks_exact(self.descriptor_size)
870 .map(|raw| unsafe { &*raw.as_ptr().cast::<MemoryDescriptor>() })
871 }
872}
873
874impl Default for MemoryMap {
875 fn default() -> Self {
876 Self::new()
877 }
878}
879
880c_enum! {
881 pub enum MemoryType(u32) {
882 Reserved = 0,
884 LoaderCode = 1,
886 LoaderData = 2,
888 BootServicesCode = 3,
890 BootServicesData = 4,
892 RuntimeServicesCode = 5,
894 RuntimeServicesData = 6,
896 Conventional = 7,
898 Unusable = 8,
900 ACPIReclaim = 9,
902 ACPINonVolatile = 10,
904 MappedIO = 11,
906 MappedIOPortSpace = 12,
908 ProcessorCode = 13,
910 Persistent = 14,
912
913 MinOEMDefined = 0x7000_0000,
915 MaxOEMDefined = 0x7fff_ffff,
917 MinOSDefined = 0x8000_0000,
919 MaxOSDefined = 0xffff_ffff,
921 }
922}
923
924c_enum! {
925 pub enum AllocateType(u32) {
926 AnyAddress = 0,
927 MaxAddress = 1,
928 ExactAddress = 2,
929 }
930}
931
932bitfield! {
933 pub struct MemoryAttributes(u64) {
934 [0] pub supports_uncacheable,
935 [1] pub supports_write_combining,
936 [2] pub supports_write_through,
937 [4] pub supports_write_back,
938 [5] pub supports_uncacheable_sem,
939 [12] pub supports_write_protect,
940 [13] pub supports_read_protect,
941 [14] pub supports_exec_protect,
942 [15] pub nonvolatile,
943 [16] pub more_reliable,
944 [17] pub supports_read_only,
945 [18] pub specific_purpose,
946 [19] pub supports_cpu_crypto,
947 [63] pub runtime,
948 }
949}
950
951
952c_enum! {
953 pub enum OpenProtocolAttributes(u32) {
954 ByHandle = 1 << 0,
955 Get = 1 << 1,
956 Test = 1 << 2,
957 ByChild = 1 << 3,
958 ByDriver = 1 << 4,
959 Exclusive = 1 << 5,
960 }
961}
962
963
964pub mod proto {
965 use super::{Handle, MemoryType, Result, Status, SystemTable, GUID};
966 use core::ffi::c_void;
967 use tartan_bitfield::bitfield;
968 use tartan_c_enum::c_enum;
969
970 pub trait Protocol {
971 const PROTOCOL_ID: GUID;
972 }
973
974
975 #[repr(C)]
976 #[derive(Debug)]
977 pub struct SimpleTextInput {
978 reset: usize,
979 read_key_stroke: usize,
980 wait_for_key: usize,
981 set_state: usize,
982 register_key_notify: usize,
983 unregister_key_notify: usize,
984 }
985
986 #[repr(C)]
987 pub struct SimpleTextOutput {
988 pub reset: unsafe extern "C" fn(
989 this: &SimpleTextOutput,
990 extended_verification: bool,
991 ) -> Status,
992 pub output_string:
993 unsafe extern "C" fn(this: &SimpleTextOutput, string: *const u16) -> Status,
994 pub test_string:
995 unsafe extern "C" fn(this: &SimpleTextOutput, string: *const u16) -> Status,
996 query_mode: usize,
997 set_mode: usize,
998 set_attribute: usize,
999 clear_screen: usize,
1000 set_cursor_position: usize,
1001 enable_cursor: usize,
1002 pub mode: SimpleTextOutputMode,
1003 }
1004
1005 #[repr(C)]
1006 #[derive(Debug, Copy, Clone, PartialEq, Eq)]
1007 pub struct SimpleTextOutputMode {
1008 pub max_mode: i32,
1009 pub mode: i32,
1010 pub attribute: i32,
1011 pub cursor_column: i32,
1012 pub cursor_row: i32,
1013 pub cursor_visible: bool,
1014 }
1015
1016
1017 #[repr(C)]
1018 #[derive(Debug)]
1019 pub struct LoadedImage<'a> {
1020 pub revision: u32,
1021 pub parent_handle: Handle,
1022 pub system_table: *const SystemTable<'a>,
1023
1024 pub device_handle: Handle,
1025 device_path_protocol: usize, _reserved: usize,
1027
1028 pub load_options_size: u32,
1029 pub load_options: *const c_void,
1030
1031 pub image_base: *mut c_void,
1032 pub image_size: u64,
1033 pub image_code_type: MemoryType,
1034 pub image_data_type: MemoryType,
1035 pub unload: unsafe extern "C" fn(handle: Handle) -> Status,
1036 }
1037
1038 impl Protocol for LoadedImage<'_> {
1039 const PROTOCOL_ID: GUID = GUID::from(0x5b1b31a1_9562_11d2_8e3f_00a0c969723b);
1040 }
1041
1042 impl LoadedImage<'_> {
1043 pub const MIN_REVISION: u32 = 0x1000;
1044 }
1045
1046
1047 #[repr(C)]
1049 pub struct SimpleFileSystem {
1050 pub revision: u64,
1052 open_volume_: unsafe extern "C" fn(
1053 this: &SimpleFileSystem,
1054 root: &mut *const File,
1055 ) -> Status,
1056 }
1057
1058 impl Protocol for SimpleFileSystem {
1059 const PROTOCOL_ID: GUID = GUID::from(0x964e5b22_6459_11d2_8e39_00a0c969723b);
1060 }
1061
1062 impl SimpleFileSystem {
1063 pub const MIN_REVISION: u32 = 0x0001_0000;
1066
1067 pub fn open_volume(&self) -> core::result::Result<&File, Status> {
1077 let mut root = core::ptr::null::<File>();
1078 unsafe {
1079 (self.open_volume_)(self, &mut root).into_result()?;
1080 Ok(root.as_ref().unwrap())
1081 }
1082 }
1083 }
1084
1085
1086 #[repr(C)]
1088 pub struct File {
1089 pub revision: u64,
1091
1092 open_: unsafe extern "C" fn(
1093 this: &File,
1094 file: &mut *const File,
1095 path: *const u16,
1096 mode: FileMode,
1097 attributes: FileAttributes,
1098 ) -> Status,
1099 close_: unsafe extern "C" fn(this: &File) -> Status,
1100 delete_: unsafe extern "C" fn(this: &File) -> Status,
1101 read_: unsafe extern "C" fn(
1102 this: &File,
1103 count: &mut usize,
1104 buffer: *mut c_void,
1105 ) -> Status,
1106 write_: unsafe extern "C" fn(
1107 this: &File,
1108 count: &mut usize,
1109 buffer: *const c_void,
1110 ) -> Status,
1111 get_position_: unsafe extern "C" fn(this: &File, position: &mut u64) -> Status,
1112 set_position_: unsafe extern "C" fn(this: &File, position: u64) -> Status,
1113 get_info: usize,
1114 set_info: usize,
1115 flush_: unsafe extern "C" fn(this: &File) -> Status,
1116
1117 open_v2: usize,
1119 read_v2: usize,
1120 write_v2: usize,
1121 flush_v2: usize,
1122 }
1123
1124 impl File {
1125 pub const MIN_REVISION: u32 = 0x0001_0000;
1128
1129 pub fn open(
1145 &self,
1146 path: &[u16],
1147 mode: FileMode,
1148 attributes: FileAttributes,
1149 ) -> core::result::Result<&File, Status> {
1150 assert!(!path.is_empty(), "Path cannot be empty");
1151 assert!(*path.last().unwrap() == 0, "Path must be null-terminated");
1152
1153 let mut file = core::ptr::null::<File>();
1154 unsafe {
1155 (self.open_)(self, &mut file, path.as_ptr(), mode, attributes)
1156 .into_result()?;
1157 Ok(file.as_ref().unwrap())
1158 }
1159 }
1160
1161 pub fn close(&self) {
1166 unsafe {
1167 assert!((self.close_)(self) == Status::Success);
1169 }
1170 }
1171
1172 pub fn delete(&self) -> Status {
1178 unsafe { (self.delete_)(self) }
1179 }
1180
1181 pub fn read(&self, buffer: &mut [u8]) -> core::result::Result<usize, Status> {
1196 let mut count = buffer.len();
1197 unsafe {
1198 (self.read_)(self, &mut count, buffer.as_mut_ptr().cast())
1199 .into_result()?;
1200 }
1201 Ok(count)
1202 }
1203
1204 pub fn write(&self, buffer: &[u8]) -> core::result::Result<usize, Status> {
1215 let mut count = buffer.len();
1216 unsafe {
1217 (self.write_)(self, &mut count, buffer.as_ptr().cast()).into_result()?;
1218 }
1219 Ok(count)
1220 }
1221
1222 pub fn get_position(&self) -> core::result::Result<u64, Status> {
1229 let mut position = 0_u64;
1230 unsafe {
1231 (self.get_position_)(self, &mut position).into_result()?;
1232 }
1233 Ok(position)
1234 }
1235
1236 pub fn set_position(&self, position: u64) -> Result {
1247 unsafe { (self.set_position_)(self, position).into_result() }
1248 }
1249
1250 pub fn flush(&self) -> Result {
1257 unsafe { (self.flush_)(self).into_result() }
1258 }
1259 }
1260
1261
1262 c_enum! {
1263 pub enum FileMode(u64) {
1268 Read = 0x1,
1270 ReadWrite = 0x3,
1272 ReadWriteCreate = 0x8000_0000_0000_0003,
1274 }
1275 }
1276
1277 bitfield! {
1278 pub struct FileAttributes(u64) {
1280 [0] read_only,
1282 [1] hidden,
1284 [2] system,
1286 [4] directory,
1288 [5] archive,
1290 }
1291 }
1292}