Formatted the code, made the arch code x86_64 only for now, fixed the

serial writer always centering and assuming, added a margin for correct
displaying. Added the IDT and GDT tables, and the TSS so double faults
cant happen. Handled all of these interrupts using functions. Serial
console is now topleft and panic messages are formatted correctly from
there. core PI constant is now used instead of our own.
This commit is contained in:
csd4ni3l
2026-03-23 18:29:33 +01:00
parent 8701b83f57
commit e28b898d79
26 changed files with 364 additions and 92 deletions

View File

@@ -0,0 +1,4 @@
[unstable]
json-target-spec = true
build-std-features = ["compiler-builtins-mem"]
build-std = ["core", "compiler_builtins"]

74
kernel/Cargo.lock generated
View File

@@ -2,18 +2,51 @@
# It is not intended for manual editing.
version = 4
[[package]]
name = "XunilOS"
version = "0.1.0"
dependencies = [
"font8x8",
"lazy_static",
"limine",
"micromath",
"spin 0.10.0",
"x86_64",
]
[[package]]
name = "bit_field"
version = "0.10.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1e4b40c7323adcfc0a41c4b88143ed58346ff65a288fc144329c5c45e05d70c6"
[[package]]
name = "bitflags"
version = "2.9.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1b8e56985ec62d17e9c1001dc89c88ecd7dc08e47eba5ec7c29c7b5eeecde967"
[[package]]
name = "const_fn"
version = "0.4.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "413d67b29ef1021b4d60f4aa1e925ca031751e213832b4b1d588fae623c05c60"
[[package]]
name = "font8x8"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "875488b8711a968268c7cf5d139578713097ca4635a76044e8fe8eedf831d07e"
[[package]]
name = "lazy_static"
version = "1.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe"
dependencies = [
"spin 0.9.8",
]
[[package]]
name = "limine"
version = "0.5.0"
@@ -23,16 +56,6 @@ dependencies = [
"bitflags",
]
[[package]]
name = "limine-rust-template"
version = "0.1.0"
dependencies = [
"font8x8",
"limine",
"micromath",
"spin",
]
[[package]]
name = "lock_api"
version = "0.4.14"
@@ -48,12 +71,24 @@ version = "2.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c3c8dda44ff03a2f238717214da50f65d5a53b45cd213a7370424ffdb6fae815"
[[package]]
name = "rustversion"
version = "1.0.22"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d"
[[package]]
name = "scopeguard"
version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
[[package]]
name = "spin"
version = "0.9.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67"
[[package]]
name = "spin"
version = "0.10.0"
@@ -62,3 +97,22 @@ checksum = "d5fe4ccb98d9c292d56fec89a5e07da7fc4cf0dc11e156b41793132775d3e591"
dependencies = [
"lock_api",
]
[[package]]
name = "volatile"
version = "0.4.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "442887c63f2c839b346c192d047a7c87e73d0689c9157b00b53dcc27dd5ea793"
[[package]]
name = "x86_64"
version = "0.15.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f7841fa0098ceb15c567d93d3fae292c49e10a7662b4936d5f6a9728594555ba"
dependencies = [
"bit_field",
"bitflags",
"const_fn",
"rustversion",
"volatile",
]

View File

@@ -1,13 +1,20 @@
[package]
name = "limine-rust-template"
name = "XunilOS"
version = "0.1.0"
edition = "2024"
[[bin]]
name = "XunilOS"
test = false
bench = false
[dependencies]
font8x8 = { version = "0.3.1", default-features = false }
lazy_static = { version = "1.5.0", features = ["spin_no_std"] }
limine = "0.5"
micromath = "2.1.0"
spin = "0.10.0"
x86_64 = "0.15.4"
[profile.dev]
panic = "abort"

View File

View File

View File

@@ -1,5 +1,3 @@
pub mod serial;
pub mod gdt;
pub mod idt;
pub mod interrupts;
pub mod paging;
#[cfg(target_arch = "x86_64")]
pub mod x86_64;

View File

