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:
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; \
fi
+29 -24
View File
@@ -1,35 +1,40 @@
use crate::arch::{
apic, timer,
x86_64::interrupts::idt::{self, InterruptStackFrame},
};
use super::idt::{self, InterruptFrame, stub_no_err};
use crate::arch::{apic, timer};
pub const PIT_CALIBRATION_VECTOR: u8 = 0xF1;
pub const APIC_TIMER_VECTOR: u8 = 0xFD;
pub const APIC_ERROR_VECTOR: u8 = 0xFE;
pub const APIC_SPURIOUS_VECTOR: u8 = 0xFF;
extern "x86-interrupt" fn error_handler(_frame: InterruptStackFrame) {
apic::record_error();
apic::end_of_interrupt();
}
stub_no_err!(stub_pit_calibration, 0xF1);
stub_no_err!(stub_apic_timer, 0xFD);
stub_no_err!(stub_apic_error, 0xFE);
stub_no_err!(stub_apic_spurious, 0xFF);
extern "x86-interrupt" fn timer_handler(_frame: InterruptStackFrame) {
apic::record_timer();
apic::end_of_interrupt();
}
extern "x86-interrupt" fn pit_calibration_handler(_frame: InterruptStackFrame) {
timer::record_pit_calibration();
apic::end_of_interrupt();
}
extern "x86-interrupt" fn spurious_handler(_frame: InterruptStackFrame) {
// No EOI
pub(super) fn handle(frame: &mut InterruptFrame) {
match frame.vector as u8 {
PIT_CALIBRATION_VECTOR => {
timer::record_pit_calibration();
apic::end_of_interrupt();
}
APIC_TIMER_VECTOR => {
apic::record_timer();
apic::end_of_interrupt();
}
APIC_ERROR_VECTOR => {
apic::record_error();
apic::end_of_interrupt();
}
APIC_SPURIOUS_VECTOR => {
// No EOI
}
_ => {}
}
}
pub(super) fn install(idt: &mut idt::Idt) {
idt.set_handler(PIT_CALIBRATION_VECTOR, pit_calibration_handler, 0);
idt.set_handler(APIC_ERROR_VECTOR, error_handler, 0);
idt.set_handler(APIC_TIMER_VECTOR, timer_handler, 0);
idt.set_handler(APIC_SPURIOUS_VECTOR, spurious_handler, 0);
idt.set_handler(PIT_CALIBRATION_VECTOR, stub_pit_calibration, 0);
idt.set_handler(APIC_ERROR_VECTOR, stub_apic_error, 0);
idt.set_handler(APIC_TIMER_VECTOR, stub_apic_timer, 0);
idt.set_handler(APIC_SPURIOUS_VECTOR, stub_apic_spurious, 0);
}
+72 -64
View File
@@ -1,79 +1,87 @@
use core::arch::asm;
use super::idt::{self, InterruptStackFrame};
use super::idt::{self, InterruptFrame, InterruptStackFrame, stub_err, stub_no_err};
use crate::{hcf, println};
macro_rules! fatal_without_error_code {
($handler:ident, $name:literal) => {
extern "x86-interrupt" fn $handler(frame: InterruptStackFrame) {
fatal_exception($name, &frame, None);
stub_no_err!(stub_divide_error, 0);
stub_no_err!(stub_debug, 1);
stub_no_err!(stub_non_maskable_interrupt, 2);
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 {
($handler:ident, $name:literal) => {
extern "x86-interrupt" fn $handler(frame: InterruptStackFrame, error_code: u64) {
fatal_exception($name, &frame, Some(error_code));
println!("User test exit");
hcf();
}
_ => {
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) {
idt.set_handler(0, divide_error_handler, 0);
idt.set_handler(1, debug_handler, 0);
idt.set_handler(2, non_maskable_interrupt_handler, 0);
idt.set_user_handler(3, breakpoint_handler, 0);
idt.set_handler(6, invalid_opcode_handler, 0);
idt.set_handler(7, device_not_available_handler, 0);
idt.set_error_code_handler(8, double_fault_handler, 1);
idt.set_error_code_handler(10, invalid_tss_handler, 0);
idt.set_error_code_handler(11, segment_not_present_handler, 0);
idt.set_error_code_handler(12, stack_segment_fault_handler, 0);
idt.set_error_code_handler(13, general_protection_handler, 0);
idt.set_error_code_handler(14, page_fault_handler, 0);
idt.set_handler(16, x87_floating_point_handler, 0);
idt.set_error_code_handler(17, alignment_check_handler, 0);
idt.set_handler(18, machine_check_handler, 0);
idt.set_handler(19, simd_floating_point_handler, 0);
idt.set_handler(0, stub_divide_error, 0);
idt.set_handler(1, stub_debug, 0);
idt.set_handler(2, stub_non_maskable_interrupt, 0);
idt.set_user_handler(3, stub_breakpoint, 0);
idt.set_handler(6, stub_invalid_opcode, 0);
idt.set_handler(7, stub_device_not_available, 0);
idt.set_handler(8, stub_double_fault, 1);
idt.set_handler(10, stub_invalid_tss, 0);
idt.set_handler(11, stub_segment_not_present, 0);
idt.set_handler(12, stub_stack_segment_fault, 0);
idt.set_handler(13, stub_general_protection, 0);
idt.set_handler(14, stub_page_fault, 0);
idt.set_handler(16, stub_x87_floating_point, 0);
idt.set_handler(17, stub_alignment_check, 0);
idt.set_handler(18, stub_machine_check, 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 {
+134 -17
View File
@@ -18,7 +18,7 @@ struct IdtEntry {
impl IdtEntry {
const fn missing() -> Self {
return Self {
Self {
offset_low: 0,
code_selector: 0,
ist: 0,
@@ -26,7 +26,7 @@ impl IdtEntry {
offset_middle: 0,
offset_high: 0,
reserved: 0,
};
}
}
}
@@ -37,8 +37,8 @@ struct IdtPointer {
}
#[repr(C)]
#[derive(Clone, Copy)]
pub(super) struct InterruptStackFrame {
#[derive(Clone, Copy, Debug)]
pub struct InterruptStackFrame {
pub instruction_pointer: VirtualAddr,
pub code_segment: u64,
pub cpu_flags: u64,
@@ -46,14 +46,36 @@ pub(super) struct InterruptStackFrame {
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 PRESENT: u8 = 1 << 7;
const KERNEL_INTERRUPT_GATE: u8 = PRESENT | INTERRUPT_GATE;
const USER_DPL: u8 = 3 << 5;
const USER_INTERRUPT_GATE: u8 = PRESENT | USER_DPL | INTERRUPT_GATE;
pub(super) type Handler = extern "x86-interrupt" fn(InterruptStackFrame);
pub(super) type ErrorCodeHandler = extern "x86-interrupt" fn(InterruptStackFrame, u64);
pub(super) type RawHandler = unsafe extern "C" fn();
pub(super) struct Idt {
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);
}
pub(super) fn set_error_code_handler(
&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) {
pub(super) fn set_user_handler(&mut self, vector: u8, handler: RawHandler, ist: u8) {
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::<IdtPointer>() == 10);
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() {
let mut idt = Idt::new();
+63 -1
View File
@@ -4,7 +4,7 @@ use crate::{
arch::x86_64::cpu::CpuFeatures,
memory::{
CachePolicy, DirectMap, FrameAddr, FrameAllocator, OwnedFrame, PagePermissions,
PhysicalAddr, VirtualAddr,
PageTableMapping, PhysicalAddr, VirtualAddr,
},
};
@@ -199,6 +199,14 @@ impl PageTableEntry {
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 {
self.0 & Self::USER_ACCESSIBLE != 0
}
@@ -226,6 +234,14 @@ impl PageTableEntry {
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 {
@@ -363,6 +379,52 @@ impl PageTable {
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(
&self,
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;
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_start = offset;
offset += core::mem::size_of::<Header>();
-1
View File
@@ -1,4 +1,3 @@
#![feature(abi_x86_interrupt)]
#![allow(clippy::needless_return)]
#![no_std]
#![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;
struct AddressSpaceTable {
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() {
if slot.is_none() {
*slot = Some(address_space);
return Some(i);
return Ok(AddressSpaceId(i));
}
}
None
Err(address_space)
}
fn get(&self, id: usize) -> Option<&AddressSpace> {
self.entries.get(id).and_then(Option::as_ref)
fn get(&self, id: AddressSpaceId) -> Option<&AddressSpace> {
self.entries.get(id.0).and_then(Option::as_ref)
}
fn get_mut(&mut self, id: usize) -> Option<&mut AddressSpace> {
self.entries.get_mut(id).and_then(Option::as_mut)
fn get_mut(&mut self, id: AddressSpaceId) -> Option<&mut AddressSpace> {
self.entries.get_mut(id.0).and_then(Option::as_mut)
}
fn remove(&mut self, id: usize) -> Option<AddressSpace> {
self.entries.get_mut(id).and_then(Option::take)
fn remove(&mut self, id: AddressSpaceId) -> Option<AddressSpace> {
self.entries.get_mut(id.0).and_then(Option::take)
}
}
@@ -51,20 +55,19 @@ unsafe impl Sync for GlobalAddressSpaceTable {}
static ADDRESS_SPACE_TABLE: GlobalAddressSpaceTable =
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 id = table.insert(address_space);
id
table.insert(address_space)
}
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() };
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 table = unsafe { &*ADDRESS_SPACE_TABLE.0.get() };
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
}
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 table = unsafe { &mut *ADDRESS_SPACE_TABLE.0.get() };
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)]
enum AddressSpaceKind {
Kernel,
@@ -427,6 +437,10 @@ impl AddressSpace {
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) {
unsafe { self.root.activate() }
}
+3 -3
View File
@@ -7,9 +7,9 @@ use core::ops::Add;
#[allow(unused)]
pub use address_space::{
AddressSpace, AddressSpaceCreateError, MapError, UnmapError, init_kernel_address_space,
insert_address_space, remove_address_space, with_address_space, with_address_space_mut,
with_kernel_address_space,
AddressSpace, AddressSpaceCreateError, AddressSpaceId, MapError, PageTableMapping, UnmapError,
init_kernel_address_space, insert_address_space, remove_address_space, with_address_space,
with_address_space_mut, with_kernel_address_space,
};
pub use frame::{
FRAME_SIZE, FrameAddr, FrameAllocator, OwnedFrame, alloc_frame, dealloc_frame,
+6 -1
View File
@@ -237,8 +237,13 @@ impl KernelStackPool {
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;
crate::memory::with_kernel_address_space(|kernel_as| {
crate::memory::with_allocator(|allocator| unsafe {
stack.destroy(kernel_as, allocator)
});
});
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 fn copy_from_user(src: VirtualAddr, dst: &mut [u8]) -> Result<(), Status> {
// TODO: guard against unmapped pages
pub fn validate_user_range(
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
.as_usize()
.checked_add(dst.len())
@@ -19,7 +61,10 @@ pub fn copy_from_user(src: VirtualAddr, dst: &mut [u8]) -> Result<(), Status> {
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
.as_usize()
.checked_add(src.len())
@@ -35,7 +80,10 @@ pub fn copy_to_user(dst: VirtualAddr, src: &[u8]) -> Result<(), Status> {
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 {
return Err(Status::InvalidArgument);
}
@@ -54,7 +102,11 @@ pub fn copy_val_to_user<T: Copy>(dst: VirtualAddr, val: &T) -> Result<(), Status
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 {
return Err(Status::InvalidArgument);
}
+10 -6
View File
@@ -2,7 +2,7 @@ mod table;
use table::*;
#[derive(Clone, Copy, PartialEq, Eq)]
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
#[repr(u64)]
pub enum Status {
// 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 syscall = SyscallNumber::try_from(num)?;
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::FrameDealloc => sys_frame_dealloc(arg0 as usize),
SyscallNumber::AsCreate => sys_as_create(arg0 as usize),
SyscallNumber::Map => {
sys_map(arg0 as usize, arg1 as usize, arg2 as usize, arg3 as usize)
}
SyscallNumber::Unmap => sys_unmap(arg0 as usize, arg1 as usize),
SyscallNumber::Map => sys_map(
arg0 as usize,
arg1 as usize,
arg2 as usize,
arg3 as usize,
arg4 as usize,
),
SyscallNumber::Unmap => sys_unmap(arg0 as usize),
SyscallNumber::TaskCreate => {
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::{
memory::{
FRAME_SIZE, OwnedFrame, PagePermissions, USER_SPACE_END, VirtualAddr, copy_from_user,
copy_to_user, copy_val_to_user,
FRAME_SIZE, MapError, PagePermissions, USER_SPACE_END, VirtualAddr, copy_from_user,
copy_to_user, copy_val_to_user, validate_user_range,
},
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;
@@ -23,32 +26,60 @@ pub fn sys_write(fd: usize, buf_ptr: usize, len: usize, out_ptr: usize) -> Resul
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 written = 0;
while written < 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]);
written += n;
}
if out_ptr != 0 {
copy_val_to_user(VirtualAddr::new(out_ptr), &written)?;
unsafe {
copy_val_to_user(VirtualAddr::new(out_ptr), &written)?;
}
}
Ok(())
}
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 {
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();
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 {
sender,
@@ -56,14 +87,18 @@ pub fn sys_send(dest_task_id: usize, msg_ptr: usize, len: usize) -> Result<(), S
data: msg_buf,
};
if !dest_task.mailbox.push(msg) {
return Err(Status::OutOfMemory);
}
let should_unblock = crate::task::scheduler::with_task_mut(dest_task_id, |dest_task| {
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!(
dest_task.state,
crate::task::tcb::ThreadState::Blocked(BlockReason::Recv { .. })
) {
if should_unblock {
crate::task::scheduler::unblock(dest_task_id);
}
@@ -80,28 +115,58 @@ pub fn sys_recv(
return Err(Status::InvalidArgument);
}
let current_task = crate::task::scheduler::get_task_mut(crate::task::scheduler::current())
.ok_or(Status::NoSuchTask)?;
crate::task::scheduler::with_task(crate::task::scheduler::current(), |current_task| {
if current_task.mailbox.len == 0 {
crate::task::scheduler::block_current(BlockReason::Recv);
}
if current_task.mailbox.len == 0 {
crate::task::scheduler::block_current(BlockReason::Recv { ep: 0 });
}
validate_user_range(current_task.as_id, VirtualAddr::new(out_ptr), max_len, true)?;
// 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())
.ok_or(Status::NoSuchTask)?;
let msg = current_task.mailbox.pop().ok_or(Status::NoSuchTask)?;
if out_sender != 0 {
if out_sender % core::mem::align_of::<usize>() != 0 {
return Err(Status::InvalidArgument);
}
validate_user_range(
current_task.as_id,
VirtualAddr::new(out_sender),
core::mem::size_of::<usize>(),
true,
)?;
}
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(())
})
.expect("failed to resolve self task")?;
let msg =
crate::task::scheduler::with_task_mut(crate::task::scheduler::current(), |current_task| {
current_task.mailbox.pop().ok_or(Status::NoSuchTask)
})
.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(())
@@ -112,49 +177,69 @@ pub fn sys_frame_alloc(out_handle: usize) -> Result<(), Status> {
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 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 {
object: KernelObject::Frame(frame_addr),
rights: Rights::READ | Rights::WRITE | Rights::EXECUTE,
object: KernelObject::Frame(frame),
rights: Rights::READ | Rights::WRITE | Rights::EXECUTE | Rights::MAP,
};
let handle_id = match task.handles.push(handle) {
Some(id) => id,
None => {
unsafe { crate::memory::dealloc_frame(OwnedFrame::from_raw(frame_addr)) };
return Err(Status::OutOfMemory);
}
};
crate::task::scheduler::with_task_mut(crate::task::scheduler::current(), |current_task| {
let handle_id = match current_task.handles.push(handle) {
Ok(id) => id,
Err(handle) => {
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)?;
Ok(())
unsafe { copy_val_to_user(VirtualAddr::new(out_handle), &handle_id) }
})
.expect("failed to resolve self task")
}
pub fn sys_frame_dealloc(frame_handle: usize) -> Result<(), Status> {
let task = crate::task::scheduler::current();
let task = crate::task::scheduler::get_task_mut(task).ok_or(Status::NoSuchTask)?;
pub fn sys_frame_dealloc(frame_handle_id: usize) -> Result<(), Status> {
crate::task::scheduler::with_task_mut(crate::task::scheduler::current(), |task| {
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)?;
let frame = match frame_handle.object {
KernelObject::Frame(frame_addr) => frame_addr,
_ => return Err(Status::InvalidArgument),
};
Ok(())
}
_ => {
// 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)) };
Ok(())
return Err(Status::InvalidArgument);
}
}
})
.expect("failed to resolve self task")
}
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);
}
let task_id = crate::task::scheduler::current();
let task = crate::task::scheduler::get_task_mut(task_id).ok_or(Status::NoSuchTask)?;
if out_handle % core::mem::align_of::<usize>() != 0 {
return Err(Status::InvalidArgument);
}
let new_as = match crate::memory::with_address_space(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),
let new_as =
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,
)?;
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 {
object: KernelObject::AddressSpace(as_id),
rights: Rights::READ | Rights::WRITE | Rights::EXECUTE,
};
let handle_id = match task.handles.push(handle) {
Some(id) => id,
None => {
crate::memory::remove_address_space(as_id);
return Err(Status::OutOfMemory);
}
};
crate::task::scheduler::with_task_mut(crate::task::scheduler::current(), |current_task| {
let handle_id = match current_task.handles.push(handle) {
Ok(id) => id,
Err(handle) => {
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)?;
Ok(())
unsafe { copy_val_to_user(VirtualAddr::new(out_handle), &handle_id) }
})
.expect("failed to resolve self task")
}
pub fn sys_map(
@@ -197,41 +310,81 @@ pub fn sys_map(
frame_handle: usize,
virtual_addr: usize,
permissions: usize,
out_handle: usize,
) -> Result<(), Status> {
if virtual_addr % FRAME_SIZE != 0 {
if out_handle == 0 || out_handle % core::mem::align_of::<usize>() != 0 {
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);
}
let task = crate::task::scheduler::current();
let task = crate::task::scheduler::get_task_mut(task).ok_or(Status::NoSuchTask)?;
let writable = permissions & (1 << 0) != 0;
let executable = permissions & (1 << 1) != 0;
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 frame_handle = task.handles.get(frame_handle).ok_or(Status::BadHandle)?;
let frame = match frame_handle.object {
KernelObject::Frame(frame_addr) => frame_addr,
_ => return Err(Status::InvalidArgument),
let end = virtual_addr
.checked_add(FRAME_SIZE)
.ok_or(Status::InvalidArgument)?;
if end > USER_SPACE_END.as_usize() {
return Err(Status::InvalidArgument);
}
let current_task = crate::task::scheduler::current();
let as_id = crate::task::scheduler::with_task(current_task, |task| {
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 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| {
crate::memory::with_allocator(|allocator| {
target_as.map(
frame.start_address(),
frame.frame_address().start_address(),
virtual_addr,
permissions,
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> {
if virtual_addr % FRAME_SIZE != 0 {
pub fn sys_unmap(mapping_handle: usize) -> Result<(), Status> {
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);
}
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 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);
}
let unmap_result = crate::memory::with_address_space_mut(address_space, |target_as| {
crate::memory::with_allocator(|allocator| unsafe {
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(
@@ -289,31 +545,64 @@ pub fn sys_task_create(
user_stack: usize,
out_task_handle: usize,
) -> 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);
}
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);
}
let task_id = crate::task::scheduler::current();
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 {
if out_task_handle % core::mem::align_of::<usize>() != 0 {
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() {
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| {
crate::memory::with_allocator(|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(
0, // assigned by scheduler::add_task
as_id,
kernel_stack,
VirtualAddr::new(entry),
@@ -344,15 +632,16 @@ pub fn sys_task_create(
rights: Rights::READ | Rights::WRITE | Rights::EXECUTE,
};
let handle_id = match task.handles.push(handle) {
Some(id) => id,
None => {
crate::task::scheduler::remove_task(new_task_id);
return Err(Status::OutOfMemory);
}
};
crate::task::scheduler::with_task_mut(crate::task::scheduler::current(), |current_task| {
let handle_id = match current_task.handles.push(handle) {
Ok(id) => id,
Err(_) => {
crate::task::scheduler::remove_task(new_task_id);
return Err(Status::OutOfMemory);
}
};
copy_val_to_user(VirtualAddr::new(out_task_handle), &handle_id)?;
Ok(())
unsafe { copy_val_to_user(VirtualAddr::new(out_task_handle), &handle_id) }
})
.expect("failed to resolve self task")
}
+9 -5
View File
@@ -4,7 +4,7 @@ use crate::{
self, AddressSpace, DirectMap, FRAME_SIZE, FrameAllocator, InitramfsImage, PagePermissions,
UserStack, VirtualAddr,
},
task::tcb::Tcb,
task::{scheduler::TaskId, tcb::Tcb},
};
pub fn spawn(
@@ -13,7 +13,7 @@ pub fn spawn(
kernel_as: &mut AddressSpace,
allocator: &mut FrameAllocator,
direct_map: DirectMap,
) -> usize {
) -> TaskId {
let bytes = format::cpio::find_file(initramfs.data(), name)
.unwrap_or_else(|| panic!("{name} missing from initramfs"));
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 as_id =
crate::memory::insert_address_space(address_space).expect("address space table is full");
let as_id = match crate::memory::insert_address_space(address_space) {
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")
}
+52 -28
View File
@@ -3,7 +3,8 @@ use core::cell::UnsafeCell;
use crate::{
arch::ThreadContext,
memory::{
AddressSpace, FrameAllocator, KernelStack, KernelStackPool, StackCreateError, VirtualAddr,
AddressSpace, AddressSpaceId, FrameAllocator, KernelStack, KernelStackPool,
StackCreateError, VirtualAddr,
},
println,
task::tcb::{BlockReason, ExitReason, Handle, KernelObject, Rights, Tcb, ThreadState},
@@ -11,7 +12,15 @@ use crate::{
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 {
current: Option<TaskId>,
@@ -33,11 +42,11 @@ impl Scheduler {
fn make_switch(&mut self, current_id: TaskId, next_id: TaskId) -> Switch {
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_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_as_id = next.as_id;
let next_kernel_stack = next.kernel_stack.top();
@@ -55,7 +64,7 @@ impl Scheduler {
struct Switch {
previous_context: *mut ThreadContext,
next_context: *const ThreadContext,
next_as_id: usize,
next_as_id: AddressSpaceId,
next_kernel_stack: VirtualAddr,
activate_address_space: bool,
}
@@ -85,7 +94,7 @@ struct ReadyQueue {
impl ReadyQueue {
pub const fn new() -> Self {
Self {
entries: [0; MAX_TASKS],
entries: [TaskId(0); MAX_TASKS],
head: 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) {
Some(id) => {
let id = TaskId(id);
task.id = id;
task.handles.push(Handle {
match task.handles.push(Handle {
object: KernelObject::Thread(id),
rights: Rights::READ | Rights::WRITE | Rights::EXECUTE,
});
}) {
Ok(_) => {}
Err(_) => unreachable!("Cant push root thread handle"),
}
task.state = ThreadState::Ready;
scheduler.tasks[id] = Some(task);
scheduler.tasks[id.0] = Some(task);
assert!(scheduler.ready.push_back(id));
Ok(id)
}
@@ -176,7 +190,7 @@ pub fn start() -> ! {
let scheduler = unsafe { &mut *SCHEDULER.0.get() };
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()
.expect("ready task is missing");
@@ -210,20 +224,20 @@ pub fn allocate_kernel_stack(
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 result = {
let scheduler = unsafe { &mut *SCHEDULER.0.get() };
if scheduler.current == Some(id) {
None
} else if let Some(task) = scheduler.tasks.get_mut(id).and_then(Option::take) {
false
} else if let Some(task) = scheduler.tasks.get_mut(id.0).and_then(Option::take) {
scheduler.ready.remove(id);
scheduler.stacks.free(&task.kernel_stack);
Some(task)
scheduler.stacks.free(task.kernel_stack);
true
} else {
None
false
}
};
@@ -231,14 +245,24 @@ pub fn remove_task(id: TaskId) -> Option<Tcb> {
result
}
pub fn get_task(id: TaskId) -> Option<&'static Tcb> {
let scheduler = unsafe { &mut *SCHEDULER.0.get() };
scheduler.tasks.get(id).and_then(Option::as_ref)
pub fn with_task<R>(id: TaskId, f: impl FnOnce(&Tcb) -> R) -> Option<R> {
let interrupt_state = crate::arch::disable_interrupts_and_save();
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() };
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 {
@@ -259,10 +283,10 @@ pub fn block_current(reason: BlockReason) {
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
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.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 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(_)) {
task.state = ThreadState::Ready;
assert!(scheduler.ready.push_back(id));
@@ -303,10 +327,10 @@ pub fn yield_current() {
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));
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.make_switch(current_id, next_id)
@@ -331,10 +355,10 @@ pub fn exit_current(exit_code: usize) -> ! {
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));
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.make_switch(current_id, next_id)
+54 -19
View File
@@ -2,22 +2,29 @@ use core::ops::BitOr;
use crate::{
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
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum ExitReason {
Exited(usize),
Killed,
Fault,
Fault(Fault),
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum BlockReason {
Send { ep: usize },
Recv { ep: usize },
Reply { client: usize },
Recv,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
@@ -33,7 +40,7 @@ pub const MAILBOX_CAPACITY: usize = 4;
#[derive(Clone, Copy)]
pub struct Message {
pub sender: usize,
pub sender: TaskId,
pub length: usize,
pub data: [u8; MAX_MSG_SIZE],
}
@@ -79,9 +86,14 @@ impl Mailbox {
const MAX_HANDLES: usize = 32;
pub enum KernelObject {
AddressSpace(usize),
Frame(FrameAddr),
Thread(usize),
AddressSpace(AddressSpaceId),
Frame(OwnedFrame),
Mapping {
frame: OwnedFrame,
address_space: AddressSpaceId,
virtual_addr: VirtualAddr,
},
Thread(TaskId),
}
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() {
if slot.is_none() {
*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> {
@@ -139,8 +172,8 @@ impl HandleTable {
}
pub struct Tcb {
pub id: usize,
pub as_id: usize,
pub id: TaskId,
pub as_id: AddressSpaceId,
pub state: ThreadState,
pub kernel_stack: KernelStack,
pub context: ThreadContext,
@@ -161,21 +194,23 @@ impl core::fmt::Debug for Tcb {
impl Tcb {
pub fn new_user(
id: usize,
as_id: usize,
as_id: AddressSpaceId,
kernel_stack: KernelStack,
entry: VirtualAddr,
user_stack: VirtualAddr,
) -> Self {
let context = ThreadContext::new(entry, user_stack, kernel_stack.top());
let mut handles = HandleTable::new();
handles.push(Handle {
match handles.push(Handle {
object: KernelObject::AddressSpace(as_id),
rights: Rights::READ | Rights::WRITE | Rights::EXECUTE,
});
}) {
Ok(_) => {}
Err(_) => unreachable!("Cant push root address space handle"),
};
Self {
id,
id: TaskId::new(0),
as_id,
state: ThreadState::Ready,
kernel_stack,
+3 -5
View File
@@ -8,12 +8,10 @@ pub extern "C" fn _start() -> ! {
let msg = "Hello from client!";
println!("[client] Sent: {}", msg);
// 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 out_ptr = out.as_ptr() as usize;
let max_len = out.len();
let (actual_len, _) = sys_recv(out_ptr, max_len).unwrap();
let mut out = [0u8; 128];
let (actual_len, _) = sys_recv(&mut out).unwrap();
println!(
"[client] Received: {}",
core::str::from_utf8(&out[..actual_len]).unwrap()
+37 -24
View File
@@ -10,15 +10,25 @@ pub enum Status {
BadFileDescriptor = 3,
NoSuchTask = 4,
OutOfMemory = 5,
BadHandle = 6,
}
// Opaque handle type
#[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
pub const SELF_AS: Handle = Handle(0);
pub const SELF_THREAD: Handle = Handle(1);
pub const SELF_AS: AddressSpaceHandle = AddressSpaceHandle(0);
pub const SELF_THREAD: ThreadHandle = ThreadHandle(1);
impl From<usize> for Status {
fn from(value: usize) -> Self {
@@ -28,6 +38,7 @@ impl From<usize> for Status {
3 => Self::BadFileDescriptor,
4 => Self::NoSuchTask,
5 => Self::OutOfMemory,
6 => Self::BadHandle,
_ => 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 {
let status: usize;
asm!(
"syscall",
in("rdi") dest_task_id,
in("rsi") msg_ptr,
in("rdx") len,
in("rsi") msg.as_ptr(),
in("rdx") msg.len(),
inlateout("rax") SyscallNumber::Send as usize => status,
lateout("rcx") _,
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 sender: usize = 0;
@@ -156,8 +167,8 @@ pub fn sys_recv(buf_ptr: usize, max_len: usize) -> Result<(usize, usize), Status
asm!(
"syscall",
in("rdi") buf_ptr,
in("rsi") max_len,
in("rdi") buf.as_mut_ptr(),
in("rsi") buf.len(),
in("rdx") &raw mut actual_len as usize,
in("r10") &raw mut sender as usize,
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;
unsafe {
let status: usize;
@@ -189,12 +200,12 @@ pub fn sys_frame_alloc() -> Result<Handle, Status> {
if status != 0 {
Err(status.into())
} 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 {
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;
unsafe {
let status: usize;
@@ -230,17 +241,19 @@ pub fn sys_as_create() -> Result<Handle, Status> {
if status != 0 {
Err(status.into())
} else {
Ok(Handle(handle))
Ok(AddressSpaceHandle(handle))
}
}
}
pub fn sys_map(
as_handle: Handle,
frame_handle: Handle,
as_handle: AddressSpaceHandle,
frame_handle: FrameHandle,
virtual_addr: usize,
permissions: usize,
) -> Result<(), Status> {
) -> Result<MappingHandle, Status> {
let mut handle: usize = 0;
unsafe {
let status: usize;
@@ -250,6 +263,7 @@ pub fn sys_map(
in("rsi") frame_handle.0,
in("rdx") virtual_addr,
in("r10") permissions,
in("r8") &raw mut handle as usize,
inlateout("rax") SyscallNumber::Map as usize => status,
lateout("rcx") _,
lateout("r11") _,
@@ -258,19 +272,18 @@ pub fn sys_map(
if status != 0 {
Err(status.into())
} 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 {
let status: usize;
asm!(
"syscall",
in("rdi") as_handle.0,
in("rsi") virtual_addr,
in("rdi") mapping_handle.0,
inlateout("rax") SyscallNumber::Unmap as usize => status,
lateout("rcx") _,
lateout("r11") _,
@@ -285,10 +298,10 @@ pub fn sys_unmap(as_handle: Handle, virtual_addr: usize) -> Result<(), Status> {
}
pub fn sys_task_create(
as_handle: Handle,
as_handle: AddressSpaceHandle,
entry: usize,
user_stack: usize,
) -> Result<Handle, Status> {
) -> Result<ThreadHandle, Status> {
let mut handle: usize = 0;
unsafe {
@@ -308,7 +321,7 @@ pub fn sys_task_create(
if status != 0 {
Err(status.into())
} 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)]
pub extern "C" fn _start() -> ! {
let out = [0u8; 128];
let mut out = [0u8; 128];
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!(
"[echo] Received: {}",
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;
use dusk_sys::{
Handle, SELF_AS, println, sys_as_create, sys_exit, sys_frame_alloc, sys_map, sys_task_create,
sys_unmap, sys_yield,
AddressSpaceHandle, SELF_AS, println, sys_as_create, sys_exit, sys_frame_alloc, sys_map,
sys_task_create, sys_unmap, sys_yield,
};
// 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);
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);
}
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() {
let header = header.unwrap();
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) {
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 {
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();
}
@@ -93,7 +98,7 @@ fn load_elf(elf: &elf::Elf, target_as: Handle) -> usize {
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 {
let frame = sys_frame_alloc().unwrap();
let page_addr = stack_top - i * 0x1000;