First commit

This commit is contained in:
2026-01-22 18:09:14 +01:00
committed by Julien THILLARD
commit 7a25e89d4c
21 changed files with 609 additions and 0 deletions

11
src/boot.rs Normal file
View File

@@ -0,0 +1,11 @@
use core::arch::global_asm;
global_asm!(
".section .text.entry
.globl entry
entry:
la sp, _heap_end
jal main
loop:
j loop"
);

18
src/critical_section.rs Normal file
View File

@@ -0,0 +1,18 @@
use critical_section::RawRestoreState;
use crate::riscv::{disable_interrupt, get_interrupt_state, restore_interrupt};
struct MyCriticalSection;
critical_section::set_impl!(MyCriticalSection);
unsafe impl critical_section::Impl for MyCriticalSection {
unsafe fn acquire() -> RawRestoreState {
let restore = get_interrupt_state();
disable_interrupt();
restore
}
unsafe fn release(token: RawRestoreState) {
restore_interrupt(token);
}
}

61
src/io.rs Normal file
View File

@@ -0,0 +1,61 @@
use crate::println;
use alloc::format;
use alloc::string::String;
use log::{Level, Metadata, Record};
use log::{LevelFilter, SetLoggerError};
use crate::uart::write_uart;
fn print(content: String) {
write_uart(content);
}
struct Logger;
impl log::Log for Logger {
fn enabled(&self, metadata: &Metadata) -> bool {
metadata.level() <= Level::Info
}
fn log(&self, record: &Record) {
if self.enabled(record.metadata()) {
if let Some((file, line)) = record.file().zip(record.line()) {
println!(
"[{}] at {}:{} - {}",
record.level(),
file,
line,
record.args()
);
} else {
println!("[{}] - {}", record.level(), record.args());
}
}
}
fn flush(&self) {}
}
static LOGGER: Logger = Logger;
pub fn init_log() -> Result<(), SetLoggerError> {
log::set_logger(&LOGGER).map(|()| log::set_max_level(LevelFilter::Info))
}
#[macro_export]
macro_rules! print {
($($args:expr),*) => {
$crate::io::print(format!($($args),*))
};
}
#[macro_export]
macro_rules! println {
() => {
$crate::print!("\n\r");
};
($($args:expr),*) => {
$crate::print!($($args),*);
$crate::println!();
};
}

42
src/main.rs Normal file
View File

@@ -0,0 +1,42 @@
#![no_std]
#![no_main]
use core::arch::asm;
use embedded_alloc::LlffHeap as Heap;
use log::info;
use crate::{
io::init_log,
vga::{Color, Vga},
};
extern crate alloc;
mod boot;
mod critical_section;
mod io;
mod panic_handler;
mod riscv;
mod uart;
mod vga;
pub const HEAP_SIZE: usize = 40960;
#[global_allocator]
static HEAP: Heap = Heap::empty();
#[unsafe(no_mangle)]
pub extern "C" fn main() {
unsafe {
embedded_alloc::init!(HEAP, HEAP_SIZE);
}
init_log().unwrap();
Vga::init();
info!("Hello World !");
unsafe { Vga::draw_string(10, 10, "Hello World !", Color::WHITE) };
loop {
unsafe { asm!("wfi") }
}
}

31
src/panic_handler.rs Normal file
View File

@@ -0,0 +1,31 @@
use core::arch::asm;
use alloc::{
format,
string::{String, ToString},
};
use log::error;
use crate::vga::{Color, FONT_HEIGHT, Vga};
#[panic_handler]
fn panic(panic_info: &core::panic::PanicInfo) -> ! {
error!("PANIC !");
let mut panic_message = if let Some(message) = panic_info.message().as_str() {
message.to_string()
} else {
String::new()
};
if let Some(location) = panic_info.location() {
panic_message = format!("{panic_message} at {}:{}", location.file(), location.line());
}
error!("{panic_message}");
Vga::clear_screen(Color::WHITE);
unsafe { Vga::draw_string(0, 0, "PANIC !", Color::BLACK) };
unsafe { Vga::draw_string(0, FONT_HEIGHT as u16, panic_message, Color::BLACK) };
loop {
unsafe { asm!("wfi") }
}
}

22
src/riscv.rs Normal file
View File

