feat: tasks
This commit is contained in:
+4
-1
@@ -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,
|
||||
};
|
||||
|
||||
@@ -13,7 +13,6 @@ use crate::{
|
||||
memory::{
|
||||
AddressSpace, CachePolicy, FrameAllocator, PagePermissions, PhysicalAddr, VirtualAddr,
|
||||
},
|
||||
println,
|
||||
};
|
||||
|
||||
const APIC_ID: u32 = 0x20;
|
||||
|
||||
+109
-1
@@ -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<CpuFeatures, CpuFeaturesError> {
|
||||
|
||||
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;
|
||||
|
||||
@@ -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,
|
||||
],
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -3,7 +3,6 @@ use crate::{
|
||||
AddressSpace, CachePolicy, FrameAllocator, PagePermissions, PhysicalAddr, VirtualAddr,
|
||||
},
|
||||
platform::acpi::{InterruptPolarity, TriggerMode},
|
||||
println,
|
||||
};
|
||||
|
||||
const IOWIN: usize = 0x10;
|
||||
|
||||
+41
-14
@@ -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,
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
Reference in New Issue
Block a user