From 4c32bcfb35103350ee445c32a2d8938211389516 Mon Sep 17 00:00:00 2001 From: Zoe Date: Mon, 31 Aug 2026 04:46:13 -0500 Subject: [PATCH] feat: tasks --- src/arch/mod.rs | 5 +- src/arch/x86_64/apic.rs | 1 - src/arch/x86_64/cpu.rs | 110 ++++++++++++++++++++++++++++- src/arch/x86_64/gdt.rs | 14 ++-- src/arch/x86_64/interrupts/mod.rs | 27 ++++++++ src/arch/x86_64/io_apic.rs | 1 - src/arch/x86_64/mod.rs | 55 +++++++++++---- src/arch/x86_64/paging.rs | 9 +++ src/arch/x86_64/syscall.rs | 111 ++++++++++++++++++++++++++++++ src/main.rs | 63 ++++++++++++++--- src/memory/address_space.rs | 1 + src/memory/mod.rs | 5 +- src/memory/stack.rs | 62 +++++++++++++++-- src/task/mod.rs | 2 + src/task/syscall.rs | 18 +++++ src/task/tcb.rs | 72 +++++++++++++++++++ 16 files changed, 513 insertions(+), 43 deletions(-) create mode 100644 src/arch/x86_64/syscall.rs create mode 100644 src/task/mod.rs create mode 100644 src/task/syscall.rs create mode 100644 src/task/tcb.rs diff --git a/src/arch/mod.rs b/src/arch/mod.rs index 7807187..8ac10fb 100644 --- a/src/arch/mod.rs +++ b/src/arch/mod.rs @@ -5,4 +5,7 @@ mod x86_64; pub use x86_64::*; #[cfg(target_arch = "x86_64")] -pub(crate) use x86_64::{PageTableCreateError, PageTableMapError, PageTableUnmapError}; +pub(crate) use x86_64::{ + PageTableCreateError, PageTableMapError, PageTableUnmapError, ThreadContext, set_kernel_stack, + switch_context, +}; diff --git a/src/arch/x86_64/apic.rs b/src/arch/x86_64/apic.rs index a03e9e0..965013b 100644 --- a/src/arch/x86_64/apic.rs +++ b/src/arch/x86_64/apic.rs @@ -13,7 +13,6 @@ use crate::{ memory::{ AddressSpace, CachePolicy, FrameAllocator, PagePermissions, PhysicalAddr, VirtualAddr, }, - println, }; const APIC_ID: u32 = 0x20; diff --git a/src/arch/x86_64/cpu.rs b/src/arch/x86_64/cpu.rs index 834599e..a0c1826 100644 --- a/src/arch/x86_64/cpu.rs +++ b/src/arch/x86_64/cpu.rs @@ -1,8 +1,104 @@ -use core::arch::asm; +use core::arch::{asm, naked_asm}; + +use crate::memory::VirtualAddr; + +#[repr(C, align(64))] +pub struct CpuLocal { + pub kernel_stack_top: usize, + pub user_rsp_scratch: usize, + pub cpu_id: u32, +} + +pub static mut BOOT_CPU: CpuLocal = CpuLocal { + kernel_stack_top: 0, + user_rsp_scratch: 0, + cpu_id: 0, +}; + +#[derive(Debug)] +pub struct ThreadContext { + rsp: usize, +} + +impl ThreadContext { + pub fn new( + user_entry: VirtualAddr, + user_stack: VirtualAddr, + kernel_stack_top: VirtualAddr, + ) -> Self { + // Stack layout (grows downwards from kernel_stack_top): + // [top - 8] = user_thread_entry (popped by `ret`) + // [top - 16] = rbp (0) + // [top - 24] = rbx (user_stack) + // [top - 32] = r12 (user_entry) + // [top - 40] = r13 (0) + // [top - 48] = r14 (0) + // [top - 56] = r15 (0) <- initial rsp + + let stack_ptr = (kernel_stack_top.as_usize() - 56) as *mut usize; + + unsafe { + stack_ptr.add(0).write(0); // r15 + stack_ptr.add(1).write(0); // r14 + stack_ptr.add(2).write(0); // r13 + stack_ptr.add(3).write(user_entry.as_usize()); // r12 (user_entry) + stack_ptr.add(4).write(user_stack.as_usize()); // rbx (user_stack) + stack_ptr.add(5).write(0); // rbp (0) + stack_ptr + .add(6) + .write(user_thread_entry as *const () as usize); // return address + } + + Self { + rsp: kernel_stack_top.as_usize() - 56, + } + } + + pub fn empty() -> Self { + Self { rsp: 0 } + } +} + +#[unsafe(naked)] +unsafe extern "C" fn user_thread_entry() -> ! { + naked_asm!( + // r12 = user_entry, rbx = user_stack + "mov rdi, r12", + "mov rsi, rbx", + "call {enter_user}", + enter_user = sym crate::arch::enter_user, + ); +} + +#[unsafe(naked)] +pub unsafe extern "C" fn switch_context(prev: *mut ThreadContext, next: *const ThreadContext) { + naked_asm!( + "push rbp", + "push rbx", + "push r12", + "push r13", + "push r14", + "push r15", + "", + // save current rsp into prev.rsp + "mov [rdi], rsp", + // load next rsp into rsp + "mov rsp, [rsi]", + "", + "pop r15", + "pop r14", + "pop r13", + "pop r12", + "pop rbx", + "pop rbp", + "ret", + ) +} #[derive(Debug)] pub enum CpuFeaturesError { CpuidFeaturesNotSupported, + SyscallNotSupported, InvalidPhysicalAddressWidth, InvalidVirtualAddressWidth, } @@ -40,6 +136,18 @@ pub fn detect_features_and_enable() -> Result { features.nx_supported = cpuid_result.edx & (1 << 20) != 0; + // TODO: on AMD K6 *only*, this bit is bit 10, should we consider that edge case? + let syscall_supported = cpuid_result.edx & (1 << 11) != 0; + if !syscall_supported { + return Err(CpuFeaturesError::SyscallNotSupported); + } + + unsafe { + let efer = read_msr(IA32_EFER); + let value = efer | 1; + write_msr(IA32_EFER, value); + }; + let cpuid_result = core::arch::x86_64::__cpuid(0x80000008); features.physical_address_bits = (cpuid_result.eax & 0xFF) as u8; diff --git a/src/arch/x86_64/gdt.rs b/src/arch/x86_64/gdt.rs index 9873280..0977a4a 100644 --- a/src/arch/x86_64/gdt.rs +++ b/src/arch/x86_64/gdt.rs @@ -3,8 +3,8 @@ use core::arch::asm; use crate::memory::VirtualAddr; #[repr(C, align(8))] -struct Gdt { - entries: [u64; 7], +pub(super) struct Gdt { + pub entries: [u64; 7], } impl Gdt { @@ -49,12 +49,12 @@ const DOUBLE_FAULT_STACK_SIZE: usize = 16 * 1024; pub(super) const KERNEL_CODE_SELECTOR: u16 = 1 * 8; pub(super) const KERNEL_DATA_SELECTOR: u16 = 2 * 8; -pub(super) const USER_CODE_SELECTOR: u16 = (3 * 8) | 3; -pub(super) const USER_DATA_SELECTOR: u16 = (4 * 8) | 3; +pub(super) const USER_DATA_SELECTOR: u16 = (3 * 8) | 3; +pub(super) const USER_CODE_SELECTOR: u16 = (4 * 8) | 3; pub(super) const TSS_SELECTOR: u16 = 5 * 8; #[repr(align(16))] -#[allow(dead_code)] // field 0 is read, rust just cant tell +#[allow(unused)] // field 0 is read, rust just cant tell struct ExceptionStack([u8; DOUBLE_FAULT_STACK_SIZE]); static mut GDT: Gdt = Gdt::new(); @@ -78,8 +78,10 @@ pub fn init() { 0, kernel_code_descriptor(), kernel_data_descriptor(), - user_code_descriptor(), + // In Long Mode, userland CS will be loaded from STAR 63:48 + 16 + // and userland SS from STAR 63:48 + 8 on SYSRET. user_data_descriptor(), + user_code_descriptor(), tss_low, tss_high, ], diff --git a/src/arch/x86_64/interrupts/mod.rs b/src/arch/x86_64/interrupts/mod.rs index 0a2e5b5..fd51830 100644 --- a/src/arch/x86_64/interrupts/mod.rs +++ b/src/arch/x86_64/interrupts/mod.rs @@ -6,6 +6,33 @@ mod idt; pub use idt::idt_init as init; +#[inline(always)] +pub fn disable_interrupts_and_save() -> u64 { + let flags: u64; + + unsafe { + asm!(" + pushfq", + "pop {flags}", + "cli", + flags = out(reg) flags, + ); + } + + flags +} + +#[inline(always)] +pub fn restore_interrupts(flags: u64) { + unsafe { + asm!( + "push {flags}", + "popfq", + flags = in(reg) flags, + ); + } +} + #[inline(always)] pub fn disable_interrupts() { unsafe { diff --git a/src/arch/x86_64/io_apic.rs b/src/arch/x86_64/io_apic.rs index c4d55d3..c78321d 100644 --- a/src/arch/x86_64/io_apic.rs +++ b/src/arch/x86_64/io_apic.rs @@ -3,7 +3,6 @@ use crate::{ AddressSpace, CachePolicy, FrameAllocator, PagePermissions, PhysicalAddr, VirtualAddr, }, platform::acpi::{InterruptPolarity, TriggerMode}, - println, }; const IOWIN: usize = 0x10; diff --git a/src/arch/x86_64/mod.rs b/src/arch/x86_64/mod.rs index fb96e33..3bad29f 100644 --- a/src/arch/x86_64/mod.rs +++ b/src/arch/x86_64/mod.rs @@ -1,16 +1,18 @@ -pub mod apic; +pub(super) mod apic; mod cpu; mod gdt; mod interrupts; -pub mod io_apic; +pub(super) mod io_apic; mod paging; mod pit; pub mod port; +mod syscall; pub mod timer; use core::arch::asm; -pub use interrupts::disable_interrupts; +pub use cpu::{ThreadContext, switch_context}; +pub use interrupts::{disable_interrupts, disable_interrupts_and_save, restore_interrupts}; pub(crate) use paging::{ MapError as PageTableMapError, PageTableCreateError, UnmapError as PageTableUnmapError, }; @@ -22,11 +24,7 @@ pub struct ArchState { use crate::{ KernelHandoff, - arch::{ - apic::LocalApic, - io_apic::{IOAPIC_VIRTUAL_ADDRESS, IoApic}, - x86_64::interrupts::apic_vectors::PIT_CALIBRATION_VECTOR, - }, + arch::x86_64::cpu::BOOT_CPU, memory::{AddressSpace, FrameAllocator, VirtualAddr}, platform::acpi::Madt, println, @@ -45,6 +43,14 @@ pub fn init() -> ArchState { ArchState { paging } } +pub fn set_kernel_stack(stack_top: VirtualAddr) { + gdt::set_kernel_stack(stack_top); + + unsafe { + BOOT_CPU.kernel_stack_top = stack_top.as_usize(); + } +} + #[derive(Debug)] #[allow(unused)] pub enum InterruptInitError { @@ -60,8 +66,8 @@ pub enum InterruptInitError { #[derive(Debug)] pub struct InterruptController { - local_apic: LocalApic, - io_apic: IoApic, + local_apic: apic::LocalApic, + io_apic: io_apic::IoApic, local_timer_frequency: u64, } @@ -89,7 +95,7 @@ pub fn init_interrupt_controller( io_apic_info.id, io_apic_info.apic_address, io_apic_info.global_system_interrupt_base, - IOAPIC_VIRTUAL_ADDRESS, + io_apic::IOAPIC_VIRTUAL_ADDRESS, allocator, address_space, ) @@ -107,7 +113,7 @@ pub fn init_interrupt_controller( .configure_masked( pit_route.gsi, io_apic::RedirectionConfig { - vector: PIT_CALIBRATION_VECTOR, + vector: interrupts::apic_vectors::PIT_CALIBRATION_VECTOR, destination, polarity: pit_route.polarity, trigger: pit_route.trigger, @@ -165,6 +171,9 @@ pub unsafe fn enter_kernel(stack_top: VirtualAddr, handoff: *mut KernelHandoff) unsafe { gdt::set_kernel_stack(stack_top); + BOOT_CPU.kernel_stack_top = stack_top.as_usize(); + syscall::init(&raw const BOOT_CPU); + asm!( "mov rsp, {stack_top}", "xor rbp, rbp", @@ -193,13 +202,31 @@ pub unsafe fn enter_user( "mov ds, {user_data_selector:x}", "mov es, {user_data_selector:x}", "mov fs, {user_data_selector:x}", - "mov gs, {user_data_selector:x}", // ss is handled by iretq + "mov gs, {user_data_selector:x}", "push {user_data_selector}", "push {user_stack_pointer}", - "pushfq", + "push 0x202", // RFLAGS (IF=1, bit 1 reserved=1) "push {user_code_selector}", "push {user_instruction_pointer}", + + // clear GPRs + "xor rax, rax", + "xor rbx, rbx", + "xor rcx, rcx", + "xor rdx, rdx", + "xor rsi, rsi", + "xor rdi, rdi", + "xor rbp, rbp", + "xor r8, r8", + "xor r9, r9", + "xor r10, r10", + "xor r11, r11", + "xor r12, r12", + "xor r13, r13", + "xor r14, r14", + "xor r15, r15", + "iretq", user_data_selector = in(reg) gdt::USER_DATA_SELECTOR as usize, user_code_selector = in(reg) gdt::USER_CODE_SELECTOR as usize, diff --git a/src/arch/x86_64/paging.rs b/src/arch/x86_64/paging.rs index 7cf05a4..0933b64 100644 --- a/src/arch/x86_64/paging.rs +++ b/src/arch/x86_64/paging.rs @@ -264,12 +264,21 @@ pub(crate) enum PageTableCreateError { OutOfFrames, } +#[derive(Debug)] pub struct PageTable { pub direct_map: DirectMap, config: PagingConfig, frame: OwnedFrame, } +impl PartialEq for PageTable { + fn eq(&self, other: &Self) -> bool { + self.frame.frame_address() == other.frame.frame_address() + } +} + +impl Eq for PageTable {} + impl PageTable { pub fn new( direct_map: DirectMap, diff --git a/src/arch/x86_64/syscall.rs b/src/arch/x86_64/syscall.rs new file mode 100644 index 0000000..f63abb4 --- /dev/null +++ b/src/arch/x86_64/syscall.rs @@ -0,0 +1,111 @@ +use core::arch::{asm, naked_asm}; + +use crate::arch::x86_64::{ + cpu::{CpuLocal, write_msr}, + gdt::{KERNEL_CODE_SELECTOR, KERNEL_DATA_SELECTOR}, +}; + +const IA32_STAR: u32 = 0xC000_0081; +const IA32_LSTAR: u32 = 0xC000_0082; +const IA32_CSTAR: u32 = 0xC000_0083; +const IA32_FMASK: u32 = 0xC000_0084; +const IA32_GS_BASE: u32 = 0xC000_0101; +const IA32_KERNEL_GS_BASE: u32 = 0xC000_0102; + +const RFLAGS_MASK: u64 = 0x257FD5; // Clear IF, TF, DF, IOPL, NT, AC + +#[repr(C)] +#[derive(Debug)] +struct SyscallFrame { + pub r15: u64, + pub r14: u64, + pub r13: u64, + pub r12: u64, + pub rbp: u64, + pub rbx: u64, + pub r9: u64, // arg5 + pub r8: u64, // arg4 + pub r10: u64, // arg3 + pub rdx: u64, // arg2 + pub rsi: u64, // arg1 + pub rdi: u64, // arg0 + pub rax: u64, // syscall number on entry / return value on exit + pub user_rip: u64, // rcx + pub user_rflags: u64, // r11 + pub user_rsp: u64, +} + +pub fn init(cpu_local: *const CpuLocal) { + unsafe { + let star = ((KERNEL_DATA_SELECTOR as u64) << 48) | ((KERNEL_CODE_SELECTOR as u64) << 32); + write_msr(IA32_STAR, star); + write_msr(IA32_LSTAR, syscall_entry as *const () as u64); + write_msr(IA32_CSTAR, 0); + write_msr(IA32_FMASK, RFLAGS_MASK); + write_msr(IA32_GS_BASE, 0); + write_msr(IA32_KERNEL_GS_BASE, cpu_local as u64); + } +} + +#[unsafe(naked)] +unsafe extern "C" fn syscall_entry() { + naked_asm!( + "swapgs", + "mov gs:[8], rsp", // user_rsp_scratch + "mov rsp, gs:[0]", // kernel_stack_top + "", + // build the syscall frame + "push qword ptr gs:[8]", // user_rsp + "push r11", // user_rflags + "push rcx", // user_rip + "push rax", + "push rdi", + "push rsi", + "push rdx", + "push r10", + "push r8", + "push r9", + "push rbx", + "push rbp", + "push r12", + "push r13", + "push r14", + "push r15", + "", + // Syscall calling convention: + // Syscall number in rax, args in rdi, rsi, rdx, r10, r8, r9 + "mov rdi, rsp", + "call {dispatch}", + "", + // restore the syscall frame + "pop r15", + "pop r14", + "pop r13", + "pop r12", + "pop rbp", + "pop rbx", + "pop r9", + "pop r8", + "pop r10", + "pop rdx", + "pop rsi", + "pop rdi", + "pop rax", // return value + "pop rcx", // user_rip for sysret + "pop r11", // user_rflags for sysret + "pop qword ptr gs:[8]", // user_rsp + "", + "mov rsp, gs:[8]", // switch to user stack + "swapgs", + "sysretq", + dispatch = sym syscall_dispatch, + ); +} + +extern "C" fn syscall_dispatch(frame: &mut SyscallFrame) { + let ret = crate::task::syscall::handle( + frame.rax, frame.rdi, frame.rsi, frame.rdx, frame.r10, frame.r8, frame.r9, + ); + + frame.rax = ret; +} diff --git a/src/main.rs b/src/main.rs index 8516997..38c8d9e 100644 --- a/src/main.rs +++ b/src/main.rs @@ -8,12 +8,14 @@ mod boot; mod debug; mod memory; mod platform; +mod task; use crate::{ debug::serial, memory::{ - AddressSpace, KernelStack, MemoryRegionKind, PagePermissions, UserStack, VirtualAddr, + AddressSpace, KernelStackPool, MemoryRegionKind, PagePermissions, UserStack, VirtualAddr, }, + task::tcb::Tcb, }; pub struct KernelHandoff { @@ -21,6 +23,7 @@ pub struct KernelHandoff { address_space: AddressSpace, direct_map: memory::DirectMap, boot_info: boot::BootInfo, + kernel_stack_pool: KernelStackPool, handoff_frame: memory::OwnedFrame, } @@ -49,7 +52,10 @@ pub extern "C" fn _start() -> ! { println!("Entering kernel main..."); - let bootstrap_stack = KernelStack::allocate(&mut address_space, &mut allocator) + let mut kernel_stack_pool = KernelStackPool::new(); + + let kernel_stack = kernel_stack_pool + .allocate(&mut address_space, &mut allocator) .expect("failed to allocate bootstrap stack"); let handoff_frame = allocator @@ -59,12 +65,13 @@ pub extern "C" fn _start() -> ! { .to_virtual(handoff_frame.frame_address().start_address()) .expect("failed to map kernel handoff"); - let bootstrap_stack_top = bootstrap_stack.top(); + let bootstrap_stack_top = kernel_stack.top(); let handoff = KernelHandoff { allocator, address_space, direct_map, boot_info, + kernel_stack_pool, handoff_frame, }; @@ -82,13 +89,21 @@ pub extern "C" fn _start() -> ! { } pub unsafe extern "C" fn kernel_main(handoff: *mut KernelHandoff) -> ! { - let (mut allocator, mut address_space, direct_map, boot_info, handoff_frame) = unsafe { + let ( + mut allocator, + mut address_space, + direct_map, + boot_info, + mut kernel_stack_pool, + handoff_frame, + ) = unsafe { let handoff = handoff.read(); ( handoff.allocator, handoff.address_space, handoff.direct_map, handoff.boot_info, + handoff.kernel_stack_pool, handoff.handoff_frame, ) }; @@ -106,19 +121,31 @@ pub unsafe extern "C" fn kernel_main(handoff: *mut KernelHandoff) -> ! { let acpi = platform::acpi::init(&boot_info, direct_map).expect("failed to initialize ACPI"); + println!("Parsing MADT..."); + let madt = acpi .madt() .expect("failed to parse ACPI") .expect("MADT not found"); + println!("Initializing interrupt controller..."); + let interrupt_controller = arch::init_interrupt_controller(&madt, &mut allocator, &mut address_space) .expect("failed to initialize interrupt controller"); + println!("Creating user address space..."); + + let task_kernel_stack = kernel_stack_pool + .allocate(&mut address_space, &mut allocator) + .expect("failed to allocate user task stack"); + let mut user_addr_space = address_space .new_user(&mut allocator) .expect("failed to create user address space"); + println!("Allocating user stack..."); + let user_stack = UserStack::allocate(&mut user_addr_space, &mut allocator) .expect("failed to allocate user stack"); @@ -138,21 +165,37 @@ pub unsafe extern "C" fn kernel_main(handoff: *mut KernelHandoff) -> ! { .expect("failed to map user code page"); unsafe { - user_addr_space.activate(); - - let user_code: [u8; 5] = [ + let user_code: &[u8] = &[ 0xCC, // INT3 - 0xCD, 0x80, // INT 0x80 + 0x0F, 0x05, // SYSCALL 0xEB, 0xFE, // JMP -2 (loop forever if exit returns) ]; + let user_code_virtual = direct_map + .translate(user_code_page.start_address()) + .unwrap(); + + println!("Copying user code to {:#X}", user_code_virtual.as_usize()); + core::ptr::copy_nonoverlapping( user_code.as_ptr(), - user_instruction_pointer.as_mut_ptr::(), + user_code_virtual.as_mut_ptr::(), user_code.len(), ); - arch::enter_user(user_instruction_pointer, user_stack.top()); + println!("Activating user address space..."); + + let user_tcb = Tcb::new_user( + 0, + user_addr_space, + task_kernel_stack, + user_instruction_pointer, + user_stack.top(), + ); + + println!("Running first user task {user_tcb:?}..."); + + task::tcb::run_first_user(&user_tcb); } hcf(); diff --git a/src/memory/address_space.rs b/src/memory/address_space.rs index a6944e0..e0132bb 100644 --- a/src/memory/address_space.rs +++ b/src/memory/address_space.rs @@ -88,6 +88,7 @@ enum AddressSpaceKind { User, } +#[derive(Debug, PartialEq, Eq)] pub struct AddressSpace { root: PageTable, kind: AddressSpaceKind, diff --git a/src/memory/mod.rs b/src/memory/mod.rs index 5a9f95f..a1318f2 100644 --- a/src/memory/mod.rs +++ b/src/memory/mod.rs @@ -1,11 +1,12 @@ mod address_space; mod frame; -pub mod stack; +mod stack; #[allow(unused)] pub use address_space::{AddressSpace, AddressSpaceCreateError, MapError, UnmapError}; pub use frame::{FRAME_SIZE, FrameAddr, FrameAllocator, OwnedFrame}; -pub use stack::{KernelStack, StackCreateError, UserStack}; +#[allow(unused)] +pub use stack::{KernelStack, KernelStackPool, StackCreateError, UserStack}; pub struct KernelSegment { pub physical_base: PhysicalAddr, diff --git a/src/memory/stack.rs b/src/memory/stack.rs index 6741f84..207647e 100644 --- a/src/memory/stack.rs +++ b/src/memory/stack.rs @@ -3,10 +3,16 @@ use crate::memory::{ VirtualAddr, }; -const STACK_PAGES: usize = 16; -const STACK_SIZE: usize = STACK_PAGES * FRAME_SIZE; +const GUARD_PAGES: usize = 1; // 4KiB +const KERNEL_STACK_PAGES: usize = 8; // 32KiB +const USER_STACK_PAGES: usize = 16; // 64KiB +const KERNEL_STACK_SIZE: usize = KERNEL_STACK_PAGES * FRAME_SIZE; +const USER_STACK_SIZE: usize = USER_STACK_PAGES * FRAME_SIZE; + +const KERNEL_SLOT_SIZE: usize = (KERNEL_STACK_PAGES + GUARD_PAGES) * FRAME_SIZE; +const MAX_KERNEL_STACKS: usize = 64; +const KERNEL_STACK_BASE: usize = 0xFFFF_FFFE_0000_0000; -const KERNEL_STACK_TOP: VirtualAddr = VirtualAddr::new(0xFFFF_FFFE_0000_0000); const USER_STACK_TOP: VirtualAddr = VirtualAddr::new(0x0000_7FFF_FFFF_F000); #[derive(Debug)] @@ -14,13 +20,16 @@ pub enum StackCreateError { AddressOverflow, UnalignedStackTop, OutOfFrames, + OutOfStacks, GuardPageMapped, Map(MapError), } +#[derive(Debug)] struct StackMapping { guard_page: VirtualAddr, mapped_start: VirtualAddr, + stack_size: usize, top: VirtualAddr, } @@ -28,6 +37,7 @@ impl StackMapping { fn allocate( address_space: &mut AddressSpace, allocator: &mut FrameAllocator, + stack_size: usize, top: VirtualAddr, permissions: PagePermissions, ) -> Result { @@ -37,7 +47,7 @@ impl StackMapping { let mapped_start = VirtualAddr::new( top.as_usize() - .checked_sub(STACK_SIZE) + .checked_sub(stack_size) .ok_or(StackCreateError::AddressOverflow)?, ); let guard_page = VirtualAddr::new( @@ -53,7 +63,7 @@ impl StackMapping { let mut mapped_pages = 0; - while mapped_pages < STACK_PAGES { + while mapped_pages < stack_size / FRAME_SIZE { let virtual_address = VirtualAddr::new( mapped_start .as_usize() @@ -89,6 +99,7 @@ impl StackMapping { Ok(Self { guard_page, mapped_start, + stack_size, top, }) } @@ -115,7 +126,7 @@ impl StackMapping { /// The caller must ensure this stack is not active on any CPU and cannot be accessed by any /// kernel operation while it is being destroyed. unsafe fn destroy(self, address_space: &mut AddressSpace, allocator: &mut FrameAllocator) { - for page in (0..STACK_PAGES).rev() { + for page in (0..self.stack_size / FRAME_SIZE).rev() { let virtual_address = VirtualAddr::new(self.mapped_start.as_usize() + page * FRAME_SIZE); let frame = unsafe { @@ -130,6 +141,7 @@ impl StackMapping { } } +#[derive(Debug)] pub struct KernelStack { mapping: StackMapping, } @@ -137,12 +149,14 @@ pub struct KernelStack { impl KernelStack { pub fn allocate( address_space: &mut AddressSpace, + top: usize, allocator: &mut FrameAllocator, ) -> Result { let mapping = StackMapping::allocate( address_space, allocator, - KERNEL_STACK_TOP, + KERNEL_STACK_SIZE, + VirtualAddr::new(top), PagePermissions::new(true, false, false), )?; @@ -175,6 +189,7 @@ impl UserStack { let mapping = StackMapping::allocate( address_space, allocator, + USER_STACK_SIZE, top, PagePermissions::new(true, false, true), )?; @@ -194,3 +209,36 @@ impl UserStack { unsafe { self.mapping.destroy(address_space, allocator) }; } } + +pub struct KernelStackPool { + free_slots: u64, // bitmap +} + +impl KernelStackPool { + pub fn new() -> Self { + Self { free_slots: 0 } + } + + pub fn allocate( + &mut self, + address_space: &mut AddressSpace, + allocator: &mut FrameAllocator, + ) -> Result { + let mut slot = 0; + while slot < MAX_KERNEL_STACKS { + if self.free_slots & (1 << slot) == 0 { + self.free_slots |= 1 << slot; + let top = KERNEL_STACK_BASE + ((slot + 1) * KERNEL_SLOT_SIZE); + return KernelStack::allocate(address_space, top, allocator); + } + slot += 1; + } + + Err(StackCreateError::OutOfStacks) + } + + pub fn free(&mut self, stack: &KernelStack) { + let slot = (stack.top().as_usize() - KERNEL_STACK_BASE) / KERNEL_SLOT_SIZE - 1; + self.free_slots &= !(1 << slot); + } +} diff --git a/src/task/mod.rs b/src/task/mod.rs new file mode 100644 index 0000000..495aff6 --- /dev/null +++ b/src/task/mod.rs @@ -0,0 +1,2 @@ +pub mod syscall; +pub mod tcb; diff --git a/src/task/syscall.rs b/src/task/syscall.rs new file mode 100644 index 0000000..c1eca4a --- /dev/null +++ b/src/task/syscall.rs @@ -0,0 +1,18 @@ +use crate::{hcf, println}; + +pub fn handle( + syscall_num: u64, + arg0: u64, + arg1: u64, + arg2: u64, + arg3: u64, + arg4: u64, + arg5: u64, +) -> u64 { + println!( + "Syscall nr={:#X} args=({:#X}, {:#X}, {:#X}, {:#X}, {:#X}, {:#X})", + syscall_num, arg0, arg1, arg2, arg3, arg4, arg5 + ); + hcf(); + 0 +} diff --git a/src/task/tcb.rs b/src/task/tcb.rs new file mode 100644 index 0000000..7b94072 --- /dev/null +++ b/src/task/tcb.rs @@ -0,0 +1,72 @@ +use crate::{ + arch::ThreadContext, + memory::{AddressSpace, KernelStack, VirtualAddr}, + println, +}; + +// Thread Control Block +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub enum ThreadState { + Ready, + Running, + Blocked, + Dead, +} + +#[derive(Debug)] +pub struct Tcb { + pub id: usize, + pub state: ThreadState, + pub kernel_stack: KernelStack, + pub context: ThreadContext, + pub address_space: AddressSpace, +} + +impl Tcb { + pub fn new_user( + id: usize, + address_space: AddressSpace, + kernel_stack: KernelStack, + user_entry: VirtualAddr, + user_stack: VirtualAddr, + ) -> Self { + let context = ThreadContext::new(user_entry, user_stack, kernel_stack.top()); + + Self { + id, + state: ThreadState::Ready, + kernel_stack, + context, + address_space, + } + } +} + +pub unsafe fn run_first_user(user_tcb: &Tcb) { + let previous_interrupts = crate::arch::disable_interrupts_and_save(); + + let mut boot_thread_ctx = ThreadContext::empty(); + unsafe { + user_tcb.address_space.activate(); + crate::arch::set_kernel_stack(user_tcb.kernel_stack.top()); + crate::arch::switch_context(&mut boot_thread_ctx, &user_tcb.context); + } + + crate::arch::restore_interrupts(previous_interrupts); +} + +pub unsafe fn switch(prev: &mut Tcb, next: &Tcb) { + let previous_interrupts = crate::arch::disable_interrupts_and_save(); + + if prev.address_space != next.address_space { + unsafe { next.address_space.activate() }; + } + + crate::arch::set_kernel_stack(next.kernel_stack.top()); + + unsafe { + crate::arch::switch_context(&mut prev.context, &next.context); + } + + crate::arch::restore_interrupts(previous_interrupts); +}