mycelium_kernel/
lib.rs

1#![cfg_attr(all(target_os = "none", test), no_main)]
2#![cfg_attr(target_os = "none", no_std)]
3#![cfg_attr(target_os = "none", feature(alloc_error_handler))]
4#![allow(unused_unsafe)]
5#![doc = include_str!("../README.md")]
6
7extern crate alloc;
8extern crate rlibc;
9
10pub mod allocator;
11pub mod arch;
12pub mod drivers;
13pub mod rt;
14pub mod shell;
15pub mod wasm;
16
17#[cfg(test)]
18mod tests;
19
20use core::fmt::Write;
21use hal_core::{boot::BootInfo, mem};
22
23pub const MYCELIUM_VERSION: &str = concat!(
24    env!("VERGEN_BUILD_SEMVER"),
25    "-",
26    env!("VERGEN_GIT_BRANCH"),
27    ".",
28    env!("VERGEN_GIT_SHA_SHORT")
29);
30
31#[cfg_attr(target_os = "none", global_allocator)]
32static ALLOC: allocator::Allocator = allocator::Allocator::new();
33
34pub fn kernel_start(bootinfo: impl BootInfo, archinfo: crate::arch::ArchInfo) -> ! {
35    let mut writer = bootinfo.writer();
36    writeln!(
37        writer,
38        "hello from mycelium {} (on {})",
39        MYCELIUM_VERSION,
40        arch::NAME
41    )
42    .unwrap();
43    writeln!(writer, "booting via {}", bootinfo.bootloader_name()).unwrap();
44
45    if let Some(subscriber) = bootinfo.subscriber() {
46        tracing::dispatch::set_global_default(subscriber).unwrap();
47    }
48
49    #[cfg(not(test))]
50    if let Some(mut framebuf) = bootinfo.framebuffer() {
51        use hal_core::framebuffer::{Draw, RgbColor};
52        tracing::trace!("framebuffer exists!");
53        framebuf.clear();
54        let mut color = 0;
55        for row in 0..framebuf.height() {
56            color += 1;
57            match color {
58                1 => {
59                    framebuf.fill_row(row, RgbColor::RED);
60                }
61                2 => {
62                    framebuf.fill_row(row, RgbColor::GREEN);
63                }
64                3 => {
65                    framebuf.fill_row(row, RgbColor::BLUE);
66                    color = 0;
67                }
68                _ => {}
69            }
70        }
71        tracing::trace!("made it grey!");
72
73        use embedded_graphics::{
74            mono_font::{ascii, MonoTextStyle},
75            pixelcolor::{Rgb888, RgbColor as _},
76            prelude::*,
77            text::{Alignment, Text},
78        };
79        let center = Point::new(
80            (framebuf.width() / 2) as i32,
81            (framebuf.height() / 2) as i32,
82        );
83        let mut target = framebuf.clear().as_draw_target();
84        let small_style = MonoTextStyle::new(&ascii::FONT_6X10, Rgb888::WHITE);
85        Text::with_alignment(
86            "the glorious\n",
87            Point::new(center.x, center.y - 20),
88            small_style,
89            Alignment::Center,
90        )
91        .draw(&mut target)
92        .expect("never panics");
93        Text::with_alignment(
94            "MYCELIUM\n",
95            center,
96            MonoTextStyle::new(&ascii::FONT_10X20, Rgb888::WHITE),
97            Alignment::Center,
98        )
99        .draw(&mut target)
100        .expect("never panics");
101        Text::with_alignment(
102            concat!("operating system\n\n v", env!("CARGO_PKG_VERSION"), "\n"),
103            Point::new(center.x, center.y + 10),
104            small_style,
105            Alignment::Center,
106        )
107        .draw(&mut target)
108        .expect("never panics");
109
110        tracing::trace!("hahahaha yayyyy we drew a screen!");
111    }
112
113    arch::interrupt::enable_exceptions();
114    bootinfo.init_paging();
115    ALLOC.init(&bootinfo);
116
117    let mut regions = 0;
118    let mut free_regions = 0;
119    let mut free_bytes = 0;
120
121    {
122        let span = tracing::info_span!("memory map");
123        let _enter = span.enter();
124        for region in bootinfo.memory_map() {
125            let kind = region.kind();
126            let size = region.size();
127            tracing::info!(
128                "  {:>10?} {:>15?} {:>15?} B",
129                region.base_addr(),
130                kind,
131                size,
132            );
133            regions += 1;
134            if region.kind() == mem::RegionKind::FREE {
135                free_regions += 1;
136                free_bytes += size;
137                unsafe {
138                    ALLOC.add_region(region);
139                }
140            }
141        }
142
143        tracing::info!(
144            "found {} memory regions, {} free regions ({} bytes)",
145            regions,
146            free_regions,
147            free_bytes,
148        );
149    }
150
151    // perform arch-specific initialization once we have an allocator and
152    // tracing.
153    let clock = arch::init(&bootinfo, &archinfo);
154
155    // initialize the kernel runtime.
156    rt::init(clock);
157
158    #[cfg(test)]
159    arch::run_tests();
160
161    kernel_main(bootinfo);
162}
163
164fn kernel_main(bootinfo: impl BootInfo) -> ! {
165    rt::spawn(keyboard_demo());
166
167    let mut core = rt::Core::new();
168    tracing::info!(
169        version = %MYCELIUM_VERSION,
170        arch = %arch::NAME,
171        bootloader = %bootinfo.bootloader_name(),
172        "welcome to the Glorious Mycelium Operating System",
173    );
174
175    loop {
176        core.run();
177        tracing::warn!("someone stopped CPU 0's core! restarting it...");
178    }
179}
180
181#[cfg_attr(target_os = "none", alloc_error_handler)]
182pub fn alloc_error(layout: core::alloc::Layout) -> ! {
183    arch::oops(arch::Oops::alloc_error(layout))
184}
185
186#[cfg_attr(target_os = "none", panic_handler)]
187#[cold]
188pub fn panic(panic: &core::panic::PanicInfo<'_>) -> ! {
189    arch::oops(arch::Oops::from(panic))
190}
191
192#[cfg(all(test, not(target_os = "none")))]
193pub fn main() {
194    /* no host-platform tests in this crate */
195}
196
197/// Keyboard handler demo task: logs each line typed by the user.
198// TODO(eliza): let's do something Actually Useful with keyboard input...
199async fn keyboard_demo() {
200    #[cfg(target_os = "none")]
201    use alloc::string::String;
202    use drivers::ps2_keyboard::{self, DecodedKey, KeyCode};
203
204    let mut line = String::new();
205    tracing::info!("type `help` to list available commands");
206    loop {
207        let key = ps2_keyboard::next_key().await;
208        let mut newline = false;
209        match key {
210            DecodedKey::Unicode('\n') | DecodedKey::Unicode('\r') => {
211                newline = true;
212            }
213            // backspace
214            DecodedKey::RawKey(KeyCode::Backspace)
215            | DecodedKey::RawKey(KeyCode::Delete)
216            | DecodedKey::Unicode('\u{0008}') => {
217                line.pop();
218            }
219            DecodedKey::Unicode(c) => line.push(c),
220            DecodedKey::RawKey(key) => tracing::warn!(?key, "you typed something weird"),
221        }
222        if newline {
223            shell::eval(&line);
224            line.clear();
225        }
226    }
227}