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 let clock = arch::init(&bootinfo, &archinfo);
154
155 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 }
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 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 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
256async 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}