@@ -0,0 +1,22 @@
use core::arch::asm;
const MSTATUS_MIE: usize = 0x8;
pub fn get_interrupt_state() -> bool {
let res: u64;
unsafe { asm!("csrr {}, mstatus", out(reg) res) };
(res & MSTATUS_MIE as u64) != 0
}
pub fn enable_interrupt() {
unsafe { asm!("csrs mstatus, {}", in(reg) MSTATUS_MIE) };
}
pub fn disable_interrupt() {
unsafe { asm!("csrc mstatus, {}", in(reg) MSTATUS_MIE) };
}
pub fn restore_interrupt(previous_state: bool) {
if previous_state {
enable_interrupt();
} else {
disable_interrupt();
}
}

9
src/uart.rs Normal file
View File

@@ -0,0 +1,9 @@
const UART_BASE: *mut u8 = 0x10000000 as *mut _;
pub fn write_char_uart(c: char) {
while unsafe { core::ptr::read_volatile(UART_BASE.byte_add(0x5)) } >> 5 & 1 == 0 {}
unsafe { core::ptr::write_volatile(UART_BASE, c as u8) };
}
pub fn write_uart<T: AsRef<str>>(print: T) {
print.as_ref().chars().for_each(write_char_uart);
}

172
src/vga.rs Normal file
View File

