mycelium_kernel/arch/x86_64/
boot.rs

1//! Glue for the `bootloader` crate
2
3use super::framebuf::{self, FramebufWriter};
4use bootloader_api::info;
5use hal_core::{boot::BootInfo, mem, PAddr, VAddr};
6use hal_x86_64::{mm, serial, vga};
7use mycelium_util::sync::InitOnce;
8
9#[derive(Debug)]
10pub struct BootloaderApiBootInfo {
11    inner: &'static info::BootInfo,
12    has_framebuffer: bool,
13}
14
15#[derive(Debug)]
16pub struct ArchInfo {
17    pub(in crate::arch) rsdp_addr: Option<PAddr>,
18}
19
20type MemRegionIter = core::slice::Iter<'static, info::MemoryRegion>;
21
22impl BootInfo for BootloaderApiBootInfo {
23    // TODO(eliza): implement
24    type MemoryMap = core::iter::Map<MemRegionIter, fn(&info::MemoryRegion) -> mem::Region>;
25
26    type Writer = vga::Writer;
27
28    type Framebuffer = FramebufWriter;
29
30    /// Returns the boot info's memory map.
31    fn memory_map(&self) -> Self::MemoryMap {
32        fn convert_region_kind(kind: info::MemoryRegionKind) -> mem::RegionKind {
33            match kind {
34                info::MemoryRegionKind::Usable => mem::RegionKind::FREE,
35                // TODO(eliza): make known
36                info::MemoryRegionKind::UnknownUefi(_) => mem::RegionKind::UNKNOWN,
37                info::MemoryRegionKind::UnknownBios(_) => mem::RegionKind::UNKNOWN,
38                info::MemoryRegionKind::Bootloader => mem::RegionKind::BOOT,
39                _ => mem::RegionKind::UNKNOWN,
40            }
41        }
42
43        fn convert_region(region: &info::MemoryRegion) -> mem::Region {
44            let start = PAddr::from_u64(region.start);
45            let size = {
46                let end = PAddr::from_u64(region.end).offset(1);
47                assert!(start < end, "bad memory range from boot_info!");
48                let size = start.difference(end);
49                assert!(size >= 0);
50                size as usize + 1
51            };
52            let kind = convert_region_kind(region.kind);
53            mem::Region::new(start, size, kind)
54        }
55        self.inner.memory_regions[..].iter().map(convert_region)
56    }
57
58    fn writer(&self) -> Self::Writer {
59        vga::writer()
60    }
61
62    fn framebuffer(&self) -> Option<Self::Framebuffer> {
63        if !self.has_framebuffer {
64            return None;
65        }
66
67        Some(unsafe { framebuf::mk_framebuf() })
68    }
69
70    fn subscriber(&self) -> Option<tracing::Dispatch> {
71        use mycelium_trace::{
72            color::AnsiEscapes,
73            embedded_graphics::MakeTextWriter,
74            writer::{self, MakeWriterExt},
75            Subscriber,
76        };
77
78        type FilteredFramebuf = writer::WithMaxLevel<MakeTextWriter<FramebufWriter>>;
79        type FilteredSerial = writer::WithFilter<
80            AnsiEscapes<&'static serial::Port>,
81            fn(&tracing::Metadata<'_>) -> bool,
82        >;
83
84        static COLLECTOR: InitOnce<Subscriber<FilteredFramebuf, Option<FilteredSerial>>> =
85            InitOnce::uninitialized();
86
87        if !self.has_framebuffer {
88            // TODO(eliza): we should probably write to just the serial port if
89            // there's no framebuffer...
90            return None;
91        }
92
93        fn serial_filter(meta: &tracing::Metadata<'_>) -> bool {
94            // disable really noisy traces from maitake
95            // TODO(eliza): it would be nice if this was configured by
96            // non-arch-specific OS code...
97            const DISABLED_TARGETS: &[&str] = &[
98                // "maitake::time::timer",
99                "maitake::task",
100                "runtime::waker",
101                "mycelium_alloc",
102            ];
103            DISABLED_TARGETS
104                .iter()
105                .all(|target| !meta.target().starts_with(target))
106        }
107
108        let collector = COLLECTOR.get_or_else(|| {
109            let display_writer = MakeTextWriter::new(|| unsafe { framebuf::mk_framebuf() })
110                .with_max_level(tracing::Level::INFO);
111            let serial = serial::com1().map(|com1| {
112                let com1 = AnsiEscapes::new(com1);
113                com1.with_filter(serial_filter as for<'a, 'b> fn(&'a tracing::Metadata<'b>) -> bool)
114            });
115            Subscriber::<_, Option<FilteredSerial>>::display_only(display_writer)
116                .with_serial(serial)
117        });
118        Some(tracing::Dispatch::from_static(collector))
119    }
120
121    fn bootloader_name(&self) -> &str {
122        "rust-osdev/bootloader"
123    }
124
125    fn init_paging(&self) {
126        mm::init_paging(self.vm_offset())
127    }
128}
129
130impl BootloaderApiBootInfo {
131    fn vm_offset(&self) -> VAddr {
132        VAddr::from_u64(
133            self.inner
134                .physical_memory_offset
135                .into_option()
136                .expect("haha wtf"),
137        )
138    }
139
140    pub(super) fn from_bootloader(inner: &'static mut info::BootInfo) -> (Self, ArchInfo) {
141        let has_framebuffer = framebuf::init(inner);
142        let archinfo = ArchInfo {
143            rsdp_addr: inner.rsdp_addr.into_option().map(PAddr::from_u64),
144        };
145        let bootinfo = Self {
146            inner,
147            has_framebuffer,
148        };
149        (bootinfo, archinfo)
150    }
151}