First commit
This commit is contained in:
11
src/boot.rs
Normal file
11
src/boot.rs
Normal 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
18
src/critical_section.rs
Normal 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
61
src/io.rs
Normal 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
42
src/main.rs
Normal 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
31
src/panic_handler.rs
Normal 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
22
src/riscv.rs
Normal 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
9
src/uart.rs
Normal 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
172
src/vga.rs
Normal 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"};
|
||||
Reference in New Issue
Block a user