refactor mega-commit.

- reorganised the entire project so that the entire kernel is a single codebase rather than a kernel and a libk.
This commit is contained in:
2025-03-03 02:49:56 +00:00
parent 53d325749d
commit 3966e697da
57 changed files with 331 additions and 997 deletions
+235
View File
@@ -0,0 +1,235 @@
use core::fmt;
use spin::{Lazy, Mutex};
use x86_64::instructions::interrupts;
use crate::arch::x86_64::drivers::framebuffer::{colour::Colour, display::FRAMEBUFFER_WRITER};
use crate::resources::font::{FONT_SPLEEN_8X16, Font};
static FONT_WIDTH: u32 = 8;
static FONT_HEIGHT: u32 = 16;
pub static WRITER: Lazy<Mutex<Writer>> = Lazy::new(|| Mutex::new(Writer::new()));
pub fn screensize_chars() -> (u32, u32) {
let writer = WRITER.lock();
(writer.screen_width, writer.screen_height)
}
pub struct Writer {
font: &'static Font,
/// Measured in chars not pixels.
screen_width: u32,
/// Measured in chars not pixels.
screen_height: u32,
/// 16 pixels tall.
text_line: u32,
/// 8 pixels wide.
text_col: u32,
fg_color: Colour,
bg_color: Colour,
}
impl Default for Writer {
fn default() -> Self {
Self::new()
}
}
impl Writer {
pub fn new() -> Self {
FRAMEBUFFER_WRITER.lock().as_mut().map_or_else(
|| {
panic!("Framebuffer writer not initialized.");
},
|writer| Self {
font: &FONT_SPLEEN_8X16,
screen_width: writer.width() / 8,
screen_height: writer.height() / 16,
text_line: 0,
text_col: 0,
fg_color: Colour::White,
bg_color: Colour::Black,
},
)
}
pub const fn set_font(&mut self, font: &'static Font) {
self.font = font;
}
/// This is sent when the user types a backspace.
const BACKSPACE: u8 = 8;
pub fn write_glyph(&mut self, c: u8) {
if c == b'\n' {
self.newline();
return;
} else if c == Self::BACKSPACE {
self.backspace();
return;
}
// Get the character data from the font array. -- each byte is a row of pixels
let data: &[u8] = self.font.glyph_for(c as u16);
if let Some(writer) = FRAMEBUFFER_WRITER.lock().as_mut() {
for (row, line) in data.iter().enumerate().take(16) {
for col in 0..8 {
let pixel_x: u32 = self.text_col * FONT_WIDTH + col;
let pixel_y: u32 = self.text_line * FONT_HEIGHT + row as u32;
if line & (0x80 >> col) != 0 {
// Write the foreground color
writer.write_pixel(pixel_x as usize, pixel_y as usize, self.fg_color);
} else {
// Write the background color
writer.write_pixel(pixel_x as usize, pixel_y as usize, self.bg_color);
}
}
}
}
// Go to next position
if self.text_col + 1 >= self.screen_width {
self.newline();
} else {
self.text_col += 1;
}
}
pub const fn dimensions(&self) -> (u32, u32) {
(self.screen_width, self.screen_height)
}
pub const fn next_char(&mut self) {
self.text_col += 1;
}
pub const fn newline(&mut self) {
self.text_col = 0;
if self.text_line + 1 >= self.screen_height {
self.text_line = 0;
} else {
self.text_line += 1;
}
}
/// Handles the backspace character. TODO: Implement VT-100 style terminal control
/// codes alongside a shell. Not simple.
pub fn backspace(&mut self) {
if self.text_col > 0 {
self.text_col -= 1;
// Blank out the previous char.
self.write_glyph(b' ');
self.text_col -= 1;
}
}
pub fn write_string(&mut self, s: &str) {
for c in s.chars() {
self.write_glyph(c as u8);
}
}
pub const fn set_colour(&mut self, fg: Colour, bg: Colour) {
self.fg_color = fg;
self.bg_color = bg;
}
pub const fn reset_colour(&mut self) {
self.fg_color = Colour::White;
self.bg_color = Colour::Black;
}
}
impl fmt::Write for Writer {
fn write_str(&mut self, s: &str) -> core::fmt::Result {
self.write_string(s);
Ok(())
}
}
fn write(args: fmt::Arguments, fg: Colour, bg: Colour) {
use core::fmt::Write;
interrupts::without_interrupts(|| {
let mut writer = WRITER.lock();
writer.set_colour(fg, bg);
writer.write_fmt(args).unwrap();
writer.reset_colour();
});
}
pub fn _print(args: fmt::Arguments) {
interrupts::without_interrupts(|| {
write(args, Colour::White, Colour::Black);
})
}
pub fn _print_err(args: fmt::Arguments) {
interrupts::without_interrupts(|| {
write(args, Colour::Red, Colour::Black);
})
}
pub fn _print_log(args: fmt::Arguments) {
interrupts::without_interrupts(|| {
write(args, Colour::Yellow, Colour::Black);
})
}
pub fn clear_screen() {
interrupts::without_interrupts(|| {
let mut writer = WRITER.lock();
writer.text_line = 0;
writer.text_col = 0;
if let Some(writer) = FRAMEBUFFER_WRITER.lock().as_mut() {
writer.clear();
}
});
}
pub fn reset_cursor() {
interrupts::without_interrupts(|| {
let mut writer = WRITER.lock();
writer.text_line = 0;
writer.text_col = 0;
});
}
#[macro_export]
macro_rules! println_log {
() => ($crate::print_log!("\n"));
($($arg:tt)*) => ($crate::print_log!("{}\n", format_args!($($arg)*)));
}
#[macro_export]
macro_rules! print_log {
($($arg:tt)*) => ($crate::prelude::_print_log(format_args!($($arg)*)));
}
#[macro_export]
macro_rules! println {
() => ($crate::print!("\n"));
($($arg:tt)*) => ($crate::print!("{}\n", format_args!($($arg)*)));
}
#[macro_export]
macro_rules! print {
($($arg:tt)*) => ($crate::prelude::_print(format_args!($($arg)*)));
}
#[macro_export]
macro_rules! printlnerr {
() => ($crate::printerr!("\n"));
($($arg:tt)*) => ($crate::printerr!("{}\n", format_args!($($arg)*)));
}
#[macro_export]
macro_rules! printerr {
($($arg:tt)*) => ($crate::prelude::_print_err(format_args!($($arg)*)));
}
@@ -0,0 +1,54 @@
#[repr(u32)]
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
pub enum Colour {
ARGB(u8, u8, u8, u8),
RGB(u8, u8, u8),
HexARGB(u32),
Black = 0x000000FF,
Blue = 0x0000FFFF,
Green = 0x00FF00FF,
Cyan = 0x00FFFFFF,
Red = 0xFF0000FF,
Magenta = 0xFF00FFFF,
Yellow = 0xFFFF00FF,
White = 0xFFFFFFFF,
}
#[allow(clippy::use_self)]
impl From<Colour> for u32 {
fn from(val: Colour) -> Self {
match val {
Colour::ARGB(a, r, g, b) => {
(a as u32) << 24 | (r as u32) << 16 | (g as u32) << 8 | (b as u32)
}
Colour::RGB(r, g, b) => ((r as u32) << 16) | (g as u32) << 8 | (b as u32),
Colour::HexARGB(hex) => hex,
Colour::Black => 0xFF000000,
Colour::Blue => 0xFF0000FF,
Colour::Green => 0xFF00FF00,
Colour::Cyan => 0xFF00FFFF,
Colour::Red => 0xFFFF0000,
Colour::Magenta => 0xFFFF00FF,
Colour::Yellow => 0xFFFFFF00,
Colour::White => 0xFFFFFFFF,
}
}
}
impl core::fmt::Display for Colour {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
match self {
Self::ARGB(r, g, b, a) => write!(f, "RGBA(#{:x}{:x}{:x}{:x})", r, g, b, a),
Self::RGB(r, g, b) => write!(f, "RGB(#{:x}{:x}{:x})", r, g, b),
Self::HexARGB(hex) => write!(f, "Hex(#{:x})", hex),
Self::Black => write!(f, "Black"),
Self::Blue => write!(f, "Blue"),
Self::Green => write!(f, "Green"),
Self::Cyan => write!(f, "Cyan"),
Self::Red => write!(f, "Red"),
Self::Magenta => write!(f, "Magenta"),
Self::Yellow => write!(f, "Yellow"),
Self::White => write!(f, "White"),
}
}
}
@@ -0,0 +1,104 @@
use limine::request::FramebufferRequest;
#[used]
#[unsafe(link_section = ".requests")]
static FRAMEBUFFER_REQUEST: FramebufferRequest = FramebufferRequest::new();
use super::colour::Colour;
use core::panic;
use limine::framebuffer::Framebuffer;
use spin::{Lazy, Mutex};
pub static FRAMEBUFFER_WRITER: Lazy<Mutex<Option<FramebufferWriter>>> = Lazy::new(|| {
Mutex::new(FRAMEBUFFER_REQUEST.get_response().map_or_else(
|| {
panic!("Framebuffer request failed");
},
|framebuffer_response| {
let framebuffer = framebuffer_response.framebuffers().next().unwrap();
Some(FramebufferWriter::new(framebuffer))
},
))
});
/// The updated writer stores necessary fields from the [Framebuffer].
/// This ensures that the contained types are Send, as Framebuffer was
/// not marked as Send.
///
/// It also avoids the requirement for lifetimes.
///
/// Note this does not implement Writer as these functions only handle drawing pixels.
pub struct FramebufferWriter {
pitch: u64,
bpp: u16,
addr: *mut u8,
width: u64,
height: u64,
}
unsafe impl Send for FramebufferWriter {}
unsafe impl Sync for FramebufferWriter {}
impl FramebufferWriter {
pub fn new(framebuffer: Framebuffer) -> Self {
Self {
pitch: framebuffer.pitch(),
bpp: framebuffer.bpp(),
addr: framebuffer.addr(),
width: framebuffer.width(),
height: framebuffer.height(),
}
}
pub fn write_pixel(&self, x: usize, y: usize, color: Colour) {
let pitch = self.pitch as usize;
let bpp = (self.bpp / 8) as usize;
let pixel_offset = y * pitch + x * bpp;
unsafe {
*(self.addr.add(pixel_offset) as *mut u32) = color.into();
}
}
pub fn render_frame(&self, buffer: &[&[Colour]]) {
// TODO: this should return errors
for (y, &row) in buffer.iter().enumerate() {
if y >= self.height() as usize {
break;
}
for (x, pixel) in row.iter().enumerate() {
if x >= self.width() as usize {
break;
}
self.write_pixel(x, y, *pixel);
}
}
}
pub const fn width(&self) -> u32 {
self.width as u32
}
pub const fn height(&self) -> u32 {
self.height as u32
}
pub fn clear(&self) {
let width = self.width as usize;
let height = self.height as usize;
for y in 0..height {
for x in 0..width {
self.write_pixel(x, y, Colour::Black);
}
}
}
}
pub fn screensize_px() -> (u32, u32) {
FRAMEBUFFER_WRITER
.lock()
.as_mut()
.map_or_else(|| (0, 0), |writer| (writer.width(), writer.height()))
}
@@ -0,0 +1,2 @@
pub mod colour;
pub mod display;
+202
View File
@@ -0,0 +1,202 @@
use core::{
pin::Pin,
task::{Context, Poll},
};
use crate::println;
use crossbeam::queue::ArrayQueue;
use futures_util::{Stream, StreamExt, task::AtomicWaker};
use pc_keyboard::{DecodedKey, HandleControl, KeyCode, Keyboard, ScancodeSet1, layouts::Uk105Key};
use spin::{Lazy, Mutex, Once};
static KBD_QUEUE: Once<ArrayQueue<u8>> = Once::new();
static WAKER: AtomicWaker = AtomicWaker::new();
pub static KEYBOARD: Lazy<Mutex<Keyboard<Uk105Key, ScancodeSet1>>> = Lazy::new(|| {
Mutex::new(Keyboard::new(
ScancodeSet1::new(),
// TODO: Expose an API to change the default KB layout.
Uk105Key,
HandleControl::Ignore,
))
});
pub static SCANCODE_STREAM: Lazy<Mutex<ScancodeStream>> =
Lazy::new(|| Mutex::new(ScancodeStream::new()));
pub fn add_scancode(scancode: u8) {
if let Some(queue) = KBD_QUEUE.get() {
if queue.push(scancode).is_err() {
println!("WARNING: scancode queue full; dropping keyboard input");
} else {
WAKER.wake();
}
} else {
println!("WARNING: scancode queue not initialized");
}
}
pub struct ScancodeStream {
_private: (),
}
impl ScancodeStream {
pub fn new() -> Self {
KBD_QUEUE.call_once(|| ArrayQueue::new(5));
Self { _private: () }
}
pub fn try_next(&mut self) -> Option<u8> {
KBD_QUEUE.get().and_then(|queue| queue.pop())
}
}
impl Default for ScancodeStream {
fn default() -> Self {
Self::new()
}
}
impl Stream for ScancodeStream {
type Item = u8;
fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
let queue = KBD_QUEUE.get().unwrap();
if let Some(scancode) = queue.pop() {
return Poll::Ready(Some(scancode));
}
WAKER.register(cx.waker());
WAKER.register(cx.waker());
queue.pop().map_or(Poll::Pending, |scancode| {
WAKER.take();
Poll::Ready(Some(scancode))
})
}
}
pub async fn get_keystroke_async() -> KeyStroke {
loop {
if let Some(scancode) = SCANCODE_STREAM.lock().next().await {
if let Ok(keystroke) = KeyStroke::try_from(scancode) {
return keystroke;
}
}
}
}
pub fn get_keystroke_optional() -> Option<KeyStroke> {
if let Some(scancode) = SCANCODE_STREAM.lock().try_next() {
if let Ok(keystroke) = KeyStroke::try_from(scancode) {
return Some(keystroke);
}
}
None
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum KeyStroke {
Char(char),
Ctrl,
RCtrl,
Alt,
RAlt,
Shift,
RShift,
Meta,
RMeta,
Backspace,
Left,
Right,
Up,
Down,
None,
Enter,
Escape,
Del,
}
impl KeyStroke {
pub const fn from_keycode(key: KeyCode) -> Self {
match key {
KeyCode::LControl => Self::Ctrl,
KeyCode::RControl => Self::RCtrl,
KeyCode::LAlt => Self::Alt,
KeyCode::RAlt2 => Self::RAlt,
KeyCode::LShift => Self::Shift,
KeyCode::RShift => Self::RShift,
KeyCode::LWin => Self::Meta,
KeyCode::RWin => Self::RMeta,
KeyCode::Backspace => Self::Backspace,
KeyCode::ArrowLeft => Self::Left,
KeyCode::ArrowRight => Self::Right,
KeyCode::ArrowUp => Self::Up,
KeyCode::ArrowDown => Self::Down,
KeyCode::Return => Self::Enter,
KeyCode::Escape => Self::Escape,
KeyCode::Delete => Self::Del,
_ => Self::None,
}
}
}
impl TryFrom<u8> for KeyStroke {
type Error = ();
fn try_from(code: u8) -> Result<Self, Self::Error> {
let mut keyboard = KEYBOARD.lock();
let key = match keyboard.add_byte(code) {
Ok(Some(event)) => match keyboard.process_keyevent(event) {
Some(key) => key,
_ => return Err(()),
},
_ => return Err(()),
};
match key {
DecodedKey::Unicode(ch) => Ok(Self::Char(ch)),
DecodedKey::RawKey(key) => match Self::from_keycode(key) {
Self::None => Err(()),
key => Ok(key),
},
}
}
}
impl TryInto<char> for KeyStroke {
type Error = ();
fn try_into(self) -> Result<char, Self::Error> {
match self {
Self::Char(c) => Ok(c),
_ => Err(()),
}
}
}
impl core::fmt::Display for KeyStroke {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
match self {
Self::Char(c) => write!(f, "{}", c),
Self::Ctrl => write!(f, "CTRL"),
Self::RCtrl => write!(f, "RCtrl"),
Self::Alt => write!(f, "ALT"),
Self::RAlt => write!(f, "RAlt"),
Self::Shift => write!(f, "SHIFT"),
Self::RShift => write!(f, "RShift"),
Self::Meta => write!(f, "META"),
Self::RMeta => write!(f, "RMeta"),
Self::Backspace => write!(f, "BACKSPACE"),
Self::Left => write!(f, "LEFT"),
Self::Right => write!(f, "RIGHT"),
Self::Up => write!(f, "UP"),
Self::Down => write!(f, "DOWN"),
Self::Enter => write!(f, "ENTER"),
Self::Escape => write!(f, "ESCAPE"),
Self::None => write!(f, "NONE"),
Self::Del => write!(f, "DEL"),
}
}
}
+5
View File
@@ -0,0 +1,5 @@
pub mod ascii;
pub mod framebuffer;
pub mod keyboard;
pub mod port;
pub mod serial;
+29
View File
@@ -0,0 +1,29 @@
//! Functions for IO using ports.
use core::arch::asm;
#[inline]
pub fn inb(port: u16) -> u8 {
let value: u8;
unsafe {
asm!(
"in al, dx",
out("al") value,
in("dx") port,
options(nomem, nostack, preserves_flags)
);
}
value
}
#[inline]
pub fn outb(port: u16, value: u8) {
unsafe {
asm!(
"out dx, al",
in("dx") port,
in("al") value,
options(nomem, nostack, preserves_flags)
);
}
}
+155
View File
@@ -0,0 +1,155 @@
use core::{
fmt,
sync::atomic::{AtomicUsize, Ordering},
};
use spin::{Lazy, Mutex};
#[macro_export]
macro_rules! serial_print {
($($arg:tt)*) => ($crate::_serial_write(format_args!($($arg)*)));
}
#[macro_export]
macro_rules! serial_println {
() => ($crate::serial_print!("\n"));
($($arg:tt)*) => (serial_print!("{}\n", format_args!($($arg)*)));
}
use crate::arch::x86_64::drivers::port::{inb, outb};
use x86_64::instructions::interrupts;
pub fn _serial_write(args: fmt::Arguments) {
use core::fmt::Write;
interrupts::without_interrupts(|| {
if let Some(writer) = WRITER.lock().as_mut() {
writer.write_fmt(args).unwrap();
}
})
}
pub fn serial_read() -> &'static str {
serial_println!("getting value!");
interrupts::without_interrupts(|| {
if let Some(reader) = READER.lock().as_mut() {
serial_println!("stuff happnin.");
reader.read_str_to_buffer();
} else {
serial_println!("failed to get writer");
}
});
serial_println!("eee");
let i = BUFFER_LEN.load(Ordering::SeqCst);
unsafe {
if i != 0 {
core::str::from_utf8(&BUFFER[..i - 1]).unwrap()
} else {
serial_println!("empty string");
""
}
}
}
static PORT: u16 = 0x3f8;
static mut BUFFER: [u8; 256] = [0; 256];
static BUFFER_LEN: AtomicUsize = AtomicUsize::new(0);
static READER: Lazy<Mutex<Option<Reader>>> = Lazy::new(|| Mutex::new(None));
static WRITER: Lazy<Mutex<Option<Writer>>> = Lazy::new(|| Mutex::new(None));
struct Reader;
struct Writer;
impl fmt::Write for Writer {
fn write_str(&mut self, s: &str) -> fmt::Result {
for c in s.chars() {
self.write_byte(c as u8);
}
Ok(())
}
}
impl Writer {
unsafe fn write_success(&self) -> bool {
inb(PORT + 5) & 0x20 != 0
}
pub fn write_byte(&self, data: u8) {
unsafe {
while !self.write_success() {}
outb(PORT, data);
}
}
}
pub fn init() -> Result<(), &'static str> {
test()?;
if READER.lock().is_none() {
*READER.lock() = Some(Reader);
}
if WRITER.lock().is_none() {
*WRITER.lock() = Some(Writer);
}
Ok(())
}
pub fn test() -> Result<(), &'static str> {
outb(PORT + 1, 0x00); // Disable all interrupts
outb(PORT + 3, 0x80); // Enable DLAB (set baud rate divisor)
outb(PORT, 0x03); // Set divisor to 3 (lo byte) 38400 baud
outb(PORT + 1, 0x00); // (hi byte)
outb(PORT + 3, 0x03); // 8 bits, no parity, one stop bit
outb(PORT + 2, 0xC7); // Enable FIFO, clear them, with 14-bytethreshold
outb(PORT + 4, 0x0B); // IRQs enabled, RTS/DSR set
outb(PORT + 4, 0x1E); // Set in loopback mode, test the serial chip
outb(PORT, 0xAE); // Test serial chip (send byte 0xAE and check if serial returns same byte)
if inb(PORT) != 0xAE {
return Err("serial test failed");
}
outb(PORT + 4, 0x0F);
Ok(())
}
impl Reader {
pub fn read_str_to_buffer(&mut self) {
unsafe {
while !self.read_ready() {}
BUFFER_LEN.store(0, Ordering::SeqCst);
while BUFFER_LEN.load(Ordering::SeqCst) < 256 {
let c = self.read();
BUFFER[BUFFER_LEN.load(Ordering::SeqCst)] = c;
if c as char == '\r' {
break;
}
BUFFER_LEN.fetch_add(1, Ordering::SeqCst);
}
serial_println!("returning")
}
}
unsafe fn read_ready(&self) -> bool {
inb(PORT + 5) & 1 != 0
}
pub fn read(&self) -> u8 {
unsafe {
while !self.read_ready() {}
inb(PORT)
}
}
}