Initial commit
This commit is contained in:
parent
9e136b0c02
commit
d6ce266fa4
9
.cargo/config.toml
Normal file
9
.cargo/config.toml
Normal file
|
@ -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"
|
108
Cargo.lock
generated
Normal file
108
Cargo.lock
generated
Normal file
|
@ -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",
|
||||
]
|
27
Cargo.toml
Normal file
27
Cargo.toml
Normal file
|
@ -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"]
|
1
rust-toolchain
Normal file
1
rust-toolchain
Normal file
|
@ -0,0 +1 @@
|
|||
nightly
|
58
src/gdt.rs
Normal file
58
src/gdt.rs
Normal file
|
@ -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);
|
||||
}
|
||||
}
|
39
src/heap.rs
Normal file
39
src/heap.rs
Normal file
|
@ -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);
|
||||
}
|
||||
}
|
182
src/interrupts.rs
Normal file
182
src/interrupts.rs
Normal file
|
@ -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<Keyboard<layouts::Us104Key, ScancodeSet1>> = {
|
||||
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<ChainedPics> = Mutex::new(unsafe { ChainedPics::new(PIC_1_OFFSET, PIC_2_OFFSET) });
|
297
src/keyboard.rs
Normal file
297
src/keyboard.rs
Normal file
|
@ -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
|
||||
// */
|
||||
// }
|
37
src/lib.rs
Normal file
37
src/lib.rs
Normal file
|
@ -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);
|
||||
}
|
||||
}
|
94
src/main.rs
Normal file
94
src/main.rs
Normal file
|
@ -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();
|
||||
}
|
||||
}
|
85
src/mem.rs
Normal file
85
src/mem.rs
Normal file
|
@ -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<Option<OffsetPageTable>> = Mutex::new(None);
|
||||
static ALLOCATOR: Mutex<Option<Allocator>> = 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<PhysAddr> {
|
||||
MAPPER.lock().as_ref().unwrap().translate_addr(addr)
|
||||
}
|
||||
|
||||
pub fn map(
|
||||
page: Page,
|
||||
frame: PhysFrame,
|
||||
flags: PageTableFlags,
|
||||
) -> Result<MapperFlush<Size4KiB>, MapToError<Size4KiB>> {
|
||||
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<MapperFlush<Size4KiB>, MapToError<Size4KiB>> {
|
||||
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<Size4KiB> for Allocator {
|
||||
fn allocate_frame(&mut self) -> Option<PhysFrame<Size4KiB>> {
|
||||
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())
|
||||
}
|
||||
}
|
164
src/proc.rs
Normal file
164
src/proc.rs
Normal file
|
@ -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<u64> = 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::<ProcListElement>()) 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!()
|
||||
// }
|
101
src/sync.rs
Normal file
101
src/sync.rs
Normal file
|
@ -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<T> {
|
||||
val: UnsafeCell<T>,
|
||||
}
|
||||
|
||||
impl<T> UnsafeWriteCell<T> {
|
||||
/// 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<T> Deref for UnsafeWriteCell<T> {
|
||||
type Target = T;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
unsafe { &*self.val.get() }
|
||||
}
|
||||
}
|
||||
|
||||
// safe because data races cannot happen without a write
|
||||
unsafe impl<T> Send for UnsafeWriteCell<T> {}
|
||||
unsafe impl<T> Sync for UnsafeWriteCell<T> {}
|
||||
|
||||
/// A struct which is only initialized once and can be shared between threads.
|
||||
pub struct SyncLazy<T, F: Fn() -> T = fn() -> T> {
|
||||
val: UnsafeCell<MaybeUninit<T>>,
|
||||
used: AtomicBool,
|
||||
closure: F,
|
||||
}
|
||||
|
||||
impl<T, F: Fn() -> T> SyncLazy<T, F> {
|
||||
/// 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, F: Fn() -> T> Deref for SyncLazy<T, F> {
|
||||
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, F: Send + Fn() -> T> Send for SyncLazy<T, F> {}
|
||||
unsafe impl<T: Sync, F: Sync + Fn() -> T> Sync for SyncLazy<T, F> {}
|
||||
|
||||
#[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);
|
||||
)+
|
||||
};
|
||||
}
|
97
src/vga_text.rs
Normal file
97
src/vga_text.rs
Normal file
|
@ -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<VgaWriter> = {
|
||||
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)+))
|
||||
};
|
||||
}
|
15
target.json
Normal file
15
target.json
Normal file
|
@ -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"
|
||||
}
|
Loading…
Reference in a new issue