kernel/
multiboot2.rs

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        // TODO: add colors iter
194    },
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                // end
419                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                        // `6` reserved bytes
531                        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                // TODO: parse dhcp ack
565                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                    // only override if new is higher version
668                    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}