@@ -0,0 +1,172 @@
use kernel_macros::include_font_plate;
use log::info;
const PCI_ECAM_BASE_ADDRESS: *mut u32 = 0x30000000 as *mut _;
const BOCHS_DISPLAY_BASE_ADDRESS: *mut u32 = 0x50000000 as *mut _;
const BOCHS_CONFIG_BASE_ADDRESS: *mut u16 = 0x40000000 as *mut _;
pub const VGA_ADDRESS: *mut Color = BOCHS_DISPLAY_BASE_ADDRESS as *mut Color;
pub const WIDTH: usize = 1600;
pub const HEIGHT: usize = 900;
#[repr(transparent)]
#[derive(Clone, Copy)]
pub struct Color(u32);
#[allow(unused)]
impl Color {
pub const fn from_rgb(r: u8, g: u8, b: u8) -> Self {
Self((r as u32) << 16 | (g as u32) << 8 | b as u32)
}
pub const WHITE: Color = Color::from_rgb(255, 255, 255);
pub const GRAY: Color = Color::from_rgb(128, 128, 128);
pub const LIGHT_GRAY: Color = Color::from_rgb(128, 64, 64);
pub const BLACK: Color = Color::from_rgb(0, 0, 0);
pub const RED: Color = Color::from_rgb(255, 0, 0);
pub const GREEN: Color = Color::from_rgb(0, 255, 0);
pub const BLUE: Color = Color::from_rgb(0, 0, 255);
}
pub struct Vga {}
impl Vga {
pub fn init() {
for i in 0..32 {
let addr = PCI_ECAM_BASE_ADDRESS.wrapping_byte_add(i << 11);
let header = unsafe { core::ptr::read_volatile(addr) };
if header >> 16 == 0x1111 && header & 0xFFFF == 0x1234 {
info!("VGA Bochs PCI found");
unsafe {
let mut command = core::ptr::read_volatile(addr.byte_add(0x04));
command |= 0b111;
core::ptr::write_volatile(addr.byte_add(0x04), command);
core::ptr::write_volatile(
addr.byte_add(0x10),
BOCHS_DISPLAY_BASE_ADDRESS as u32,
);
core::ptr::write_volatile(addr.byte_add(0x18), BOCHS_CONFIG_BASE_ADDRESS as u32)
};
break;
}
}
unsafe {
core::ptr::write_volatile(BOCHS_CONFIG_BASE_ADDRESS.byte_add(0x508), 0x0);
core::ptr::write_volatile(BOCHS_CONFIG_BASE_ADDRESS.byte_add(0x502), WIDTH as u16);
core::ptr::write_volatile(BOCHS_CONFIG_BASE_ADDRESS.byte_add(0x504), HEIGHT as u16);
core::ptr::write_volatile(BOCHS_CONFIG_BASE_ADDRESS.byte_add(0x506), 32);
core::ptr::write_volatile(BOCHS_CONFIG_BASE_ADDRESS.byte_add(0x50a), 0x0);
core::ptr::write_volatile(BOCHS_CONFIG_BASE_ADDRESS.byte_add(0x510), 0x0);
core::ptr::write_volatile(BOCHS_CONFIG_BASE_ADDRESS.byte_add(0x512), 0x0);
core::ptr::write_volatile(BOCHS_CONFIG_BASE_ADDRESS.byte_add(0x508), 0x41)
};
Vga::clear_screen(Color::BLACK);
}
/// # Safety
/// `x` must be less than `WIDTH` and `y` must be less than `HEIGHT`
pub unsafe fn write_pixel_unsafe(x: u16, y: u16, color: Color) {
let pixel_index = x as usize + y as usize * WIDTH;
unsafe { *VGA_ADDRESS.add(pixel_index) = color }
}
/// # Safety
/// `x` must be less than `WIDTH` and `y` must be less than `HEIGHT`
pub unsafe fn draw_char(x: u16, y: u16, c: char, color: Color) {
let c = if (c as u8 > b'~') || ((c as u8) < b' ') {
b'/' - b' '
} else {
c as u8 - b' '
};
// Get char position within font plate
let char_x = (c as usize % 32) * FONT_WIDTH;
let char_y = (c as usize / 32) * FONT_HEIGHT;
for i in 0..(FONT_WIDTH as u16) {
for j in 0..(FONT_HEIGHT as u16) {
let xx = x + i;
let yy = y + j;
if xx < (WIDTH as u16)
&& yy < (HEIGHT as u16)
&& unsafe { Self::font_plate_index(char_x as u16 + i, char_y as u16 + j) }
{
unsafe { Self::write_pixel_unsafe(xx, yy, color) }
}
}
}
}
#[allow(unused)]
pub unsafe fn draw_char_bg(x: u16, y: u16, c: char, color: Color, bg_color: Color) {
let c = if (c as u8 > b'~') || ((c as u8) < b' ') {
b'/' - b' '
} else {
c as u8 - b' '
};
// Get char position within font plate
let char_x = (c as usize % 32) * FONT_WIDTH;
let char_y = (c as usize / 32) * FONT_HEIGHT;
for i in 0..(FONT_WIDTH as u16) {
for j in 0..(FONT_HEIGHT as u16) {
let xx = x + i;
let yy = y + j;
if xx < (WIDTH as u16) && yy < (HEIGHT as u16) {
if unsafe { Self::font_plate_index(char_x as u16 + i, char_y as u16 + j) } {
unsafe { Self::write_pixel_unsafe(xx, yy, color) }
} else {
unsafe { Self::write_pixel_unsafe(xx, yy, bg_color) }
}
}
}
}
}
/// # Safety
/// The text must have a length that can fit within a `u16`
pub unsafe fn draw_string<T: AsRef<str>>(x: u16, mut y: u16, str: T, color: Color) {
let mut current_x = x;
str.as_ref().chars().for_each(|c| unsafe {
match c {
'\n' => {
current_x = x;
y += FONT_HEIGHT as u16;
}
'\r' => {
current_x = x;
}
c => {
Self::draw_char(current_x, y, c, color);
current_x += FONT_WIDTH as u16;
}
}
});
}
pub fn clear_screen(color: Color) {
for i in 0..WIDTH * HEIGHT {
unsafe { *VGA_ADDRESS.add(i) = color }
}
}
unsafe fn font_plate_index(x: u16, y: u16) -> bool {
let pixel_index = (y as usize) * FONTPLATE_WIDTH + (x as usize);
let byte_index = pixel_index / 8;
let bit_index = pixel_index % 8;
(FONTPLATE[byte_index] >> bit_index) & 0b1 == 0b1
}
}
pub const FONT_WIDTH: usize = 6;
pub const FONT_HEIGHT: usize = 13;
pub const FONTPLATE_WIDTH: usize = 32 * FONT_WIDTH;
pub const FONTPLATE_HEIGHT: usize = 3 * FONT_HEIGHT;
pub const FONTPLATE_SIZE: usize = FONTPLATE_WIDTH * FONTPLATE_HEIGHT / 8;
pub const FONTPLATE: [u8; FONTPLATE_SIZE] = include_font_plate! {"assets/fontplate.png"};