From c68fdd7dd2869870519035bcd9df9cd520441788 Mon Sep 17 00:00:00 2001 From: missing Date: Mon, 2 May 2022 09:16:27 -0500 Subject: [PATCH] can someone help me fix my code pls --- .cargo/config.toml | 9 ++ Cargo.lock | 108 +++++++++++++++++ Cargo.toml | 27 +++++ rust-toolchain | 1 + src/gdt.rs | 58 +++++++++ src/heap.rs | 39 ++++++ src/interrupts.rs | 182 +++++++++++++++++++++++++++ src/keyboard.rs | 297 +++++++++++++++++++++++++++++++++++++++++++++ src/lib.rs | 37 ++++++ src/main.rs | 94 ++++++++++++++ src/mem.rs | 85 +++++++++++++ src/proc.rs | 164 +++++++++++++++++++++++++ src/sync.rs | 101 +++++++++++++++ src/vga_text.rs | 97 +++++++++++++++ target.json | 15 +++ 15 files changed, 1314 insertions(+) create mode 100644 .cargo/config.toml create mode 100644 Cargo.lock create mode 100644 Cargo.toml create mode 100644 rust-toolchain create mode 100644 src/gdt.rs create mode 100644 src/heap.rs create mode 100644 src/interrupts.rs create mode 100644 src/keyboard.rs create mode 100644 src/lib.rs create mode 100644 src/main.rs create mode 100644 src/mem.rs create mode 100644 src/proc.rs create mode 100644 src/sync.rs create mode 100644 src/vga_text.rs create mode 100644 target.json diff --git a/.cargo/config.toml b/.cargo/config.toml new file mode 100644 index 0000000..2d8048b --- /dev/null +++ b/.cargo/config.toml @@ -0,0 +1,9 @@ +[build] +target = "/Users/missing/Projects/os/target.json" + +[unstable] +build-std-features = ["compiler-builtins-mem"] +build-std = ["core", "compiler_builtins", "alloc"] + +[target.'cfg(target_os = "none")'] +runner = "bootimage runner" \ No newline at end of file diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000..7443903 --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,108 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "bit_field" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dcb6dd1c2376d2e096796e234a70e17e94cc2d5d54ff8ce42b28cef1d0d359a4" + +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "bootloader" +version = "0.9.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "de78decc37247c7cfac5dbf3495c7298c6ac97cb355161caa7e15969c6648e6c" + +[[package]] +name = "linked_list_allocator" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "549ce1740e46b291953c4340adcd74c59bcf4308f4cac050fd33ba91b7168f4a" +dependencies = [ + "spinning_top", +] + +[[package]] +name = "lock_api" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "88943dd7ef4a2e5a4bfa2753aaab3013e34ce2533d1996fb18ef591e315e2b3b" +dependencies = [ + "scopeguard", +] + +[[package]] +name = "os" +version = "0.1.0" +dependencies = [ + "bitflags", + "bootloader", + "linked_list_allocator", + "pc-keyboard", + "pic8259", + "spin", + "x86_64", +] + +[[package]] +name = "pc-keyboard" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c6f2d937e3b8d63449b01401e2bae4041bc9dd1129c2e3e0d239407cf6635ac" + +[[package]] +name = "pic8259" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24ec21f514e2e16e94649f1d041ca4a7069b512c037ac156360652a775e6229d" +dependencies = [ + "x86_64", +] + +[[package]] +name = "scopeguard" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" + +[[package]] +name = "spin" +version = "0.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "511254be0c5bcf062b019a6c89c01a664aa359ded62f78aa72c6fc137c0590e5" +dependencies = [ + "lock_api", +] + +[[package]] +name = "spinning_top" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75adad84ee84b521fb2cca2d4fd0f1dab1d8d026bda3c5bea4ca63b5f9f9293c" +dependencies = [ + "lock_api", +] + +[[package]] +name = "volatile" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e4c2dbd44eb8b53973357e6e207e370f0c1059990df850aca1eca8947cf464f0" + +[[package]] +name = "x86_64" +version = "0.14.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "958ab3202b01bc43ba2eb832102c4a487ed93151667a2289062e5f2b00058be2" +dependencies = [ + "bit_field", + "bitflags", + "volatile", +] diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..a24518b --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,27 @@ +[package] +name = "os" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[lib] +name = "os" +harness = false + +[[bin]] +name = "os" +harness = false + +[dependencies] +bitflags = "1.3.2" +bootloader = { version = "0.9.8", features = ["map_physical_memory"] } +linked_list_allocator = "0.9.1" +pc-keyboard = "0.5.1" +pic8259 = "0.10.2" +spin = "0.9.2" +# x = { path = "../x" } +x86_64 = "0.14.8" + +[package.metadata.bootimage] +# run-args = ["-s", "-S"] \ No newline at end of file diff --git a/rust-toolchain b/rust-toolchain new file mode 100644 index 0000000..07ade69 --- /dev/null +++ b/rust-toolchain @@ -0,0 +1 @@ +nightly \ No newline at end of file diff --git a/src/gdt.rs b/src/gdt.rs new file mode 100644 index 0000000..55ce759 --- /dev/null +++ b/src/gdt.rs @@ -0,0 +1,58 @@ +use crate::lazy_static; +use x86_64::{ + instructions::tables::load_tss, + registers::segmentation::{Segment, SegmentSelector, CS}, + structures::{ + gdt::{Descriptor, GlobalDescriptorTable}, + tss::TaskStateSegment, + }, + VirtAddr, +}; + +lazy_static! { + static ref TSS: TaskStateSegment = { + let mut tss = TaskStateSegment::new(); + tss.interrupt_stack_table[crate::interrupts::DOUBLE_FAULT_STACK_IDX 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.interrupt_stack_table[crate::interrupts::TIMER_STACK_IDX 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 + }; + static ref GDT: (GlobalDescriptorTable, Selectors) = { + let mut gdt = GlobalDescriptorTable::new(); + let code_selector = gdt.add_entry(Descriptor::kernel_code_segment()); + let tss_selector = gdt.add_entry(Descriptor::tss_segment(&TSS)); + ( + gdt, + Selectors { + code_selector, + tss_selector, + }, + ) + }; +} + +struct Selectors { + code_selector: SegmentSelector, + tss_selector: SegmentSelector, +} + +pub fn init_gdt() { + GDT.0.load(); + unsafe { + CS::set_reg(GDT.1.code_selector); + load_tss(GDT.1.tss_selector); + } +} diff --git a/src/heap.rs b/src/heap.rs new file mode 100644 index 0000000..9688343 --- /dev/null +++ b/src/heap.rs @@ -0,0 +1,39 @@ +use core::alloc::Layout; + +use linked_list_allocator::LockedHeap; +use x86_64::{ + structures::paging::{Page, PageTableFlags}, + VirtAddr, +}; + +use crate::{mem::map_next, assert_canonical}; + +// address range is 0x4ea900000000 to 0x4ea9000fffff +static HEAP_START: u64 = 0x_4ea9_0000_0000; // '4ea9' is supposed to be 'heap', so its easy to spot +static HEAP_SIZE: u64 = 1024 * 1024; // 1 MebiByte or 0xfffff + +#[global_allocator] +static ALLOC: LockedHeap = LockedHeap::empty(); + +#[alloc_error_handler] +fn alloc_error_handler(layout: Layout) -> ! { + panic!("Alloc error: {:#?}", layout) +} + +pub fn init() { + assert_canonical(HEAP_START); + + let page_start = Page::containing_address(VirtAddr::new(HEAP_START)); + let page_end = Page::containing_address(VirtAddr::new(HEAP_START + HEAP_SIZE - 1)); + let page_range = Page::range_inclusive(page_start, page_end); + + for page in page_range { + map_next(page, PageTableFlags::PRESENT | PageTableFlags::WRITABLE) + .unwrap() + .flush(); + } + + unsafe { + ALLOC.lock().init(HEAP_START as usize, HEAP_SIZE as usize); + } +} diff --git a/src/interrupts.rs b/src/interrupts.rs new file mode 100644 index 0000000..fb8f796 --- /dev/null +++ b/src/interrupts.rs @@ -0,0 +1,182 @@ +use core::{intrinsics::transmute, arch::asm}; + +use crate::{lazy_static, print, println, proc::{self, Registers}}; +use pc_keyboard::{layouts, DecodedKey, Keyboard, ScancodeSet1}; +use pic8259::ChainedPics; +use spin::Mutex; +use x86_64::{ + instructions::{interrupts, port::Port}, + structures::idt::{InterruptDescriptorTable, InterruptStackFrame, PageFaultErrorCode}, +}; + +pub const DOUBLE_FAULT_STACK_IDX: u16 = 0; +pub const TIMER_STACK_IDX: u16 = 1; + +#[repr(u8)] +enum Interrupt { + Timer = PIC_1_OFFSET, + Keyboard, + Syscall = 0x80 +} + +lazy_static! { + static ref IDT: InterruptDescriptorTable = { + let mut idt = InterruptDescriptorTable::new(); + + unsafe { + idt.double_fault + .set_handler_fn(double_fault) + .set_stack_index(DOUBLE_FAULT_STACK_IDX) + }; + + idt.breakpoint.set_handler_fn(breakpoint); + idt.page_fault.set_handler_fn(page_fault); + idt.invalid_opcode.set_handler_fn(invalid_opcode); + idt.segment_not_present.set_handler_fn(segment_not_present); + idt.stack_segment_fault.set_handler_fn(stack_segment_fault); + idt.general_protection_fault.set_handler_fn(general_protection_fault); + + unsafe { + idt[Interrupt::Timer as usize] + .set_handler_fn(transmute(timer_naked as *const ())) + .set_stack_index(TIMER_STACK_IDX) + .disable_interrupts(true) // this is the default but i want to make EXTRA SURE there will be no stupid edge case + }; + idt[Interrupt::Keyboard as usize].set_handler_fn(keyboard); + idt[Interrupt::Syscall as usize].set_handler_fn(syscall); + + idt + }; +} + +extern "x86-interrupt" fn breakpoint(stack: InterruptStackFrame) { + println!("breakpoint: {:#?}", stack); +} + +extern "x86-interrupt" fn page_fault(stack: InterruptStackFrame, error: PageFaultErrorCode) { + panic!("page fault: {:?}\n{:#?}", error, stack) +} + +extern "x86-interrupt" fn invalid_opcode(stack: InterruptStackFrame) { + panic!("invalid opcode: {:#?}", stack) +} + +extern "x86-interrupt" fn segment_not_present(stack: InterruptStackFrame, error: u64) { + panic!("segment not present: {} {:#?}", error, stack) +} + +extern "x86-interrupt" fn stack_segment_fault(stack: InterruptStackFrame, error: u64) { + panic!("stack segment fault: {} {:#?}", error, stack) +} + +extern "x86-interrupt" fn general_protection_fault(stack: InterruptStackFrame, error: u64) { + panic!("general protection fault: {} {:#?}", error, stack) +} + +extern "x86-interrupt" fn double_fault(stack: InterruptStackFrame, error: u64) -> ! { + panic!("aaaaaaaaaaaaa (error code: {}) {:#?}", error, stack); +} + +extern "x86-interrupt" fn syscall(_: InterruptStackFrame) { + +} + +// dont even breathe on this code +#[naked] +unsafe extern "x86-interrupt" fn timer_naked() { + asm! { + // push the `Registers` struct + "push r15", + "push r14", + "push r13", + "push r12", + "push r11", + "push r10", + "push r9", + "push r8", + "push rbp", + "push rdi", + "push rsi", + "push rdx", + "push rcx", + "push rbx", + "push rax", + // call the `extern "C"` version + "call {}", + // pop the `Registers` struct off the stack and back into the registers + "pop rax", + "pop rbx", + "pop rcx", + "pop rdx", + "pop rsi", + "pop rdi", + "pop rbp", + "pop r8", + "pop r9", + "pop r10", + "pop r11", + "pop r12", + "pop r13", + "pop r14", + "pop r15", + "iretq", + sym timer_c, + options(noreturn), + } +} + +const FREQUENCY_DIVIDER: u8 = 15; + +static mut TIMER_COUNT: u8 = 0; + +extern "C" fn timer_c(mut registers: Registers, mut stack: InterruptStackFrame) { + unsafe { + TIMER_COUNT = (TIMER_COUNT + 1) % FREQUENCY_DIVIDER; + if TIMER_COUNT == 0 { + proc::timer(stack.as_mut().extract_inner(), &mut registers); + } + } + + unsafe { + PICS.lock().notify_end_of_interrupt(Interrupt::Timer as u8); + } +} + +extern "x86-interrupt" fn keyboard(_: InterruptStackFrame) { + lazy_static! { + static ref KEYBOARD: Mutex> = { + Mutex::new(Keyboard::new( + layouts::Us104Key, + ScancodeSet1, + pc_keyboard::HandleControl::Ignore, + )) + }; + } + + let mut port = Port::new(0x60); + let scancode: u8 = unsafe { port.read() }; + let mut keyboard = KEYBOARD.lock(); + if let Ok(Some(event)) = keyboard.add_byte(scancode) { + if let Some(key) = keyboard.process_keyevent(event) { + match key { + DecodedKey::RawKey(key_code) => print!("{:?}", key_code), + DecodedKey::Unicode(char) => print!("{}", char), + } + } + } + + unsafe { + PICS.lock().notify_end_of_interrupt(Interrupt::Keyboard as u8); + } +} + +pub fn init_interrupts() { + IDT.load(); + unsafe { PICS.lock().initialize() } + interrupts::enable() +} + +pub const PIC_1_OFFSET: u8 = 32; +pub const PIC_2_OFFSET: u8 = PIC_1_OFFSET + 8; + +pub static PICS: Mutex = Mutex::new(unsafe { ChainedPics::new(PIC_1_OFFSET, PIC_2_OFFSET) }); \ No newline at end of file diff --git a/src/keyboard.rs b/src/keyboard.rs new file mode 100644 index 0000000..ab4f0ea --- /dev/null +++ b/src/keyboard.rs @@ -0,0 +1,297 @@ +// #[derive(Debug)] +// pub struct KeyPress { +// released: bool, +// key: Key +// } + +// #[derive(Debug)] +// pub enum Key { +// Char(char), +// Control(ControlKey), +// Escape, +// Unknown(u8) +// } + +// #[derive(Debug)] +// pub enum ControlKey { +// Escape, +// Backspace, +// Tab, +// Enter, +// LControl, +// LShift, +// RShift, +// LAlt, +// CapsLock, +// F(u8), +// NumberLock, +// ScrollLock, +// } + +// pub fn get_key_from_scancode(scancode: u8) -> KeyPress { +// if scancode == 0xe0 { +// return KeyPress { +// released: false, +// key: Key::Escape +// } +// } + +// let released = (scancode >> 7) == 1; +// let scancode = scancode & 0b01111111; + +// let key = match scancode { +// 0x01 => Key::Control(ControlKey::Escape), +// 0x02 => Key::Char('1'), +// 0x03 => Key::Char('2'), +// 0x04 => Key::Char('3'), +// 0x05 => Key::Char('4'), +// 0x06 => Key::Char('5'), +// 0x07 => Key::Char('6'), +// 0x08 => Key::Char('7'), +// 0x09 => Key::Char('8'), +// 0x0A => Key::Char('9'), +// 0x0B => Key::Char('0'), +// 0x0C => Key::Char('-'), +// 0x0D => Key::Char('='), +// 0x0E => Key::Control(ControlKey::Backspace), +// 0x0F => Key::Control(ControlKey::Tab), +// 0x10 => Key::Char('Q'), +// 0x11 => Key::Char('W'), +// 0x12 => Key::Char('E'), +// 0x13 => Key::Char('R'), +// 0x14 => Key::Char('T'), +// 0x15 => Key::Char('Y'), +// 0x16 => Key::Char('U'), +// 0x17 => Key::Char('I'), +// 0x18 => Key::Char('O'), +// 0x19 => Key::Char('P'), +// 0x1A => Key::Char('['), +// 0x1B => Key::Char(']'), +// 0x1C => Key::Control(ControlKey::Enter), +// 0x1D => Key::Control(ControlKey::LControl), +// 0x1E => Key::Char('A'), +// 0x1F => Key::Char('S'), +// 0x20 => Key::Char('D'), +// 0x21 => Key::Char('F'), +// 0x22 => Key::Char('G'), +// 0x23 => Key::Char('H'), +// 0x24 => Key::Char('J'), +// 0x25 => Key::Char('K'), +// 0x26 => Key::Char('L'), +// 0x27 => Key::Char(';'), +// 0x28 => Key::Char('\''), +// 0x29 => Key::Char('`'), +// 0x2A => Key::Control(ControlKey::LShift), +// 0x2B => Key::Char('\\'), +// 0x2C => Key::Char('Z'), +// 0x2D => Key::Char('X'), +// 0x2E => Key::Char('C'), +// 0x2F => Key::Char('V'), +// 0x30 => Key::Char('B'), +// 0x31 => Key::Char('N'), +// 0x32 => Key::Char('M'), +// 0x33 => Key::Char(','), +// 0x34 => Key::Char('.'), +// 0x35 => Key::Char('/'), +// 0x36 => Key::Control(ControlKey::RShift), +// 0x37 => Key::Char('*'), +// 0x38 => Key::Control(ControlKey::LAlt), +// 0x39 => Key::Char(' '), +// 0x3A => Key::Control(ControlKey::CapsLock), +// 0x3B => Key::Control(ControlKey::F(1)), +// 0x3C => Key::Control(ControlKey::F(2)), +// 0x3D => Key::Control(ControlKey::F(3)), +// 0x3E => Key::Control(ControlKey::F(4)), +// 0x3F => Key::Control(ControlKey::F(5)), +// 0x40 => Key::Control(ControlKey::F(6)), +// 0x41 => Key::Control(ControlKey::F(7)), +// 0x42 => Key::Control(ControlKey::F(8)), +// 0x43 => Key::Control(ControlKey::F(9)), +// 0x44 => Key::Control(ControlKey::F(10 )), +// 0x45 => Key::Control(ControlKey::NumberLock), +// 0x46 => Key::Control(ControlKey::ScrollLock), +// 0x47 => Key::Char('7'), // keypad +// 0x48 => Key::Char('8'), // keypad +// 0x49 => Key::Char('9'), // keypad +// 0x4A => Key::Char('-'), // keypad +// 0x4B => Key::Char('4'), // keypad +// 0x4C => Key::Char('5'), // keypad +// 0x4D => Key::Char('6'), // keypad +// 0x4E => Key::Char('+'), // keypad +// 0x4F => Key::Char('1'), // keypad +// 0x50 => Key::Char('2'), // keypad +// 0x51 => Key::Char('3'), // keypad +// 0x52 => Key::Char('0'), // keypad +// 0x53 => Key::Char('.'), // keypad +// // 3 missing here? +// 0x57 => Key::Control(ControlKey::F(11)), +// 0x58 => Key::Control(ControlKey::F(12)), +// _ => Key::Unknown(scancode) +// }; + +// KeyPress { +// released, +// key +// } + +// /* +// 0xE0, 0x10 +// (multimedia) previous track pressed +// 0xE0, 0x19 +// (multimedia) next track pressed +// 0xE0, 0x1C +// (keypad) enter pressed +// 0xE0, 0x1D +// right control pressed +// 0xE0, 0x20 +// (multimedia) mute pressed +// 0xE0, 0x21 +// (multimedia) calculator pressed +// 0xE0, 0x22 +// (multimedia) play pressed +// 0xE0, 0x24 +// (multimedia) stop pressed +// 0xE0, 0x2E +// (multimedia) volume down pressed +// 0xE0, 0x30 +// (multimedia) volume up pressed +// 0xE0, 0x32 +// (multimedia) WWW home pressed +// 0xE0, 0x35 +// (keypad) / pressed +// 0xE0, 0x38 +// right alt (or altGr) pressed +// 0xE0, 0x47 +// home pressed +// 0xE0, 0x48 +// cursor up pressed +// 0xE0, 0x49 +// page up pressed +// 0xE0, 0x4B +// cursor left pressed +// 0xE0, 0x4D +// cursor right pressed +// 0xE0, 0x4F +// end pressed +// 0xE0, 0x50 +// cursor down pressed +// 0xE0, 0x51 +// page down pressed +// 0xE0, 0x52 +// insert pressed +// 0xE0, 0x53 +// delete pressed +// 0xE0, 0x5B +// left GUI pressed +// 0xE0, 0x5C +// right GUI pressed +// 0xE0, 0x5D +// "apps" pressed +// 0xE0, 0x5E +// (ACPI) power pressed +// 0xE0, 0x5F +// (ACPI) sleep pressed +// 0xE0, 0x63 +// (ACPI) wake pressed +// 0xE0, 0x65 +// (multimedia) WWW search pressed +// 0xE0, 0x66 +// (multimedia) WWW favorites pressed +// 0xE0, 0x67 +// (multimedia) WWW refresh pressed +// 0xE0, 0x68 +// (multimedia) WWW stop pressed +// 0xE0, 0x69 +// (multimedia) WWW forward pressed +// 0xE0, 0x6A +// (multimedia) WWW back pressed +// 0xE0, 0x6B +// (multimedia) my computer pressed +// 0xE0, 0x6C +// (multimedia) email pressed +// 0xE0, 0x6D +// (multimedia) media select pressed +// 0xE0, 0x90 +// (multimedia) previous track released +// 0xE0, 0x99 +// (multimedia) next track released +// 0xE0, 0x9C +// (keypad) enter released +// 0xE0, 0x9D +// right control released +// 0xE0, 0xA0 +// (multimedia) mute released +// 0xE0, 0xA1 +// (multimedia) calculator released +// 0xE0, 0xA2 +// (multimedia) play released +// 0xE0, 0xA4 +// (multimedia) stop released +// 0xE0, 0xAE +// (multimedia) volume down released +// 0xE0, 0xB0 +// (multimedia) volume up released +// 0xE0, 0xB2 +// (multimedia) WWW home released +// 0xE0, 0xB5 +// (keypad) / released +// 0xE0, 0xB8 +// right alt (or altGr) released +// 0xE0, 0xC7 +// home released +// 0xE0, 0xC8 +// cursor up released +// 0xE0, 0xC9 +// page up released +// 0xE0, 0xCB +// cursor left released +// 0xE0, 0xCD +// cursor right released +// 0xE0, 0xCF +// end released +// 0xE0, 0xD0 +// cursor down released +// 0xE0, 0xD1 +// page down released +// 0xE0, 0xD2 +// insert released +// 0xE0, 0xD3 +// delete released +// 0xE0, 0xDB +// left GUI released +// 0xE0, 0xDC +// right GUI released +// 0xE0, 0xDD +// "apps" released +// 0xE0, 0xDE +// (ACPI) power released +// 0xE0, 0xDF +// (ACPI) sleep released +// 0xE0, 0xE3 +// (ACPI) wake released +// 0xE0, 0xE5 +// (multimedia) WWW search released +// 0xE0, 0xE6 +// (multimedia) WWW favorites released +// 0xE0, 0xE7 +// (multimedia) WWW refresh released +// 0xE0, 0xE8 +// (multimedia) WWW stop released +// 0xE0, 0xE9 +// (multimedia) WWW forward released +// 0xE0, 0xEA +// (multimedia) WWW back released +// 0xE0, 0xEB +// (multimedia) my computer released +// 0xE0, 0xEC +// (multimedia) email released +// 0xE0, 0xED +// (multimedia) media select released +// 0xE0, 0x2A, 0xE0, 0x37 +// print screen pressed +// 0xE0, 0xB7, 0xE0, 0xAA +// print screen released +// 0xE1, 0x1D, 0x45, 0xE1, 0x9D, 0xC5 +// pause pressed +// */ +// } diff --git a/src/lib.rs b/src/lib.rs new file mode 100644 index 0000000..3214661 --- /dev/null +++ b/src/lib.rs @@ -0,0 +1,37 @@ +#![no_std] +#![no_main] +#![feature(abi_x86_interrupt)] // for `mod interrupts` +#![feature(const_fn_trait_bound)] // for `mod sync` +#![feature(alloc_error_handler)] // for `mod heap` +#![feature(naked_functions)] // for `mod proc` +#![feature(asm_sym)] // for `mod interrupts` +#![feature(bench_black_box)] // for `mod proc` + +extern crate alloc; + +use bootloader::BootInfo; +use x86_64::VirtAddr; + +mod gdt; +mod heap; +mod interrupts; +mod keyboard; +pub mod mem; +pub mod sync; +pub mod vga_text; +pub mod proc; +// pub use x; + +pub fn init(info: &'static BootInfo) { + gdt::init_gdt(); + interrupts::init_interrupts(); + mem::init(info.physical_memory_offset, &info.memory_map); + heap::init(); + proc::init(); +} + +fn assert_canonical(addr: u64) { + if VirtAddr::new_truncate(addr).as_u64() != addr { + panic!("Address 0x{:x} was not canonical!", addr); + } +} \ No newline at end of file diff --git a/src/main.rs b/src/main.rs new file mode 100644 index 0000000..bb30ebe --- /dev/null +++ b/src/main.rs @@ -0,0 +1,94 @@ +#![no_std] +#![no_main] +#![feature(bench_black_box)] + +extern crate alloc; + +use bootloader::{entry_point, BootInfo}; +use core::{panic::PanicInfo, arch::asm, hint::black_box}; +use os::{println, proc::{PROCESSES, ProcessControlBlock, Registers}}; +use x86_64::{instructions::{hlt, interrupts::{without_interrupts, self}}, structures::idt::InterruptStackFrameValue, registers::{segmentation::{CS, Segment, SS}, self}, VirtAddr}; + +entry_point!(main); +fn main(bootinfo: &'static BootInfo) -> ! { + without_interrupts(|| os::init(bootinfo)); + + println!("creating procs..."); + + // or else interrupts are disabled in the new processes + let rflags = registers::rflags::read_raw(); + + // without_interrupts for the PROCESSES lock + without_interrupts(|| { + let mut procs = PROCESSES.lock(); + + static mut PROC_1_STACK: [u8; 1024] = [0; 1024]; + + let stack_frame = InterruptStackFrameValue { + instruction_pointer: VirtAddr::new(proc1 as u64), + code_segment: CS::get_reg().0 as u64, + cpu_flags: rflags, + stack_pointer: VirtAddr::new(unsafe { &PROC_1_STACK } as *const _ as u64) + 1024usize, + stack_segment: SS::get_reg().0 as u64, + }; + + let registers = Registers::default(); + + procs[1].write(ProcessControlBlock { stack_frame, registers }); + + static mut PROC_2_STACK: [u8; 1024] = [0; 1024]; + + let stack_frame = InterruptStackFrameValue { + instruction_pointer: VirtAddr::new(proc2 as u64), + code_segment: CS::get_reg().0 as u64, + cpu_flags: rflags, + stack_pointer: VirtAddr::new(unsafe { &PROC_2_STACK } as *const _ as u64) + 1024usize, + stack_segment: SS::get_reg().0 as u64, + }; + + let registers = Registers::default(); + + procs[2].write(ProcessControlBlock { stack_frame, registers }); + + procs[0].next = 1; + procs[0].prev = 2; + + procs[1].next = 2; + procs[1].prev = 2; + + procs[2].next = 1; + procs[2].prev = 1; + }); + + loop { + println!("Hi from main proc!"); + for i in 0..500000 { black_box(i); } + } + + loop { + hlt(); + } +} + +#[allow(named_asm_labels)] +extern "C" fn proc1() -> ! { + loop { + println!("Hi from proc 1!"); + for i in 0..500000 { black_box(i); } + } +} + +extern "C" fn proc2() -> ! { + loop { + println!("Hi from proc 2, foo bar baz!"); + for i in 0..500000 { black_box(i); } + } +} + +#[panic_handler] +fn panic(info: &PanicInfo) -> ! { + println!("{}", info); + loop { + hlt(); + } +} \ No newline at end of file diff --git a/src/mem.rs b/src/mem.rs new file mode 100644 index 0000000..ff39dbf --- /dev/null +++ b/src/mem.rs @@ -0,0 +1,85 @@ +use bootloader::bootinfo::{MemoryMap, MemoryRegionType}; +use spin::Mutex; +use x86_64::{ + registers::control::Cr3, + structures::paging::{ + mapper::{MapToError, MapperFlush}, + *, + }, + PhysAddr, VirtAddr, +}; + +static MAPPER: Mutex> = Mutex::new(None); +static ALLOCATOR: Mutex> = Mutex::new(None); + +pub(crate) fn init(phys_offset: u64, memory_map: &'static MemoryMap) { + let table4 = unsafe { &mut *(Cr3::read().0.start_address().as_u64() as *mut PageTable) }; + MAPPER + .lock() + .replace(unsafe { OffsetPageTable::new(table4, VirtAddr::new(phys_offset)) }); + ALLOCATOR + .lock() + .replace(unsafe { Allocator::new(memory_map) }); +} + +pub fn translate_addr(addr: VirtAddr) -> Option { + MAPPER.lock().as_ref().unwrap().translate_addr(addr) +} + +pub fn map( + page: Page, + frame: PhysFrame, + flags: PageTableFlags, +) -> Result, MapToError> { + unsafe { + MAPPER.lock().as_mut().unwrap().map_to( + page, + frame, + flags, + ALLOCATOR.lock().as_mut().unwrap(), + ) + } +} + +pub fn map_next( + page: Page, + flags: PageTableFlags, +) -> Result, MapToError> { + let next_page = ALLOCATOR + .lock() + .as_mut() + .unwrap() + .allocate_frame() + .ok_or(MapToError::FrameAllocationFailed)?; + map(page, next_page, flags) +} + +struct Allocator { + memory_map: &'static MemoryMap, + next: usize, +} + +impl Allocator { + unsafe fn new(memory_map: &'static MemoryMap) -> Self { + Self { + memory_map, + next: 0, + } + } +} + +unsafe impl FrameAllocator for Allocator { + fn allocate_frame(&mut self) -> Option> { + let next = self + .memory_map + .iter() + .filter(|v| v.region_type == MemoryRegionType::Usable) + .map(|v| v.range.start_addr()..v.range.end_addr()) + .flat_map(|v| v.step_by(4096)) + .nth(self.next); + + self.next += 1; + + Some(PhysFrame::from_start_address(PhysAddr::new(next?)).unwrap()) + } +} diff --git a/src/proc.rs b/src/proc.rs new file mode 100644 index 0000000..984c826 --- /dev/null +++ b/src/proc.rs @@ -0,0 +1,164 @@ +use core::{fmt::Debug, mem, hint::black_box}; + +use alloc::boxed::Box; +use spin::Mutex; +use x86_64::{VirtAddr, structures::{paging::{Page, PageTableFlags}, idt::InterruptStackFrameValue}}; + +use crate::{sync::SyncLazy, mem::map_next, lazy_static, assert_canonical, println}; + +#[derive(Clone, Copy, Debug)] +pub struct ProcessControlBlock { + pub stack_frame: InterruptStackFrameValue, + pub registers: Registers +} + +#[derive(Clone, Copy, Default)] +#[non_exhaustive] +#[repr(C)] +pub struct Registers { + pub rax: usize, + pub rbx: usize, + pub rcx: usize, + pub rdx: usize, + pub rsi: usize, + pub rdi: usize, + pub rbp: usize, + pub r8: usize, + pub r9: usize, + pub r10: usize, + pub r11: usize, + pub r12: usize, + pub r13: usize, + pub r14: usize, + pub r15: usize, +} + +impl Debug for Registers { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + f.debug_struct("Registers") + .field("rax", &format_args!("{:#018x}", self.rax)) + .field("rbx", &format_args!("{:#018x}", self.rbx)) + .field("rcx", &format_args!("{:#018x}", self.rcx)) + .field("rdx", &format_args!("{:#018x}", self.rdx)) + .field("rsi", &format_args!("{:#018x}", self.rsi)) + .field("rdi", &format_args!("{:#018x}", self.rdi)) + .field("rbp", &format_args!("{:#018x}", self.rbp)) + .field("r8", &format_args!("{:#018x}", self.r8)) + .field("r9", &format_args!("{:#018x}", self.r9)) + .field("r10", &format_args!("{:#018x}", self.r10)) + .field("r11", &format_args!("{:#018x}", self.r11)) + .field("r12", &format_args!("{:#018x}", self.r12)) + .field("r13", &format_args!("{:#018x}", self.r13)) + .field("r14", &format_args!("{:#018x}", self.r14)) + .field("r15", &format_args!("{:#018x}", self.r15)) + .finish() + } +} + +const MAX_PROCESSES: usize = 0x7fff; +const PROCESS_LIST_ADDR: usize = 0x7fff_0000_0000; + +pub struct ProcListElement { + pub pcb: Option<&'static mut ProcessControlBlock>, // npo for the win! + pub prev: u16, // only needed to patch an element out of the list + pub next: u16, +} + +impl ProcListElement { + pub fn write(&mut self, pcb: ProcessControlBlock) { + println!("write"); + match self.pcb.as_deref_mut() { + None => { + let addr = Box::leak(Box::new(pcb)); + // with this println, it tries to execute 0x0. without it, it (sometimes) tries to execute PROCESS_LIST_ADDR. + // whatever the case, it happens after 1 println in `fn timer`, before this code is even hit. + // if the line up above ^^^ is inlined, it faults after 2 printlns instead. what in the actual fuck + // println!("addr: {:x}", addr as *mut _ as usize); + self.pcb.replace(addr); + } + Some(v) => { *v = pcb; } + } + // println!("end write"); + } +} + +lazy_static! { + pub static ref PROCESSES: Mutex<&'static mut [ProcListElement; MAX_PROCESSES]> = { + let ptr = PROCESS_LIST_ADDR as *mut [_; MAX_PROCESSES]; + // const gaming + const DEFAULT: ProcListElement = ProcListElement { pcb: None, next: 0, prev: 0 }; + unsafe { ptr.write([DEFAULT; MAX_PROCESSES]) } + Mutex::new(unsafe { &mut *ptr }) + }; +} + +static CURRENT_PROCESS: Mutex = Mutex::new(0); + +// here i make the assumption that the privilege level is *somewhere* in `InterruptStackFrame` +pub fn timer(stack_frame: &mut InterruptStackFrameValue, registers: &mut Registers) { + println!("Hello there from timer interrupt"); + + let mut curr_pid = CURRENT_PROCESS.lock(); + let mut procs = PROCESSES.lock(); + + println!("Hello there from timer interrupt 2"); + + // first, save the stack and registers values in the pcb for this pid + procs[*curr_pid as usize].write(ProcessControlBlock { + stack_frame: *stack_frame, + registers: *registers, + }); + + println!("Hello there from timer interrupt 3"); + + // then, choose the next pid + let next_pid = procs[*curr_pid as usize].next as u64; + *curr_pid = next_pid; + let next = procs[next_pid as usize].pcb.as_deref().unwrap(); + + println!("Hello there from timer interrupt 4"); + + // last, load the stack and registers values from the new pid + *stack_frame = next.stack_frame; + *registers = next.registers; + + // println!("Next pid: {}", *curr_pid); + // println!("Next stack_frame: {:?}", stack_frame); + // println!("Next registers: {:?}", registers); + + for i in 0..5000000 { black_box(i); } +} + +pub fn init() { + assert_canonical(PROCESS_LIST_ADDR as u64); + + let page_start = Page::containing_address(VirtAddr::new(PROCESS_LIST_ADDR as u64)); + let page_end = Page::containing_address(VirtAddr::new((PROCESS_LIST_ADDR + MAX_PROCESSES * mem::size_of::()) as u64 - 1)); + let page_range = Page::range_inclusive(page_start, page_end); + + for page in page_range { + map_next(page, PageTableFlags::PRESENT | PageTableFlags::WRITABLE) + .unwrap() + .flush(); + } + + let _ = SyncLazy::get(&PROCESSES); +} + +// macro_rules! new_stack_frame { +// () => {unsafe { +// let ip: usize; +// let cs: usize; +// let flags: usize; +// let sp: usize; +// let ss: usize; +// ::core::arch::asm! { +// "mov rax, rip", +// out("rax") ip +// } +// }}; +// } + +// fn foo() { +// new_stack_frame!() +// } \ No newline at end of file diff --git a/src/sync.rs b/src/sync.rs new file mode 100644 index 0000000..42569d1 --- /dev/null +++ b/src/sync.rs @@ -0,0 +1,101 @@ +use core::{ + cell::UnsafeCell, + ops::Deref, + sync::atomic::{AtomicBool, Ordering}, mem::MaybeUninit, +}; + +/// A wrapper around a type that is safe to read but unsafe to write. +/// +/// Always implements [`Send`] and [`Sync`], because data races cannot happen without writes. +/// +/// This type [`Deref`]s to its contents, so you write using [`UnsafeWriteCell::write()`] instead of `cell.write()`. +#[repr(transparent)] +pub struct UnsafeWriteCell { + val: UnsafeCell, +} + +impl UnsafeWriteCell { + /// Creates a new `UnsafeWrite` containing the passed value. + pub const fn new(val: T) -> Self { + Self { + val: UnsafeCell::new(val), + } + } + + /// Overwrites the value in the `UnsafeWrite` with the provided value. + /// + /// Takes an `&Self` so that one can write to statics. + pub unsafe fn write(this: &Self, val: T) { + *this.val.get() = val; + } +} + +impl Deref for UnsafeWriteCell { + type Target = T; + + fn deref(&self) -> &Self::Target { + unsafe { &*self.val.get() } + } +} + +// safe because data races cannot happen without a write +unsafe impl Send for UnsafeWriteCell {} +unsafe impl Sync for UnsafeWriteCell {} + +/// A struct which is only initialized once and can be shared between threads. +pub struct SyncLazy T = fn() -> T> { + val: UnsafeCell>, + used: AtomicBool, + closure: F, +} + +impl T> SyncLazy { + /// Creates a new `SyncLazy` containing the specified value. + pub const fn new(closure: F) -> Self { + Self { + val: UnsafeCell::new(MaybeUninit::uninit()), + used: AtomicBool::new(false), + closure, + } + } + + /// Gets a reference to the value in the `SyncLazy`. + /// + /// Cannot be called as `lazy.get()` to prevent interference with a method on `T` through [`Deref`]. + pub fn get(this: &Self) -> &T { + let val = unsafe { &mut *this.val.get() }; + + if this + .used + .compare_exchange(false, true, Ordering::Acquire, Ordering::Acquire) + .is_ok() + { + val.write((this.closure)()); + // i think this is the right way to ensure that the write above never causes a data race? + this.used.store(true, Ordering::Release); + } + + unsafe { val.assume_init_ref() } + } +} + +impl T> Deref for SyncLazy { + type Target = T; + + fn deref(&self) -> &Self::Target { + Self::get(self) + } +} + +// safe cause we use AtomicBool to make sure that we only write once +unsafe impl T> Send for SyncLazy {} +unsafe impl T> Sync for SyncLazy {} + +#[macro_export] +macro_rules! lazy_static { + ($($vis:vis static ref $ident:ident : $ty:ty = $expr:expr;)+) => { + $( + $vis static $ident: $crate::sync::SyncLazy<$ty> = $crate::sync::SyncLazy::new(|| $expr); + )+ + }; +} diff --git a/src/vga_text.rs b/src/vga_text.rs new file mode 100644 index 0000000..8a15244 --- /dev/null +++ b/src/vga_text.rs @@ -0,0 +1,97 @@ +use crate::lazy_static; +use core::fmt::{Arguments, Write}; +use spin::Mutex; +use x86_64::instructions::interrupts::without_interrupts; + +#[repr(u8)] +pub enum Color { + Black = 0x0, + Blue = 0x1, + Green = 0x2, + Cyan = 0x3, + Red = 0x4, + Magenta = 0x5, + Brown = 0x6, + Gray = 0x7, +} + +const VGA_COLUMNS: usize = 80; +const VGA_ROWS: usize = 25; + +#[repr(transparent)] +struct Buffer([[(u8, u8); VGA_COLUMNS]; VGA_ROWS]); + +pub struct VgaWriter { + buf: &'static mut Buffer, + row: usize, + col: usize, +} + +impl VgaWriter { + fn write_byte(&mut self, c: u8) { + self.buf.0[self.row][self.col] = (c, 0xb); + self.col += 1; + if self.col == VGA_COLUMNS { + self.new_line() + } + } + + fn new_line(&mut self) { + if self.row == VGA_ROWS - 1 { + for i in 0..(VGA_ROWS - 1) { + self.buf.0[i] = self.buf.0[i + 1]; + } + self.buf.0[VGA_ROWS - 1] = [(0, 0); VGA_COLUMNS]; + } else { + self.row += 1; + } + self.col = 0; + } +} + +impl Write for VgaWriter { + fn write_str(&mut self, s: &str) -> core::fmt::Result { + for byte in s.bytes() { + if byte == b'\n' { + self.new_line(); + } else { + self.write_byte(byte); + } + } + + Ok(()) + } +} + +lazy_static! { + static ref VGA_TEXT: Mutex = { + Mutex::new(VgaWriter { + buf: unsafe { &mut *(0xb8000 as *mut Buffer) }, + row: 0, + col: 0, + }) + }; +} + +pub fn _print(args: Arguments) { + without_interrupts(|| { + write!(VGA_TEXT.lock(), "{}", args).unwrap(); + }) +} + +#[macro_export] +macro_rules! print { + ($($t:tt)+) => { + $crate::vga_text::_print(::core::format_args!($($t)+)) + }; +} + +#[macro_export] +macro_rules! println { + () => { + $crate::print!("\n") + }; + ($($t:tt)+) => { + $crate::print!("{}\n", ::core::format_args!($($t)+)) + }; +} diff --git a/target.json b/target.json new file mode 100644 index 0000000..346fefd --- /dev/null +++ b/target.json @@ -0,0 +1,15 @@ +{ + "llvm-target": "x86_64-unknown-none", + "data-layout": "e-m:e-i64:64-f80:128-n8:16:32:64-S128", + "arch": "x86_64", + "target-endian": "little", + "target-pointer-width": "64", + "target-c-int-width": "32", + "os": "none", + "executables": true, + "linker-flavor": "ld.lld", + "linker": "rust-lld", + "panic-strategy": "abort", + "disable-redzone": true, + "features": "-mmx,-sse,+soft-float" +} \ No newline at end of file