diff --git a/Cargo.lock b/Cargo.lock index 70897a3..52c1392 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -55,7 +55,6 @@ dependencies = [ "aarch32-cpu", "aarch64-cpu", "cfg-if", - "lazyinit", "linkme", "log", "loongArch64", @@ -64,7 +63,7 @@ dependencies = [ "page_table_multiarch", "percpu", "riscv", - "static_assertions", + "spin", "tock-registers", "x86", "x86_64", @@ -124,12 +123,6 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "361a90feb7004eca4019fb28352a9465666b24f840f5c3cddf0ff13920590b89" -[[package]] -name = "lazyinit" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "17f03abfebdaaf0fad16790237a0348baf84886d3ade460db13bae59e614a180" - [[package]] name = "linkme" version = "0.3.35" @@ -150,6 +143,15 @@ dependencies = [ "syn", ] +[[package]] +name = "lock_api" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "224399e74b87b5f3557511d98dff8b14089b3dadafcab6bb93eab67d3aace965" +dependencies = [ + "scopeguard", +] + [[package]] name = "log" version = "0.4.29" @@ -311,10 +313,19 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d" [[package]] -name = "static_assertions" -version = "1.1.0" +name = "scopeguard" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" +checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" + +[[package]] +name = "spin" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d5fe4ccb98d9c292d56fec89a5e07da7fc4cf0dc11e156b41793132775d3e591" +dependencies = [ + "lock_api", +] [[package]] name = "syn" diff --git a/Cargo.toml b/Cargo.toml index 4da4992..a3b1088 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -30,18 +30,19 @@ uspace = [] arm-el2 = [] [dependencies] +cfg-if = "1.0" linkme = "0.3" log = "0.4" -cfg-if = "1.0" memory_addr = "0.4" page_table_entry = "0.6" -static_assertions = "1.1.0" [target.'cfg(target_arch = "x86_64")'.dependencies] -x86 = "0.52" -x86_64 = "0.15.2" percpu = "0.4" -lazyinit = "0.2" +spin = "0.10" +x86 = "0.52" +x86_64 = { version = "0.15", default-features = false, features = [ + "instructions", +] } [target.'cfg(target_arch = "aarch64")'.dependencies] aarch64-cpu = "11.2" diff --git a/src/x86_64/context.rs b/src/x86_64/context.rs index 0104057..f1112c0 100644 --- a/src/x86_64/context.rs +++ b/src/x86_64/context.rs @@ -104,7 +104,7 @@ pub struct FxsaveArea { _padding: [u64; 12], } -static_assertions::const_assert_eq!(core::mem::size_of::(), 512); +const _: () = assert!(core::mem::size_of::() == 512); /// Extended state of a task, such as FP/SIMD states. pub struct ExtendedState { diff --git a/src/x86_64/gdt.rs b/src/x86_64/gdt.rs index 535b851..609f92e 100644 --- a/src/x86_64/gdt.rs +++ b/src/x86_64/gdt.rs @@ -1,112 +1,42 @@ -use core::fmt; +use x86_64::{ + instructions::tables::load_tss, + registers::segmentation::{Segment, SegmentSelector, CS}, + structures::{ + gdt::{Descriptor, GlobalDescriptorTable}, + tss::TaskStateSegment, + }, + PrivilegeLevel, +}; -use lazyinit::LazyInit; -use x86_64::instructions::tables::{lgdt, load_tss}; -use x86_64::registers::segmentation::{Segment, SegmentSelector, CS}; -use x86_64::structures::gdt::{Descriptor, DescriptorFlags}; -use x86_64::structures::{tss::TaskStateSegment, DescriptorTablePointer}; -use x86_64::{addr::VirtAddr, PrivilegeLevel}; - -#[unsafe(no_mangle)] #[percpu::def_percpu] +#[unsafe(no_mangle)] static TSS: TaskStateSegment = TaskStateSegment::new(); #[percpu::def_percpu] -static GDT: LazyInit = LazyInit::new(); - -/// A wrapper of the Global Descriptor Table (GDT) with maximum 16 entries. -#[repr(align(16))] -pub struct GdtStruct { - table: [u64; 16], -} +static GDT: GlobalDescriptorTable = GlobalDescriptorTable::new(); -impl GdtStruct { - /// Kernel code segment for 32-bit mode. - pub const KCODE32_SELECTOR: SegmentSelector = SegmentSelector::new(1, PrivilegeLevel::Ring0); - /// Kernel code segment for 64-bit mode. - pub const KCODE64_SELECTOR: SegmentSelector = SegmentSelector::new(2, PrivilegeLevel::Ring0); - /// Kernel data segment. - pub const KDATA_SELECTOR: SegmentSelector = SegmentSelector::new(3, PrivilegeLevel::Ring0); - /// User code segment for 32-bit mode. - pub const UCODE32_SELECTOR: SegmentSelector = SegmentSelector::new(4, PrivilegeLevel::Ring3); - /// User data segment. - pub const UDATA_SELECTOR: SegmentSelector = SegmentSelector::new(5, PrivilegeLevel::Ring3); - /// User code segment for 64-bit mode. - pub const UCODE64_SELECTOR: SegmentSelector = SegmentSelector::new(6, PrivilegeLevel::Ring3); - /// TSS segment. - pub const TSS_SELECTOR: SegmentSelector = SegmentSelector::new(7, PrivilegeLevel::Ring0); - - /// Constructs a new GDT struct that filled with the default segment - /// descriptors, including the given TSS segment. - pub fn new(tss: &'static TaskStateSegment) -> Self { - let mut table = [0; 16]; - // first 3 entries are the same as in multiboot.S - table[1] = DescriptorFlags::KERNEL_CODE32.bits(); // 0x00cf9b000000ffff - table[2] = DescriptorFlags::KERNEL_CODE64.bits(); // 0x00af9b000000ffff - table[3] = DescriptorFlags::KERNEL_DATA.bits(); // 0x00cf93000000ffff - table[4] = DescriptorFlags::USER_CODE32.bits(); // 0x00cffb000000ffff - table[5] = DescriptorFlags::USER_DATA.bits(); // 0x00cff3000000ffff - table[6] = DescriptorFlags::USER_CODE64.bits(); // 0x00affb000000ffff - if let Descriptor::SystemSegment(low, high) = Descriptor::tss_segment(tss) { - table[7] = low; - table[8] = high; - } - Self { table } - } - - /// Returns the GDT pointer (base and limit) that can be used in `lgdt` - /// instruction. - pub fn pointer(&self) -> DescriptorTablePointer { - DescriptorTablePointer { - base: VirtAddr::new(self.table.as_ptr() as u64), - limit: (core::mem::size_of_val(&self.table) - 1) as u16, - } - } - - /// Loads the GDT into the CPU (executes the `lgdt` instruction), and - /// updates the code segment register (`CS`). - /// - /// # Safety - /// - /// This function is unsafe because it manipulates the CPU's privileged - /// states. - pub unsafe fn load(&'static self) { - unsafe { - lgdt(&self.pointer()); - CS::set_reg(Self::KCODE64_SELECTOR); - } - } - - /// Loads the TSS into the CPU (executes the `ltr` instruction). - /// - /// # Safety - /// - /// This function is unsafe because it manipulates the CPU's privileged - /// states. - pub unsafe fn load_tss(&'static self) { - unsafe { - load_tss(Self::TSS_SELECTOR); - } - } -} - -impl fmt::Debug for GdtStruct { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - f.debug_struct("GdtStruct") - .field("pointer", &self.pointer()) - .field("table", &self.table) - .finish() - } -} +/// Kernel code segment for 64-bit mode. +pub const KCODE64: SegmentSelector = SegmentSelector::new(1, PrivilegeLevel::Ring0); +/// Kernel data segment. +pub const KDATA: SegmentSelector = SegmentSelector::new(2, PrivilegeLevel::Ring0); +/// User data segment. +pub const UDATA: SegmentSelector = SegmentSelector::new(3, PrivilegeLevel::Ring3); +/// User code segment for 64-bit mode. +pub const UCODE64: SegmentSelector = SegmentSelector::new(4, PrivilegeLevel::Ring3); /// Initializes the per-CPU TSS and GDT structures and loads them into the /// current CPU. -pub fn init_gdt() { +pub(super) fn init() { + let gdt = unsafe { GDT.current_ref_mut_raw() }; + assert_eq!(gdt.append(Descriptor::kernel_code_segment()), KCODE64); + assert_eq!(gdt.append(Descriptor::kernel_data_segment()), KDATA); + assert_eq!(gdt.append(Descriptor::user_data_segment()), UDATA); + assert_eq!(gdt.append(Descriptor::user_code_segment()), UCODE64); + let tss = gdt.append(Descriptor::tss_segment(unsafe { TSS.current_ref_raw() })); + gdt.load(); unsafe { - let gdt = GDT.current_ref_raw(); - gdt.init_once(GdtStruct::new(TSS.current_ref_raw())); - gdt.load(); - gdt.load_tss(); + CS::set_reg(KCODE64); + load_tss(tss); } } @@ -125,5 +55,5 @@ pub(crate) fn read_tss_rsp0() -> memory_addr::VirtAddr { #[cfg(feature = "uspace")] pub(crate) unsafe fn write_tss_rsp0(rsp0: memory_addr::VirtAddr) { let tss = unsafe { TSS.current_ref_mut_raw() }; - tss.privilege_stack_table[0] = VirtAddr::new_truncate(rsp0.as_usize() as u64); + tss.privilege_stack_table[0] = x86_64::VirtAddr::new_truncate(rsp0.as_usize() as u64); } diff --git a/src/x86_64/idt.rs b/src/x86_64/idt.rs index 08efac3..a2cb3d2 100644 --- a/src/x86_64/idt.rs +++ b/src/x86_64/idt.rs @@ -1,81 +1,32 @@ -use core::fmt; - -use lazyinit::LazyInit; -use x86_64::addr::VirtAddr; -use x86_64::structures::idt::{Entry, HandlerFunc, InterruptDescriptorTable}; -use x86_64::structures::DescriptorTablePointer; - -const NUM_INT: usize = 256; - -static IDT: LazyInit = LazyInit::new(); - -/// A wrapper of the Interrupt Descriptor Table (IDT). -#[repr(transparent)] -pub struct IdtStruct { - table: InterruptDescriptorTable, -} - -impl IdtStruct { - /// Constructs a new IDT struct that filled with entries from - /// `trap_handler_table`. - #[allow(clippy::new_without_default)] - pub fn new() -> Self { - unsafe extern "C" { - #[link_name = "trap_handler_table"] - static ENTRIES: [extern "C" fn(); NUM_INT]; - } - let mut idt = Self { - table: InterruptDescriptorTable::new(), - }; - - let entries = unsafe { - core::slice::from_raw_parts_mut( - &mut idt.table as *mut _ as *mut Entry, - NUM_INT, - ) - }; - for i in 0..NUM_INT { - #[allow(clippy::missing_transmute_annotations)] - let opt = entries[i].set_handler_fn(unsafe { core::mem::transmute(ENTRIES[i]) }); - if i == 0x3 || i == 0x80 { - // enable user space breakpoints and legacy int 0x80 syscall - opt.set_privilege_level(x86_64::PrivilegeLevel::Ring3); - } - } - idt +use spin::Lazy; +use x86_64::{ + addr::VirtAddr, + structures::idt::{Entry, InterruptDescriptorTable}, +}; + +static IDT: Lazy = Lazy::new(|| { + const NUM_INT: usize = 256; + + unsafe extern "C" { + #[link_name = "trap_handler_table"] + static ENTRIES: [VirtAddr; NUM_INT]; } - - /// Returns the IDT pointer (base and limit) that can be used in the `lidt` - /// instruction. - pub fn pointer(&self) -> DescriptorTablePointer { - DescriptorTablePointer { - base: VirtAddr::new(&self.table as *const _ as u64), - limit: (core::mem::size_of::() - 1) as u16, + let mut table = InterruptDescriptorTable::new(); + let entries = unsafe { + core::mem::transmute::<&mut InterruptDescriptorTable, &mut [Entry<()>; NUM_INT]>(&mut table) + }; + for i in 0..NUM_INT { + let opt = unsafe { entries[i].set_handler_addr(ENTRIES[i]) }; + if i == 0x3 || i == 0x80 { + // enable user space breakpoints and legacy int 0x80 syscall + opt.set_privilege_level(x86_64::PrivilegeLevel::Ring3); } } - /// Loads the IDT into the CPU (executes the `lidt` instruction). - /// - /// # Safety - /// - /// This function is unsafe because it manipulates the CPU's privileged - /// states. - pub unsafe fn load(&'static self) { - self.table.load(); - } -} - -impl fmt::Debug for IdtStruct { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - f.debug_struct("IdtStruct") - .field("pointer", &self.pointer()) - .field("table", &self.table) - .finish() - } -} + table +}); /// Initializes the global IDT and loads it into the current CPU. -pub fn init_idt() { - IDT.call_once(IdtStruct::new); - unsafe { IDT.load() }; +pub(super) fn init() { + IDT.load(); } diff --git a/src/x86_64/init.rs b/src/x86_64/init.rs index 5ccf43d..d00354b 100644 --- a/src/x86_64/init.rs +++ b/src/x86_64/init.rs @@ -1,38 +1,20 @@ //! Helper functions to initialize the CPU states on systems bootstrapping. -pub use super::gdt::init_gdt; -pub use super::idt::init_idt; - -#[cfg(feature = "uspace")] -pub use super::syscall::init_syscall; - -/// Initializes the per-CPU data structures. -/// -/// It calls the initialization function of the [`percpu`] crate. It (or other -/// alternative initialization) should be called before [`init_trap`]. -/// -/// [`percpu`]: https://docs.rs/percpu/latest/percpu/index.html -pub fn init_percpu(cpu_id: usize) { - percpu::init_in_place().expect("failed to initialize per-CPU data area"); - percpu::init_percpu_reg(cpu_id); -} - /// Initializes trap handling on the current CPU. /// -/// In detail, it initializes the GDT, IDT on x86_64 platforms ([`init_gdt`] and -/// [`init_idt`]). If the `uspace` feature is enabled, it also initializes -/// relevant model-specific registers to configure the handler for `syscall` -/// instruction ([`init_syscall`]). +/// In detail, it initializes the GDT, IDT on x86_64 platforms. If the `uspace` +/// feature is enabled, it also initializes relevant model-specific registers to +/// configure the handler for `syscall` instruction. /// /// # Notes -/// Before calling this function, the initialization function of the [`percpu`] crate -/// should have been invoked to ensure that the per-CPU data structures are set up -/// correctly (i.e., by calling [`init_percpu`]). +/// Before calling this function, the initialization function of the [`percpu`] +/// crate should have been invoked to ensure that the per-CPU data structures +/// are set up correctly. /// /// [`percpu`]: https://docs.rs/percpu/latest/percpu/index.html pub fn init_trap() { - init_gdt(); - init_idt(); + super::gdt::init(); + super::idt::init(); #[cfg(feature = "uspace")] - init_syscall(); + super::syscall::init_syscall(); } diff --git a/src/x86_64/mod.rs b/src/x86_64/mod.rs index eef46af..95e3da1 100644 --- a/src/x86_64/mod.rs +++ b/src/x86_64/mod.rs @@ -15,6 +15,3 @@ mod syscall; pub mod uspace; pub use self::context::{ExtendedState, FxsaveArea, TaskContext, TrapFrame}; -pub use self::gdt::GdtStruct; -pub use self::idt::IdtStruct; -pub use x86_64::structures::tss::TaskStateSegment; diff --git a/src/x86_64/syscall.rs b/src/x86_64/syscall.rs index 55413f5..8a6b1bf 100644 --- a/src/x86_64/syscall.rs +++ b/src/x86_64/syscall.rs @@ -3,7 +3,7 @@ use x86_64::registers::model_specific::{Efer, EferFlags, KernelGsBase, LStar, SF use x86_64::registers::rflags::RFlags; use x86_64::structures::tss::TaskStateSegment; -use super::{GdtStruct, TrapFrame}; +use super::{gdt, TrapFrame}; #[unsafe(no_mangle)] #[percpu::def_percpu] @@ -26,13 +26,7 @@ pub fn init_syscall() { } unsafe { LStar::write(VirtAddr::new(syscall_entry as *const () as u64)); - Star::write( - GdtStruct::UCODE64_SELECTOR, - GdtStruct::UDATA_SELECTOR, - GdtStruct::KCODE64_SELECTOR, - GdtStruct::KDATA_SELECTOR, - ) - .unwrap(); + Star::write(gdt::UCODE64, gdt::UDATA, gdt::KCODE64, gdt::KDATA).unwrap(); SFMask::write( RFlags::TRAP_FLAG | RFlags::INTERRUPT_FLAG diff --git a/src/x86_64/trap.S b/src/x86_64/trap.S index 4bd0d94..7d78bc5 100644 --- a/src/x86_64/trap.S +++ b/src/x86_64/trap.S @@ -3,7 +3,7 @@ .altmacro .macro DEF_HANDLER, i .Ltrap_handler_\i: -.if \i == 8 || (\i >= 10 && \i <= 14) || \i == 17 +.if \i == 8 || (\i >= 10 && \i <= 14) || \i == 17 || \i == 21 || \i == 29 || \i == 30 # error code pushed by CPU push \i # interrupt vector jmp .Ltrap_common diff --git a/src/x86_64/trap.rs b/src/x86_64/trap.rs index 98e2734..9cb2f08 100644 --- a/src/x86_64/trap.rs +++ b/src/x86_64/trap.rs @@ -70,7 +70,8 @@ fn err_code_to_flags(err_code: u64) -> Result { let code = PageFaultErrorCode::from_bits_truncate(err_code); let reserved_bits = (PageFaultErrorCode::CAUSED_BY_WRITE | PageFaultErrorCode::USER_MODE - | PageFaultErrorCode::INSTRUCTION_FETCH) + | PageFaultErrorCode::INSTRUCTION_FETCH + | PageFaultErrorCode::PROTECTION_VIOLATION) .complement(); if code.intersects(reserved_bits) { Err(err_code) diff --git a/src/x86_64/uspace.rs b/src/x86_64/uspace.rs index 868ab01..cc8de65 100644 --- a/src/x86_64/uspace.rs +++ b/src/x86_64/uspace.rs @@ -2,7 +2,7 @@ use memory_addr::VirtAddr; -use crate::TrapFrame; +use super::{gdt, TrapFrame}; /// Context to enter user space. pub struct UspaceContext(TrapFrame); @@ -16,15 +16,14 @@ impl UspaceContext { /// Creates a new context with the given entry point, user stack pointer, /// and the argument. pub fn new(entry: usize, ustack_top: VirtAddr, arg0: usize) -> Self { - use crate::GdtStruct; use x86_64::registers::rflags::RFlags; Self(TrapFrame { rdi: arg0 as _, rip: entry as _, - cs: GdtStruct::UCODE64_SELECTOR.0 as _, + cs: gdt::UCODE64.0 as _, rflags: RFlags::INTERRUPT_FLAG.bits(), // IOPL = 0, IF = 1 rsp: ustack_top.as_usize() as _, - ss: GdtStruct::UDATA_SELECTOR.0 as _, + ss: gdt::UDATA.0 as _, ..Default::default() }) } @@ -34,10 +33,9 @@ impl UspaceContext { /// It copies almost all registers except `CS` and `SS` which need to be /// set to the user segment selectors. pub const fn from(tf: &TrapFrame) -> Self { - use crate::GdtStruct; let mut tf = *tf; - tf.cs = GdtStruct::UCODE64_SELECTOR.0 as _; - tf.ss = GdtStruct::UDATA_SELECTOR.0 as _; + tf.cs = gdt::UCODE64.0 as _; + tf.ss = gdt::UDATA.0 as _; Self(tf) }