initial commit

This commit is contained in:
Bryan McShea
2024-01-16 00:15:13 -05:00
commit 42b47201fe
18 changed files with 1922 additions and 0 deletions

172
kernel/src/framebuffer.rs Normal file
View File

@@ -0,0 +1,172 @@
use bootloader_api::info::FrameBuffer;
use embedded_graphics::{
draw_target::DrawTarget,
geometry::{Dimensions, OriginDimensions, Point, Size},
mono_font::{ascii::FONT_6X10, MonoTextStyle},
pixelcolor::{Rgb888, RgbColor},
primitives::{
Circle, Primitive, PrimitiveStyle, PrimitiveStyleBuilder, Rectangle, StrokeAlignment,
Triangle,
},
text::{Alignment, Text},
Drawable, Pixel,
};
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct Position {
pub x: usize,
pub y: usize,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct Color {
pub red: u8,
pub green: u8,
pub blue: u8,
}
pub fn set_pixel_in(framebuffer: &mut FrameBuffer, position: Position, color: Color) {
let info = framebuffer.info();
let byte_offset = {
let line_offset = position.y * info.stride;
let pixel_offset = line_offset + position.x;
pixel_offset * info.bytes_per_pixel
};
let pixel_buffer = &mut framebuffer.buffer_mut()[byte_offset..];
match info.pixel_format {
bootloader_api::info::PixelFormat::Rgb => {
pixel_buffer[0] = color.red;
pixel_buffer[1] = color.green;
pixel_buffer[2] = color.blue;
}
bootloader_api::info::PixelFormat::Bgr => {
pixel_buffer[0] = color.blue;
pixel_buffer[1] = color.green;
pixel_buffer[2] = color.red;
}
bootloader_api::info::PixelFormat::U8 => {
let gray = color.red / 3 + color.green / 3 + color.blue / 3;
pixel_buffer[0] = gray;
}
other => panic!("unknown pixel format {other:?}"),
}
}
pub struct Display<'a> {
framebuffer: &'a mut FrameBuffer,
}
impl<'a> Display<'a> {
pub fn new(framebuffer: &'a mut FrameBuffer) -> Self {
Self { framebuffer }
}
fn draw_pixel(&mut self, pos: Position, color: Color) {
let (width, height) = {
let info = self.framebuffer.info();
(info.width, info.height)
};
if pos.x >= width || pos.y >= height {
return;
}
set_pixel_in(&mut self.framebuffer, pos, color);
}
}
impl OriginDimensions for Display<'_> {
fn size(&self) -> embedded_graphics::prelude::Size {
let info = self.framebuffer.info();
Size {
width: info.width as u32,
height: info.height as u32,
}
}
}
impl DrawTarget for Display<'_> {
type Color = Rgb888;
type Error = core::convert::Infallible;
fn draw_iter<I>(&mut self, pixels: I) -> Result<(), Self::Error>
where
I: IntoIterator<Item = Pixel<Self::Color>>,
{
for Pixel(coordinates, color) in pixels.into_iter() {
self.draw_pixel(
Position {
x: coordinates.x as usize,
y: coordinates.y as usize,
},
Color {
red: color.r(),
green: color.g(),
blue: color.b(),
},
);
}
Ok(())
}
}
pub fn draw_test(framebuffer: &mut FrameBuffer) {
for b in framebuffer.buffer_mut() {
*b = 0;
}
let mut display = Display::new(framebuffer);
// Create styles used by the drawing operations.
let thin_stroke = PrimitiveStyle::with_stroke(Rgb888::new(0, 255, 255), 1);
let thick_stroke = PrimitiveStyle::with_stroke(Rgb888::new(0, 255, 255), 3);
let border_stroke = PrimitiveStyleBuilder::new()
.stroke_color(Rgb888::new(0, 255, 255))
.stroke_width(3)
.stroke_alignment(StrokeAlignment::Inside)
.build();
let fill = PrimitiveStyle::with_fill(Rgb888::new(0, 255, 255));
let character_style = MonoTextStyle::new(&FONT_6X10, Rgb888::new(0, 255, 255));
let yoffset = 10;
// Draw a 3px wide outline around the display.
display
.bounding_box()
.into_styled(border_stroke)
.draw(&mut display)
.unwrap();
Rectangle::new(Point::new(52, yoffset), Size::new(16, 16))
.into_styled(fill)
.draw(&mut display)
.unwrap();
// Draw a triangle.
Triangle::new(
Point::new(16, 16 + yoffset),
Point::new(16 + 16, 16 + yoffset),
Point::new(16 + 8, yoffset),
)
.into_styled(thin_stroke)
.draw(&mut display)
.unwrap();
// Draw a filled square
Rectangle::new(Point::new(52, yoffset), Size::new(16, 16))
.into_styled(fill)
.draw(&mut display)
.unwrap();
// Draw a circle with a 3px wide stroke.
Circle::new(Point::new(88, yoffset), 17)
.into_styled(thick_stroke)
.draw(&mut display)
.unwrap();
// Draw centered text.
let text = "embedded-graphics";
Text::with_alignment(
text,
display.bounding_box().center() + Point::new(0, 15),
character_style,
Alignment::Center,
)
.draw(&mut display)
.unwrap();
}

