feat: tasks

This commit is contained in:
Zoe
2026-08-31 04:46:13 -05:00
parent b1f9dc0f8a
commit 4c32bcfb35
16 changed files with 513 additions and 43 deletions
+4 -1
View File
@@ -5,4 +5,7 @@ mod x86_64;
pub use x86_64::*; pub use x86_64::*;
#[cfg(target_arch = "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,
};
-1
View File
@@ -13,7 +13,6 @@ use crate::{
memory::{ memory::{
AddressSpace, CachePolicy, FrameAllocator, PagePermissions, PhysicalAddr, VirtualAddr, AddressSpace, CachePolicy, FrameAllocator, PagePermissions, PhysicalAddr, VirtualAddr,
}, },
println,
}; };
const APIC_ID: u32 = 0x20; const APIC_ID: u32 = 0x20;
+109 -1
View File
@@ -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)] #[derive(Debug)]
pub enum CpuFeaturesError { pub enum CpuFeaturesError {
CpuidFeaturesNotSupported, CpuidFeaturesNotSupported,
SyscallNotSupported,
InvalidPhysicalAddressWidth, InvalidPhysicalAddressWidth,
InvalidVirtualAddressWidth, InvalidVirtualAddressWidth,
} }
@@ -40,6 +136,18 @@ pub fn detect_features_and_enable() -> Result<CpuFeatures, CpuFeaturesError> {
features.nx_supported = cpuid_result.edx & (1 << 20) != 0; 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); let cpuid_result = core::arch::x86_64::__cpuid(0x80000008);
features.physical_address_bits = (cpuid_result.eax & 0xFF) as u8; features.physical_address_bits = (cpuid_result.eax & 0xFF) as u8;
+8 -6
View File
@@ -3,8 +3,8 @@ use core::arch::asm;
use crate::memory::VirtualAddr; use crate::memory::VirtualAddr;
#[repr(C, align(8))] #[repr(C, align(8))]
struct Gdt { pub(super) struct Gdt {
entries: [u64; 7], pub entries: [u64; 7],
} }
impl Gdt { 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_CODE_SELECTOR: u16 = 1 * 8;
pub(super) const KERNEL_DATA_SELECTOR: u16 = 2 * 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 = (3 * 8) | 3;
pub(super) const USER_DATA_SELECTOR: u16 = (4 * 8) | 3; pub(super) const USER_CODE_SELECTOR: u16 = (4 * 8) | 3;
pub(super) const TSS_SELECTOR: u16 = 5 * 8; pub(super) const TSS_SELECTOR: u16 = 5 * 8;
#[repr(align(16))] #[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]); struct ExceptionStack([u8; DOUBLE_FAULT_STACK_SIZE]);
static mut GDT: Gdt = Gdt::new(); static mut GDT: Gdt = Gdt::new();
@@ -78,8 +78,10 @@ pub fn init() {
0, 0,
kernel_code_descriptor(), kernel_code_descriptor(),
kernel_data_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_data_descriptor(),
user_code_descriptor(),
tss_low, tss_low,
tss_high, tss_high,
], ],
+27
View File
@@ -6,6 +6,33 @@ mod idt;
pub use idt::idt_init as init; 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)] #[inline(always)]
pub fn disable_interrupts() { pub fn disable_interrupts() {
unsafe { unsafe {
-1
View File
@@ -3,7 +3,6 @@ use crate::{
AddressSpace, CachePolicy, FrameAllocator, PagePermissions, PhysicalAddr, VirtualAddr, AddressSpace, CachePolicy, FrameAllocator, PagePermissions, PhysicalAddr, VirtualAddr,
}, },
platform::acpi::{InterruptPolarity, TriggerMode}, platform::acpi::{InterruptPolarity, TriggerMode},
println,
}; };
const IOWIN: usize = 0x10; const IOWIN: usize = 0x10;
+41 -14
View File
@@ -1,16 +1,18 @@
pub mod apic; pub(super) mod apic;
mod cpu; mod cpu;
mod gdt; mod gdt;
mod interrupts; mod interrupts;
pub mod io_apic; pub(super) mod io_apic;
mod paging; mod paging;
mod pit; mod pit;
pub mod port; pub mod port;
mod syscall;
pub mod timer; pub mod timer;
use core::arch::asm; 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::{ pub(crate) use paging::{
MapError as PageTableMapError, PageTableCreateError, UnmapError as PageTableUnmapError, MapError as PageTableMapError, PageTableCreateError, UnmapError as PageTableUnmapError,
}; };
@@ -22,11 +24,7 @@ pub struct ArchState {
use crate::{ use crate::{
KernelHandoff, KernelHandoff,
arch::{ arch::x86_64::cpu::BOOT_CPU,
apic::LocalApic,
io_apic::{IOAPIC_VIRTUAL_ADDRESS, IoApic},
x86_64::interrupts::apic_vectors::PIT_CALIBRATION_VECTOR,
},
memory::{AddressSpace, FrameAllocator, VirtualAddr}, memory::{AddressSpace, FrameAllocator, VirtualAddr},
platform::acpi::Madt, platform::acpi::Madt,
println, println,
@@ -45,6 +43,14 @@ pub fn init() -> ArchState {
ArchState { paging } 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)] #[derive(Debug)]
#[allow(unused)] #[allow(unused)]
pub enum InterruptInitError { pub enum InterruptInitError {
@@ -60,8 +66,8 @@ pub enum InterruptInitError {
#[derive(Debug)] #[derive(Debug)]
pub struct InterruptController { pub struct InterruptController {
local_apic: LocalApic, local_apic: apic::LocalApic,
io_apic: IoApic, io_apic: io_apic::IoApic,
local_timer_frequency: u64, local_timer_frequency: u64,
} }
@@ -89,7 +95,7 @@ pub fn init_interrupt_controller(
io_apic_info.id, io_apic_info.id,
io_apic_info.apic_address, io_apic_info.apic_address,
io_apic_info.global_system_interrupt_base, io_apic_info.global_system_interrupt_base,
IOAPIC_VIRTUAL_ADDRESS, io_apic::IOAPIC_VIRTUAL_ADDRESS,
allocator, allocator,
address_space, address_space,
) )
@@ -107,7 +113,7 @@ pub fn init_interrupt_controller(
.configure_masked( .configure_masked(
pit_route.gsi, pit_route.gsi,
io_apic::RedirectionConfig { io_apic::RedirectionConfig {
vector: PIT_CALIBRATION_VECTOR, vector: interrupts::apic_vectors::PIT_CALIBRATION_VECTOR,
destination, destination,
polarity: pit_route.polarity, polarity: pit_route.polarity,
trigger: pit_route.trigger, trigger: pit_route.trigger,
@@ -165,6 +171,9 @@ pub unsafe fn enter_kernel(stack_top: VirtualAddr, handoff: *mut KernelHandoff)
unsafe { unsafe {
gdt::set_kernel_stack(stack_top); gdt::set_kernel_stack(stack_top);
BOOT_CPU.kernel_stack_top = stack_top.as_usize();
syscall::init(&raw const BOOT_CPU);
asm!( asm!(
"mov rsp, {stack_top}", "mov rsp, {stack_top}",
"xor rbp, rbp", "xor rbp, rbp",
@@ -193,13 +202,31 @@ pub unsafe fn enter_user(
"mov ds, {user_data_selector:x}", "mov ds, {user_data_selector:x}",
"mov es, {user_data_selector:x}", "mov es, {user_data_selector:x}",
"mov fs, {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_data_selector}",
"push {user_stack_pointer}", "push {user_stack_pointer}",
"pushfq", "push 0x202", // RFLAGS (IF=1, bit 1 reserved=1)
"push {user_code_selector}", "push {user_code_selector}",
"push {user_instruction_pointer}", "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", "iretq",
user_data_selector = in(reg) gdt::USER_DATA_SELECTOR as usize, user_data_selector = in(reg) gdt::USER_DATA_SELECTOR as usize,
user_code_selector = in(reg) gdt::USER_CODE_SELECTOR as usize, user_code_selector = in(reg) gdt::USER_CODE_SELECTOR as usize,
+9
View File
@@ -264,12 +264,21 @@ pub(crate) enum PageTableCreateError {
OutOfFrames, OutOfFrames,
} }
#[derive(Debug)]
pub struct PageTable { pub struct PageTable {
pub direct_map: DirectMap, pub direct_map: DirectMap,
config: PagingConfig, config: PagingConfig,
frame: OwnedFrame, 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 { impl PageTable {
pub fn new( pub fn new(
direct_map: DirectMap, direct_map: DirectMap,
+111
View File
@@ -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;
}
+53 -10
View File
@@ -8,12 +8,14 @@ mod boot;
mod debug; mod debug;
mod memory; mod memory;
mod platform; mod platform;
mod task;
use crate::{ use crate::{
debug::serial, debug::serial,
memory::{ memory::{
AddressSpace, KernelStack, MemoryRegionKind, PagePermissions, UserStack, VirtualAddr, AddressSpace, KernelStackPool, MemoryRegionKind, PagePermissions, UserStack, VirtualAddr,
}, },
task::tcb::Tcb,
}; };
pub struct KernelHandoff { pub struct KernelHandoff {
@@ -21,6 +23,7 @@ pub struct KernelHandoff {
address_space: AddressSpace, address_space: AddressSpace,
direct_map: memory::DirectMap, direct_map: memory::DirectMap,
boot_info: boot::BootInfo, boot_info: boot::BootInfo,
kernel_stack_pool: KernelStackPool,
handoff_frame: memory::OwnedFrame, handoff_frame: memory::OwnedFrame,
} }
@@ -49,7 +52,10 @@ pub extern "C" fn _start() -> ! {
println!("Entering kernel main..."); 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"); .expect("failed to allocate bootstrap stack");
let handoff_frame = allocator let handoff_frame = allocator
@@ -59,12 +65,13 @@ pub extern "C" fn _start() -> ! {
.to_virtual(handoff_frame.frame_address().start_address()) .to_virtual(handoff_frame.frame_address().start_address())
.expect("failed to map kernel handoff"); .expect("failed to map kernel handoff");
let bootstrap_stack_top = bootstrap_stack.top(); let bootstrap_stack_top = kernel_stack.top();
let handoff = KernelHandoff { let handoff = KernelHandoff {
allocator, allocator,
address_space, address_space,
direct_map, direct_map,
boot_info, boot_info,
kernel_stack_pool,
handoff_frame, handoff_frame,
}; };
@@ -82,13 +89,21 @@ pub extern "C" fn _start() -> ! {
} }
pub unsafe extern "C" fn kernel_main(handoff: *mut KernelHandoff) -> ! { 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(); let handoff = handoff.read();
( (
handoff.allocator, handoff.allocator,
handoff.address_space, handoff.address_space,
handoff.direct_map, handoff.direct_map,
handoff.boot_info, handoff.boot_info,
handoff.kernel_stack_pool,
handoff.handoff_frame, 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"); let acpi = platform::acpi::init(&boot_info, direct_map).expect("failed to initialize ACPI");
println!("Parsing MADT...");
let madt = acpi let madt = acpi
.madt() .madt()
.expect("failed to parse ACPI") .expect("failed to parse ACPI")
.expect("MADT not found"); .expect("MADT not found");
println!("Initializing interrupt controller...");
let interrupt_controller = let interrupt_controller =
arch::init_interrupt_controller(&madt, &mut allocator, &mut address_space) arch::init_interrupt_controller(&madt, &mut allocator, &mut address_space)
.expect("failed to initialize interrupt controller"); .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 let mut user_addr_space = address_space
.new_user(&mut allocator) .new_user(&mut allocator)
.expect("failed to create user address space"); .expect("failed to create user address space");
println!("Allocating user stack...");
let user_stack = UserStack::allocate(&mut user_addr_space, &mut allocator) let user_stack = UserStack::allocate(&mut user_addr_space, &mut allocator)
.expect("failed to allocate user stack"); .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"); .expect("failed to map user code page");
unsafe { unsafe {
user_addr_space.activate(); let user_code: &[u8] = &[
let user_code: [u8; 5] = [
0xCC, // INT3 0xCC, // INT3
0xCD, 0x80, // INT 0x80 0x0F, 0x05, // SYSCALL
0xEB, 0xFE, // JMP -2 (loop forever if exit returns) 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( core::ptr::copy_nonoverlapping(
user_code.as_ptr(), user_code.as_ptr(),
user_instruction_pointer.as_mut_ptr::<u8>(), user_code_virtual.as_mut_ptr::<u8>(),
user_code.len(), 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(); hcf();
+1
View File
@@ -88,6 +88,7 @@ enum AddressSpaceKind {
User, User,
} }
#[derive(Debug, PartialEq, Eq)]
pub struct AddressSpace { pub struct AddressSpace {
root: PageTable, root: PageTable,
kind: AddressSpaceKind, kind: AddressSpaceKind,
+3 -2
View File
@@ -1,11 +1,12 @@
mod address_space; mod address_space;
mod frame; mod frame;
pub mod stack; mod stack;
#[allow(unused)] #[allow(unused)]
pub use address_space::{AddressSpace, AddressSpaceCreateError, MapError, UnmapError}; pub use address_space::{AddressSpace, AddressSpaceCreateError, MapError, UnmapError};
pub use frame::{FRAME_SIZE, FrameAddr, FrameAllocator, OwnedFrame}; 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 struct KernelSegment {
pub physical_base: PhysicalAddr, pub physical_base: PhysicalAddr,
+55 -7
View File
@@ -3,10 +3,16 @@ use crate::memory::{
VirtualAddr, VirtualAddr,
}; };
const STACK_PAGES: usize = 16; const GUARD_PAGES: usize = 1; // 4KiB
const STACK_SIZE: usize = STACK_PAGES * FRAME_SIZE; 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); const USER_STACK_TOP: VirtualAddr = VirtualAddr::new(0x0000_7FFF_FFFF_F000);
#[derive(Debug)] #[derive(Debug)]
@@ -14,13 +20,16 @@ pub enum StackCreateError {
AddressOverflow, AddressOverflow,
UnalignedStackTop, UnalignedStackTop,
OutOfFrames, OutOfFrames,
OutOfStacks,
GuardPageMapped, GuardPageMapped,
Map(MapError), Map(MapError),
} }
#[derive(Debug)]
struct StackMapping { struct StackMapping {
guard_page: VirtualAddr, guard_page: VirtualAddr,
mapped_start: VirtualAddr, mapped_start: VirtualAddr,
stack_size: usize,
top: VirtualAddr, top: VirtualAddr,
} }
@@ -28,6 +37,7 @@ impl StackMapping {
fn allocate( fn allocate(
address_space: &mut AddressSpace, address_space: &mut AddressSpace,
allocator: &mut FrameAllocator, allocator: &mut FrameAllocator,
stack_size: usize,
top: VirtualAddr, top: VirtualAddr,
permissions: PagePermissions, permissions: PagePermissions,
) -> Result<Self, StackCreateError> { ) -> Result<Self, StackCreateError> {
@@ -37,7 +47,7 @@ impl StackMapping {
let mapped_start = VirtualAddr::new( let mapped_start = VirtualAddr::new(
top.as_usize() top.as_usize()
.checked_sub(STACK_SIZE) .checked_sub(stack_size)
.ok_or(StackCreateError::AddressOverflow)?, .ok_or(StackCreateError::AddressOverflow)?,
); );
let guard_page = VirtualAddr::new( let guard_page = VirtualAddr::new(
@@ -53,7 +63,7 @@ impl StackMapping {
let mut mapped_pages = 0; let mut mapped_pages = 0;
while mapped_pages < STACK_PAGES { while mapped_pages < stack_size / FRAME_SIZE {
let virtual_address = VirtualAddr::new( let virtual_address = VirtualAddr::new(
mapped_start mapped_start
.as_usize() .as_usize()
@@ -89,6 +99,7 @@ impl StackMapping {
Ok(Self { Ok(Self {
guard_page, guard_page,
mapped_start, mapped_start,
stack_size,
top, 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 /// 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. /// kernel operation while it is being destroyed.
unsafe fn destroy(self, address_space: &mut AddressSpace, allocator: &mut FrameAllocator) { 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 = let virtual_address =
VirtualAddr::new(self.mapped_start.as_usize() + page * FRAME_SIZE); VirtualAddr::new(self.mapped_start.as_usize() + page * FRAME_SIZE);
let frame = unsafe { let frame = unsafe {
@@ -130,6 +141,7 @@ impl StackMapping {
} }
} }
#[derive(Debug)]
pub struct KernelStack { pub struct KernelStack {
mapping: StackMapping, mapping: StackMapping,
} }
@@ -137,12 +149,14 @@ pub struct KernelStack {
impl KernelStack { impl KernelStack {
pub fn allocate( pub fn allocate(
address_space: &mut AddressSpace, address_space: &mut AddressSpace,
top: usize,
allocator: &mut FrameAllocator, allocator: &mut FrameAllocator,
) -> Result<Self, StackCreateError> { ) -> Result<Self, StackCreateError> {
let mapping = StackMapping::allocate( let mapping = StackMapping::allocate(
address_space, address_space,
allocator, allocator,
KERNEL_STACK_TOP, KERNEL_STACK_SIZE,
VirtualAddr::new(top),
PagePermissions::new(true, false, false), PagePermissions::new(true, false, false),
)?; )?;
@@ -175,6 +189,7 @@ impl UserStack {
let mapping = StackMapping::allocate( let mapping = StackMapping::allocate(
address_space, address_space,
allocator, allocator,
USER_STACK_SIZE,
top, top,
PagePermissions::new(true, false, true), PagePermissions::new(true, false, true),
)?; )?;
@@ -194,3 +209,36 @@ impl UserStack {
unsafe { self.mapping.destroy(address_space, allocator) }; 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<KernelStack, StackCreateError> {
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);
}
}
+2
View File
@@ -0,0 +1,2 @@
pub mod syscall;
pub mod tcb;
+18
View File
@@ -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
}
+72
View File
@@ -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);
}