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(demo_shell(|linebuf| {
166        alloc::boxed::Box::pin(keyboard_line(linebuf))
167    }));
168    rt::spawn(demo_shell(|linebuf| {
169        alloc::boxed::Box::pin(serial_line(0, linebuf))
170    }));
171
172    let mut core = rt::Core::new();
173    tracing::info!(
174        version = %MYCELIUM_VERSION,
175        arch = %arch::NAME,
176        bootloader = %bootinfo.bootloader_name(),
177        "welcome to the Glorious Mycelium Operating System",
178    );
179
180    loop {
181        core.run();
182        tracing::warn!("someone stopped CPU 0's core! restarting it...");
183    }
184}
185
186#[cfg_attr(target_os = "none", alloc_error_handler)]
187pub fn alloc_error(layout: core::alloc::Layout) -> ! {
188    arch::oops(arch::Oops::alloc_error(layout))
189}
190
191#[cfg_attr(target_os = "none", panic_handler)]
192#[cold]
193pub fn panic(panic: &core::panic::PanicInfo<'_>) -> ! {
194    arch::oops(arch::Oops::from(panic))
195}
196
197#[cfg(all(test, not(target_os = "none")))]
198pub fn main() {
199    /* no host-platform tests in this crate */
200}
201
202async fn keyboard_line(mut line: alloc::string::String) -> alloc::string::String {
203    use drivers::ps2_keyboard::{self, DecodedKey, KeyCode};
204
205    loop {
206        let key = ps2_keyboard::next_key().await;
207        let mut newline = false;
208        match key {
209            DecodedKey::Unicode('\n') | DecodedKey::Unicode('\r') => {
210                newline = true;
211            }
212            // backspace
213            DecodedKey::RawKey(KeyCode::Backspace)
214            | DecodedKey::RawKey(KeyCode::Delete)
215            | DecodedKey::Unicode('\u{0008}') => {
216                line.pop();
217            }
218            DecodedKey::Unicode(c) => line.push(c),
219            DecodedKey::RawKey(key) => tracing::warn!(?key, "you typed something weird"),
220        }
221        if newline {
222            return line;
223        }
224    }
225}
226
227async fn serial_line(port: usize, mut line: alloc::string::String) -> alloc::string::String {
228    let serial_iface = &crate::drivers::serial_input::SERIAL_INPUTS.get()[port];
229
230    loop {
231        let b = serial_iface.next_byte().await;
232        let mut newline = false;
233        match b {
234            b'\n' | b'\r' => {
235                newline = true;
236            }
237            // backspace
238            0x7f => {
239                line.pop();
240            }
241            c @ b'a'..=b'z' | c @ b'A'..=b'Z' | c @ b' ' => line.push(c as char),
242            other => tracing::warn!(?other, "the serial port got something weird"),
243        }
244        if newline {
245            return line;
246        }
247    }
248}
249
250type ReadLineFn = fn(
251    alloc::string::String,
252) -> core::pin::Pin<
253    alloc::boxed::Box<dyn core::future::Future<Output = alloc::string::String> + Send>,
254>;
255
256/// Demo shell task: *very* minimal debug shell.
257// TODO(eliza): let's do something Actually Useful with keyboard input...
258async fn demo_shell(line_source: ReadLineFn) {
259    #[cfg(target_os = "none")]
260    use alloc::string::String;
261
262    let mut line = String::new();
263    tracing::info!("type `help` to list available commands");
264    loop {
265        let fut = line_source(line);
266        line = fut.await;
267        shell::eval(&line);
268        line.clear();
269    }
270}