@@ -1,42 +1,64 @@
use crate::driver::graphics::framebuffer::{with_framebuffer, Framebuffer};
use crate::driver::graphics::font_render::render_text;
use crate::driver::graphics::base::rgb;
use spin::Mutex;
use crate::driver::graphics::font_render::render_text;
use crate::driver::graphics::framebuffer::Framebuffer;
use core::fmt::{self, Write};
const DEFAULT_FONT_SIZE: usize = 3;
use spin::Mutex;
pub struct ConsoleWriter<'a> {
pub fb: &'a mut Framebuffer,
pub console: &'a mut SerialConsole,
pub should_center: bool,
}
impl Write for ConsoleWriter<'_> {
fn write_str(&mut self, s: &str) -> fmt::Result {
self.console.render_text(self.fb, s);
self.console.render_text(self.fb, s, 2, false);
Ok(())
}
}
pub struct SerialConsole {
start_x: usize,
current_y: usize
pub current_x: usize,
current_y: usize,
}
impl SerialConsole {
pub fn new(start_x: usize, start_y: usize) -> SerialConsole {
SerialConsole {
start_x,
current_y: start_y
current_x: start_x,
current_y: start_y,
}
}
pub fn render_text(&mut self, fb: &mut Framebuffer, text: &str) {
self.current_y = render_text(fb, self.start_x - ((text.len() - text.matches('\n').count()) * DEFAULT_FONT_SIZE * 4), self.current_y, text, DEFAULT_FONT_SIZE, rgb(255, 255, 255));
self.current_y = render_text(fb, self.start_x - ((text.len() - text.matches('\n').count()) * DEFAULT_FONT_SIZE * 4), self.current_y, "\n", DEFAULT_FONT_SIZE, rgb(255, 255, 255)); // add a newline
pub fn render_text(
&mut self,
fb: &mut Framebuffer,
text: &str,
font_size: usize,
should_center: bool,
) {
let (new_x, new_y) = render_text(
fb,
if should_center {
self.current_x - (text.len() - text.matches('\n').count()) * (font_size * 4)
} else {
self.current_x
},
self.current_y,
text,
font_size,
rgb(255, 255, 255),
self.start_x,
);
self.current_x = new_x;
self.current_y = new_y;
}
pub fn clear(&mut self, start_y: usize) {
pub fn clear(&mut self, start_x: usize, start_y: usize) {
self.start_x = start_x;
self.current_x = start_x;
self.current_y = start_y;
}
}

View File

@@ -0,0 +1,60 @@
use lazy_static::lazy_static;
use x86_64::VirtAddr;
use x86_64::instructions::segmentation::{CS, DS, ES, SS, Segment};
use x86_64::instructions::tables::load_tss;
use x86_64::structures::{
gdt::{Descriptor, GlobalDescriptorTable, SegmentSelector},
tss::TaskStateSegment,
};
pub const DOUBLE_FAULT_IST_INDEX: u16 = 0;
struct Selectors {
code_selector: SegmentSelector,
data_selector: SegmentSelector,
tss_selector: SegmentSelector,
}
lazy_static! {
static ref TSS: TaskStateSegment = {
let mut tss = TaskStateSegment::new();
tss.interrupt_stack_table[DOUBLE_FAULT_IST_INDEX as usize] = {
const STACK_SIZE: usize = 4096 * 5;
static mut STACK: [u8; STACK_SIZE] = [0; STACK_SIZE];
let stack_start = VirtAddr::from_ptr(&raw const STACK);
let stack_end = stack_start + STACK_SIZE as u64;
stack_end
};
tss
};
}
lazy_static! {
static ref GDT: (GlobalDescriptorTable, Selectors) = {
let mut gdt = GlobalDescriptorTable::new();
let code_selector = gdt.append(Descriptor::kernel_code_segment());
let data_selector = gdt.append(Descriptor::kernel_data_segment());
let tss_selector = gdt.append(Descriptor::tss_segment(&TSS));
(
gdt,
Selectors {
code_selector,
data_selector,
tss_selector,
},
)
};
}
pub fn load_gdt_x86_64() {
GDT.0.load();
unsafe {
CS::set_reg(GDT.1.code_selector);
DS::set_reg(GDT.1.data_selector);
ES::set_reg(GDT.1.data_selector);
SS::set_reg(GDT.1.data_selector);
load_tss(GDT.1.tss_selector);
}
}

View File

@@ -0,0 +1,23 @@
use crate::arch::x86_64::{
gdt,
interrupts::{breakpoint_handler, double_fault_handler},
};
use lazy_static::lazy_static;
use x86_64::structures::idt::InterruptDescriptorTable;
lazy_static! {
static ref IDT: InterruptDescriptorTable = {
let mut idt = InterruptDescriptorTable::new();
idt.breakpoint.set_handler_fn(breakpoint_handler);
unsafe {
idt.double_fault
.set_handler_fn(double_fault_handler)
.set_stack_index(gdt::DOUBLE_FAULT_IST_INDEX);
}
idt
};
}
pub fn init_idt_x86_64() {
IDT.load();
}

View File

@@ -0,0 +1,13 @@
use crate::println;
use x86_64::structures::idt::InterruptStackFrame;
pub extern "x86-interrupt" fn breakpoint_handler(stack_frame: InterruptStackFrame) {
println!("EXCEPTION: BREAKPOINT\n{:#?}", stack_frame);
}
pub extern "x86-interrupt" fn double_fault_handler(
stack_frame: InterruptStackFrame,
_error_code: u64,
) -> ! {
panic!("EXCEPTION: DOUBLE FAULT\n{:#?}", stack_frame);
}

View File

@@ -0,0 +1,4 @@
pub mod gdt;
pub mod idt;
pub mod interrupts;
pub mod paging;

View File

@@ -0,0 +1 @@

View File

@@ -1,5 +1,3 @@
pub const PI: f32 = 3.1415926535897;
pub fn rgb(r: u8, g: u8, b: u8) -> u32 {
((r as u32) << 16) | ((g as u32) << 8) | (b as u32)
}

View File

@@ -4,33 +4,55 @@ use crate::driver::graphics::framebuffer::Framebuffer;
use crate::driver::graphics::primitives::rectangle_filled;
use font8x8::legacy::BASIC_LEGACY;
pub fn render_char(framebuffer: &mut Framebuffer, start_x: usize, start_y: usize, char: usize, font_size: usize, color: u32) {
pub fn render_char(
framebuffer: &mut Framebuffer,
start_x: usize,
start_y: usize,
char: usize,
font_size: usize,
color: u32,
) {
if let Some(glyph) = BASIC_LEGACY.get(char) {
for (row, row_bits) in glyph.iter().enumerate() {
for bit in 0..8 {
if (row_bits & (1 << bit)) != 0 {
rectangle_filled(framebuffer, start_x + bit * font_size, start_y + row * font_size, font_size, font_size, color, false);
rectangle_filled(
framebuffer,
start_x + bit * font_size,
start_y + row * font_size,
font_size,
font_size,
color,
false,
);
}
}
}
}
}
pub fn render_text(framebuffer: &mut Framebuffer, start_x: usize, start_y: usize, text: &str, font_size: usize, color: u32) -> usize {
pub fn render_text(
framebuffer: &mut Framebuffer,
start_x: usize,
start_y: usize,
text: &str,
font_size: usize,
color: u32,
margin_left: usize,
) -> (usize, usize) {
let mut x = start_x;
let mut y = start_y;
for b in text.bytes() {
if b == b'\n' {
y += 12 * font_size;
x = start_x;
x = margin_left;
continue;
}
render_char(framebuffer, x, y, b as usize, font_size, color);
x += 8 * font_size;
}
framebuffer.swap();
y
(x, y)
}

View File

@@ -1,4 +1,4 @@
pub mod base;
pub mod framebuffer;
pub mod font_render;
pub mod framebuffer;
pub mod primitives;

View File

@@ -1,6 +1,6 @@
use micromath::F32Ext;
use crate::driver::graphics::framebuffer::Framebuffer;
use crate::driver::graphics::base::{rgb, PI};
use core::f32::consts::PI;
use micromath::F32Ext;
pub fn line(framebuffer: &mut Framebuffer, mut x0: i64, mut y0: i64, x1: i64, y1: i64, color: u32) {
let mut dx = x1 - x0;
@@ -38,14 +38,43 @@ pub fn line(framebuffer: &mut Framebuffer, mut x0: i64, mut y0: i64, x1: i64, y1
error += dx;
y0 += step_y;
}
}
}
pub fn triangle_outline(framebuffer: &mut Framebuffer, x1: usize, y1: usize, x2: usize, y2: usize, x3: usize, y3: usize, color: u32) {
line(framebuffer, x1 as i64, y1 as i64, x2 as i64, y2 as i64, color);
line(framebuffer, x1 as i64, y1 as i64, x3 as i64, y3 as i64, color);
line(framebuffer, x2 as i64, y2 as i64, x3 as i64, y3 as i64, color);
pub fn triangle_outline(
framebuffer: &mut Framebuffer,
x1: usize,
y1: usize,
x2: usize,
y2: usize,
x3: usize,
y3: usize,
color: u32,
) {
line(
framebuffer,
x1 as i64,
y1 as i64,
x2 as i64,
y2 as i64,
color,
);
line(
framebuffer,
x1 as i64,
y1 as i64,
x3 as i64,
y3 as i64,
color,
);
line(
framebuffer,
x2 as i64,
y2 as i64,
x3 as i64,
y3 as i64,
color,
);
framebuffer.swap();
}
@@ -55,12 +84,12 @@ pub fn circle_outline(framebuffer: &mut Framebuffer, x: usize, y: usize, radius:
loop {
i += 0.1;
let x1: f32 = radius * (i * PI / 180.0).cos();
let x1: f32 = radius * (i * core::f32::consts::PI / 180.0).cos();
let y1: f32 = radius * (i * PI / 180.0).sin();
framebuffer.put_pixel((x as f32 + x1) as usize, (y as f32 + y1) as usize, color);
if i >= 360.0 {
break
break;
}
}
framebuffer.swap();
@@ -101,7 +130,15 @@ pub fn circle_filled(framebuffer: &mut Framebuffer, x0: usize, y0: usize, radius
framebuffer.swap();
}
pub fn rectangle_filled(framebuffer: &mut Framebuffer, x: usize, y: usize, width: usize, height: usize, color: u32, swap: bool) {
pub fn rectangle_filled(
framebuffer: &mut Framebuffer,
x: usize,
y: usize,
width: usize,
height: usize,
color: u32,
swap: bool,
) {
for fb_x in x..x + width {
for fb_y in y..y + height {
framebuffer.put_pixel(fb_x, fb_y, color);
@@ -112,7 +149,14 @@ pub fn rectangle_filled(framebuffer: &mut Framebuffer, x: usize, y: usize, width
}
}
pub fn rectangle_outline(framebuffer: &mut Framebuffer, x: i64, y: i64, width: i64, height: i64, color: u32) {
pub fn rectangle_outline(
framebuffer: &mut Framebuffer,
x: i64,
y: i64,
width: i64,
height: i64,
color: u32,
) {
line(framebuffer, x, y, x + width, y, color); // bottomleft -> bottomright
line(framebuffer, x, y + height, x + width, y + height, color); // topleft -> topright
line(framebuffer, x, y, x, y + height, color); // bottomleft -> topleft

View File

@@ -0,0 +1 @@

View File

@@ -0,0 +1 @@

View File

@@ -1,5 +1,6 @@
#![no_std]
#![no_main]
#![feature(abi_x86_interrupt)]
use core::arch::asm;
use core::fmt::Write;
@@ -7,15 +8,19 @@ use core::fmt::Write;
use limine::BaseRevision;
use limine::request::{FramebufferRequest, RequestsEndMarker, RequestsStartMarker};
pub mod driver;
pub mod arch;
pub mod driver;
use spin::Mutex;
use crate::arch::serial::{ConsoleWriter, SerialConsole, init_serial_console, with_serial_console};
use crate::driver::graphics::font_render::render_text;
use crate::driver::graphics::primitives::{circle_filled, circle_outline, rectangle_filled, rectangle_outline, triangle_outline};
use crate::arch::serial::{ConsoleWriter, init_serial_console, with_serial_console};
#[cfg(target_arch = "x86_64")]
use crate::arch::x86_64::gdt::load_gdt_x86_64;
use crate::driver::graphics::base::rgb;
use crate::driver::graphics::framebuffer::{Framebuffer, init_framebuffer, with_framebuffer};
use crate::driver::graphics::framebuffer::{init_framebuffer, with_framebuffer};
use crate::driver::graphics::primitives::{
circle_filled, circle_outline, rectangle_filled, rectangle_outline, triangle_outline,
};
use crate::arch::x86_64::idt::init_idt_x86_64;
/// Sets the base revision to the latest revision supported by the crate.
/// See specification for further info.
@@ -58,9 +63,14 @@ macro_rules! println {
pub fn _print(args: core::fmt::Arguments) {
with_framebuffer(|fb| {
with_serial_console(|console| {
let mut writer = ConsoleWriter { fb, console };
let mut writer = ConsoleWriter {
fb,
console,
should_center: false,
};
let _ = writer.write_fmt(args);
});
fb.swap();
});
}
@@ -70,12 +80,18 @@ unsafe extern "C" fn kmain() -> ! {
// removed by the linker.
assert!(BASE_REVISION.is_supported());
#[cfg(target_arch = "x86_64")]
{
load_gdt_x86_64();
init_idt_x86_64();
}
if let Some(framebuffer_response) = FRAMEBUFFER_REQUEST.get_response() {
if let Some(limine_framebuffer) = framebuffer_response.framebuffers().next() {
init_framebuffer(&limine_framebuffer);
with_framebuffer(|mut fb| {
let (width, height) = (fb.width.clone(), fb.height.clone());
init_serial_console(width / 2, height / 3);
init_serial_console(0, 0);
rectangle_filled(&mut fb, 0, 0, width, height, rgb(253, 129, 0), true);
rectangle_filled(&mut fb, 700, 400, 200, 200, rgb(0, 0, 0), true);
rectangle_outline(&mut fb, 400, 400, 100, 100, rgb(0, 0, 0));
@@ -83,7 +99,6 @@ unsafe extern "C" fn kmain() -> ! {
circle_outline(&mut fb, 400, 200, 100.0, rgb(0, 0, 0));
triangle_outline(&mut fb, 100, 400, 200, 400, 150, 600, rgb(0, 0, 0));
});
panic!("idk, test");
}
}
@@ -95,16 +110,19 @@ fn rust_panic(_info: &core::panic::PanicInfo) -> ! {
with_framebuffer(|mut fb| {
let (width, height) = (fb.width.clone(), fb.height.clone());
rectangle_filled(&mut fb, 0, 0, width, height, rgb(180, 0, 0), true);
with_serial_console(|serial_console| {
serial_console.clear(height / 3);
serial_console.render_text(&mut fb, "Kernel Panic! :C\n\n\n");
if let Some(message) = _info.message().as_str() {
serial_console.render_text(&mut fb, "Message:");
serial_console.render_text(&mut fb, message);
}
crate::println!("Kernel Panic! :C");
crate::print!("Message: ");
crate::println!("{}", _info);
with_serial_console(|console| {
console.clear(5, 5);
let mut writer = ConsoleWriter {
fb: &mut fb,
console,
should_center: true,
};
let _ = writer.write_str("KERNEL PANIC\n\n");
let _ = writer.write_fmt(core::format_args!("{}", _info));
fb.swap();
});
});

View File

@@ -0,0 +1 @@

View File

@@ -0,0 +1 @@

View File