can someone help me fix my code pls

This commit is contained in:
missing 2022-05-02 09:16:27 -05:00 committed by missing
parent 9e136b0c02
commit c68fdd7dd2
15 changed files with 1314 additions and 0 deletions

9
.cargo/config.toml Normal file
View 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
View 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
View 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
View File

@ -0,0 +1 @@
nightly

58
src/gdt.rs Normal file
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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"
}