1use core::{ffi, fmt, mem};
2
3use crate::{
4 acpi::tables::{Rsdp, RsdpV1, RsdpV2},
5 io::{HexArray, NoDebug},
6 memory_management::memory_layout::{align_up, physical2virtual, MemSize, PAGE_4K},
7};
8
9#[repr(u32)]
10#[derive(Debug, PartialEq, Eq)]
11pub enum MemoryMapType {
12 Available = 1,
13 Reserved = 2,
14 ACPIReclaimable = 3,
15 ACPINonVolatile = 4,
16 BadMemory = 5,
17 Undefined(u32),
18}
19
20#[derive(Debug)]
21pub struct MemoryMap {
22 pub base_addr: u64,
23 pub length: u64,
24 pub mem_type: MemoryMapType,
25}
26
27impl fmt::Display for MemoryMap {
28 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
29 write!(
30 f,
31 "range={:016X}..{:016X}, len={:4}, ty={:?}",
32 self.base_addr,
33 self.base_addr + self.length,
34 MemSize(self.length),
35 self.mem_type
36 )
37 }
38}
39
40struct MemoryMapTagRaw {
41 entry_size: u32,
42 _entry_version: u32,
43}
44
45#[derive(Clone, Debug)]
46pub struct MemoryMapIter {
47 remaining: usize,
48 entry_size: u32,
49 memory_map_raw: *const MemoryMapsRaw,
50}
51
52impl Iterator for MemoryMapIter {
53 type Item = MemoryMap;
54
55 fn next(&mut self) -> Option<Self::Item> {
56 if self.remaining == 0 {
57 return None;
58 }
59 let ptr = self.memory_map_raw;
60 let mmap = unsafe { &*ptr };
61 let memory_map = MemoryMap {
62 base_addr: mmap.base_addr,
63 length: mmap.length,
64 mem_type: match mmap.mem_type {
65 1 => MemoryMapType::Available,
66 2 => MemoryMapType::Reserved,
67 3 => MemoryMapType::ACPIReclaimable,
68 4 => MemoryMapType::ACPINonVolatile,
69 5 => MemoryMapType::BadMemory,
70 n => MemoryMapType::Undefined(n),
71 },
72 };
73 self.memory_map_raw =
74 (self.memory_map_raw as u64).wrapping_add(self.entry_size as _) as *const MemoryMapsRaw;
75 self.remaining = self.remaining.saturating_sub(self.entry_size as _);
76 Some(memory_map)
77 }
78}
79
80#[repr(u32)]
81#[derive(Debug, PartialEq, Eq)]
82pub enum EfiMemoryMapType {
83 Reserved = 0,
84 LoaderCode = 1,
85 LoaderData = 2,
86 BootServicesCode = 3,
87 BootServicesData = 4,
88 RuntimeServicesCode = 5,
89 RuntimeServicesData = 6,
90 Conventional = 7,
91 Unusable = 8,
92 ACPIReclaimable = 9,
93 ACPINonVolatile = 10,
94 MemoryMappedIO = 11,
95 MemoryMappedIOPortSpace = 12,
96 PalCode = 13,
97 PersistentMemory = 14,
98 Undefined(u32),
99}
100
101#[derive(Debug)]
102pub struct EfiMemoryMap {
103 pub mem_type: EfiMemoryMapType,
104 pub physical_start: u64,
105 pub virtual_start: u64,
106 pub number_of_pages: u64,
107 pub attributes: u64,
108}
109
110impl fmt::Display for EfiMemoryMap {
111 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
112 write!(
113 f,
114 "range={:016X}..{:016X}, (virt_start={:016X}), len={:4}, ty={:?}, attributes={:X}",
115 self.physical_start,
116 self.physical_start + self.number_of_pages * PAGE_4K as u64,
117 self.virtual_start,
118 MemSize(self.number_of_pages * PAGE_4K as u64),
119 self.mem_type,
120 self.attributes
121 )
122 }
123}
124
125#[repr(C, packed(4))]
126struct EfiMemoryMapsRaw {
127 pub mem_type: u64,
128 pub physical_start: u64,
129 pub virtual_start: u64,
130 pub number_of_pages: u64,
131 pub attributes: u64,
132}
133
134#[derive(Clone, Debug)]
135pub struct EfiMemoryMapIter {
136 remaining: usize,
137 entry_size: u32,
138 memory_map_raw: *const EfiMemoryMapsRaw,
139}
140
141impl Iterator for EfiMemoryMapIter {
142 type Item = EfiMemoryMap;
143
144 fn next(&mut self) -> Option<Self::Item> {
145 if self.remaining == 0 {
146 return None;
147 }
148 let ptr = self.memory_map_raw;
149 let mmap = unsafe { &*ptr };
150 let memory_map = EfiMemoryMap {
151 physical_start: mmap.physical_start,
152 virtual_start: mmap.virtual_start,
153 number_of_pages: mmap.number_of_pages,
154 attributes: mmap.attributes,
155 mem_type: match mmap.mem_type {
156 0 => EfiMemoryMapType::Reserved,
157 1 => EfiMemoryMapType::LoaderCode,
158 2 => EfiMemoryMapType::LoaderData,
159 3 => EfiMemoryMapType::BootServicesCode,
160 4 => EfiMemoryMapType::BootServicesData,
161 5 => EfiMemoryMapType::RuntimeServicesCode,
162 6 => EfiMemoryMapType::RuntimeServicesData,
163 7 => EfiMemoryMapType::Conventional,
164 8 => EfiMemoryMapType::Unusable,
165 9 => EfiMemoryMapType::ACPIReclaimable,
166 10 => EfiMemoryMapType::ACPINonVolatile,
167 11 => EfiMemoryMapType::MemoryMappedIO,
168 12 => EfiMemoryMapType::MemoryMappedIOPortSpace,
169 13 => EfiMemoryMapType::PalCode,
170 14 => EfiMemoryMapType::PersistentMemory,
171 n => EfiMemoryMapType::Undefined(n as _),
172 },
173 };
174 self.memory_map_raw = (self.memory_map_raw as u64).wrapping_add(self.entry_size as _)
175 as *const EfiMemoryMapsRaw;
176 self.remaining = self.remaining.saturating_sub(self.entry_size as _);
177 Some(memory_map)
178 }
179}
180
181#[repr(C, packed(4))]
182struct MemoryMapsRaw {
183 base_addr: u64,
184 length: u64,
185 mem_type: u32,
186 reserved: u32,
187}
188
189#[derive(Debug, Clone)]
190pub enum FramebufferColorInfo {
191 Indexed {
192 num_colors: u32,
193 },
195 Rgb {
196 red_field_position: u8,
197 red_mask_size: u8,
198 green_field_position: u8,
199 green_mask_size: u8,
200 blue_field_position: u8,
201 blue_mask_size: u8,
202 },
203 EgaText,
204}
205
206impl FramebufferColorInfo {
207 fn from_color_info(ty: u8, color_info: &[u8]) -> Self {
208 match ty {
209 0 => {
210 let num_colors = u32::from_le_bytes([
211 color_info[0],
212 color_info[1],
213 color_info[2],
214 color_info[3],
215 ]);
216 Self::Indexed { num_colors }
217 }
218 1 => Self::Rgb {
219 red_field_position: color_info[0],
220 red_mask_size: color_info[1],
221 green_field_position: color_info[2],
222 green_mask_size: color_info[3],
223 blue_field_position: color_info[4],
224 blue_mask_size: color_info[5],
225 },
226 2 => Self::EgaText,
227 _ => panic!("unknown framebuffer color info type"),
228 }
229 }
230
231 pub fn is_rgb(&self) -> bool {
232 matches!(self, Self::Rgb { .. })
233 }
234}
235
236#[repr(C)]
237struct FramebufferRaw {
238 addr: u64,
239 pitch: u32,
240 width: u32,
241 height: u32,
242 bpp: u8,
243 framebuffer_type: u8,
244 reserved: u16,
245}
246
247#[derive(Debug, Clone)]
248pub struct Framebuffer {
249 pub addr: u64,
250 pub pitch: u32,
251 pub width: u32,
252 pub height: u32,
253 pub bpp: u8,
254 pub color_info: FramebufferColorInfo,
255}
256
257#[derive(Debug, Clone)]
258#[repr(C, packed)]
259pub struct VbeControlInfo {
260 pub signature: [u8; 4],
261 pub version: u16,
262 pub oem_str_ptr: u32,
263 pub capabilities: u32,
264 pub video_modes_ptr: u32,
265 pub video_memory_size_blocks: u16,
266 pub software_rev: u16,
267 pub vendor: u32,
268 pub product_name: u32,
269 pub product_rev: u32,
270 pub reserved: NoDebug<[u8; 222]>,
271 pub oem_data: NoDebug<[u8; 256]>,
272}
273
274#[derive(Debug, Clone)]
275#[repr(C, packed)]
276pub struct VbeModeInfo {
277 pub attributes: u16,
278 pub window_a_attributes: u8,
279 pub window_b_attributes: u8,
280 pub window_granularity: u16,
281 pub window_size: u16,
282 pub window_a_segment: u16,
283 pub window_b_segment: u16,
284 pub window_func_ptr: u32,
285 pub bytes_per_scanline: u16,
286 pub width: u16,
287 pub height: u16,
288 pub w_char: u8,
289 pub y_char: u8,
290 pub planes: u8,
291 pub bpp: u8,
292 pub banks: u8,
293 pub memory_model: u8,
294 pub bank_size: u8,
295 pub image_pages: u8,
296 pub reserved0: u8,
297 pub red_mask_size: u8,
298 pub red_field_position: u8,
299 pub green_mask_size: u8,
300 pub green_field_position: u8,
301 pub blue_mask_size: u8,
302 pub blue_field_position: u8,
303 pub rsvd_mask_size: u8,
304 pub rsvd_field_position: u8,
305 pub direct_color_mode_attributes: u8,
306 pub framebuffer_addr: u32,
307 pub reserved1: NoDebug<[u8; 212]>,
308}
309
310#[derive(Debug, Clone)]
311#[repr(C)]
312pub struct VbeInfo {
313 pub mode: u16,
314 pub interface_seg: u16,
315 pub interface_off: u16,
316 pub interface_len: u16,
317 pub control_info: VbeControlInfo,
318 pub mode_info: VbeModeInfo,
319}
320
321struct MultiBootTagRaw {
322 ty: u32,
323 size: u32,
324}
325
326#[derive(Debug, Clone)]
327#[repr(C, packed)]
328pub struct BasicMemoryInfo {
329 mem_lower: u32,
330 mem_upper: u32,
331}
332
333#[derive(Debug, Clone)]
334#[repr(C, packed)]
335pub struct AdvancedPowerManagementTable {
336 version: u16,
337 cseg: u16,
338 offset: u32,
339 cseg_16: u16,
340 dseg: u16,
341 flags: u16,
342 cseg_len: u16,
343 cseg_16_len: u16,
344 dseg_len: u16,
345}
346
347#[derive(Debug, Clone)]
348pub enum MultiBootTag<'a> {
349 BootCommandLine {
350 cmdline: &'a str,
351 },
352 BootLoaderName {
353 name: &'a str,
354 },
355 BootModule {
356 string: &'a str,
357 data: HexArray<&'a [u8]>,
358 },
359 BasicMemoryInfo(&'a BasicMemoryInfo),
360 AdvancedPowerManagementTable(&'a AdvancedPowerManagementTable),
361 ImageLoadBasePhysical {
362 base_addr: u32,
363 },
364 MemoryMap(MemoryMapIter),
365 EfiMemoryMap(EfiMemoryMapIter),
366 ElfSymbols,
367 BiosBootDevice {
368 biosdev: u32,
369 partition: u32,
370 sub_partition: u32,
371 },
372 FrameBufferInfo(Framebuffer),
373 SMBiosTables {
374 major: u8,
375 minor: u8,
376 tables: HexArray<&'a [u8]>,
377 },
378 OldRsdp(Rsdp),
379 NewRsdp(Rsdp),
380 DhcpAck(HexArray<&'a [u8]>),
381 Efi32SystemTablePtr {
382 ptr: u32,
383 },
384 Efi64SystemTablePtr {
385 ptr: u64,
386 },
387 EfiBootServicesNotTerminated,
388 Efi32ImageHandle {
389 ptr: u32,
390 },
391 Efi64ImageHandle {
392 ptr: u64,
393 },
394 VbeInfo(&'a VbeInfo),
395}
396
397pub struct MultiBootTagIter<'a> {
398 current: *const MultiBootTagRaw,
399 remaining: usize,
400 phantom: core::marker::PhantomData<&'a ()>,
401}
402
403impl<'a> Iterator for MultiBootTagIter<'a> {
404 type Item = MultiBootTag<'a>;
405
406 fn next(&mut self) -> Option<Self::Item> {
407 if self.remaining == 0 {
408 return None;
409 }
410 let ptr = self.current;
411 let tag = unsafe { &*ptr };
412 let tag_size = align_up(tag.size as _, 8);
413 let next = unsafe { (ptr as *const u8).add(tag_size) as *const MultiBootTagRaw };
414 self.remaining -= tag_size;
415 self.current = next;
416 let tag = match tag.ty {
417 0 => {
418 assert_eq!(tag.size as usize, mem::size_of::<MultiBootTagRaw>());
420 assert_eq!(self.remaining, 0);
421 return None;
422 }
423 1 => {
424 let str_ptr = unsafe { ptr.add(1) as *const i8 };
425 let cmdline =
426 unsafe { ffi::CStr::from_ptr(str_ptr).to_str().expect("invalid utf8") };
427 MultiBootTag::BootCommandLine { cmdline }
428 }
429 2 => {
430 let str_ptr = unsafe { ptr.add(1) as *const i8 };
431 let name = unsafe { ffi::CStr::from_ptr(str_ptr).to_str().expect("invalid utf8") };
432 MultiBootTag::BootLoaderName { name }
433 }
434 3 => {
435 let after_header = unsafe { ptr.add(1) as *const u32 };
436 let mod_start_phy = unsafe { *after_header };
437 let mod_end_phy = unsafe { *after_header.add(1) };
438 let str_ptr = unsafe { after_header.add(2) as *const i8 };
439 let string =
440 unsafe { ffi::CStr::from_ptr(str_ptr).to_str().expect("invalid utf8") };
441
442 let len = if mod_end_phy > mod_start_phy {
443 (mod_end_phy - mod_start_phy) as usize
444 } else {
445 0
446 };
447
448 let data = unsafe {
449 core::slice::from_raw_parts(
450 physical2virtual(mod_start_phy as u64) as *const u8,
451 len,
452 )
453 };
454
455 MultiBootTag::BootModule {
456 string,
457 data: HexArray(data),
458 }
459 }
460 4 => {
461 let tag = unsafe { &*(ptr.add(1) as *const BasicMemoryInfo) };
462 MultiBootTag::BasicMemoryInfo(tag)
463 }
464 5 => {
465 let tag = unsafe { ptr.add(1) as *const u32 };
466 let data_slice = unsafe { core::slice::from_raw_parts(tag, 3) };
467 MultiBootTag::BiosBootDevice {
468 biosdev: data_slice[0],
469 partition: data_slice[1],
470 sub_partition: data_slice[2],
471 }
472 }
473 6 => {
474 let mmap_tag = unsafe { &*(ptr.add(1) as *const MemoryMapTagRaw) };
475 MultiBootTag::MemoryMap(MemoryMapIter {
476 remaining: tag.size as usize
477 - mem::size_of::<MultiBootTagRaw>()
478 - mem::size_of::<MemoryMapTagRaw>(),
479 entry_size: mmap_tag.entry_size,
480 memory_map_raw: unsafe { (mmap_tag as *const MemoryMapTagRaw).add(1) as _ },
481 })
482 }
483 7 => {
484 let vbe_tag = unsafe { &*(ptr.add(1) as *const VbeInfo) };
485 MultiBootTag::VbeInfo(vbe_tag)
486 }
487 8 => {
488 let frame_tag = unsafe { &*(ptr.add(1) as *const FramebufferRaw) };
489 let color_info_start =
490 unsafe { (frame_tag as *const FramebufferRaw).add(1) as *const u8 };
491 let remaining_size = tag.size as usize
492 - mem::size_of::<MultiBootTagRaw>()
493 - mem::size_of::<FramebufferRaw>();
494 let color_info =
495 unsafe { core::slice::from_raw_parts(color_info_start, remaining_size) };
496 MultiBootTag::FrameBufferInfo(Framebuffer {
497 addr: frame_tag.addr,
498 pitch: frame_tag.pitch,
499 width: frame_tag.width,
500 height: frame_tag.height,
501 bpp: frame_tag.bpp,
502 color_info: FramebufferColorInfo::from_color_info(
503 frame_tag.framebuffer_type,
504 color_info,
505 ),
506 })
507 }
508 9 => {
509 let _tag = unsafe { &*(ptr.add(1) as *const u32) };
510 MultiBootTag::ElfSymbols
511 }
512 10 => {
513 let tag = unsafe { &*(ptr.add(1) as *const AdvancedPowerManagementTable) };
514 MultiBootTag::AdvancedPowerManagementTable(tag)
515 }
516 11 => {
517 let efi32_ptr = unsafe { &*(ptr.add(1) as *const u32) };
518 MultiBootTag::Efi32SystemTablePtr { ptr: *efi32_ptr }
519 }
520 12 => {
521 let efi64_ptr = unsafe { &*(ptr.add(1) as *const u64) };
522 MultiBootTag::Efi64SystemTablePtr { ptr: *efi64_ptr }
523 }
524 13 => {
525 let after_header = unsafe { ptr.add(1) as *const u8 };
526 let major = unsafe { *after_header };
527 let minor = unsafe { *after_header.add(1) };
528 let tables = unsafe {
529 core::slice::from_raw_parts(
530 after_header.add(2 + 6),
532 tag.size as usize - mem::size_of::<MultiBootTagRaw>() - 2 - 6,
533 )
534 };
535
536 MultiBootTag::SMBiosTables {
537 major,
538 minor,
539 tables: HexArray(tables),
540 }
541 }
542 14 => {
543 let old_rsdp = unsafe { &*(ptr.add(1) as *const RsdpV1) };
544 assert!(
545 tag.size as usize - mem::size_of::<MemoryMapTagRaw>()
546 == mem::size_of::<RsdpV1>(),
547 );
548
549 MultiBootTag::OldRsdp(Rsdp::from_v1(old_rsdp))
550 }
551 15 => {
552 let new_rsdp = unsafe { &*(ptr.add(1) as *const RsdpV2) };
553 assert!(
554 tag.size as usize - mem::size_of::<MemoryMapTagRaw>()
555 == mem::size_of::<RsdpV2>(),
556 );
557
558 MultiBootTag::NewRsdp(Rsdp::from_v2(new_rsdp))
559 }
560 16 => {
561 let size = tag.size as usize - mem::size_of::<MultiBootTagRaw>();
562 let data = unsafe { core::slice::from_raw_parts(ptr.add(1) as *const u8, size) };
563
564 MultiBootTag::DhcpAck(HexArray(data))
566 }
567 17 => {
568 let efi_mmap = unsafe { &*(ptr.add(1) as *const MemoryMapTagRaw) };
569
570 MultiBootTag::EfiMemoryMap(EfiMemoryMapIter {
571 remaining: tag.size as usize
572 - mem::size_of::<MultiBootTagRaw>()
573 - mem::size_of::<MemoryMapTagRaw>(),
574 entry_size: efi_mmap.entry_size,
575 memory_map_raw: unsafe { (efi_mmap as *const MemoryMapTagRaw).add(1) as _ },
576 })
577 }
578 18 => MultiBootTag::EfiBootServicesNotTerminated,
579 19 => {
580 let efi32_image_handle = unsafe { &*(ptr.add(1) as *const u32) };
581 MultiBootTag::Efi32ImageHandle {
582 ptr: *efi32_image_handle,
583 }
584 }
585 20 => {
586 let efi64_image_handle = unsafe { &*(ptr.add(1) as *const u64) };
587 MultiBootTag::Efi64ImageHandle {
588 ptr: *efi64_image_handle,
589 }
590 }
591 21 => {
592 let tag = unsafe { &*(ptr.add(1) as *const u32) };
593 MultiBootTag::ImageLoadBasePhysical { base_addr: *tag }
594 }
595 t => unimplemented!("tag {t}"),
596 };
597 Some(tag)
598 }
599}
600
601#[repr(C, packed(4))]
602pub struct MultiBoot2Info {
603 total_size: u32,
604 reserved: u32,
605}
606
607impl MultiBoot2Info {
608 fn data_ptr(&self) -> *const u8 {
609 unsafe { (self as *const Self as *const u8).add(8) }
610 }
611
612 #[allow(dead_code)]
613 fn data_slice(&self) -> &[u8] {
614 unsafe {
615 core::slice::from_raw_parts(
616 self.data_ptr(),
617 self.total_size as usize - mem::size_of::<MultiBoot2Info>(),
618 )
619 }
620 }
621
622 pub fn end_address(&self) -> u64 {
623 unsafe { (self as *const Self as *const u8).add(self.total_size as _) as _ }
624 }
625
626 pub fn tags(&self) -> MultiBootTagIter<'_> {
627 MultiBootTagIter {
628 current: unsafe { (self as *const Self as *const u8).add(8) as _ },
629 remaining: self.total_size as usize - mem::size_of::<MultiBoot2Info>(),
630 phantom: core::marker::PhantomData,
631 }
632 }
633
634 pub fn cmdline(&self) -> Option<&str> {
635 self.tags().find_map(|tag| match tag {
636 MultiBootTag::BootCommandLine { cmdline } => Some(cmdline),
637 _ => None,
638 })
639 }
640
641 pub fn memory_maps(&self) -> Option<impl Iterator<Item = MemoryMap> + '_> {
642 self.tags().find_map(|tag| match tag {
643 MultiBootTag::MemoryMap(mmap) => Some(mmap),
644 _ => None,
645 })
646 }
647
648 pub fn framebuffer(&self) -> Option<Framebuffer> {
649 self.tags().find_map(|tag| match tag {
650 MultiBootTag::FrameBufferInfo(fb) => Some(fb),
651 _ => None,
652 })
653 }
654
655 pub fn vbe_info(&self) -> Option<&VbeInfo> {
656 self.tags().find_map(|tag| match tag {
657 MultiBootTag::VbeInfo(vbe) => Some(vbe),
658 _ => None,
659 })
660 }
661
662 pub fn get_most_recent_rsdp(&self) -> Option<Rsdp> {
663 let mut ret_rdsp: Option<Rsdp> = None;
664 for tag in self.tags() {
665 match tag {
666 MultiBootTag::OldRsdp(rsdp) | MultiBootTag::NewRsdp(rsdp) => {
667 if ret_rdsp.is_none() || ret_rdsp.as_ref().unwrap().revision < rsdp.revision {
669 ret_rdsp = Some(rsdp);
670 }
671 }
672 _ => {}
673 }
674 }
675 ret_rdsp
676 }
677}
678
679impl fmt::Display for MultiBoot2Info {
680 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
681 writeln!(f, "Multiboot2:")?;
682 for tag in self.tags() {
683 match tag {
684 MultiBootTag::MemoryMap(mmap) => {
685 writeln!(f, " MemoryMap:")?;
686 for memory in mmap {
687 writeln!(f, " {memory}")?;
688 }
689 }
690 MultiBootTag::EfiMemoryMap(mmap) => {
691 writeln!(f, " EfiMemoryMap:")?;
692 for memory in mmap {
693 writeln!(f, " {memory}")?;
694 }
695 }
696 t => writeln!(f, " {t:X?}")?,
697 }
698 }
699 Ok(())
700 }
701}