working on kernel
This commit is contained in:
+4
-1
@@ -3,7 +3,7 @@ target = "x86_64-kernel"
|
|||||||
target-dir = "build/target"
|
target-dir = "build/target"
|
||||||
|
|
||||||
[unstable]
|
[unstable]
|
||||||
build-std = ["core", "compiler_builtins", "alloc"]
|
build-std = ["core", "compiler_builtins"]
|
||||||
build-std-features = ["compiler-builtins-mem"]
|
build-std-features = ["compiler-builtins-mem"]
|
||||||
|
|
||||||
[env]
|
[env]
|
||||||
@@ -11,3 +11,6 @@ RUST_TARGET_PATH = { value = "kernel", relative = true }
|
|||||||
|
|
||||||
[target.x86_64-kernel]
|
[target.x86_64-kernel]
|
||||||
runner = "scripts/run.sh"
|
runner = "scripts/run.sh"
|
||||||
|
|
||||||
|
[target.'cfg(target_os = "none")']
|
||||||
|
runner = "scripts/run.sh --test"
|
||||||
|
|||||||
@@ -5,7 +5,6 @@ members = [
|
|||||||
resolver = "2"
|
resolver = "2"
|
||||||
|
|
||||||
[profile.dev]
|
[profile.dev]
|
||||||
panic = "abort"
|
|
||||||
opt-level = "z"
|
opt-level = "z"
|
||||||
debug = true
|
debug = true
|
||||||
debug-assertions = true
|
debug-assertions = true
|
||||||
@@ -15,7 +14,6 @@ incremental = false
|
|||||||
codegen-units = 1
|
codegen-units = 1
|
||||||
|
|
||||||
[profile.release]
|
[profile.release]
|
||||||
panic = "abort"
|
|
||||||
opt-level = "z"
|
opt-level = "z"
|
||||||
debug = false
|
debug = false
|
||||||
debug-assertions = false
|
debug-assertions = false
|
||||||
|
|||||||
+14
-7
@@ -1,16 +1,23 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "kernel"
|
name = "kernel"
|
||||||
version.workspace = true
|
version = "0.1.0"
|
||||||
edition.workspace = true
|
edition = "2021"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
limine = "0.3.1"
|
limine = "0.3.1"
|
||||||
spin = "0.9.8"
|
spin = { version = "0.9.8", features = ["lazy"] }
|
||||||
bitflags = "2.4.0"
|
bitflags = { version = "2.4.0", default-features = false }
|
||||||
lazy_static = { version = "1.5.0", features = ["spin", "spin_no_std"] }
|
lazy_static = { version = "1.5.0", features = ["spin_no_std"] }
|
||||||
|
x86_64 = { version = "0.15.1" }
|
||||||
|
|
||||||
|
[features]
|
||||||
|
default = []
|
||||||
|
|
||||||
[[bin]]
|
[[bin]]
|
||||||
name = "kernel"
|
name = "kernel"
|
||||||
path = "src/main.rs"
|
path = "src/main.rs"
|
||||||
test = false
|
|
||||||
bench = false
|
# setup lib.rs
|
||||||
|
[lib]
|
||||||
|
name = "GoofyAhhOS"
|
||||||
|
path = "src/lib.rs"
|
||||||
|
|||||||
@@ -0,0 +1,64 @@
|
|||||||
|
#![no_std]
|
||||||
|
#![cfg_attr(test, no_main)]
|
||||||
|
#![feature(custom_test_frameworks)]
|
||||||
|
#![test_runner(crate::tests::test_runner)]
|
||||||
|
#![reexport_test_harness_main = "test_main"]
|
||||||
|
#![feature(abi_x86_interrupt)]
|
||||||
|
|
||||||
|
// #[cfg(test)]
|
||||||
|
// use limine::BaseRevision;
|
||||||
|
|
||||||
|
#[cfg(not(test))]
|
||||||
|
use core::panic::PanicInfo;
|
||||||
|
|
||||||
|
pub mod tests;
|
||||||
|
pub mod sys;
|
||||||
|
pub mod usr;
|
||||||
|
|
||||||
|
|
||||||
|
pub use sys::kernel::drivers::framebuffer::textwriter::{
|
||||||
|
_print,
|
||||||
|
_printerr,
|
||||||
|
_log,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub use sys::kernel::drivers::serial::{
|
||||||
|
_serial_write,
|
||||||
|
serial_read
|
||||||
|
};
|
||||||
|
|
||||||
|
pub fn init() {
|
||||||
|
sys::kernel::cpu::interrupts::init();
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn hcf() -> ! {
|
||||||
|
loop {
|
||||||
|
unsafe { core::arch::asm!("cli; hlt") }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Called on panic
|
||||||
|
#[cfg(not(test))]
|
||||||
|
#[panic_handler]
|
||||||
|
fn panic(_info: &PanicInfo) -> ! {
|
||||||
|
println!("{}", _info);
|
||||||
|
hcf()
|
||||||
|
}
|
||||||
|
|
||||||
|
// code for testing etc.
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
#[no_mangle]
|
||||||
|
pub extern "C" fn kmain() -> ! {
|
||||||
|
// Set up the base revision for Limine
|
||||||
|
// static BASE_REVISION: BaseRevision = BaseRevision::new();
|
||||||
|
|
||||||
|
init();
|
||||||
|
test_main();
|
||||||
|
|
||||||
|
serial_println!("All tests passed! exiting.");
|
||||||
|
|
||||||
|
sys::qemu::exit_success();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
+31
-26
@@ -1,40 +1,45 @@
|
|||||||
#![no_std] // Don't link the Rust standard library
|
#![no_std] // Don't link the Rust standard library
|
||||||
#![no_main] // Disable all Rust-level entry points
|
#![no_main] // Disable all Rust-level entry points
|
||||||
|
#![feature(custom_test_frameworks)]
|
||||||
|
#![test_runner(GoofyAhhOS::tests::test_runner)]
|
||||||
|
#![reexport_test_harness_main = "test_main"]
|
||||||
|
|
||||||
use core::panic::PanicInfo;
|
|
||||||
use limine::*;
|
use limine::*;
|
||||||
|
|
||||||
use limine::request::{
|
use GoofyAhhOS::sys::kernel::drivers::serial::serial_read;
|
||||||
FramebufferRequest, RequestsEndMarker, RequestsStartMarker,
|
use GoofyAhhOS::{print, println, serial_println};
|
||||||
};
|
use GoofyAhhOS::sys::kernel::drivers::framebuffer::textwriter::clear_screen;
|
||||||
|
|
||||||
mod font;
|
|
||||||
mod render;
|
|
||||||
|
|
||||||
use crate::font::FONT;
|
|
||||||
|
|
||||||
// Set the base revision
|
// Set the base revision
|
||||||
static BASE_REVISION: BaseRevision = BaseRevision::new();
|
static BASE_REVISION: BaseRevision = BaseRevision::new();
|
||||||
|
|
||||||
|
|
||||||
// Halt and catch fire function
|
|
||||||
fn hcf() -> ! {
|
|
||||||
loop {
|
|
||||||
unsafe { core::arch::asm!("cli; hlt") }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Called on panic
|
|
||||||
#[panic_handler]
|
|
||||||
fn panic(_info: &PanicInfo) -> ! {
|
|
||||||
hcf()
|
|
||||||
}
|
|
||||||
|
|
||||||
// Kernel entry point
|
// Kernel entry point
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
pub extern "C" fn kmain() -> ! {
|
pub extern "C" fn kmain() -> ! {
|
||||||
render::init();
|
|
||||||
render::write_string("Welcome to GoofyAhhOS!\nthis is the superior os\nif you disagree you are a heretic in the name of steven.", 0xff0000, 0);
|
|
||||||
|
|
||||||
hcf()
|
GoofyAhhOS::init();
|
||||||
|
|
||||||
|
println!("Hello from GoofyAhhOS!");
|
||||||
|
serial_println!("SERIAL OUT ACHIEVED :check:");
|
||||||
|
|
||||||
|
loop {
|
||||||
|
let input: &str = serial_read();
|
||||||
|
|
||||||
|
clear_screen();
|
||||||
|
|
||||||
|
if input.starts_with("print ") {
|
||||||
|
let input = &input[6..];
|
||||||
|
println!("{}", input);
|
||||||
|
} else if input == "" {
|
||||||
|
let x: i32 = 239423889;
|
||||||
|
let y: i32 = 123456678;
|
||||||
|
|
||||||
|
print!("num: {} {}", x, y);
|
||||||
|
} else {
|
||||||
|
println!("Unknown command: {}", input);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
GoofyAhhOS::hcf()
|
||||||
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,118 +0,0 @@
|
|||||||
use core::sync::atomic::{AtomicUsize, Ordering};
|
|
||||||
use lazy_static::lazy_static;
|
|
||||||
use limine::framebuffer::Framebuffer;
|
|
||||||
use spin::Mutex;
|
|
||||||
use limine::request::FramebufferRequest;
|
|
||||||
|
|
||||||
use crate::font::FONT;
|
|
||||||
|
|
||||||
pub struct FramebufferWriter<'a> {
|
|
||||||
framebuffer: Framebuffer<'a>,
|
|
||||||
x_pos: AtomicUsize,
|
|
||||||
y_pos: AtomicUsize,
|
|
||||||
}
|
|
||||||
|
|
||||||
unsafe impl<'a> Send for FramebufferWriter<'a> {}
|
|
||||||
unsafe impl<'a> Sync for FramebufferWriter<'a> {}
|
|
||||||
|
|
||||||
impl<'a> FramebufferWriter<'a> {
|
|
||||||
pub fn new(framebuffer: Framebuffer<'a>) -> Self {
|
|
||||||
Self {
|
|
||||||
framebuffer,
|
|
||||||
x_pos: AtomicUsize::new(0),
|
|
||||||
y_pos: AtomicUsize::new(0),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn write_pixel(&self, x: usize, y: usize, color: u32) {
|
|
||||||
let pitch = self.framebuffer.pitch() as usize;
|
|
||||||
let bpp = (self.framebuffer.bpp() / 8) as usize;
|
|
||||||
let pixel_offset = y * pitch + x * bpp;
|
|
||||||
|
|
||||||
unsafe {
|
|
||||||
*(self.framebuffer.addr().add(pixel_offset) as *mut u32) = color;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn write_char(&self, x: u32, y: u32, fg_color: u32, bg_color: u32, mut c: u8) {
|
|
||||||
if c < 32 || c > 126 {
|
|
||||||
c = '?' as u8;
|
|
||||||
}
|
|
||||||
|
|
||||||
let data: &[u8] = &FONT[c as usize * 16..(c as usize + 1) * 16];
|
|
||||||
|
|
||||||
for row in 0..16 {
|
|
||||||
let line: u8 = data[row];
|
|
||||||
for col in 0..8 {
|
|
||||||
let pixel_x: u32 = x + col;
|
|
||||||
let pixel_y: u32 = y + row as u32;
|
|
||||||
if line & (0x80 >> col) != 0 {
|
|
||||||
self.write_pixel(pixel_x as usize, pixel_y as usize, fg_color);
|
|
||||||
} else {
|
|
||||||
self.write_pixel(pixel_x as usize, pixel_y as usize, bg_color);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn write_string(&self, x: u32, y: u32, fg_color: u32, bg_color: u32, s: &str) {
|
|
||||||
let mut curr_x: u32 = x;
|
|
||||||
let mut curr_y: u32 = y;
|
|
||||||
|
|
||||||
for c in s.chars() {
|
|
||||||
if c == '\n' {
|
|
||||||
curr_x = x;
|
|
||||||
curr_y += 16;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if curr_x + 8 > self.framebuffer.width() as u32 {
|
|
||||||
curr_x = x;
|
|
||||||
curr_y += 16;
|
|
||||||
}
|
|
||||||
|
|
||||||
if curr_y + 16 > self.framebuffer.height() as u32 {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
self.write_char(curr_x, curr_y, fg_color, bg_color, c as u8);
|
|
||||||
curr_x += 8;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn clear(&self, color: u32) {
|
|
||||||
let width = self.framebuffer.width() as usize;
|
|
||||||
let height = self.framebuffer.height() as usize;
|
|
||||||
|
|
||||||
for y in 0..height {
|
|
||||||
for x in 0..width {
|
|
||||||
self.write_pixel(x, y, color);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static FRAMEBUFFER_REQUEST: FramebufferRequest = FramebufferRequest::new();
|
|
||||||
|
|
||||||
lazy_static! {
|
|
||||||
pub static ref FRAMEBUFFER_WRITER: Mutex<Option<FramebufferWriter<'static>>> = Mutex::new(None);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn init() {
|
|
||||||
if let Some(framebuffer_response) = FRAMEBUFFER_REQUEST.get_response() {
|
|
||||||
// The framebuffer from the response has a 'static lifetime
|
|
||||||
let framebuffer = framebuffer_response.framebuffers().next().unwrap();
|
|
||||||
*FRAMEBUFFER_WRITER.lock() = Some(FramebufferWriter::new(framebuffer));
|
|
||||||
clear_screen(0x00000000);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn clear_screen(color: u32) {
|
|
||||||
if let Some(writer) = FRAMEBUFFER_WRITER.lock().as_ref() {
|
|
||||||
writer.clear(color);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn write_string(s: &str, fg_color: u32, bg_color: u32) {
|
|
||||||
FRAMEBUFFER_WRITER.lock().as_ref().unwrap().write_string(0, 0, fg_color, bg_color, s);
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,67 @@
|
|||||||
|
//! CPU I/O port operations
|
||||||
|
|
||||||
|
use core::arch::asm;
|
||||||
|
|
||||||
|
/// Read a byte from the specified I/O port
|
||||||
|
///
|
||||||
|
/// # Safety
|
||||||
|
/// This function is unsafe because it performs direct I/O port operations
|
||||||
|
/// which could have unpredictable effects on hardware
|
||||||
|
#[inline]
|
||||||
|
pub unsafe fn inb(port: u16) -> u8 {
|
||||||
|
let value: u8;
|
||||||
|
asm!(
|
||||||
|
"in al, dx",
|
||||||
|
out("al") value,
|
||||||
|
in("dx") port,
|
||||||
|
options(nomem, nostack, preserves_flags)
|
||||||
|
);
|
||||||
|
value
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Write a byte to the specified I/O port
|
||||||
|
///
|
||||||
|
/// # Safety
|
||||||
|
/// This function is unsafe because it performs direct I/O port operations
|
||||||
|
/// which could have unpredictable effects on hardware
|
||||||
|
#[inline]
|
||||||
|
pub unsafe fn outb(port: u16, value: u8) {
|
||||||
|
asm!(
|
||||||
|
"out dx, al",
|
||||||
|
in("dx") port,
|
||||||
|
in("al") value,
|
||||||
|
options(nomem, nostack, preserves_flags)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Read a word (16 bits) from the specified I/O port
|
||||||
|
///
|
||||||
|
/// # Safety
|
||||||
|
/// This function is unsafe because it performs direct I/O port operations
|
||||||
|
/// which could have unpredictable effects on hardware
|
||||||
|
#[inline]
|
||||||
|
pub unsafe fn inw(port: u16) -> u16 {
|
||||||
|
let value: u16;
|
||||||
|
asm!(
|
||||||
|
"in ax, dx",
|
||||||
|
out("ax") value,
|
||||||
|
in("dx") port,
|
||||||
|
options(nomem, nostack, preserves_flags)
|
||||||
|
);
|
||||||
|
value
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Write a word (16 bits) to the specified I/O port
|
||||||
|
///
|
||||||
|
/// # Safety
|
||||||
|
/// This function is unsafe because it performs direct I/O port operations
|
||||||
|
/// which could have unpredictable effects on hardware
|
||||||
|
#[inline]
|
||||||
|
pub unsafe fn outw(port: u16, value: u16) {
|
||||||
|
asm!(
|
||||||
|
"out dx, ax",
|
||||||
|
in("dx") port,
|
||||||
|
in("ax") value,
|
||||||
|
options(nomem, nostack, preserves_flags)
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
#[cfg(target_arch = "x86_64")]
|
||||||
|
pub mod x86_64;
|
||||||
|
|
||||||
|
#[cfg(target_arch = "x86_64")]
|
||||||
|
pub use x86_64::*;
|
||||||
|
|
||||||
|
pub mod io;
|
||||||
|
|
||||||
|
pub use io::*;
|
||||||
|
|
||||||
|
// Add common CPU traits/interfaces here that all architectures must implement
|
||||||
@@ -0,0 +1,73 @@
|
|||||||
|
use core::arch::asm;
|
||||||
|
|
||||||
|
use lazy_static::lazy_static;
|
||||||
|
use spin::lazy;
|
||||||
|
use x86_64::structures::idt::{InterruptDescriptorTable, InterruptStackFrame};
|
||||||
|
|
||||||
|
use crate::{println_log, serial_println};
|
||||||
|
|
||||||
|
use super::pics::ChainedPics;
|
||||||
|
|
||||||
|
pub const PIC_1_OFFSET: u8 = 32;
|
||||||
|
pub const PIC_2_OFFSET: u8 = PIC_1_OFFSET + 8;
|
||||||
|
|
||||||
|
pub static PICS: spin::Mutex<ChainedPics> =
|
||||||
|
spin::Mutex::new(unsafe { ChainedPics::new(PIC_1_OFFSET, PIC_2_OFFSET) });
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy)]
|
||||||
|
#[repr(u8)]
|
||||||
|
pub enum InterruptIndex {
|
||||||
|
Timer = PIC_1_OFFSET,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl InterruptIndex {
|
||||||
|
fn as_u8(self) -> u8 {
|
||||||
|
self as u8
|
||||||
|
}
|
||||||
|
|
||||||
|
fn as_usize(self) -> usize {
|
||||||
|
usize::from(self.as_u8())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
lazy_static! {
|
||||||
|
static ref IDT: InterruptDescriptorTable = {
|
||||||
|
let mut idt = InterruptDescriptorTable::new();
|
||||||
|
idt.breakpoint.set_handler_fn(breakpoint_handler);
|
||||||
|
idt.double_fault.set_handler_fn(double_fault_handler);
|
||||||
|
idt[InterruptIndex::Timer.as_u8()].set_handler_fn(clock_handler);
|
||||||
|
idt
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn init() {
|
||||||
|
IDT.load();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
pub fn disable(func: impl Fn()) {
|
||||||
|
unsafe {
|
||||||
|
asm!("cli", options(nomem, nostack, preserves_flags));
|
||||||
|
func();
|
||||||
|
asm!("sti", options(nomem, nostack, preserves_flags));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
extern "x86-interrupt" fn breakpoint_handler(stack_frame: InterruptStackFrame) {
|
||||||
|
println_log!("EXCEPTION: BREAKPOINT\n{:#?}", stack_frame);
|
||||||
|
serial_println!("EXCEPTION: BREAKPOINT\n{:#?}", stack_frame);
|
||||||
|
}
|
||||||
|
|
||||||
|
extern "x86-interrupt" fn double_fault_handler(stack_frame: InterruptStackFrame, _error_code: u64) -> ! {
|
||||||
|
serial_println!("EXCEPTION: DOUBLE FAULT\n{:#?}", stack_frame);
|
||||||
|
panic!("EXCEPTION: DOUBLE FAULT\n{:#?}", stack_frame)
|
||||||
|
}
|
||||||
|
|
||||||
|
extern "x86-interrupt" fn clock_handler(stack_frame: InterruptStackFrame) {
|
||||||
|
println_log!("EXCEPTION: CLOCK\n{:#?}", stack_frame);
|
||||||
|
serial_println!("EXCEPTION: CLOCK\n{:#?}", stack_frame);
|
||||||
|
|
||||||
|
unsafe {
|
||||||
|
PICS.lock().notify_end_of_interrupt(InterruptIndex::Timer.as_u8());
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
pub mod interrupts;
|
||||||
|
mod pics;
|
||||||
|
|
||||||
|
pub use interrupts::*;
|
||||||
@@ -0,0 +1,137 @@
|
|||||||
|
use x86_64::instructions::port::Port;
|
||||||
|
|
||||||
|
const CMD_INIT: u8 = 0x11;
|
||||||
|
const CMD_END_OF_INT: u8 = 0x20;
|
||||||
|
const MODE_8086: u8 = 0x01;
|
||||||
|
|
||||||
|
struct Pic {
|
||||||
|
offset: u8,
|
||||||
|
data: Port<u8>,
|
||||||
|
command: Port<u8>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Pic {
|
||||||
|
/// Are we in charge of handling the specified interrupt?
|
||||||
|
/// (Each PIC handles 8 interrupts.)
|
||||||
|
fn handles_interrupt(&self, interrupt_id: u8) -> bool {
|
||||||
|
self.offset <= interrupt_id && interrupt_id < self.offset + 8
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Notify us that an interrupt has been handled and that we're ready
|
||||||
|
/// for more.
|
||||||
|
unsafe fn end_of_interrupt(&mut self) {
|
||||||
|
self.command.write(CMD_END_OF_INT);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Reads the interrupt mask of this PIC.
|
||||||
|
unsafe fn read_mask(&mut self) -> u8 {
|
||||||
|
self.data.read()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Writes the interrupt mask of this PIC.
|
||||||
|
unsafe fn write_mask(&mut self, mask: u8) {
|
||||||
|
self.data.write(mask)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A pair of chained PICs. This is the standard setup on x86.
|
||||||
|
pub struct ChainedPics {
|
||||||
|
pics: [Pic; 2],
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ChainedPics {
|
||||||
|
/// Create a new interface for the standard PIC1 and PIC2,
|
||||||
|
/// specifying the desired interrupt offsets.
|
||||||
|
pub const unsafe fn new(offset1: u8, offset2: u8) -> ChainedPics {
|
||||||
|
ChainedPics {
|
||||||
|
pics: [
|
||||||
|
Pic {
|
||||||
|
offset: offset1,
|
||||||
|
command: Port::new(0x20),
|
||||||
|
data: Port::new(0x21),
|
||||||
|
},
|
||||||
|
Pic {
|
||||||
|
offset: offset2,
|
||||||
|
command: Port::new(0xA0),
|
||||||
|
data: Port::new(0xA1),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub const unsafe fn new_contiguous(primary_offset: u8) -> ChainedPics {
|
||||||
|
Self::new(primary_offset, primary_offset + 8)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub unsafe fn initialize(&mut self) {
|
||||||
|
|
||||||
|
let mut wait_port: Port<u8> = Port::new(0x80);
|
||||||
|
let mut wait = || wait_port.write(0);
|
||||||
|
|
||||||
|
// Save our original interrupt masks, because I'm too lazy to
|
||||||
|
// figure out reasonable values. We'll restore these when we're
|
||||||
|
// done.
|
||||||
|
let saved_masks = self.read_masks();
|
||||||
|
|
||||||
|
// Tell each PIC that we're going to send it a three-byte
|
||||||
|
// initialization sequence on its data port.
|
||||||
|
self.pics[0].command.write(CMD_INIT);
|
||||||
|
wait();
|
||||||
|
self.pics[1].command.write(CMD_INIT);
|
||||||
|
wait();
|
||||||
|
|
||||||
|
// Byte 1: Set up our base offsets.
|
||||||
|
self.pics[0].data.write(self.pics[0].offset);
|
||||||
|
wait();
|
||||||
|
self.pics[1].data.write(self.pics[1].offset);
|
||||||
|
wait();
|
||||||
|
|
||||||
|
// Byte 2: Configure chaining between PIC1 and PIC2.
|
||||||
|
self.pics[0].data.write(4);
|
||||||
|
wait();
|
||||||
|
self.pics[1].data.write(2);
|
||||||
|
wait();
|
||||||
|
|
||||||
|
// Byte 3: Set our mode.
|
||||||
|
self.pics[0].data.write(MODE_8086);
|
||||||
|
wait();
|
||||||
|
self.pics[1].data.write(MODE_8086);
|
||||||
|
wait();
|
||||||
|
|
||||||
|
// Restore our saved masks.
|
||||||
|
self.write_masks(saved_masks[0], saved_masks[1])
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Reads the interrupt masks of both PICs.
|
||||||
|
pub unsafe fn read_masks(&mut self) -> [u8; 2] {
|
||||||
|
[self.pics[0].read_mask(), self.pics[1].read_mask()]
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Writes the interrupt masks of both PICs.
|
||||||
|
pub unsafe fn write_masks(&mut self, mask1: u8, mask2: u8) {
|
||||||
|
self.pics[0].write_mask(mask1);
|
||||||
|
self.pics[1].write_mask(mask2);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Disables both PICs by masking all interrupts.
|
||||||
|
pub unsafe fn disable(&mut self) {
|
||||||
|
self.write_masks(u8::MAX, u8::MAX)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Do we handle this interrupt?
|
||||||
|
pub fn handles_interrupt(&self, interrupt_id: u8) -> bool {
|
||||||
|
self.pics.iter().any(|p| p.handles_interrupt(interrupt_id))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Figure out which (if any) PICs in our chain need to know about this
|
||||||
|
/// interrupt. This is tricky, because all interrupts from `pics[1]`
|
||||||
|
/// get chained through `pics[0]`.
|
||||||
|
pub unsafe fn notify_end_of_interrupt(&mut self, interrupt_id: u8) {
|
||||||
|
if self.handles_interrupt(interrupt_id) {
|
||||||
|
if self.pics[1].handles_interrupt(interrupt_id) {
|
||||||
|
self.pics[1].end_of_interrupt();
|
||||||
|
}
|
||||||
|
self.pics[0].end_of_interrupt();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
pub mod textwriter;
|
||||||
|
pub mod render;
|
||||||
|
|
||||||
|
mod font;
|
||||||
|
|
||||||
|
|
||||||
|
//
|
||||||
@@ -0,0 +1,63 @@
|
|||||||
|
use core::panic;
|
||||||
|
use lazy_static::lazy_static;
|
||||||
|
use limine::framebuffer::Framebuffer;
|
||||||
|
use spin::Mutex;
|
||||||
|
use limine::request::FramebufferRequest;
|
||||||
|
|
||||||
|
static FRAMEBUFFER_REQUEST: FramebufferRequest = FramebufferRequest::new();
|
||||||
|
|
||||||
|
lazy_static! {
|
||||||
|
pub static ref FRAMEBUFFER_WRITER: Mutex<Option<FramebufferWriter<'static>>> = Mutex::new({
|
||||||
|
if let Some(framebuffer_response) = FRAMEBUFFER_REQUEST.get_response() {
|
||||||
|
let framebuffer = framebuffer_response.framebuffers().next().unwrap();
|
||||||
|
Some(FramebufferWriter::new(framebuffer))
|
||||||
|
} else {
|
||||||
|
panic!("Framebuffer request failed");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct FramebufferWriter<'a> {
|
||||||
|
framebuffer: Framebuffer<'a>,
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe impl<'a> Send for FramebufferWriter<'a> {}
|
||||||
|
unsafe impl<'a> Sync for FramebufferWriter<'a> {}
|
||||||
|
|
||||||
|
impl<'a> FramebufferWriter<'a> {
|
||||||
|
pub fn new(framebuffer: Framebuffer<'a>) -> Self {
|
||||||
|
Self {
|
||||||
|
framebuffer,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn write_pixel(&self, x: usize, y: usize, color: u32) {
|
||||||
|
let pitch = self.framebuffer.pitch() as usize;
|
||||||
|
let bpp = (self.framebuffer.bpp() / 8) as usize;
|
||||||
|
let pixel_offset = y * pitch + x * bpp;
|
||||||
|
|
||||||
|
unsafe {
|
||||||
|
*(self.framebuffer.addr().add(pixel_offset) as *mut u32) = color;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn width(&self) -> u32 {
|
||||||
|
self.framebuffer.width() as u32
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn height(&self) -> u32 {
|
||||||
|
self.framebuffer.height() as u32
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn clear(&self) {
|
||||||
|
let width = self.framebuffer.width() as usize;
|
||||||
|
let height = self.framebuffer.height() as usize;
|
||||||
|
|
||||||
|
for y in 0..height {
|
||||||
|
for x in 0..width {
|
||||||
|
self.write_pixel(x, y, 0x000000);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@@ -0,0 +1,189 @@
|
|||||||
|
|
||||||
|
use core::fmt;
|
||||||
|
|
||||||
|
use lazy_static::lazy_static;
|
||||||
|
use spin::Mutex;
|
||||||
|
|
||||||
|
use crate::sys::kernel::cpu::interrupts;
|
||||||
|
|
||||||
|
use super::{font::FONT, render::FRAMEBUFFER_WRITER};
|
||||||
|
|
||||||
|
static FONT_WIDTH: u32 = 8;
|
||||||
|
static FONT_HEIGHT: u32 = 16;
|
||||||
|
|
||||||
|
lazy_static!{
|
||||||
|
static ref TEXT_WRITER: Mutex<TextWriter> = Mutex::new(TextWriter::new());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
pub struct TextWriter {
|
||||||
|
// these are measured in chars NOT pixels
|
||||||
|
screen_width: u32,
|
||||||
|
screen_height: u32,
|
||||||
|
|
||||||
|
text_line: u32, // 16 pixels tall
|
||||||
|
text_col: u32, // 8 pixels wide
|
||||||
|
|
||||||
|
fg_color: u32,
|
||||||
|
bg_color: u32
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TextWriter {
|
||||||
|
|
||||||
|
pub fn new() -> Self {
|
||||||
|
if let Some(writer) = FRAMEBUFFER_WRITER.lock().as_mut() {
|
||||||
|
Self {
|
||||||
|
screen_width: writer.width() as u32 / 8,
|
||||||
|
screen_height: writer.height() as u32 / 16,
|
||||||
|
text_line: 0,
|
||||||
|
text_col: 0,
|
||||||
|
fg_color: 0xFFFFFF,
|
||||||
|
bg_color: 0x000000
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
panic!("Framebuffer writer not initialized");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn write_char(&mut self, mut c: u8) {
|
||||||
|
if c == b'\n' {
|
||||||
|
self.newline();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if c < 32 || c > 126 {
|
||||||
|
c = '?' as u8;
|
||||||
|
}
|
||||||
|
|
||||||
|
// get the character data from the font array. -- each byte is a row of pixels
|
||||||
|
let data: &[u8] = &FONT[c as usize * 16..(c as usize + 1) * 16];
|
||||||
|
|
||||||
|
if let Some(writer) = FRAMEBUFFER_WRITER.lock().as_mut() {
|
||||||
|
for row in 0..16 {
|
||||||
|
let line: u8 = data[row];
|
||||||
|
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 fn next_char(&mut self) {
|
||||||
|
self.text_col += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn write_string(&mut self, s: &str) {
|
||||||
|
for c in s.chars() {
|
||||||
|
self.write_char(c as u8);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_colour(&mut self, col: (u32, u32)) {
|
||||||
|
self.fg_color = col.0;
|
||||||
|
self.bg_color = col.1;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn reset_colour(&mut self) {
|
||||||
|
self.fg_color = 0xFFFFFF;
|
||||||
|
self.bg_color = 0x000000;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl core::fmt::Write for TextWriter {
|
||||||
|
fn write_str(&mut self, s: &str) -> core::fmt::Result {
|
||||||
|
self.write_string(s);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn write(args: fmt::Arguments, fg_color: u32, bg_color: u32) {
|
||||||
|
use core::fmt::Write;
|
||||||
|
|
||||||
|
interrupts::disable(|| {
|
||||||
|
let mut writer = TEXT_WRITER.lock();
|
||||||
|
writer.set_colour((fg_color, bg_color));
|
||||||
|
writer.write_fmt(args).unwrap();
|
||||||
|
writer.reset_colour();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn _print(args: fmt::Arguments) {
|
||||||
|
write(args, 0xFFFFFF, 0x000000);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn _printerr(args: fmt::Arguments) {
|
||||||
|
write(args, 0xFF8080, 0x000000);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn _log(args: fmt::Arguments) {
|
||||||
|
write(args, 0xFFFF00, 0x000000);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn clear_screen() {
|
||||||
|
let mut writer = TEXT_WRITER.lock();
|
||||||
|
writer.text_line = 0;
|
||||||
|
writer.text_col = 0;
|
||||||
|
|
||||||
|
if let Some(writer) = FRAMEBUFFER_WRITER.lock().as_mut() {
|
||||||
|
writer.clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[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::_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::_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::_printerr(format_args!($($arg)*)));
|
||||||
|
}
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
pub mod framebuffer;
|
||||||
|
pub mod serial;
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
pub mod serial;
|
||||||
|
|
||||||
|
pub use serial::{
|
||||||
|
_serial_write,
|
||||||
|
serial_read
|
||||||
|
};
|
||||||
@@ -0,0 +1,117 @@
|
|||||||
|
use core::fmt;
|
||||||
|
use spin::Mutex;
|
||||||
|
use lazy_static::lazy_static;
|
||||||
|
|
||||||
|
use crate::sys::kernel::cpu::{inb, outb};
|
||||||
|
|
||||||
|
static PORT: u16 = 0x3f8;
|
||||||
|
static mut BUFFER: [u8; 256] = [0; 256];
|
||||||
|
|
||||||
|
lazy_static!{
|
||||||
|
static ref SERIAL_WRITER: Mutex<SerialWriter> = Mutex::new(SerialWriter::new());
|
||||||
|
}
|
||||||
|
|
||||||
|
struct SerialWriter {
|
||||||
|
buffer_len: usize
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Write for SerialWriter {
|
||||||
|
fn write_str(&mut self, s: &str) -> fmt::Result {
|
||||||
|
for c in s.chars() {
|
||||||
|
self.write(c as u8);
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SerialWriter {
|
||||||
|
pub fn new() -> SerialWriter {
|
||||||
|
// first we make sure that the serial port is setup and working.
|
||||||
|
unsafe {
|
||||||
|
outb(PORT + 1, 0x00); // Disable all interrupts
|
||||||
|
outb(PORT + 3, 0x80); // Enable DLAB (set baud rate divisor)
|
||||||
|
outb(PORT + 0, 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 + 0, 0xAE); // Test serial chip (send byte 0xAE and check if serial returns same byte)
|
||||||
|
|
||||||
|
if inb(PORT + 0) != 0xAE {
|
||||||
|
panic!("serial port is not working!");
|
||||||
|
}
|
||||||
|
|
||||||
|
outb(PORT + 4, 0x0F);
|
||||||
|
}
|
||||||
|
|
||||||
|
SerialWriter {
|
||||||
|
buffer_len: 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// returnstrue if there is new data on the serial port
|
||||||
|
unsafe fn serial_recieved(&self) -> bool {
|
||||||
|
inb(PORT + 5) & 1 != 0
|
||||||
|
}
|
||||||
|
|
||||||
|
// returns true if the transmit buffer is empty
|
||||||
|
unsafe fn serial_sent(&self) -> bool {
|
||||||
|
inb(PORT + 5) & 0x20 != 0
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn read(&self) -> u8 { unsafe {
|
||||||
|
while !self.serial_recieved() {};
|
||||||
|
return inb(PORT + 0);
|
||||||
|
}}
|
||||||
|
|
||||||
|
pub fn read_str_to_buffer(&mut self) { unsafe {
|
||||||
|
while !self.serial_recieved() {};
|
||||||
|
|
||||||
|
self.buffer_len = 0;
|
||||||
|
|
||||||
|
while self.serial_recieved() && self.buffer_len < 256 {
|
||||||
|
let c = inb(PORT + 0);
|
||||||
|
BUFFER[self.buffer_len] = c;
|
||||||
|
if c == b'\n' {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
self.buffer_len += 1;
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
|
||||||
|
pub fn write(&self, data: u8) { unsafe {
|
||||||
|
while !self.serial_sent() {};
|
||||||
|
outb(PORT + 0, data);
|
||||||
|
}}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn _serial_write(args: fmt::Arguments) {
|
||||||
|
use core::fmt::Write;
|
||||||
|
|
||||||
|
SERIAL_WRITER.lock().write_fmt(args).unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn serial_read() -> &'static str {
|
||||||
|
SERIAL_WRITER.lock().read_str_to_buffer();
|
||||||
|
let i = SERIAL_WRITER.lock().buffer_len;
|
||||||
|
|
||||||
|
if i == 0 {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe {
|
||||||
|
return core::str::from_utf8(&BUFFER[..i - 1]).unwrap();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[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)*) => ($crate::serial_print!("{}\n", format_args!($($arg)*)));
|
||||||
|
}
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
pub mod drivers;
|
||||||
|
pub mod cpu;
|
||||||
@@ -1,2 +1,7 @@
|
|||||||
pub mod std;
|
pub mod std;
|
||||||
mod kernel;
|
|
||||||
|
// TODO: make this private
|
||||||
|
pub mod kernel;
|
||||||
|
|
||||||
|
#[cfg(any(test, feature = "qemu"))]
|
||||||
|
pub mod qemu;
|
||||||
|
|||||||
@@ -0,0 +1,43 @@
|
|||||||
|
use x86_64::instructions::port::Port;
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||||
|
#[repr(u32)]
|
||||||
|
pub enum ExitCode {
|
||||||
|
// Writing 0x10 will make QEMU exit with code 33 ((0x10 << 1) | 1)
|
||||||
|
Success = 0x10,
|
||||||
|
// Writing 0x11 will make QEMU exit with code 35 ((0x11 << 1) | 1)
|
||||||
|
Failed = 0x11,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Exit QEMU with the given exit code.
|
||||||
|
///
|
||||||
|
/// This function uses the special QEMU debug exit device with
|
||||||
|
/// port 0xf4 to exit QEMU with a specified exit code.
|
||||||
|
/// QEMU will exit with code: (value << 1) | 1
|
||||||
|
pub fn exit(exit_code: ExitCode) -> ! {
|
||||||
|
unsafe {
|
||||||
|
let mut port = Port::new(0xf4);
|
||||||
|
port.write(exit_code as u32);
|
||||||
|
}
|
||||||
|
loop {
|
||||||
|
unsafe {
|
||||||
|
core::arch::asm!("cli; hlt", options(nomem, nostack))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Exit QEMU with a success code
|
||||||
|
/// Writing 0x10 will make QEMU exit with code 33
|
||||||
|
#[cfg(test)]
|
||||||
|
pub fn exit_success() -> ! {
|
||||||
|
exit(ExitCode::Success)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Exit QEMU with a failed code
|
||||||
|
/// Writing 0x11 will make QEMU exit with code 35
|
||||||
|
#[cfg(test)]
|
||||||
|
pub fn exit_failed() -> ! {
|
||||||
|
use crate::serial_println;
|
||||||
|
serial_println!("[failed]\n");
|
||||||
|
exit(ExitCode::Failed)
|
||||||
|
}
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
// mod file for standard library in kernel;
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
#[cfg(test)]
|
||||||
|
use x86_64::instructions::interrupts::int3;
|
||||||
|
#[cfg(test)]
|
||||||
|
use crate::println;
|
||||||
|
|
||||||
|
#[test_case]
|
||||||
|
pub fn test_println_many() {
|
||||||
|
for _ in 0..200 {
|
||||||
|
println!("test_println_many output");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test_case]
|
||||||
|
pub fn test_println_output() {
|
||||||
|
println!("test_println output");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test_case]
|
||||||
|
pub fn test_breakpoint_exception() {
|
||||||
|
// invoke a breakpoint exception
|
||||||
|
int3();
|
||||||
|
}
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
mod runner;
|
||||||
|
pub use runner::test_runner;
|
||||||
|
|
||||||
|
pub mod kernel;
|
||||||
|
|
||||||
|
/// Called on panic
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
///
|
||||||
|
/// * `info` - The panic info
|
||||||
|
#[cfg(test)]
|
||||||
|
#[panic_handler]
|
||||||
|
fn panic(info: &core::panic::PanicInfo) -> ! {
|
||||||
|
use crate::serial_println;
|
||||||
|
|
||||||
|
// print a failed message saying the kernel panicked
|
||||||
|
serial_println!("[failed]\n");
|
||||||
|
serial_println!("Error: {}\n", info);
|
||||||
|
crate::sys::qemu::exit_failed();
|
||||||
|
}
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
use crate::{serial_print, serial_println};
|
||||||
|
|
||||||
|
pub fn test_runner(tests: &[&dyn Testable]) {
|
||||||
|
serial_println!("Running {} tests\n", tests.len());
|
||||||
|
for test in tests {
|
||||||
|
test.run();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait Testable {
|
||||||
|
fn run(&self) -> ();
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> Testable for T
|
||||||
|
where
|
||||||
|
T: Fn(),
|
||||||
|
{
|
||||||
|
fn run(&self) {
|
||||||
|
serial_print!("{}...\t", core::any::type_name::<T>());
|
||||||
|
self();
|
||||||
|
serial_println!("[ok]");
|
||||||
|
}
|
||||||
|
}
|
||||||
+69
-9
@@ -12,6 +12,10 @@ NC='\033[0m' # No Color
|
|||||||
set -e
|
set -e
|
||||||
trap 'echo -e "${RED}${BOLD}error${NC}: build failed" >&2' ERR
|
trap 'echo -e "${RED}${BOLD}error${NC}: build failed" >&2' ERR
|
||||||
|
|
||||||
|
# Get absolute path to project root
|
||||||
|
script_dir=$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" &>/dev/null && pwd)
|
||||||
|
project_root=$(cd "$script_dir/.." &>/dev/null && pwd)
|
||||||
|
|
||||||
# Logging functions
|
# Logging functions
|
||||||
info() {
|
info() {
|
||||||
echo -e "${BLUE}${BOLD}info${NC}: $1"
|
echo -e "${BLUE}${BOLD}info${NC}: $1"
|
||||||
@@ -25,15 +29,29 @@ warning() {
|
|||||||
echo -e "${YELLOW}${BOLD}warning${NC}: $1" >&2
|
echo -e "${YELLOW}${BOLD}warning${NC}: $1" >&2
|
||||||
}
|
}
|
||||||
|
|
||||||
|
building() {
|
||||||
|
echo -e "${GREEN}${BOLD}Building${NC} $1"
|
||||||
|
}
|
||||||
|
|
||||||
error() {
|
error() {
|
||||||
echo -e "${RED}${BOLD}error${NC}: $1" >&2
|
echo -e "${RED}${BOLD}error${NC}: $1" >&2
|
||||||
exit 1
|
exit 1
|
||||||
}
|
}
|
||||||
|
|
||||||
build_dir=build
|
build_dir="$project_root/build"
|
||||||
iso_root=$build_dir/iso_root
|
iso_root="$build_dir/iso_root"
|
||||||
kernel_path=$1
|
|
||||||
kernel_name=$(basename $kernel_path)
|
# Check if we're running tests
|
||||||
|
is_test=0
|
||||||
|
if [[ $1 == *"deps"* ]]; then
|
||||||
|
is_test=1
|
||||||
|
kernel_path="$1"
|
||||||
|
else
|
||||||
|
# Build the kernel normally
|
||||||
|
cd "$project_root"
|
||||||
|
cargo build
|
||||||
|
kernel_path="$build_dir/target/x86_64-kernel/debug/kernel"
|
||||||
|
fi
|
||||||
|
|
||||||
# Check for required tools
|
# Check for required tools
|
||||||
check_tools() {
|
check_tools() {
|
||||||
@@ -57,21 +75,23 @@ mkdir -p "$iso_root/EFI/BOOT"
|
|||||||
# Clone Limine if needed
|
# Clone Limine if needed
|
||||||
if [ ! -d "$build_dir/limine" ]; then
|
if [ ! -d "$build_dir/limine" ]; then
|
||||||
compiling "limine bootloader"
|
compiling "limine bootloader"
|
||||||
|
cd "$build_dir"
|
||||||
git clone https://github.com/limine-bootloader/limine.git --branch=v8.x-binary --depth=1 "$build_dir/limine" || error "failed to clone limine"
|
git clone https://github.com/limine-bootloader/limine.git --branch=v8.x-binary --depth=1 "$build_dir/limine" || error "failed to clone limine"
|
||||||
make -C "$build_dir/limine" || error "failed to build limine"
|
make -C "$build_dir/limine" || error "failed to build limine"
|
||||||
|
cd "$project_root"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Copy files
|
# Copy files
|
||||||
info "Copying files to ISO root"
|
info "Copying files to ISO root"
|
||||||
cp -v "$kernel_path" "$iso_root/boot/kernel" || error "failed to copy kernel"
|
cp -v "$kernel_path" "$iso_root/boot/kernel" || error "failed to copy kernel"
|
||||||
cp -v config/limine.conf "$iso_root/boot/limine/" || error "failed to copy limine config"
|
cp -v "$project_root/config/limine.conf" "$iso_root/boot/limine/limine.conf" || error "failed to copy limine config"
|
||||||
cp -v "$build_dir/limine/limine-bios.sys" "$build_dir/limine/limine-bios-cd.bin" \
|
cp -v "$build_dir/limine/limine-bios.sys" "$build_dir/limine/limine-bios-cd.bin" \
|
||||||
"$build_dir/limine/limine-uefi-cd.bin" "$iso_root/boot/limine/" || error "failed to copy limine files"
|
"$build_dir/limine/limine-uefi-cd.bin" "$iso_root/boot/limine/" || error "failed to copy limine files"
|
||||||
cp -v "$build_dir/limine/BOOTX64.EFI" "$iso_root/EFI/BOOT/" || error "failed to copy BOOTX64.EFI"
|
cp -v "$build_dir/limine/BOOTX64.EFI" "$iso_root/EFI/BOOT/" || error "failed to copy BOOTX64.EFI"
|
||||||
cp -v "$build_dir/limine/BOOTIA32.EFI" "$iso_root/EFI/BOOT/" || error "failed to copy BOOTIA32.EFI"
|
cp -v "$build_dir/limine/BOOTIA32.EFI" "$iso_root/EFI/BOOT/" || error "failed to copy BOOTIA32.EFI"
|
||||||
|
|
||||||
# Create ISO
|
# Create ISO
|
||||||
compiling "bootable ISO image"
|
building "bootable ISO image"
|
||||||
xorriso -as mkisofs -R -r -J -b boot/limine/limine-bios-cd.bin \
|
xorriso -as mkisofs -R -r -J -b boot/limine/limine-bios-cd.bin \
|
||||||
-no-emul-boot -boot-load-size 4 -boot-info-table -hfsplus \
|
-no-emul-boot -boot-load-size 4 -boot-info-table -hfsplus \
|
||||||
-apm-block-size 2048 --efi-boot boot/limine/limine-uefi-cd.bin \
|
-apm-block-size 2048 --efi-boot boot/limine/limine-uefi-cd.bin \
|
||||||
@@ -94,6 +114,22 @@ else
|
|||||||
kvm_flag=""
|
kvm_flag=""
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
# Check if we're running in debug mode
|
||||||
|
if [[ "$(cargo metadata --format-version=1 | jq -r '.workspace_members[0]' | cut -d' ' -f2)" == "(debug)" ]]; then
|
||||||
|
debug_flags="-s -S"
|
||||||
|
else
|
||||||
|
debug_flags=""
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Set up test-specific flags
|
||||||
|
if [ $is_test -eq 1 ]; then
|
||||||
|
test_flags="-device isa-debug-exit,iobase=0xf4,iosize=0x04 -display none"
|
||||||
|
serial_flags="-serial stdio"
|
||||||
|
else
|
||||||
|
test_flags=""
|
||||||
|
serial_flags="-serial tcp:127.0.0.1:1234,server,nowait"
|
||||||
|
fi
|
||||||
|
|
||||||
# Run in QEMU
|
# Run in QEMU
|
||||||
if [[ ${QEMU_FLAGS} == *-S* ]]; then
|
if [[ ${QEMU_FLAGS} == *-S* ]]; then
|
||||||
info "Running OS in QEMU with GDB debugging enabled"
|
info "Running OS in QEMU with GDB debugging enabled"
|
||||||
@@ -103,12 +139,36 @@ else
|
|||||||
info "Running OS in QEMU..."
|
info "Running OS in QEMU..."
|
||||||
fi
|
fi
|
||||||
|
|
||||||
exec qemu-system-x86_64 -M q35 \
|
echo "test: " $is_test
|
||||||
|
|
||||||
|
cd "$project_root"
|
||||||
|
qemu-system-x86_64 -M q35 \
|
||||||
${kvm_flag} \
|
${kvm_flag} \
|
||||||
-cdrom "$build_dir/image.iso" \
|
-cdrom "$build_dir/image.iso" \
|
||||||
-boot d \
|
-boot d \
|
||||||
-m 2G \
|
-m 2G \
|
||||||
-serial stdio \
|
${serial_flags} \
|
||||||
-no-reboot \
|
-no-reboot \
|
||||||
-no-shutdown \
|
${test_flags} \
|
||||||
|
${debug_flags} \
|
||||||
${QEMU_FLAGS:-}
|
${QEMU_FLAGS:-}
|
||||||
|
|
||||||
|
# Get QEMU's exit code
|
||||||
|
qemu_exit_code=$?
|
||||||
|
|
||||||
|
# If this is a test run, translate QEMU exit codes to test exit codes
|
||||||
|
if [ $is_test -eq 1 ]; then
|
||||||
|
if [ $qemu_exit_code -eq 33 ]; then
|
||||||
|
# Success case (0x10 << 1) | 1 = 33
|
||||||
|
exit 0
|
||||||
|
elif [ $qemu_exit_code -eq 35 ]; then
|
||||||
|
# Failure case (0x11 << 1) | 1 = 35
|
||||||
|
exit 1
|
||||||
|
else
|
||||||
|
# Any other exit code is treated as a failure
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
# For non-test runs, pass through the exit code
|
||||||
|
exit $qemu_exit_code
|
||||||
|
fi
|
||||||
|
|||||||
Reference in New Issue
Block a user