refactor: large code cleanup and safety pass

This commit is contained in:
Zoe
2026-09-07 13:31:49 -05:00
parent 3308fd2959
commit bf1a81cf82
20 changed files with 1026 additions and 388 deletions
+1 -1
View File
@@ -137,7 +137,7 @@ compile-binaries:
ovmf-x86_64: ovmf-x86_64:
mkdir -p ovmf/ovmf-x86_64 mkdir -p ovmf/ovmf-x86_64
@if [ ! -d "ovmf/ovmf-x86_64/OVMF.fd" ]; then \ @if [ ! -f "ovmf/ovmf-x86_64/OVMF.fd" ]; then \
cd ovmf/ovmf-x86_64 && curl -Lo OVMF.fd https://retrage.github.io/edk2-nightly/bin/RELEASEX64_OVMF.fd; \ cd ovmf/ovmf-x86_64 && curl -Lo OVMF.fd https://retrage.github.io/edk2-nightly/bin/RELEASEX64_OVMF.fd; \
fi fi
+29 -24
View File
@@ -1,35 +1,40 @@
use crate::arch::{ use super::idt::{self, InterruptFrame, stub_no_err};
apic, timer, use crate::arch::{apic, timer};
x86_64::interrupts::idt::{self, InterruptStackFrame},
};
pub const PIT_CALIBRATION_VECTOR: u8 = 0xF1; pub const PIT_CALIBRATION_VECTOR: u8 = 0xF1;
pub const APIC_TIMER_VECTOR: u8 = 0xFD; pub const APIC_TIMER_VECTOR: u8 = 0xFD;
pub const APIC_ERROR_VECTOR: u8 = 0xFE; pub const APIC_ERROR_VECTOR: u8 = 0xFE;
pub const APIC_SPURIOUS_VECTOR: u8 = 0xFF; pub const APIC_SPURIOUS_VECTOR: u8 = 0xFF;
extern "x86-interrupt" fn error_handler(_frame: InterruptStackFrame) { stub_no_err!(stub_pit_calibration, 0xF1);
apic::record_error(); stub_no_err!(stub_apic_timer, 0xFD);
apic::end_of_interrupt(); stub_no_err!(stub_apic_error, 0xFE);
} stub_no_err!(stub_apic_spurious, 0xFF);
extern "x86-interrupt" fn timer_handler(_frame: InterruptStackFrame) { pub(super) fn handle(frame: &mut InterruptFrame) {
apic::record_timer(); match frame.vector as u8 {
apic::end_of_interrupt(); PIT_CALIBRATION_VECTOR => {
} timer::record_pit_calibration();
apic::end_of_interrupt();
extern "x86-interrupt" fn pit_calibration_handler(_frame: InterruptStackFrame) { }
timer::record_pit_calibration(); APIC_TIMER_VECTOR => {
apic::end_of_interrupt(); apic::record_timer();
} apic::end_of_interrupt();
}
extern "x86-interrupt" fn spurious_handler(_frame: InterruptStackFrame) { APIC_ERROR_VECTOR => {
// No EOI apic::record_error();
apic::end_of_interrupt();
}
APIC_SPURIOUS_VECTOR => {
// No EOI
}
_ => {}
}
} }
pub(super) fn install(idt: &mut idt::Idt) { pub(super) fn install(idt: &mut idt::Idt) {
idt.set_handler(PIT_CALIBRATION_VECTOR, pit_calibration_handler, 0); idt.set_handler(PIT_CALIBRATION_VECTOR, stub_pit_calibration, 0);
idt.set_handler(APIC_ERROR_VECTOR, error_handler, 0); idt.set_handler(APIC_ERROR_VECTOR, stub_apic_error, 0);
idt.set_handler(APIC_TIMER_VECTOR, timer_handler, 0); idt.set_handler(APIC_TIMER_VECTOR, stub_apic_timer, 0);
idt.set_handler(APIC_SPURIOUS_VECTOR, spurious_handler, 0); idt.set_handler(APIC_SPURIOUS_VECTOR, stub_apic_spurious, 0);
} }
+72 -64
View File
@@ -1,79 +1,87 @@
use core::arch::asm; use core::arch::asm;
use super::idt::{self, InterruptStackFrame}; use super::idt::{self, InterruptFrame, InterruptStackFrame, stub_err, stub_no_err};
use crate::{hcf, println}; use crate::{hcf, println};
macro_rules! fatal_without_error_code { stub_no_err!(stub_divide_error, 0);
($handler:ident, $name:literal) => { stub_no_err!(stub_debug, 1);
extern "x86-interrupt" fn $handler(frame: InterruptStackFrame) { stub_no_err!(stub_non_maskable_interrupt, 2);
fatal_exception($name, &frame, None); stub_no_err!(stub_breakpoint, 3);
stub_no_err!(stub_invalid_opcode, 6);
stub_no_err!(stub_device_not_available, 7);
stub_err!(stub_double_fault, 8);
stub_err!(stub_invalid_tss, 10);
stub_err!(stub_segment_not_present, 11);
stub_err!(stub_stack_segment_fault, 12);
stub_err!(stub_general_protection, 13);
stub_err!(stub_page_fault, 14);
stub_no_err!(stub_x87_floating_point, 16);
stub_err!(stub_alignment_check, 17);
stub_no_err!(stub_machine_check, 18);
stub_no_err!(stub_simd_floating_point, 19);
stub_no_err!(stub_user_test_exit, 0x80);
pub(super) fn handle(frame: &mut InterruptFrame) {
match frame.vector as u8 {
0 => fatal_exception("DIVIDE ERROR", &frame.stack_frame, None),
1 => fatal_exception("DEBUG EXCEPTION", &frame.stack_frame, None),
2 => fatal_exception("NON-MASKABLE INTERRUPT", &frame.stack_frame, None),
3 => report_exception("BREAKPOINT", &frame.stack_frame, None),
6 => fatal_exception("INVALID OPCODE", &frame.stack_frame, None),
7 => fatal_exception("DEVICE NOT AVAILABLE", &frame.stack_frame, None),
8 => fatal_exception("DOUBLE FAULT", &frame.stack_frame, Some(frame.error_code)),
10 => fatal_exception("INVALID TSS", &frame.stack_frame, Some(frame.error_code)),
11 => fatal_exception("SEGMENT NOT PRESENT", &frame.stack_frame, Some(frame.error_code)),
12 => fatal_exception("STACK-SEGMENT FAULT", &frame.stack_frame, Some(frame.error_code)),
13 => fatal_exception(
"GENERAL PROTECTION FAULT",
&frame.stack_frame,
Some(frame.error_code),
),
14 => {
report_exception("PAGE FAULT", &frame.stack_frame, Some(frame.error_code));
println!("Faulting address: {:#X}", read_cr2());
print_page_fault_error(frame.error_code);
hcf();
} }
}; 16 => fatal_exception("X87 FLOATING-POINT EXCEPTION", &frame.stack_frame, None),
} 17 => fatal_exception("ALIGNMENT CHECK", &frame.stack_frame, Some(frame.error_code)),
18 => fatal_exception("MACHINE CHECK", &frame.stack_frame, None),
19 => fatal_exception("SIMD FLOATING-POINT EXCEPTION", &frame.stack_frame, None),
0x80 => {
if frame.stack_frame.code_segment & 0b11 != 3 {
panic!("user_test_exit_handler called from kernel");
}
macro_rules! fatal_with_error_code { println!("User test exit");
($handler:ident, $name:literal) => { hcf();
extern "x86-interrupt" fn $handler(frame: InterruptStackFrame, error_code: u64) { }
fatal_exception($name, &frame, Some(error_code)); _ => {
println!("Unhandled exception vector: {:#X}", frame.vector);
hcf();
} }
};
}
extern "x86-interrupt" fn breakpoint_handler(frame: InterruptStackFrame) {
report_exception("BREAKPOINT", &frame, None);
}
extern "x86-interrupt" fn page_fault_handler(frame: InterruptStackFrame, error_code: u64) {
report_exception("PAGE FAULT", &frame, Some(error_code));
println!("Faulting address: {:#X}", read_cr2());
print_page_fault_error(error_code);
hcf();
}
fatal_without_error_code!(divide_error_handler, "DIVIDE ERROR");
fatal_without_error_code!(debug_handler, "DEBUG EXCEPTION");
fatal_without_error_code!(non_maskable_interrupt_handler, "NON-MASKABLE INTERRUPT");
fatal_without_error_code!(invalid_opcode_handler, "INVALID OPCODE");
fatal_without_error_code!(device_not_available_handler, "DEVICE NOT AVAILABLE");
fatal_without_error_code!(x87_floating_point_handler, "X87 FLOATING-POINT EXCEPTION");
fatal_without_error_code!(machine_check_handler, "MACHINE CHECK");
fatal_without_error_code!(simd_floating_point_handler, "SIMD FLOATING-POINT EXCEPTION");
fatal_with_error_code!(double_fault_handler, "DOUBLE FAULT");
fatal_with_error_code!(invalid_tss_handler, "INVALID TSS");
fatal_with_error_code!(segment_not_present_handler, "SEGMENT NOT PRESENT");
fatal_with_error_code!(stack_segment_fault_handler, "STACK-SEGMENT FAULT");
fatal_with_error_code!(general_protection_handler, "GENERAL PROTECTION FAULT");
fatal_with_error_code!(alignment_check_handler, "ALIGNMENT CHECK");
extern "x86-interrupt" fn user_test_exit_handler(frame: InterruptStackFrame) {
if frame.code_segment & 0b11 != 3 {
panic!("user_test_exit_handler called from kernel");
} }
println!("User test exit");
hcf();
} }
pub(super) fn install(idt: &mut idt::Idt) { pub(super) fn install(idt: &mut idt::Idt) {
idt.set_handler(0, divide_error_handler, 0); idt.set_handler(0, stub_divide_error, 0);
idt.set_handler(1, debug_handler, 0); idt.set_handler(1, stub_debug, 0);
idt.set_handler(2, non_maskable_interrupt_handler, 0); idt.set_handler(2, stub_non_maskable_interrupt, 0);
idt.set_user_handler(3, breakpoint_handler, 0); idt.set_user_handler(3, stub_breakpoint, 0);
idt.set_handler(6, invalid_opcode_handler, 0); idt.set_handler(6, stub_invalid_opcode, 0);
idt.set_handler(7, device_not_available_handler, 0); idt.set_handler(7, stub_device_not_available, 0);
idt.set_error_code_handler(8, double_fault_handler, 1); idt.set_handler(8, stub_double_fault, 1);
idt.set_error_code_handler(10, invalid_tss_handler, 0); idt.set_handler(10, stub_invalid_tss, 0);
idt.set_error_code_handler(11, segment_not_present_handler, 0); idt.set_handler(11, stub_segment_not_present, 0);
idt.set_error_code_handler(12, stack_segment_fault_handler, 0); idt.set_handler(12, stub_stack_segment_fault, 0);
idt.set_error_code_handler(13, general_protection_handler, 0); idt.set_handler(13, stub_general_protection, 0);
idt.set_error_code_handler(14, page_fault_handler, 0); idt.set_handler(14, stub_page_fault, 0);
idt.set_handler(16, x87_floating_point_handler, 0); idt.set_handler(16, stub_x87_floating_point, 0);
idt.set_error_code_handler(17, alignment_check_handler, 0); idt.set_handler(17, stub_alignment_check, 0);
idt.set_handler(18, machine_check_handler, 0); idt.set_handler(18, stub_machine_check, 0);
idt.set_handler(19, simd_floating_point_handler, 0); idt.set_handler(19, stub_simd_floating_point, 0);
idt.set_user_handler(0x80, user_test_exit_handler, 0); idt.set_user_handler(0x80, stub_user_test_exit, 0);
} }
fn read_cr2() -> u64 { fn read_cr2() -> u64 {
+134 -17
View File
@@ -18,7 +18,7 @@ struct IdtEntry {
impl IdtEntry { impl IdtEntry {
const fn missing() -> Self { const fn missing() -> Self {
return Self { Self {
offset_low: 0, offset_low: 0,
code_selector: 0, code_selector: 0,
ist: 0, ist: 0,
@@ -26,7 +26,7 @@ impl IdtEntry {
offset_middle: 0, offset_middle: 0,
offset_high: 0, offset_high: 0,
reserved: 0, reserved: 0,
}; }
} }
} }
@@ -37,8 +37,8 @@ struct IdtPointer {
} }
#[repr(C)] #[repr(C)]
#[derive(Clone, Copy)] #[derive(Clone, Copy, Debug)]
pub(super) struct InterruptStackFrame { pub struct InterruptStackFrame {
pub instruction_pointer: VirtualAddr, pub instruction_pointer: VirtualAddr,
pub code_segment: u64, pub code_segment: u64,
pub cpu_flags: u64, pub cpu_flags: u64,
@@ -46,14 +46,36 @@ pub(super) struct InterruptStackFrame {
pub stack_segment: u64, pub stack_segment: u64,
} }
#[repr(C)]
#[derive(Debug)]
pub struct InterruptFrame {
pub rax: u64,
pub rcx: u64,
pub rdx: u64,
pub rsi: u64,
pub rdi: u64,
pub r8: u64,
pub r9: u64,
pub r10: u64,
pub r11: u64,
pub rbx: u64,
pub rbp: u64,
pub r12: u64,
pub r13: u64,
pub r14: u64,
pub r15: u64,
pub vector: u64,
pub error_code: u64,
pub stack_frame: InterruptStackFrame,
}
const INTERRUPT_GATE: u8 = 0b1110; const INTERRUPT_GATE: u8 = 0b1110;
const PRESENT: u8 = 1 << 7; const PRESENT: u8 = 1 << 7;
const KERNEL_INTERRUPT_GATE: u8 = PRESENT | INTERRUPT_GATE; const KERNEL_INTERRUPT_GATE: u8 = PRESENT | INTERRUPT_GATE;
const USER_DPL: u8 = 3 << 5; const USER_DPL: u8 = 3 << 5;
const USER_INTERRUPT_GATE: u8 = PRESENT | USER_DPL | INTERRUPT_GATE; const USER_INTERRUPT_GATE: u8 = PRESENT | USER_DPL | INTERRUPT_GATE;
pub(super) type Handler = extern "x86-interrupt" fn(InterruptStackFrame); pub(super) type RawHandler = unsafe extern "C" fn();
pub(super) type ErrorCodeHandler = extern "x86-interrupt" fn(InterruptStackFrame, u64);
pub(super) struct Idt { pub(super) struct Idt {
entries: [IdtEntry; 256], entries: [IdtEntry; 256],
@@ -66,20 +88,11 @@ impl Idt {
} }
} }
pub(super) fn set_handler(&mut self, vector: u8, handler: Handler, ist: u8) { pub(super) fn set_handler(&mut self, vector: u8, handler: RawHandler, ist: u8) {
self.set_handler_address(vector, handler as usize, ist, KERNEL_INTERRUPT_GATE); self.set_handler_address(vector, handler as usize, ist, KERNEL_INTERRUPT_GATE);
} }
pub(super) fn set_error_code_handler( pub(super) fn set_user_handler(&mut self, vector: u8, handler: RawHandler, ist: u8) {
&mut self,
vector: u8,
handler: ErrorCodeHandler,
ist: u8,
) {
self.set_handler_address(vector, handler as usize, ist, KERNEL_INTERRUPT_GATE);
}
pub(super) fn set_user_handler(&mut self, vector: u8, handler: Handler, ist: u8) {
self.set_handler_address(vector, handler as usize, ist, USER_INTERRUPT_GATE); self.set_handler_address(vector, handler as usize, ist, USER_INTERRUPT_GATE);
} }
@@ -101,6 +114,110 @@ static mut IDT: Idt = Idt::new();
const _: () = assert!(core::mem::size_of::<IdtEntry>() == 16); const _: () = assert!(core::mem::size_of::<IdtEntry>() == 16);
const _: () = assert!(core::mem::size_of::<IdtPointer>() == 10); const _: () = assert!(core::mem::size_of::<IdtPointer>() == 10);
const _: () = assert!(core::mem::size_of::<InterruptStackFrame>() == 40); const _: () = assert!(core::mem::size_of::<InterruptStackFrame>() == 40);
const _: () = assert!(core::mem::size_of::<InterruptFrame>() == 176);
const _: () = assert!(core::mem::offset_of!(InterruptFrame, stack_frame) == 136);
#[unsafe(naked)]
pub(super) unsafe extern "C" fn interrupt_common() {
core::arch::naked_asm!(
"push r15",
"push r14",
"push r13",
"push r12",
"push rbp",
"push rbx",
"push r11",
"push r10",
"push r9",
"push r8",
"push rdi",
"push rsi",
"push rdx",
"push rcx",
"push rax",
// Check CS: bit 0 and 1 are CPL. If CPL != 0 (user mode), swapgs
"test byte ptr [rsp + 144], 3",
"jz 1f",
"swapgs",
"1:",
"mov rdi, rsp",
"cld",
"call {dispatch}",
// Check CS: if returning to user mode, swapgs
"test byte ptr [rsp + 144], 3",
"jz 2f",
"swapgs",
"2:",
"pop rax",
"pop rcx",
"pop rdx",
"pop rsi",
"pop rdi",
"pop r8",
"pop r9",
"pop r10",
"pop r11",
"pop rbx",
"pop rbp",
"pop r12",
"pop r13",
"pop r14",
"pop r15",
"add rsp, 16",
"iretq",
dispatch = sym interrupt_dispatch,
);
}
extern "C" fn interrupt_dispatch(frame: &mut InterruptFrame) {
let vector = frame.vector as u8;
match vector {
0..=31 | 0x80 => exceptions::handle(frame),
apic_vectors::PIT_CALIBRATION_VECTOR
| apic_vectors::APIC_TIMER_VECTOR
| apic_vectors::APIC_ERROR_VECTOR
| apic_vectors::APIC_SPURIOUS_VECTOR => apic_vectors::handle(frame),
_ => {
crate::println!("Unhandled interrupt vector: {:#X}", vector);
}
}
}
macro_rules! stub_no_err {
($name:ident, $vec:literal) => {
#[unsafe(naked)]
pub(super) unsafe extern "C" fn $name() {
core::arch::naked_asm!(
"push 0",
concat!("push ", stringify!($vec)),
"jmp {common}",
common = sym $crate::arch::x86_64::interrupts::idt::interrupt_common,
);
}
};
}
macro_rules! stub_err {
($name:ident, $vec:literal) => {
#[unsafe(naked)]
pub(super) unsafe extern "C" fn $name() {
core::arch::naked_asm!(
concat!("push ", stringify!($vec)),
"jmp {common}",
common = sym $crate::arch::x86_64::interrupts::idt::interrupt_common,
);
}
};
}
pub(super) use stub_err;
pub(super) use stub_no_err;
pub fn idt_init() { pub fn idt_init() {
let mut idt = Idt::new(); let mut idt = Idt::new();
+63 -1
View File
@@ -4,7 +4,7 @@ use crate::{
arch::x86_64::cpu::CpuFeatures, arch::x86_64::cpu::CpuFeatures,
memory::{ memory::{
CachePolicy, DirectMap, FrameAddr, FrameAllocator, OwnedFrame, PagePermissions, CachePolicy, DirectMap, FrameAddr, FrameAllocator, OwnedFrame, PagePermissions,
PhysicalAddr, VirtualAddr, PageTableMapping, PhysicalAddr, VirtualAddr,
}, },
}; };
@@ -199,6 +199,14 @@ impl PageTableEntry {
self.0 & Self::PRESENT != 0 self.0 & Self::PRESENT != 0
} }
fn writable(&self) -> bool {
self.0 & Self::WRITABLE != 0
}
fn executable(&self) -> bool {
self.0 & Self::NX == 0
}
fn is_user_accessible(&self) -> bool { fn is_user_accessible(&self) -> bool {
self.0 & Self::USER_ACCESSIBLE != 0 self.0 & Self::USER_ACCESSIBLE != 0
} }
@@ -226,6 +234,14 @@ impl PageTableEntry {
FrameAddr::from_start_address(self.physical_address(config)) FrameAddr::from_start_address(self.physical_address(config))
} }
fn permissions(&self) -> PagePermissions {
PagePermissions::new(
self.writable(),
self.executable(),
self.is_user_accessible(),
)
}
} }
pub(crate) enum MapError { pub(crate) enum MapError {
@@ -363,6 +379,52 @@ impl PageTable {
self.direct_map.translate(addr) self.direct_map.translate(addr)
} }
pub fn mapping(&self, virtual_addr: VirtualAddr) -> Option<PageTableMapping> {
let address = virtual_addr.as_usize();
if !self.is_canonical(address) {
return None;
}
let mut table_frame = self.frame.frame_address();
let mut permissions = PagePermissions::new(true, true, true);
for &level in self.config.mode.intermediate_levels() {
let table = self.table(table_frame)?;
let entry = table[level.index(address)];
if !entry.is_present() {
return None;
}
let entry_permissions = entry.permissions();
permissions.writable &= entry_permissions.writable;
permissions.user_accessible &= entry_permissions.user_accessible;
permissions.executable &= entry_permissions.executable;
if entry.is_huge() {
level.large_page_size()?;
return Some(PageTableMapping { permissions });
}
table_frame = entry.table_frame(self.config)?;
}
let page_table = self.table(table_frame)?;
let entry = page_table[p1_index(address)];
if !entry.is_present() {
return None;
}
let entry_permissions = entry.permissions();
permissions.writable &= entry_permissions.writable;
permissions.user_accessible &= entry_permissions.user_accessible;
permissions.executable &= entry_permissions.executable;
Some(PageTableMapping { permissions })
}
fn get_next_level( fn get_next_level(
&self, &self,
parent: FrameAddr, parent: FrameAddr,
+4
View File
@@ -38,6 +38,10 @@ pub fn find_file<'a>(archive: &'a [u8], target: &str) -> Option<&'a [u8]> {
let mut offset = 0; let mut offset = 0;
while offset + core::mem::size_of::<Header>() <= archive.len() { while offset + core::mem::size_of::<Header>() <= archive.len() {
if offset + core::mem::size_of::<Header>() > archive.len() {
return None;
}
let header = Header::from_bytes(&archive[offset..])?; let header = Header::from_bytes(&archive[offset..])?;
let header_start = offset; let header_start = offset;
offset += core::mem::size_of::<Header>(); offset += core::mem::size_of::<Header>();
-1
View File
@@ -1,4 +1,3 @@
#![feature(abi_x86_interrupt)]
#![allow(clippy::needless_return)] #![allow(clippy::needless_return)]
#![no_std] #![no_std]
#![no_main] #![no_main]
+29 -15
View File
@@ -8,6 +8,10 @@ use crate::{
}, },
}; };
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
#[repr(transparent)]
pub struct AddressSpaceId(usize);
const MAX_ADDRESS_SPACES: usize = 32; const MAX_ADDRESS_SPACES: usize = 32;
struct AddressSpaceTable { struct AddressSpaceTable {
entries: [Option<AddressSpace>; MAX_ADDRESS_SPACES], entries: [Option<AddressSpace>; MAX_ADDRESS_SPACES],
@@ -20,27 +24,27 @@ impl AddressSpaceTable {
} }
} }
fn insert(&mut self, address_space: AddressSpace) -> Option<usize> { fn insert(&mut self, address_space: AddressSpace) -> Result<AddressSpaceId, AddressSpace> {
for (i, slot) in self.entries.iter_mut().enumerate() { for (i, slot) in self.entries.iter_mut().enumerate() {
if slot.is_none() { if slot.is_none() {
*slot = Some(address_space); *slot = Some(address_space);
return Some(i); return Ok(AddressSpaceId(i));
} }
} }
None Err(address_space)
} }
fn get(&self, id: usize) -> Option<&AddressSpace> { fn get(&self, id: AddressSpaceId) -> Option<&AddressSpace> {
self.entries.get(id).and_then(Option::as_ref) self.entries.get(id.0).and_then(Option::as_ref)
} }
fn get_mut(&mut self, id: usize) -> Option<&mut AddressSpace> { fn get_mut(&mut self, id: AddressSpaceId) -> Option<&mut AddressSpace> {
self.entries.get_mut(id).and_then(Option::as_mut) self.entries.get_mut(id.0).and_then(Option::as_mut)
} }
fn remove(&mut self, id: usize) -> Option<AddressSpace> { fn remove(&mut self, id: AddressSpaceId) -> Option<AddressSpace> {
self.entries.get_mut(id).and_then(Option::take) self.entries.get_mut(id.0).and_then(Option::take)
} }
} }
@@ -51,20 +55,19 @@ unsafe impl Sync for GlobalAddressSpaceTable {}
static ADDRESS_SPACE_TABLE: GlobalAddressSpaceTable = static ADDRESS_SPACE_TABLE: GlobalAddressSpaceTable =
GlobalAddressSpaceTable(UnsafeCell::new(AddressSpaceTable::new())); GlobalAddressSpaceTable(UnsafeCell::new(AddressSpaceTable::new()));
pub fn insert_address_space(address_space: AddressSpace) -> Option<usize> { pub fn insert_address_space(address_space: AddressSpace) -> Result<AddressSpaceId, AddressSpace> {
let table = unsafe { &mut *ADDRESS_SPACE_TABLE.0.get() }; let table = unsafe { &mut *ADDRESS_SPACE_TABLE.0.get() };
let id = table.insert(address_space); table.insert(address_space)
id
} }
pub fn remove_address_space(id: usize) -> Option<AddressSpace> { pub fn remove_address_space(id: AddressSpaceId) -> Option<AddressSpace> {
let table = unsafe { &mut *ADDRESS_SPACE_TABLE.0.get() }; let table = unsafe { &mut *ADDRESS_SPACE_TABLE.0.get() };
table.remove(id) table.remove(id)
} }
pub fn with_address_space<R>(id: usize, f: impl FnOnce(&AddressSpace) -> R) -> Option<R> { pub fn with_address_space<R>(id: AddressSpaceId, f: impl FnOnce(&AddressSpace) -> R) -> Option<R> {
let interrupt_state = crate::arch::disable_interrupts_and_save(); let interrupt_state = crate::arch::disable_interrupts_and_save();
let table = unsafe { &*ADDRESS_SPACE_TABLE.0.get() }; let table = unsafe { &*ADDRESS_SPACE_TABLE.0.get() };
let res = table.get(id).map(f); let res = table.get(id).map(f);
@@ -72,7 +75,10 @@ pub fn with_address_space<R>(id: usize, f: impl FnOnce(&AddressSpace) -> R) -> O
res res
} }
pub fn with_address_space_mut<R>(id: usize, f: impl FnOnce(&mut AddressSpace) -> R) -> Option<R> { pub fn with_address_space_mut<R>(
id: AddressSpaceId,
f: impl FnOnce(&mut AddressSpace) -> R,
) -> Option<R> {
let interrupt_state = crate::arch::disable_interrupts_and_save(); let interrupt_state = crate::arch::disable_interrupts_and_save();
let table = unsafe { &mut *ADDRESS_SPACE_TABLE.0.get() }; let table = unsafe { &mut *ADDRESS_SPACE_TABLE.0.get() };
let res = table.get_mut(id).map(f); let res = table.get_mut(id).map(f);
@@ -183,6 +189,10 @@ impl From<PageTableCreateError> for AddressSpaceCreateError {
} }
} }
pub struct PageTableMapping {
pub permissions: PagePermissions,
}
#[derive(PartialEq, Eq)] #[derive(PartialEq, Eq)]
enum AddressSpaceKind { enum AddressSpaceKind {
Kernel, Kernel,
@@ -427,6 +437,10 @@ impl AddressSpace {
self.root.to_virtual(physical_addr) self.root.to_virtual(physical_addr)
} }
pub fn mapping(&self, virtual_addr: VirtualAddr) -> Option<PageTableMapping> {
self.root.mapping(virtual_addr)
}
pub unsafe fn activate(&self) { pub unsafe fn activate(&self) {
unsafe { self.root.activate() } unsafe { self.root.activate() }
} }
+3 -3
View File
@@ -7,9 +7,9 @@ use core::ops::Add;
#[allow(unused)] #[allow(unused)]
pub use address_space::{ pub use address_space::{
AddressSpace, AddressSpaceCreateError, MapError, UnmapError, init_kernel_address_space, AddressSpace, AddressSpaceCreateError, AddressSpaceId, MapError, PageTableMapping, UnmapError,
insert_address_space, remove_address_space, with_address_space, with_address_space_mut, init_kernel_address_space, insert_address_space, remove_address_space, with_address_space,
with_kernel_address_space, with_address_space_mut, with_kernel_address_space,
}; };
pub use frame::{ pub use frame::{
FRAME_SIZE, FrameAddr, FrameAllocator, OwnedFrame, alloc_frame, dealloc_frame, FRAME_SIZE, FrameAddr, FrameAllocator, OwnedFrame, alloc_frame, dealloc_frame,
+6 -1
View File
@@ -237,8 +237,13 @@ impl KernelStackPool {
Err(StackCreateError::OutOfStacks) Err(StackCreateError::OutOfStacks)
} }
pub fn free(&mut self, stack: &KernelStack) { pub fn free(&mut self, stack: KernelStack) {
let slot = (stack.top().as_usize() - KERNEL_STACK_BASE) / KERNEL_SLOT_SIZE - 1; let slot = (stack.top().as_usize() - KERNEL_STACK_BASE) / KERNEL_SLOT_SIZE - 1;
crate::memory::with_kernel_address_space(|kernel_as| {
crate::memory::with_allocator(|allocator| unsafe {
stack.destroy(kernel_as, allocator)
});
});
self.free_slots &= !(1 << slot); self.free_slots &= !(1 << slot);
} }
} }
+58 -6
View File
@@ -1,9 +1,51 @@
use crate::{memory::VirtualAddr, syscall::Status}; use crate::{
memory::{FRAME_SIZE, VirtualAddr, address_space::AddressSpaceId, with_address_space},
syscall::Status,
};
pub const USER_SPACE_END: VirtualAddr = VirtualAddr::new(0x0000_8000_0000_0000); pub const USER_SPACE_END: VirtualAddr = VirtualAddr::new(0x0000_8000_0000_0000);
pub fn copy_from_user(src: VirtualAddr, dst: &mut [u8]) -> Result<(), Status> { pub fn validate_user_range(
// TODO: guard against unmapped pages as_id: AddressSpaceId,
start: VirtualAddr,
len: usize,
writable: bool,
) -> Result<(), Status> {
let start_addr = start.as_usize();
let end_addr = start_addr.checked_add(len).ok_or(Status::BadAddress)?;
if start_addr >= USER_SPACE_END.as_usize() || end_addr > USER_SPACE_END.as_usize() {
return Err(Status::BadAddress);
}
if len == 0 {
return Ok(());
}
let page_start = start_addr & !(FRAME_SIZE - 1);
for page in (page_start..end_addr).step_by(FRAME_SIZE) {
let is_valid = with_address_space(as_id, |address_space| {
address_space
.mapping(VirtualAddr::new(page))
.is_some_and(|mapping| {
mapping.permissions.user_accessible
&& (!writable || mapping.permissions.writable)
})
})
.ok_or(Status::BadAddress)?;
if !is_valid {
return Err(Status::BadAddress);
}
}
Ok(())
}
/// # Safety
///
/// The caller must ensure that the user address range is valid, mapped, and user-accessible (e.g. via [`validate_user_range`]).
pub unsafe fn copy_from_user(src: VirtualAddr, dst: &mut [u8]) -> Result<(), Status> {
let end = src let end = src
.as_usize() .as_usize()
.checked_add(dst.len()) .checked_add(dst.len())
@@ -19,7 +61,10 @@ pub fn copy_from_user(src: VirtualAddr, dst: &mut [u8]) -> Result<(), Status> {
Ok(()) Ok(())
} }
pub fn copy_to_user(dst: VirtualAddr, src: &[u8]) -> Result<(), Status> { /// # Safety
///
/// The caller must ensure that the user address range is valid, mapped, user-accessible, and writable (e.g. via [`validate_user_range`]).
pub unsafe fn copy_to_user(dst: VirtualAddr, src: &[u8]) -> Result<(), Status> {
let end = dst let end = dst
.as_usize() .as_usize()
.checked_add(src.len()) .checked_add(src.len())
@@ -35,7 +80,10 @@ pub fn copy_to_user(dst: VirtualAddr, src: &[u8]) -> Result<(), Status> {
Ok(()) Ok(())
} }
pub fn copy_val_to_user<T: Copy>(dst: VirtualAddr, val: &T) -> Result<(), Status> { /// # Safety
///
/// The caller must ensure that the user address is valid, mapped, user-accessible, and writable (e.g. via [`validate_user_range`]).
pub unsafe fn copy_val_to_user<T: Copy>(dst: VirtualAddr, val: &T) -> Result<(), Status> {
if dst.as_usize() % core::mem::align_of::<T>() != 0 { if dst.as_usize() % core::mem::align_of::<T>() != 0 {
return Err(Status::InvalidArgument); return Err(Status::InvalidArgument);
} }
@@ -54,7 +102,11 @@ pub fn copy_val_to_user<T: Copy>(dst: VirtualAddr, val: &T) -> Result<(), Status
Ok(()) Ok(())
} }
pub fn copy_val_from_user<T: Copy>(src: VirtualAddr) -> Result<T, Status> { /// # Safety
///
/// The caller must ensure that the user address is valid, mapped, and user-accessible (e.g. via [`validate_user_range`]).
#[allow(unused)]
pub unsafe fn copy_val_from_user<T: Copy>(src: VirtualAddr) -> Result<T, Status> {
if src.as_usize() % core::mem::align_of::<T>() != 0 { if src.as_usize() % core::mem::align_of::<T>() != 0 {
return Err(Status::InvalidArgument); return Err(Status::InvalidArgument);
} }
+10 -6
View File
@@ -2,7 +2,7 @@ mod table;
use table::*; use table::*;
#[derive(Clone, Copy, PartialEq, Eq)] #[derive(Clone, Copy, Debug, PartialEq, Eq)]
#[repr(u64)] #[repr(u64)]
pub enum Status { pub enum Status {
// Status::Success = 0 // Status::Success = 0
@@ -50,7 +50,7 @@ impl TryFrom<u64> for SyscallNumber {
} }
} }
pub fn handle(num: u64, arg0: u64, arg1: u64, arg2: u64, arg3: u64, _arg4: u64, _arg5: u64) -> u64 { pub fn handle(num: u64, arg0: u64, arg1: u64, arg2: u64, arg3: u64, arg4: u64, _arg5: u64) -> u64 {
let result = (|| -> Result<(), Status> { let result = (|| -> Result<(), Status> {
let syscall = SyscallNumber::try_from(num)?; let syscall = SyscallNumber::try_from(num)?;
match syscall { match syscall {
@@ -66,10 +66,14 @@ pub fn handle(num: u64, arg0: u64, arg1: u64, arg2: u64, arg3: u64, _arg4: u64,
SyscallNumber::FrameAlloc => sys_frame_alloc(arg0 as usize), SyscallNumber::FrameAlloc => sys_frame_alloc(arg0 as usize),
SyscallNumber::FrameDealloc => sys_frame_dealloc(arg0 as usize), SyscallNumber::FrameDealloc => sys_frame_dealloc(arg0 as usize),
SyscallNumber::AsCreate => sys_as_create(arg0 as usize), SyscallNumber::AsCreate => sys_as_create(arg0 as usize),
SyscallNumber::Map => { SyscallNumber::Map => sys_map(
sys_map(arg0 as usize, arg1 as usize, arg2 as usize, arg3 as usize) arg0 as usize,
} arg1 as usize,
SyscallNumber::Unmap => sys_unmap(arg0 as usize, arg1 as usize), arg2 as usize,
arg3 as usize,
arg4 as usize,
),
SyscallNumber::Unmap => sys_unmap(arg0 as usize),
SyscallNumber::TaskCreate => { SyscallNumber::TaskCreate => {
sys_task_create(arg0 as usize, arg1 as usize, arg2 as usize, arg3 as usize) sys_task_create(arg0 as usize, arg1 as usize, arg2 as usize, arg3 as usize)
} }
+448 -159
View File
@@ -1,10 +1,13 @@
use crate::{ use crate::{
memory::{ memory::{
FRAME_SIZE, OwnedFrame, PagePermissions, USER_SPACE_END, VirtualAddr, copy_from_user, FRAME_SIZE, MapError, PagePermissions, USER_SPACE_END, VirtualAddr, copy_from_user,
copy_to_user, copy_val_to_user, copy_to_user, copy_val_to_user, validate_user_range,
}, },
println, println,
task::tcb::{BlockReason, Handle, KernelObject, MAX_MSG_SIZE, Message, Rights}, task::{
scheduler::TaskId,
tcb::{BlockReason, Handle, KernelObject, MAX_MSG_SIZE, Message, Rights},
},
}; };
use super::Status; use super::Status;
@@ -23,32 +26,60 @@ pub fn sys_write(fd: usize, buf_ptr: usize, len: usize, out_ptr: usize) -> Resul
return Err(Status::BadFileDescriptor); return Err(Status::BadFileDescriptor);
} }
crate::task::scheduler::with_task(crate::task::scheduler::current(), |current_task| {
validate_user_range(current_task.as_id, VirtualAddr::new(buf_ptr), len, false)?;
if out_ptr != 0 {
if out_ptr % core::mem::align_of::<usize>() != 0 {
return Err(Status::InvalidArgument);
}
validate_user_range(
current_task.as_id,
VirtualAddr::new(out_ptr),
core::mem::size_of::<usize>(),
true,
)?;
}
Ok(())
})
.expect("failed to resolve self task")?;
let mut chunk = [0u8; 128]; let mut chunk = [0u8; 128];
let mut written = 0; let mut written = 0;
while written < len { while written < len {
let n = (len - written).min(chunk.len()); let n = (len - written).min(chunk.len());
copy_from_user(VirtualAddr::new(buf_ptr + written), &mut chunk[..n])?; unsafe {
copy_from_user(VirtualAddr::new(buf_ptr + written), &mut chunk[..n])?;
}
crate::debug::serial::write_bytes(&chunk[..n]); crate::debug::serial::write_bytes(&chunk[..n]);
written += n; written += n;
} }
if out_ptr != 0 { if out_ptr != 0 {
copy_val_to_user(VirtualAddr::new(out_ptr), &written)?; unsafe {
copy_val_to_user(VirtualAddr::new(out_ptr), &written)?;
}
} }
Ok(()) Ok(())
} }
pub fn sys_send(dest_task_id: usize, msg_ptr: usize, len: usize) -> Result<(), Status> { pub fn sys_send(dest_task_id: usize, msg_ptr: usize, len: usize) -> Result<(), Status> {
let dest_task_id = TaskId::new(dest_task_id);
if len > MAX_MSG_SIZE { if len > MAX_MSG_SIZE {
return Err(Status::InvalidArgument); return Err(Status::InvalidArgument);
} }
let mut msg_buf = [0u8; MAX_MSG_SIZE];
copy_from_user(VirtualAddr::new(msg_ptr), &mut msg_buf[..len])?;
let dest_task = crate::task::scheduler::get_task_mut(dest_task_id).ok_or(Status::NoSuchTask)?;
let sender = crate::task::scheduler::current(); let sender = crate::task::scheduler::current();
crate::task::scheduler::with_task(sender, |current_task| {
validate_user_range(current_task.as_id, VirtualAddr::new(msg_ptr), len, false)
})
.expect("failed to resolve self task")?;
let mut msg_buf = [0u8; MAX_MSG_SIZE];
unsafe { copy_from_user(VirtualAddr::new(msg_ptr), &mut msg_buf[..len])? };
let msg = Message { let msg = Message {
sender, sender,
@@ -56,14 +87,18 @@ pub fn sys_send(dest_task_id: usize, msg_ptr: usize, len: usize) -> Result<(), S
data: msg_buf, data: msg_buf,
}; };
if !dest_task.mailbox.push(msg) { let should_unblock = crate::task::scheduler::with_task_mut(dest_task_id, |dest_task| {
return Err(Status::OutOfMemory); if !dest_task.mailbox.push(msg) {
} return Err(Status::OutOfMemory);
}
Ok(matches!(
dest_task.state,
crate::task::tcb::ThreadState::Blocked(BlockReason::Recv)
))
})
.ok_or(Status::NoSuchTask)??;
if matches!( if should_unblock {
dest_task.state,
crate::task::tcb::ThreadState::Blocked(BlockReason::Recv { .. })
) {
crate::task::scheduler::unblock(dest_task_id); crate::task::scheduler::unblock(dest_task_id);
} }
@@ -80,28 +115,58 @@ pub fn sys_recv(
return Err(Status::InvalidArgument); return Err(Status::InvalidArgument);
} }
let current_task = crate::task::scheduler::get_task_mut(crate::task::scheduler::current()) crate::task::scheduler::with_task(crate::task::scheduler::current(), |current_task| {
.ok_or(Status::NoSuchTask)?; if current_task.mailbox.len == 0 {
crate::task::scheduler::block_current(BlockReason::Recv);
}
if current_task.mailbox.len == 0 { validate_user_range(current_task.as_id, VirtualAddr::new(out_ptr), max_len, true)?;
crate::task::scheduler::block_current(BlockReason::Recv { ep: 0 });
}
// if we blocked, we will wake up when the mailbox is non-empty if out_actual_len != 0 {
if out_actual_len % core::mem::align_of::<usize>() != 0 {
return Err(Status::InvalidArgument);
}
validate_user_range(
current_task.as_id,
VirtualAddr::new(out_actual_len),
core::mem::size_of::<usize>(),
true,
)?;
}
let current_task = crate::task::scheduler::get_task_mut(crate::task::scheduler::current()) if out_sender != 0 {
.ok_or(Status::NoSuchTask)?; if out_sender % core::mem::align_of::<usize>() != 0 {
let msg = current_task.mailbox.pop().ok_or(Status::NoSuchTask)?; return Err(Status::InvalidArgument);
}
validate_user_range(
current_task.as_id,
VirtualAddr::new(out_sender),
core::mem::size_of::<usize>(),
true,
)?;
}
copy_to_user( Ok(())
VirtualAddr::new(out_ptr), })
&msg.data[..msg.length.min(max_len)], .expect("failed to resolve self task")?;
)?;
if out_actual_len != 0 { let msg =
copy_val_to_user(VirtualAddr::new(out_actual_len), &msg.length)?; crate::task::scheduler::with_task_mut(crate::task::scheduler::current(), |current_task| {
} current_task.mailbox.pop().ok_or(Status::NoSuchTask)
if out_sender != 0 { })
copy_val_to_user(VirtualAddr::new(out_sender), &msg.sender)?; .expect("failed to resolve self task")?;
unsafe {
copy_to_user(
VirtualAddr::new(out_ptr),
&msg.data[..msg.length.min(max_len)],
)?;
if out_actual_len != 0 {
copy_val_to_user(VirtualAddr::new(out_actual_len), &msg.length)?;
}
if out_sender != 0 {
copy_val_to_user(VirtualAddr::new(out_sender), &msg.sender)?;
}
} }
Ok(()) Ok(())
@@ -112,49 +177,69 @@ pub fn sys_frame_alloc(out_handle: usize) -> Result<(), Status> {
return Err(Status::InvalidArgument); return Err(Status::InvalidArgument);
} }
if out_handle % core::mem::align_of::<usize>() != 0 {
return Err(Status::InvalidArgument);
}
crate::task::scheduler::with_task(crate::task::scheduler::current(), |current_task| {
validate_user_range(
current_task.as_id,
VirtualAddr::new(out_handle),
core::mem::size_of::<usize>(),
true,
)
})
.expect("failed to resolve self task")?;
let frame = crate::memory::alloc_frame().ok_or(Status::OutOfMemory)?; let frame = crate::memory::alloc_frame().ok_or(Status::OutOfMemory)?;
let task_id = crate::task::scheduler::current();
let task = match crate::task::scheduler::get_task_mut(task_id) {
Some(task) => task,
None => {
unsafe { crate::memory::dealloc_frame(frame) };
return Err(Status::NoSuchTask);
}
};
let frame_addr = frame.into_raw();
let handle = Handle { let handle = Handle {
object: KernelObject::Frame(frame_addr), object: KernelObject::Frame(frame),
rights: Rights::READ | Rights::WRITE | Rights::EXECUTE, rights: Rights::READ | Rights::WRITE | Rights::EXECUTE | Rights::MAP,
}; };
let handle_id = match task.handles.push(handle) { crate::task::scheduler::with_task_mut(crate::task::scheduler::current(), |current_task| {
Some(id) => id, let handle_id = match current_task.handles.push(handle) {
None => { Ok(id) => id,
unsafe { crate::memory::dealloc_frame(OwnedFrame::from_raw(frame_addr)) }; Err(handle) => {
return Err(Status::OutOfMemory); let frame = match handle.object {
} KernelObject::Frame(frame) => frame,
}; _ => unreachable!("pushed handle was not a frame"),
};
unsafe { crate::memory::dealloc_frame(frame) };
return Err(Status::OutOfMemory);
}
};
copy_val_to_user(VirtualAddr::new(out_handle), &handle_id)?; unsafe { copy_val_to_user(VirtualAddr::new(out_handle), &handle_id) }
})
Ok(()) .expect("failed to resolve self task")
} }
pub fn sys_frame_dealloc(frame_handle: usize) -> Result<(), Status> { pub fn sys_frame_dealloc(frame_handle_id: usize) -> Result<(), Status> {
let task = crate::task::scheduler::current(); crate::task::scheduler::with_task_mut(crate::task::scheduler::current(), |task| {
let task = crate::task::scheduler::get_task_mut(task).ok_or(Status::NoSuchTask)?; let frame_handle = task
.handles
.take(frame_handle_id)
.ok_or(Status::BadHandle)?;
match frame_handle.object {
KernelObject::Frame(frame_addr) => {
unsafe { crate::memory::dealloc_frame(frame_addr) };
let frame_handle = task.handles.get(frame_handle).ok_or(Status::BadHandle)?; Ok(())
let frame = match frame_handle.object { }
KernelObject::Frame(frame_addr) => frame_addr, _ => {
_ => return Err(Status::InvalidArgument), // Wrong-type operations must not consume the handle
}; match task.handles.put(frame_handle_id, frame_handle) {
Ok(_) => {}
Err(_) => panic!("taken handle was unexpectedly occupied"),
}
unsafe { crate::memory::dealloc_frame(OwnedFrame::from_raw(frame)) }; return Err(Status::InvalidArgument);
}
Ok(()) }
})
.expect("failed to resolve self task")
} }
pub fn sys_as_create(out_handle: usize) -> Result<(), Status> { pub fn sys_as_create(out_handle: usize) -> Result<(), Status> {
@@ -162,34 +247,62 @@ pub fn sys_as_create(out_handle: usize) -> Result<(), Status> {
return Err(Status::InvalidArgument); return Err(Status::InvalidArgument);
} }
let task_id = crate::task::scheduler::current(); if out_handle % core::mem::align_of::<usize>() != 0 {
let task = crate::task::scheduler::get_task_mut(task_id).ok_or(Status::NoSuchTask)?; return Err(Status::InvalidArgument);
}
let new_as = match crate::memory::with_address_space(task.as_id, |caller_as| { let new_as =
crate::memory::with_allocator(|allocator| caller_as.new_user(allocator)) crate::task::scheduler::with_task(crate::task::scheduler::current(), |current_task| {
}) { validate_user_range(
Some(Ok(as_space)) => as_space, current_task.as_id,
_ => return Err(Status::OutOfMemory), VirtualAddr::new(out_handle),
core::mem::size_of::<usize>(),
true,
)?;
let new_as = match crate::memory::with_address_space(current_task.as_id, |caller_as| {
crate::memory::with_allocator(|allocator| caller_as.new_user(allocator))
}) {
Some(Ok(as_space)) => as_space,
_ => return Err(Status::OutOfMemory),
};
Ok(new_as)
})
.expect("failed to resolve self task")?;
let as_id = match crate::memory::insert_address_space(new_as) {
Ok(id) => id,
Err(addr_space) => {
crate::memory::with_allocator(|allocator| unsafe { addr_space.destroy(allocator) });
return Err(Status::OutOfMemory);
}
}; };
let as_id = crate::memory::insert_address_space(new_as).ok_or(Status::OutOfMemory)?;
let handle = Handle { let handle = Handle {
object: KernelObject::AddressSpace(as_id), object: KernelObject::AddressSpace(as_id),
rights: Rights::READ | Rights::WRITE | Rights::EXECUTE, rights: Rights::READ | Rights::WRITE | Rights::EXECUTE,
}; };
let handle_id = match task.handles.push(handle) { crate::task::scheduler::with_task_mut(crate::task::scheduler::current(), |current_task| {
Some(id) => id, let handle_id = match current_task.handles.push(handle) {
None => { Ok(id) => id,
crate::memory::remove_address_space(as_id); Err(handle) => {
return Err(Status::OutOfMemory); let address_space = match handle.object {
} KernelObject::AddressSpace(as_id) => crate::memory::remove_address_space(as_id)
}; .expect("address space was just inserted"),
_ => unreachable!("pushed handle was not an address space"),
};
crate::memory::with_allocator(|allocator| unsafe {
address_space.destroy(allocator)
});
return Err(Status::OutOfMemory);
}
};
copy_val_to_user(VirtualAddr::new(out_handle), &handle_id)?; unsafe { copy_val_to_user(VirtualAddr::new(out_handle), &handle_id) }
})
Ok(()) .expect("failed to resolve self task")
} }
pub fn sys_map( pub fn sys_map(
@@ -197,41 +310,81 @@ pub fn sys_map(
frame_handle: usize, frame_handle: usize,
virtual_addr: usize, virtual_addr: usize,
permissions: usize, permissions: usize,
out_handle: usize,
) -> Result<(), Status> { ) -> Result<(), Status> {
if virtual_addr % FRAME_SIZE != 0 { if out_handle == 0 || out_handle % core::mem::align_of::<usize>() != 0 {
return Err(Status::InvalidArgument); return Err(Status::InvalidArgument);
} }
if virtual_addr >= USER_SPACE_END.as_usize() { if virtual_addr % FRAME_SIZE != 0 || permissions & !0b11 != 0 {
return Err(Status::InvalidArgument); return Err(Status::InvalidArgument);
} }
let task = crate::task::scheduler::current(); let writable = permissions & (1 << 0) != 0;
let task = crate::task::scheduler::get_task_mut(task).ok_or(Status::NoSuchTask)?; let executable = permissions & (1 << 1) != 0;
let as_handle = task.handles.get(as_handle).ok_or(Status::BadHandle)?; let end = virtual_addr
let as_id = match as_handle.object { .checked_add(FRAME_SIZE)
KernelObject::AddressSpace(as_id) => as_id, .ok_or(Status::InvalidArgument)?;
_ => return Err(Status::InvalidArgument), if end > USER_SPACE_END.as_usize() {
}; return Err(Status::InvalidArgument);
}
let frame_handle = task.handles.get(frame_handle).ok_or(Status::BadHandle)?;
let frame = match frame_handle.object { let current_task = crate::task::scheduler::current();
KernelObject::Frame(frame_addr) => frame_addr, let as_id = crate::task::scheduler::with_task(current_task, |task| {
_ => return Err(Status::InvalidArgument), validate_user_range(
task.as_id,
VirtualAddr::new(out_handle),
core::mem::size_of::<usize>(),
true,
)?;
let as_handle = task.handles.get(as_handle).ok_or(Status::BadHandle)?;
let as_id = match as_handle.object {
KernelObject::AddressSpace(as_id) => as_id,
_ => return Err(Status::InvalidArgument),
};
if as_handle.rights.0 & Rights::WRITE.0 == 0 {
return Err(Status::InvalidArgument);
}
let frame_handle = task.handles.get(frame_handle).ok_or(Status::BadHandle)?;
if !matches!(frame_handle.object, KernelObject::Frame(_)) {
return Err(Status::InvalidArgument);
}
let mut required_rights = Rights::READ | Rights::MAP;
if writable {
required_rights = required_rights | Rights::WRITE;
}
if executable {
required_rights = required_rights | Rights::EXECUTE;
}
if frame_handle.rights.0 & required_rights.0 != required_rights.0 {
return Err(Status::InvalidArgument);
}
Ok(as_id)
})
.expect("failed to resolve self task")?;
let handle = crate::task::scheduler::with_task_mut(current_task, |task| {
task.handles.take(frame_handle).ok_or(Status::BadHandle)
})
.expect("failed to resolve self task")?;
let Handle { object, rights } = handle;
let KernelObject::Frame(frame) = object else {
panic!("validated frame handle changed before it was taken");
}; };
let permissions = PagePermissions::new(writable, executable, true);
let virtual_addr = VirtualAddr::new(virtual_addr); let virtual_addr = VirtualAddr::new(virtual_addr);
let permissions = PagePermissions::new(
permissions & (1 << 0) != 0,
permissions & (1 << 1) != 0,
true,
);
let map_result = crate::memory::with_address_space_mut(as_id, |target_as| { let map_result = crate::memory::with_address_space_mut(as_id, |target_as| {
crate::memory::with_allocator(|allocator| { crate::memory::with_allocator(|allocator| {
target_as.map( target_as.map(
frame.start_address(), frame.frame_address().start_address(),
virtual_addr, virtual_addr,
permissions, permissions,
allocator, allocator,
@@ -239,48 +392,151 @@ pub fn sys_map(
) )
}) })
}) })
.ok_or(Status::BadHandle)?; .expect("failed to resolve self address space");
map_result.map_err(|_| Status::OutOfMemory)?; match map_result {
Ok(_) => {
crate::task::scheduler::with_task_mut(crate::task::scheduler::current(), |task| {
match task.handles.put(
frame_handle,
Handle {
object: KernelObject::Mapping {
frame,
address_space: as_id,
virtual_addr,
},
rights,
},
) {
Ok(_) => {}
Err(_) => panic!("taken handle was unexpectedly occupied"),
}
});
Ok(()) unsafe {
copy_val_to_user(VirtualAddr::new(out_handle), &frame_handle)
.expect("out_handle has already been checked")
}
Ok(())
}
Err(err) => {
crate::task::scheduler::with_task_mut(current_task, |task| {
match task.handles.put(
frame_handle,
Handle {
object: KernelObject::Frame(frame),
rights,
},
) {
Ok(_) => {}
Err(_) => panic!("taken handle was unexpectedly occupied"),
}
})
.expect("failed to resolve self task");
match err {
MapError::AlreadyMapped | MapError::UnsupportedPermissions => {
Err(Status::InvalidArgument)
}
MapError::OutOfMemory => Err(Status::OutOfMemory),
MapError::InvalidVirtualAddress
| MapError::VirtualAddressUnaligned
| MapError::PhysicalAddressTooLarge
| MapError::PhysicalAddressUnaligned
| MapError::RangeLengthUnaligned
| MapError::AddressOverflow
| MapError::MappingConflict
| MapError::PageTableUnavailable
| MapError::CorruptedPageTable
| MapError::InvalidUserAddress
| MapError::InvalidUserMap => {
panic!("validated user mapping failed with an impossible error: {err:?}")
}
}
}
}
} }
pub fn sys_unmap(as_handle: usize, virtual_addr: usize) -> Result<(), Status> { pub fn sys_unmap(mapping_handle: usize) -> Result<(), Status> {
if virtual_addr % FRAME_SIZE != 0 { let handle = crate::task::scheduler::with_task_mut(crate::task::scheduler::current(), |task| {
task.handles.take(mapping_handle).ok_or(Status::BadHandle)
})
.expect("failed to resolve self task")?;
let Handle { object, rights } = handle;
let KernelObject::Mapping {
frame,
address_space,
virtual_addr,
} = object
else {
// Wrong-type operations must not consume the handle
crate::task::scheduler::with_task_mut(crate::task::scheduler::current(), |task| match task
.handles
.put(mapping_handle, Handle { object, rights })
{
Ok(_) => {}
Err(_) => panic!("taken handle was unexpectedly occupied"),
})
.expect("failed to resolve self task");
return Err(Status::InvalidArgument); return Err(Status::InvalidArgument);
}
if virtual_addr >= USER_SPACE_END.as_usize() {
return Err(Status::InvalidArgument);
}
let task = crate::task::scheduler::current();
let task = crate::task::scheduler::get_task_mut(task).ok_or(Status::NoSuchTask)?;
let as_handle = task.handles.get(as_handle).ok_or(Status::BadHandle)?;
let as_id = match as_handle.object {
KernelObject::AddressSpace(as_id) => as_id,
_ => return Err(Status::InvalidArgument),
}; };
let virtual_addr = VirtualAddr::new(virtual_addr); let unmap_result = crate::memory::with_address_space_mut(address_space, |target_as| {
let map_result = crate::memory::with_address_space_mut(as_id, |target_as| {
if target_as.to_physical(virtual_addr).is_none() {
return Err(Status::InvalidArgument);
}
crate::memory::with_allocator(|allocator| unsafe { crate::memory::with_allocator(|allocator| unsafe {
target_as.unmap(virtual_addr, allocator) target_as.unmap(virtual_addr, allocator)
}) })
.map_err(|_| Status::BadAddress) });
})
.ok_or(Status::BadHandle)?;
map_result.map_err(|_| Status::OutOfMemory)?; match unmap_result {
Some(Ok(unmapped_frame)) => {
if unmapped_frame == frame.frame_address() {
crate::task::scheduler::with_task_mut(crate::task::scheduler::current(), |task| {
match task.handles.put(
mapping_handle,
Handle {
object: KernelObject::Frame(frame),
rights,
},
) {
Ok(_) => {}
Err(_) => panic!("taken handle was unexpectedly occupied"),
}
});
Ok(()) Ok(())
} else {
panic!("unmap resulted in a frame that was not the one we expected")
}
}
Some(Err(err)) => {
// every unmapping error should be impossible to occur
panic!("failed to unmap: {err:?}");
}
None => {
crate::task::scheduler::with_task_mut(crate::task::scheduler::current(), |task| {
match task.handles.put(
mapping_handle,
Handle {
object: KernelObject::Mapping {
frame: frame,
address_space,
virtual_addr,
},
rights,
},
) {
Ok(_) => {}
Err(_) => panic!("taken handle was unexpectedly occupied"),
}
});
Err(Status::BadHandle)
}
}
} }
pub fn sys_task_create( pub fn sys_task_create(
@@ -289,31 +545,64 @@ pub fn sys_task_create(
user_stack: usize, user_stack: usize,
out_task_handle: usize, out_task_handle: usize,
) -> Result<(), Status> { ) -> Result<(), Status> {
if out_task_handle == 0 || entry == 0 || user_stack == 0 { if entry == 0 || user_stack == 0 || out_task_handle == 0 {
return Err(Status::InvalidArgument); return Err(Status::InvalidArgument);
} }
if entry >= 0x0000_8000_0000_0000 || user_stack >= 0x0000_8000_0000_0000 { if entry >= USER_SPACE_END.as_usize() || user_stack > USER_SPACE_END.as_usize() {
return Err(Status::BadAddress); return Err(Status::BadAddress);
} }
let task_id = crate::task::scheduler::current(); if out_task_handle % core::mem::align_of::<usize>() != 0 {
let task = crate::task::scheduler::get_task_mut(task_id).ok_or(Status::NoSuchTask)?;
let as_handle = task.handles.get(as_handle).ok_or(Status::BadHandle)?;
let as_id = match as_handle.object {
KernelObject::AddressSpace(as_id) => as_id,
_ => return Err(Status::InvalidArgument),
};
if as_handle.rights.0 & Rights::EXECUTE.0 == 0 {
return Err(Status::InvalidArgument); return Err(Status::InvalidArgument);
} }
let as_id =
crate::task::scheduler::with_task(crate::task::scheduler::current(), |current_task| {
validate_user_range(
current_task.as_id,
VirtualAddr::new(out_task_handle),
core::mem::size_of::<usize>(),
true,
)?;
let as_handle = current_task
.handles
.get(as_handle)
.ok_or(Status::BadHandle)?;
let as_id = match as_handle.object {
KernelObject::AddressSpace(as_id) => as_id,
_ => return Err(Status::InvalidArgument),
};
if as_handle.rights.0 & Rights::EXECUTE.0 == 0 {
return Err(Status::InvalidArgument);
}
Ok(as_id)
})
.expect("failed to resolve self task")?;
if crate::memory::with_address_space(as_id, |_| ()).is_none() { if crate::memory::with_address_space(as_id, |_| ()).is_none() {
return Err(Status::BadHandle); return Err(Status::BadHandle);
} }
let stack_probe = user_stack.checked_sub(1).ok_or(Status::BadAddress)?;
validate_user_range(as_id, VirtualAddr::new(stack_probe), 1, true)?;
let entry_is_valid = crate::memory::with_address_space(as_id, |address_space| {
address_space
.mapping(VirtualAddr::new(entry))
.is_some_and(|mapping| {
mapping.permissions.user_accessible && mapping.permissions.executable
})
})
.ok_or(Status::BadHandle)?;
if !entry_is_valid {
return Err(Status::BadAddress);
}
let kernel_stack = match crate::memory::with_kernel_address_space(|kernel_as| { let kernel_stack = match crate::memory::with_kernel_address_space(|kernel_as| {
crate::memory::with_allocator(|allocator| { crate::memory::with_allocator(|allocator| {
crate::task::scheduler::allocate_kernel_stack(kernel_as, allocator) crate::task::scheduler::allocate_kernel_stack(kernel_as, allocator)
@@ -327,7 +616,6 @@ pub fn sys_task_create(
}; };
let new_tcb = crate::task::tcb::Tcb::new_user( let new_tcb = crate::task::tcb::Tcb::new_user(
0, // assigned by scheduler::add_task
as_id, as_id,
kernel_stack, kernel_stack,
VirtualAddr::new(entry), VirtualAddr::new(entry),
@@ -344,15 +632,16 @@ pub fn sys_task_create(
rights: Rights::READ | Rights::WRITE | Rights::EXECUTE, rights: Rights::READ | Rights::WRITE | Rights::EXECUTE,
}; };
let handle_id = match task.handles.push(handle) { crate::task::scheduler::with_task_mut(crate::task::scheduler::current(), |current_task| {
Some(id) => id, let handle_id = match current_task.handles.push(handle) {
None => { Ok(id) => id,
crate::task::scheduler::remove_task(new_task_id); Err(_) => {
return Err(Status::OutOfMemory); crate::task::scheduler::remove_task(new_task_id);
} return Err(Status::OutOfMemory);
}; }
};
copy_val_to_user(VirtualAddr::new(out_task_handle), &handle_id)?; unsafe { copy_val_to_user(VirtualAddr::new(out_task_handle), &handle_id) }
})
Ok(()) .expect("failed to resolve self task")
} }
+9 -5
View File
@@ -4,7 +4,7 @@ use crate::{
self, AddressSpace, DirectMap, FRAME_SIZE, FrameAllocator, InitramfsImage, PagePermissions, self, AddressSpace, DirectMap, FRAME_SIZE, FrameAllocator, InitramfsImage, PagePermissions,
UserStack, VirtualAddr, UserStack, VirtualAddr,
}, },
task::tcb::Tcb, task::{scheduler::TaskId, tcb::Tcb},
}; };
pub fn spawn( pub fn spawn(
@@ -13,7 +13,7 @@ pub fn spawn(
kernel_as: &mut AddressSpace, kernel_as: &mut AddressSpace,
allocator: &mut FrameAllocator, allocator: &mut FrameAllocator,
direct_map: DirectMap, direct_map: DirectMap,
) -> usize { ) -> TaskId {
let bytes = format::cpio::find_file(initramfs.data(), name) let bytes = format::cpio::find_file(initramfs.data(), name)
.unwrap_or_else(|| panic!("{name} missing from initramfs")); .unwrap_or_else(|| panic!("{name} missing from initramfs"));
let kernel_stack = crate::task::scheduler::allocate_kernel_stack(kernel_as, allocator) let kernel_stack = crate::task::scheduler::allocate_kernel_stack(kernel_as, allocator)
@@ -41,10 +41,14 @@ pub fn spawn(
let entry = load_elf(bytes, &mut address_space, allocator, direct_map).expect("invalid ELF"); let entry = load_elf(bytes, &mut address_space, allocator, direct_map).expect("invalid ELF");
let as_id = let as_id = match crate::memory::insert_address_space(address_space) {
crate::memory::insert_address_space(address_space).expect("address space table is full"); Ok(id) => id,
Err(_) => {
panic!("address space table is full");
}
};
let task = Tcb::new_user(0, as_id, kernel_stack, entry, user_stack.top()); let task = Tcb::new_user(as_id, kernel_stack, entry, user_stack.top());
crate::task::scheduler::add_task(task).expect("scheduler is full") crate::task::scheduler::add_task(task).expect("scheduler is full")
} }
+52 -28
View File
@@ -3,7 +3,8 @@ use core::cell::UnsafeCell;
use crate::{ use crate::{
arch::ThreadContext, arch::ThreadContext,
memory::{ memory::{
AddressSpace, FrameAllocator, KernelStack, KernelStackPool, StackCreateError, VirtualAddr, AddressSpace, AddressSpaceId, FrameAllocator, KernelStack, KernelStackPool,
StackCreateError, VirtualAddr,
}, },
println, println,
task::tcb::{BlockReason, ExitReason, Handle, KernelObject, Rights, Tcb, ThreadState}, task::tcb::{BlockReason, ExitReason, Handle, KernelObject, Rights, Tcb, ThreadState},
@@ -11,7 +12,15 @@ use crate::{
const MAX_TASKS: usize = 32; const MAX_TASKS: usize = 32;
type TaskId = usize; #[derive(Clone, Copy, Debug, PartialEq, Eq)]
#[repr(transparent)]
pub struct TaskId(usize);
impl TaskId {
pub const fn new(id: usize) -> Self {
Self(id)
}
}
struct Scheduler { struct Scheduler {
current: Option<TaskId>, current: Option<TaskId>,
@@ -33,11 +42,11 @@ impl Scheduler {
fn make_switch(&mut self, current_id: TaskId, next_id: TaskId) -> Switch { fn make_switch(&mut self, current_id: TaskId, next_id: TaskId) -> Switch {
assert_ne!(current_id, next_id); assert_ne!(current_id, next_id);
let current = self.tasks[current_id].as_mut().unwrap(); let current = self.tasks[current_id.0].as_mut().unwrap();
let prev_ctx = &mut current.context as *mut ThreadContext; let prev_ctx = &mut current.context as *mut ThreadContext;
let prev_as_id = current.as_id; let prev_as_id = current.as_id;
let next = self.tasks[next_id].as_ref().unwrap(); let next = self.tasks[next_id.0].as_ref().unwrap();
let next_ctx = &next.context as *const ThreadContext; let next_ctx = &next.context as *const ThreadContext;
let next_as_id = next.as_id; let next_as_id = next.as_id;
let next_kernel_stack = next.kernel_stack.top(); let next_kernel_stack = next.kernel_stack.top();
@@ -55,7 +64,7 @@ impl Scheduler {
struct Switch { struct Switch {
previous_context: *mut ThreadContext, previous_context: *mut ThreadContext,
next_context: *const ThreadContext, next_context: *const ThreadContext,
next_as_id: usize, next_as_id: AddressSpaceId,
next_kernel_stack: VirtualAddr, next_kernel_stack: VirtualAddr,
activate_address_space: bool, activate_address_space: bool,
} }
@@ -85,7 +94,7 @@ struct ReadyQueue {
impl ReadyQueue { impl ReadyQueue {
pub const fn new() -> Self { pub const fn new() -> Self {
Self { Self {
entries: [0; MAX_TASKS], entries: [TaskId(0); MAX_TASKS],
head: 0, head: 0,
len: 0, len: 0,
} }
@@ -147,15 +156,20 @@ pub fn add_task(mut task: Tcb) -> Result<TaskId, Tcb> {
match scheduler.tasks.iter().position(Option::is_none) { match scheduler.tasks.iter().position(Option::is_none) {
Some(id) => { Some(id) => {
let id = TaskId(id);
task.id = id; task.id = id;
task.handles.push(Handle { match task.handles.push(Handle {
object: KernelObject::Thread(id), object: KernelObject::Thread(id),
rights: Rights::READ | Rights::WRITE | Rights::EXECUTE, rights: Rights::READ | Rights::WRITE | Rights::EXECUTE,
}); }) {
Ok(_) => {}
Err(_) => unreachable!("Cant push root thread handle"),
}
task.state = ThreadState::Ready; task.state = ThreadState::Ready;
scheduler.tasks[id] = Some(task); scheduler.tasks[id.0] = Some(task);
assert!(scheduler.ready.push_back(id)); assert!(scheduler.ready.push_back(id));
Ok(id) Ok(id)
} }
@@ -176,7 +190,7 @@ pub fn start() -> ! {
let scheduler = unsafe { &mut *SCHEDULER.0.get() }; let scheduler = unsafe { &mut *SCHEDULER.0.get() };
let next_id = scheduler.ready.pop_front().expect("no tasks to run"); let next_id = scheduler.ready.pop_front().expect("no tasks to run");
let next = scheduler.tasks[next_id] let next = scheduler.tasks[next_id.0]
.as_mut() .as_mut()
.expect("ready task is missing"); .expect("ready task is missing");
@@ -210,20 +224,20 @@ pub fn allocate_kernel_stack(
result result
} }
pub fn remove_task(id: TaskId) -> Option<Tcb> { pub fn remove_task(id: TaskId) -> bool {
let interrupt_state = crate::arch::disable_interrupts_and_save(); let interrupt_state = crate::arch::disable_interrupts_and_save();
let result = { let result = {
let scheduler = unsafe { &mut *SCHEDULER.0.get() }; let scheduler = unsafe { &mut *SCHEDULER.0.get() };
if scheduler.current == Some(id) { if scheduler.current == Some(id) {
None false
} else if let Some(task) = scheduler.tasks.get_mut(id).and_then(Option::take) { } else if let Some(task) = scheduler.tasks.get_mut(id.0).and_then(Option::take) {
scheduler.ready.remove(id); scheduler.ready.remove(id);
scheduler.stacks.free(&task.kernel_stack); scheduler.stacks.free(task.kernel_stack);
Some(task) true
} else { } else {
None false
} }
}; };
@@ -231,14 +245,24 @@ pub fn remove_task(id: TaskId) -> Option<Tcb> {
result result
} }
pub fn get_task(id: TaskId) -> Option<&'static Tcb> { pub fn with_task<R>(id: TaskId, f: impl FnOnce(&Tcb) -> R) -> Option<R> {
let scheduler = unsafe { &mut *SCHEDULER.0.get() }; let interrupt_state = crate::arch::disable_interrupts_and_save();
scheduler.tasks.get(id).and_then(Option::as_ref) let scheduler = unsafe { &*SCHEDULER.0.get() };
let res = scheduler.tasks.get(id.0).and_then(Option::as_ref).map(f);
crate::arch::restore_interrupts(interrupt_state);
res
} }
pub fn get_task_mut(id: TaskId) -> Option<&'static mut Tcb> { pub fn with_task_mut<R>(id: TaskId, f: impl FnOnce(&mut Tcb) -> R) -> Option<R> {
let interrupt_state = crate::arch::disable_interrupts_and_save();
let scheduler = unsafe { &mut *SCHEDULER.0.get() }; let scheduler = unsafe { &mut *SCHEDULER.0.get() };
scheduler.tasks.get_mut(id).and_then(Option::as_mut) let res = scheduler
.tasks
.get_mut(id.0)
.and_then(Option::as_mut)
.map(f);
crate::arch::restore_interrupts(interrupt_state);
res
} }
pub fn current() -> TaskId { pub fn current() -> TaskId {
@@ -259,10 +283,10 @@ pub fn block_current(reason: BlockReason) {
let current_id = scheduler.current.expect("no current task"); let current_id = scheduler.current.expect("no current task");
scheduler.tasks[current_id].as_mut().unwrap().state = ThreadState::Blocked(reason); scheduler.tasks[current_id.0].as_mut().unwrap().state = ThreadState::Blocked(reason);
// explicitly do NOT push back the current task, because it is not ready // explicitly do NOT push back the current task, because it is not ready
scheduler.tasks[next_id].as_mut().unwrap().state = ThreadState::Running; scheduler.tasks[next_id.0].as_mut().unwrap().state = ThreadState::Running;
scheduler.current = Some(next_id); scheduler.current = Some(next_id);
scheduler.make_switch(current_id, next_id) scheduler.make_switch(current_id, next_id)
@@ -280,7 +304,7 @@ pub fn unblock(id: TaskId) {
let interrupt_state = crate::arch::disable_interrupts_and_save(); let interrupt_state = crate::arch::disable_interrupts_and_save();
let scheduler = unsafe { &mut *SCHEDULER.0.get() }; let scheduler = unsafe { &mut *SCHEDULER.0.get() };
if let Some(task) = scheduler.tasks[id].as_mut() { if let Some(task) = scheduler.tasks[id.0].as_mut() {
if matches!(task.state, ThreadState::Blocked(_)) { if matches!(task.state, ThreadState::Blocked(_)) {
task.state = ThreadState::Ready; task.state = ThreadState::Ready;
assert!(scheduler.ready.push_back(id)); assert!(scheduler.ready.push_back(id));
@@ -303,10 +327,10 @@ pub fn yield_current() {
let current_id = scheduler.current.expect("no current task"); let current_id = scheduler.current.expect("no current task");
scheduler.tasks[current_id].as_mut().unwrap().state = ThreadState::Ready; scheduler.tasks[current_id.0].as_mut().unwrap().state = ThreadState::Ready;
assert!(scheduler.ready.push_back(current_id)); assert!(scheduler.ready.push_back(current_id));
scheduler.tasks[next_id].as_mut().unwrap().state = ThreadState::Running; scheduler.tasks[next_id.0].as_mut().unwrap().state = ThreadState::Running;
scheduler.current = Some(next_id); scheduler.current = Some(next_id);
scheduler.make_switch(current_id, next_id) scheduler.make_switch(current_id, next_id)
@@ -331,10 +355,10 @@ pub fn exit_current(exit_code: usize) -> ! {
crate::hcf(); crate::hcf();
}; };
let current = scheduler.tasks[current_id].as_mut().unwrap(); let current = scheduler.tasks[current_id.0].as_mut().unwrap();
current.state = ThreadState::Dead(ExitReason::Exited(exit_code)); current.state = ThreadState::Dead(ExitReason::Exited(exit_code));
scheduler.tasks[next_id].as_mut().unwrap().state = ThreadState::Running; scheduler.tasks[next_id.0].as_mut().unwrap().state = ThreadState::Running;
scheduler.current = Some(next_id); scheduler.current = Some(next_id);
scheduler.make_switch(current_id, next_id) scheduler.make_switch(current_id, next_id)
+54 -19
View File
@@ -2,22 +2,29 @@ use core::ops::BitOr;
use crate::{ use crate::{
arch::ThreadContext, arch::ThreadContext,
memory::{FrameAddr, KernelStack, VirtualAddr}, memory::{AddressSpaceId, KernelStack, OwnedFrame, VirtualAddr},
task::scheduler::TaskId,
}; };
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum Fault {
SegmentationFault,
IllegalInstruction,
Abort,
BadSystemCall,
}
// Thread Control Block // Thread Control Block
#[derive(Clone, Copy, Debug, PartialEq, Eq)] #[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum ExitReason { pub enum ExitReason {
Exited(usize), Exited(usize),
Killed, Killed,
Fault, Fault(Fault),
} }
#[derive(Clone, Copy, Debug, PartialEq, Eq)] #[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum BlockReason { pub enum BlockReason {
Send { ep: usize }, Recv,
Recv { ep: usize },
Reply { client: usize },
} }
#[derive(Clone, Copy, Debug, PartialEq, Eq)] #[derive(Clone, Copy, Debug, PartialEq, Eq)]
@@ -33,7 +40,7 @@ pub const MAILBOX_CAPACITY: usize = 4;
#[derive(Clone, Copy)] #[derive(Clone, Copy)]
pub struct Message { pub struct Message {
pub sender: usize, pub sender: TaskId,
pub length: usize, pub length: usize,
pub data: [u8; MAX_MSG_SIZE], pub data: [u8; MAX_MSG_SIZE],
} }
@@ -79,9 +86,14 @@ impl Mailbox {
const MAX_HANDLES: usize = 32; const MAX_HANDLES: usize = 32;
pub enum KernelObject { pub enum KernelObject {
AddressSpace(usize), AddressSpace(AddressSpaceId),
Frame(FrameAddr), Frame(OwnedFrame),
Thread(usize), Mapping {
frame: OwnedFrame,
address_space: AddressSpaceId,
virtual_addr: VirtualAddr,
},
Thread(TaskId),
} }
pub struct Handle { pub struct Handle {
@@ -118,15 +130,36 @@ impl HandleTable {
} }
} }
pub fn push(&mut self, handle: Handle) -> Option<usize> { pub fn push(&mut self, handle: Handle) -> Result<usize, Handle> {
for (i, slot) in self.handles.iter_mut().enumerate() { for (i, slot) in self.handles.iter_mut().enumerate() {
if slot.is_none() { if slot.is_none() {
*slot = Some(handle); *slot = Some(handle);
return Some(i); return Ok(i);
} }
} }
None Err(handle)
}
pub fn remove(&mut self, id: usize) -> Option<Handle> {
self.handles.get_mut(id).and_then(Option::take)
}
pub fn take(&mut self, id: usize) -> Option<Handle> {
self.handles.get_mut(id)?.take()
}
pub fn put(&mut self, id: usize, handle: Handle) -> Result<(), Handle> {
let Some(slot) = self.handles.get_mut(id) else {
return Err(handle);
};
if slot.is_some() {
return Err(handle);
}
*slot = Some(handle);
Ok(())
} }
pub fn get(&self, id: usize) -> Option<&Handle> { pub fn get(&self, id: usize) -> Option<&Handle> {
@@ -139,8 +172,8 @@ impl HandleTable {
} }
pub struct Tcb { pub struct Tcb {
pub id: usize, pub id: TaskId,
pub as_id: usize, pub as_id: AddressSpaceId,
pub state: ThreadState, pub state: ThreadState,
pub kernel_stack: KernelStack, pub kernel_stack: KernelStack,
pub context: ThreadContext, pub context: ThreadContext,
@@ -161,21 +194,23 @@ impl core::fmt::Debug for Tcb {
impl Tcb { impl Tcb {
pub fn new_user( pub fn new_user(
id: usize, as_id: AddressSpaceId,
as_id: usize,
kernel_stack: KernelStack, kernel_stack: KernelStack,
entry: VirtualAddr, entry: VirtualAddr,
user_stack: VirtualAddr, user_stack: VirtualAddr,
) -> Self { ) -> Self {
let context = ThreadContext::new(entry, user_stack, kernel_stack.top()); let context = ThreadContext::new(entry, user_stack, kernel_stack.top());
let mut handles = HandleTable::new(); let mut handles = HandleTable::new();
handles.push(Handle { match handles.push(Handle {
object: KernelObject::AddressSpace(as_id), object: KernelObject::AddressSpace(as_id),
rights: Rights::READ | Rights::WRITE | Rights::EXECUTE, rights: Rights::READ | Rights::WRITE | Rights::EXECUTE,
}); }) {
Ok(_) => {}
Err(_) => unreachable!("Cant push root address space handle"),
};
Self { Self {
id, id: TaskId::new(0),
as_id, as_id,
state: ThreadState::Ready, state: ThreadState::Ready,
kernel_stack, kernel_stack,
+3 -5
View File
@@ -8,12 +8,10 @@ pub extern "C" fn _start() -> ! {
let msg = "Hello from client!"; let msg = "Hello from client!";
println!("[client] Sent: {}", msg); println!("[client] Sent: {}", msg);
// TODO: we assume the echo server is task 1 (spawned by omega3) // TODO: we assume the echo server is task 1 (spawned by omega3)
sys_send(1, msg.as_ptr() as usize, msg.len()).unwrap(); sys_send(1, msg.as_bytes()).unwrap();
let out = [0u8; 128]; let mut out = [0u8; 128];
let out_ptr = out.as_ptr() as usize; let (actual_len, _) = sys_recv(&mut out).unwrap();
let max_len = out.len();
let (actual_len, _) = sys_recv(out_ptr, max_len).unwrap();
println!( println!(
"[client] Received: {}", "[client] Received: {}",
core::str::from_utf8(&out[..actual_len]).unwrap() core::str::from_utf8(&out[..actual_len]).unwrap()
+37 -24
View File
@@ -10,15 +10,25 @@ pub enum Status {
BadFileDescriptor = 3, BadFileDescriptor = 3,
NoSuchTask = 4, NoSuchTask = 4,
OutOfMemory = 5, OutOfMemory = 5,
BadHandle = 6,
} }
// Opaque handle type // Opaque handle type
#[derive(Clone, Copy, Debug, PartialEq, Eq)] #[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct Handle(usize); pub struct AddressSpaceHandle(usize);
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct FrameHandle(usize);
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct MappingHandle(usize);
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct ThreadHandle(usize);
// our own address space and thread handle are always given to us // our own address space and thread handle are always given to us
pub const SELF_AS: Handle = Handle(0); pub const SELF_AS: AddressSpaceHandle = AddressSpaceHandle(0);
pub const SELF_THREAD: Handle = Handle(1); pub const SELF_THREAD: ThreadHandle = ThreadHandle(1);
impl From<usize> for Status { impl From<usize> for Status {
fn from(value: usize) -> Self { fn from(value: usize) -> Self {
@@ -28,6 +38,7 @@ impl From<usize> for Status {
3 => Self::BadFileDescriptor, 3 => Self::BadFileDescriptor,
4 => Self::NoSuchTask, 4 => Self::NoSuchTask,
5 => Self::OutOfMemory, 5 => Self::OutOfMemory,
6 => Self::BadHandle,
_ => Self::InvalidArgument, _ => Self::InvalidArgument,
} }
} }
@@ -125,15 +136,15 @@ pub fn sys_exit(exit_code: usize) -> ! {
} }
} }
pub fn sys_send(dest_task_id: usize, msg_ptr: usize, len: usize) -> Result<(), Status> { pub fn sys_send(dest_task_id: usize, msg: &[u8]) -> Result<(), Status> {
unsafe { unsafe {
let status: usize; let status: usize;
asm!( asm!(
"syscall", "syscall",
in("rdi") dest_task_id, in("rdi") dest_task_id,
in("rsi") msg_ptr, in("rsi") msg.as_ptr(),
in("rdx") len, in("rdx") msg.len(),
inlateout("rax") SyscallNumber::Send as usize => status, inlateout("rax") SyscallNumber::Send as usize => status,
lateout("rcx") _, lateout("rcx") _,
lateout("r11") _, lateout("r11") _,
@@ -147,7 +158,7 @@ pub fn sys_send(dest_task_id: usize, msg_ptr: usize, len: usize) -> Result<(), S
} }
} }
pub fn sys_recv(buf_ptr: usize, max_len: usize) -> Result<(usize, usize), Status> { pub fn sys_recv(buf: &mut [u8]) -> Result<(usize, usize), Status> {
let mut actual_len: usize = 0; let mut actual_len: usize = 0;
let mut sender: usize = 0; let mut sender: usize = 0;
@@ -156,8 +167,8 @@ pub fn sys_recv(buf_ptr: usize, max_len: usize) -> Result<(usize, usize), Status
asm!( asm!(
"syscall", "syscall",
in("rdi") buf_ptr, in("rdi") buf.as_mut_ptr(),
in("rsi") max_len, in("rsi") buf.len(),
in("rdx") &raw mut actual_len as usize, in("rdx") &raw mut actual_len as usize,
in("r10") &raw mut sender as usize, in("r10") &raw mut sender as usize,
inlateout("rax") SyscallNumber::Recv as usize => status, inlateout("rax") SyscallNumber::Recv as usize => status,
@@ -173,7 +184,7 @@ pub fn sys_recv(buf_ptr: usize, max_len: usize) -> Result<(usize, usize), Status
} }
} }
pub fn sys_frame_alloc() -> Result<Handle, Status> { pub fn sys_frame_alloc() -> Result<FrameHandle, Status> {
let mut handle: usize = 0; let mut handle: usize = 0;
unsafe { unsafe {
let status: usize; let status: usize;
@@ -189,12 +200,12 @@ pub fn sys_frame_alloc() -> Result<Handle, Status> {
if status != 0 { if status != 0 {
Err(status.into()) Err(status.into())
} else { } else {
Ok(Handle(handle)) Ok(FrameHandle(handle))
} }
} }
} }
pub fn sys_frame_dealloc(frame_handle: Handle) -> Result<(), Status> { pub fn sys_frame_dealloc(frame_handle: FrameHandle) -> Result<(), Status> {
unsafe { unsafe {
let status: usize; let status: usize;
@@ -214,7 +225,7 @@ pub fn sys_frame_dealloc(frame_handle: Handle) -> Result<(), Status> {
} }
} }
pub fn sys_as_create() -> Result<Handle, Status> { pub fn sys_as_create() -> Result<AddressSpaceHandle, Status> {
let mut handle: usize = 0; let mut handle: usize = 0;
unsafe { unsafe {
let status: usize; let status: usize;
@@ -230,17 +241,19 @@ pub fn sys_as_create() -> Result<Handle, Status> {
if status != 0 { if status != 0 {
Err(status.into()) Err(status.into())
} else { } else {
Ok(Handle(handle)) Ok(AddressSpaceHandle(handle))
} }
} }
} }
pub fn sys_map( pub fn sys_map(
as_handle: Handle, as_handle: AddressSpaceHandle,
frame_handle: Handle, frame_handle: FrameHandle,
virtual_addr: usize, virtual_addr: usize,
permissions: usize, permissions: usize,
) -> Result<(), Status> { ) -> Result<MappingHandle, Status> {
let mut handle: usize = 0;
unsafe { unsafe {
let status: usize; let status: usize;
@@ -250,6 +263,7 @@ pub fn sys_map(
in("rsi") frame_handle.0, in("rsi") frame_handle.0,
in("rdx") virtual_addr, in("rdx") virtual_addr,
in("r10") permissions, in("r10") permissions,
in("r8") &raw mut handle as usize,
inlateout("rax") SyscallNumber::Map as usize => status, inlateout("rax") SyscallNumber::Map as usize => status,
lateout("rcx") _, lateout("rcx") _,
lateout("r11") _, lateout("r11") _,
@@ -258,19 +272,18 @@ pub fn sys_map(
if status != 0 { if status != 0 {
Err(status.into()) Err(status.into())
} else { } else {
Ok(()) Ok(MappingHandle(handle))
} }
} }
} }
pub fn sys_unmap(as_handle: Handle, virtual_addr: usize) -> Result<(), Status> { pub fn sys_unmap(mapping_handle: MappingHandle) -> Result<(), Status> {
unsafe { unsafe {
let status: usize; let status: usize;
asm!( asm!(
"syscall", "syscall",
in("rdi") as_handle.0, in("rdi") mapping_handle.0,
in("rsi") virtual_addr,
inlateout("rax") SyscallNumber::Unmap as usize => status, inlateout("rax") SyscallNumber::Unmap as usize => status,
lateout("rcx") _, lateout("rcx") _,
lateout("r11") _, lateout("r11") _,
@@ -285,10 +298,10 @@ pub fn sys_unmap(as_handle: Handle, virtual_addr: usize) -> Result<(), Status> {
} }
pub fn sys_task_create( pub fn sys_task_create(
as_handle: Handle, as_handle: AddressSpaceHandle,
entry: usize, entry: usize,
user_stack: usize, user_stack: usize,
) -> Result<Handle, Status> { ) -> Result<ThreadHandle, Status> {
let mut handle: usize = 0; let mut handle: usize = 0;
unsafe { unsafe {
@@ -308,7 +321,7 @@ pub fn sys_task_create(
if status != 0 { if status != 0 {
Err(status.into()) Err(status.into())
} else { } else {
Ok(Handle(handle)) Ok(ThreadHandle(handle))
} }
} }
} }
+3 -3
View File
@@ -5,14 +5,14 @@ use dusk_sys::{println, sys_exit, sys_recv, sys_send};
#[unsafe(no_mangle)] #[unsafe(no_mangle)]
pub extern "C" fn _start() -> ! { pub extern "C" fn _start() -> ! {
let out = [0u8; 128]; let mut out = [0u8; 128];
loop { loop {
let (actual_len, sender) = sys_recv(out.as_ptr() as usize, out.len()).unwrap(); let (actual_len, sender) = sys_recv(&mut out).unwrap();
println!( println!(
"[echo] Received: {}", "[echo] Received: {}",
core::str::from_utf8(&out[..actual_len]).unwrap() core::str::from_utf8(&out[..actual_len]).unwrap()
); );
sys_send(sender, out.as_ptr() as usize, actual_len).unwrap(); sys_send(sender, &out[..actual_len]).unwrap();
} }
} }
+11 -6
View File
@@ -5,8 +5,8 @@ mod cpio;
mod elf; mod elf;
use dusk_sys::{ use dusk_sys::{
Handle, SELF_AS, println, sys_as_create, sys_exit, sys_frame_alloc, sys_map, sys_task_create, AddressSpaceHandle, SELF_AS, println, sys_as_create, sys_exit, sys_frame_alloc, sys_map,
sys_unmap, sys_yield, sys_task_create, sys_unmap, sys_yield,
}; };
// Mapped into the root task's address space by the kernel. // Mapped into the root task's address space by the kernel.
@@ -40,10 +40,15 @@ pub extern "C" fn _start() -> ! {
map_stack(client_as, STACK_TOP, STACK_PAGES); map_stack(client_as, STACK_TOP, STACK_PAGES);
let _ = sys_task_create(client_as, client_entry, STACK_TOP).unwrap(); let _ = sys_task_create(client_as, client_entry, STACK_TOP).unwrap();
let ptr = 0xDEAD_BEEF as *mut u32;
unsafe {
core::ptr::write_volatile(&mut *ptr, 0xDEAD_BEEF);
}
sys_exit(0); sys_exit(0);
} }
fn load_elf(elf: &elf::Elf, target_as: Handle) -> usize { fn load_elf(elf: &elf::Elf, target_as: AddressSpaceHandle) -> usize {
for header in elf.program_headers().unwrap() { for header in elf.program_headers().unwrap() {
let header = header.unwrap(); let header = header.unwrap();
if header.segment_type != elf::ProgramHeaderType::Load || header.memory_size == 0 { if header.segment_type != elf::ProgramHeaderType::Load || header.memory_size == 0 {
@@ -66,7 +71,7 @@ fn load_elf(elf: &elf::Elf, target_as: Handle) -> usize {
for page in (page_start..segment_end).step_by(0x1000) { for page in (page_start..segment_end).step_by(0x1000) {
let frame = sys_frame_alloc().unwrap(); let frame = sys_frame_alloc().unwrap();
sys_map(SELF_AS, frame, SCRATCH_PAGE, 0b01).unwrap(); let scratch_handle = sys_map(SELF_AS, frame, SCRATCH_PAGE, 0b01).unwrap();
unsafe { unsafe {
core::ptr::write_bytes(SCRATCH_PAGE as *mut u8, 0, 0x1000); core::ptr::write_bytes(SCRATCH_PAGE as *mut u8, 0, 0x1000);
@@ -84,7 +89,7 @@ fn load_elf(elf: &elf::Elf, target_as: Handle) -> usize {
); );
} }
} }
sys_unmap(SELF_AS, SCRATCH_PAGE).unwrap(); sys_unmap(scratch_handle).unwrap();
sys_map(target_as, frame, page, perms).unwrap(); sys_map(target_as, frame, page, perms).unwrap();
} }
@@ -93,7 +98,7 @@ fn load_elf(elf: &elf::Elf, target_as: Handle) -> usize {
elf.entry() elf.entry()
} }
fn map_stack(target_as: Handle, stack_top: usize, pages: usize) { fn map_stack(target_as: AddressSpaceHandle, stack_top: usize, pages: usize) {
for i in 1..=pages { for i in 1..=pages {
let frame = sys_frame_alloc().unwrap(); let frame = sys_frame_alloc().unwrap();
let page_addr = stack_top - i * 0x1000; let page_addr = stack_top - i * 0x1000;