49
kernel/src/gdt.rs Normal file
View File

@@ -0,0 +1,49 @@
use lazy_static::lazy_static;
use x86_64::{
structures::{
gdt::{Descriptor, GlobalDescriptorTable, SegmentSelector},
tss::TaskStateSegment,
},
VirtAddr, registers::segmentation::{CS, Segment}, instructions::tables::load_tss,
};
pub const DOUBLE_FAULT_IST_INDEX: u16 = 0;
pub fn init() {
GDT.0.load();
unsafe {
CS::set_reg(GDT.1.code);
load_tss(GDT.1.tss);
// TODO: properly handle this
x86_64::registers::segmentation::SS::set_reg(SegmentSelector::NULL);
}
}
struct Selectors {
code: SegmentSelector,
tss: SegmentSelector,
}
lazy_static! {
static ref GDT: (GlobalDescriptorTable, Selectors) = {
let mut gdt = GlobalDescriptorTable::new();
let code = gdt.add_entry(Descriptor::kernel_code_segment());
let tss = gdt.add_entry(Descriptor::tss_segment(&TSS));
(gdt, Selectors { code, tss })
};
}
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(unsafe { &STACK });
let stack_end = stack_start + STACK_SIZE;
stack_end
};
tss
};
}

94
kernel/src/interrupts.rs Normal file
View File

@@ -0,0 +1,94 @@
use crate::{gdt, print, println};
use lazy_static::lazy_static;
use pic8259::ChainedPics;
use x86_64::structures::idt::{InterruptDescriptorTable, InterruptStackFrame};
#[derive(Debug, Clone, Copy)]
#[repr(u8)]
pub enum InterruptIndex {
Timer = PIC_1_OFFSET,
Keyboard = PIC_1_OFFSET + 1,
}
impl InterruptIndex {
fn as_u8(self) -> u8 {
self as u8
}
fn as_usize(self) -> usize {
self as usize
}
}
pub fn init() {
IDT.load();
unsafe { PICS.lock().initialize() }
x86_64::instructions::interrupts::enable();
}
lazy_static! {
static ref IDT: InterruptDescriptorTable = {
let mut idt = InterruptDescriptorTable::new();
unsafe {
idt.double_fault
.set_handler_fn(double_fault)
.set_stack_index(gdt::DOUBLE_FAULT_IST_INDEX);
}
idt.breakpoint.set_handler_fn(breakpoint);
idt[InterruptIndex::Timer.as_usize()].set_handler_fn(timer);
idt[InterruptIndex::Keyboard.as_usize()].set_handler_fn(keyboard);
idt
};
}
extern "x86-interrupt" fn timer(_stack_frame: InterruptStackFrame) {
// print!(".");
unsafe {
PICS.lock()
.notify_end_of_interrupt(InterruptIndex::Timer as u8)
}
}
extern "x86-interrupt" fn breakpoint(_stack_frame: InterruptStackFrame) {
println!("POGGERS");
}
extern "x86-interrupt" fn double_fault(stack_frame: InterruptStackFrame, _error_code: u64) -> ! {
panic!("double fault exception: {:#?}", stack_frame);
}
extern "x86-interrupt" fn keyboard(_stack_frame: InterruptStackFrame) {
use pc_keyboard::{layouts, DecodedKey, HandleControl, Keyboard, ScancodeSet1};
use spin::Mutex;
use x86_64::instructions::port::Port;
lazy_static! {
static ref KEYBOARD: Mutex<Keyboard<layouts::Us104Key, ScancodeSet1>> =
Mutex::new(Keyboard::new(layouts::Us104Key, ScancodeSet1,
HandleControl::Ignore)
);
}
let mut keyboard = KEYBOARD.lock();
let mut port = Port::new(0x60);
let scancode: u8 = unsafe { port.read() };
if let Ok(Some(key_event)) = keyboard.add_byte(scancode) {
if let Some(key) = keyboard.process_keyevent(key_event) {
match key {
DecodedKey::Unicode(character) => print!("{}", character),
DecodedKey::RawKey(key) => print!("{:?}", key),
}
}
}
unsafe {
PICS.lock()
.notify_end_of_interrupt(InterruptIndex::Keyboard.as_u8());
}
}
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) });

25
kernel/src/lib.rs Normal file
View File

@@ -0,0 +1,25 @@
#![no_std]
#![feature(abi_x86_interrupt)]
pub mod framebuffer;
pub mod gdt;
pub mod interrupts;
pub mod qemu;
pub mod log;
pub fn init() {
gdt::init();
interrupts::init();
}
pub fn exit() -> ! {
qemu::exit();
hlt_loop()
}
pub fn hlt_loop() -> ! {
loop {
x86_64::instructions::hlt();
}
}

16
kernel/src/log.rs Normal file
View File

@@ -0,0 +1,16 @@
use core::fmt::Arguments;
#[doc(hidden)]
pub fn _log(args: Arguments<'_>) {
use core::fmt::Write;
interrupts::without_interrupts(|| {
UART.lock().write_fmt(args).unwrap();
})
}
#[macro_export]
macro_rules! log {
($($arg:tt)*) => ($crate::_log(format_args!($($arg)*)));
}

20
kernel/src/main.rs Normal file
View File

@@ -0,0 +1,20 @@
#![no_std]
#![no_main]
use kernel::{framebuffer, init, exit, println, hlt_loop};
bootloader_api::entry_point!(kernel_main);
fn kernel_main(boot_info: &'static mut bootloader_api::BootInfo) -> ! {
init();
if let Some(framebuffer) = boot_info.framebuffer.as_mut() {
framebuffer::draw_test(framebuffer);
}
for _ in 0..20000000 {}
hlt_loop();
}
#[panic_handler]
fn panic(info: &core::panic::PanicInfo) -> ! {
println!("{}", info);
exit()
}

33
kernel/src/qemu.rs Normal file
View File

@@ -0,0 +1,33 @@
use core::fmt::Arguments;
use spin::Mutex;
use uart_16550::SerialPort;
use x86_64::instructions::{interrupts, port::Port};
pub static UART: Mutex<SerialPort> = Mutex::new(unsafe { SerialPort::new(0x3F8) });
pub fn exit() {
unsafe {
let mut port = Port::new(0xf4);
port.write(0x10u32);
}
}
#[doc(hidden)]
pub fn _print(args: Arguments<'_>) {
use core::fmt::Write;
interrupts::without_interrupts(|| {
UART.lock().write_fmt(args).unwrap();
})
}
#[macro_export]
macro_rules! print {
($($arg:tt)*) => ($crate::qemu::_print(format_args!($($arg)*)));
}
#[macro_export]
macro_rules! println {
() => ($crate::print!("\n"));
($($arg:tt)*) => ($crate::print!("{}\n", format_args!($($arg)*)));
}