From 3dc5a0bb7274978706264064b6825248b2ca9294 Mon Sep 17 00:00:00 2001 From: Yuekai Jia Date: Fri, 31 May 2024 17:22:54 +0800 Subject: [PATCH 01/17] [hal]: Add UspaceContext to enter user space on x86_64 --- modules/axhal/Cargo.toml | 1 + modules/axhal/src/arch/aarch64/context.rs | 8 +- modules/axhal/src/arch/aarch64/mod.rs | 11 ++ modules/axhal/src/arch/riscv/context.rs | 8 +- modules/axhal/src/arch/riscv/mod.rs | 10 ++ modules/axhal/src/arch/riscv/trap.S | 2 +- modules/axhal/src/arch/x86_64/context.rs | 151 +++++++++++++++++- modules/axhal/src/arch/x86_64/gdt.rs | 35 ++++ modules/axhal/src/arch/x86_64/idt.rs | 15 +- modules/axhal/src/arch/x86_64/mod.rs | 23 ++- modules/axhal/src/arch/x86_64/syscall.S | 54 +++++++ modules/axhal/src/arch/x86_64/syscall.rs | 51 ++++++ modules/axhal/src/arch/x86_64/trap.rs | 10 ++ modules/axhal/src/cpu.rs | 2 + .../src/platform/aarch64_bsta1000b/mod.rs | 2 - .../src/platform/aarch64_qemu_virt/mod.rs | 4 - .../src/platform/riscv64_qemu_virt/mod.rs | 2 - modules/axhal/src/platform/x86_pc/dtables.rs | 37 ----- modules/axhal/src/platform/x86_pc/mod.rs | 3 - modules/axmm/src/aspace.rs | 15 ++ modules/axmm/src/lib.rs | 14 ++ 21 files changed, 401 insertions(+), 57 deletions(-) create mode 100644 modules/axhal/src/arch/x86_64/syscall.S create mode 100644 modules/axhal/src/arch/x86_64/syscall.rs delete mode 100644 modules/axhal/src/platform/x86_pc/dtables.rs diff --git a/modules/axhal/Cargo.toml b/modules/axhal/Cargo.toml index 607cc75212..f1cc469045 100644 --- a/modules/axhal/Cargo.toml +++ b/modules/axhal/Cargo.toml @@ -17,6 +17,7 @@ paging = ["axalloc", "page_table_multiarch"] irq = [] tls = ["alloc"] rtc = ["x86_rtc", "riscv_goldfish", "arm_pl031"] +uspace = ["paging"] default = [] [dependencies] diff --git a/modules/axhal/src/arch/aarch64/context.rs b/modules/axhal/src/arch/aarch64/context.rs index e457cdf060..ae8635d48c 100644 --- a/modules/axhal/src/arch/aarch64/context.rs +++ b/modules/axhal/src/arch/aarch64/context.rs @@ -68,7 +68,13 @@ pub struct TaskContext { } impl TaskContext { - /// Creates a new default context for a new task. + /// Creates a dummy context for a new task. + /// + /// Note the context is not initialized, it will be filled by [`switch_to`] + /// (for initial tasks) and [`init`] (for regular tasks) methods. + /// + /// [`init`]: TaskContext::init + /// [`switch_to`]: TaskContext::switch_to pub const fn new() -> Self { unsafe { core::mem::MaybeUninit::zeroed().assume_init() } } diff --git a/modules/axhal/src/arch/aarch64/mod.rs b/modules/axhal/src/arch/aarch64/mod.rs index 19c0134bb8..0d1f70530a 100644 --- a/modules/axhal/src/arch/aarch64/mod.rs +++ b/modules/axhal/src/arch/aarch64/mod.rs @@ -137,3 +137,14 @@ pub fn read_thread_pointer() -> usize { pub unsafe fn write_thread_pointer(tpidr_el0: usize) { TPIDR_EL0.set(tpidr_el0 as _) } + +/// Initializes CPU states on the current CPU. +/// +/// On AArch64, it sets the exception vector base address (`VBAR_EL1`) and `TTBR0_EL1`. +pub fn cpu_init() { + extern "C" { + fn exception_vector_base(); + } + set_exception_vector_base(exception_vector_base as usize); + unsafe { write_page_table_root0(0.into()) }; // disable low address access in EL1 +} diff --git a/modules/axhal/src/arch/riscv/context.rs b/modules/axhal/src/arch/riscv/context.rs index df612a7fc7..40eb9d9195 100644 --- a/modules/axhal/src/arch/riscv/context.rs +++ b/modules/axhal/src/arch/riscv/context.rs @@ -88,7 +88,13 @@ pub struct TaskContext { } impl TaskContext { - /// Creates a new default context for a new task. + /// Creates a dummy context for a new task. + /// + /// Note the context is not initialized, it will be filled by [`switch_to`] + /// (for initial tasks) and [`init`] (for regular tasks) methods. + /// + /// [`init`]: TaskContext::init + /// [`switch_to`]: TaskContext::switch_to pub const fn new() -> Self { unsafe { core::mem::MaybeUninit::zeroed().assume_init() } } diff --git a/modules/axhal/src/arch/riscv/mod.rs b/modules/axhal/src/arch/riscv/mod.rs index df6927dddc..eb3bc82278 100644 --- a/modules/axhal/src/arch/riscv/mod.rs +++ b/modules/axhal/src/arch/riscv/mod.rs @@ -107,3 +107,13 @@ pub fn read_thread_pointer() -> usize { pub unsafe fn write_thread_pointer(tp: usize) { core::arch::asm!("mv tp, {}", in(reg) tp) } + +/// Initializes CPU states on the current CPU. +/// +/// On RISC-V, it sets the trap vector base address. +pub fn cpu_init() { + extern "C" { + fn trap_vector_base(); + } + set_trap_vector_base(trap_vector_base as usize); +} diff --git a/modules/axhal/src/arch/riscv/trap.S b/modules/axhal/src/arch/riscv/trap.S index 9de738fe3a..811d1aa1a4 100644 --- a/modules/axhal/src/arch/riscv/trap.S +++ b/modules/axhal/src/arch/riscv/trap.S @@ -81,7 +81,7 @@ trap_vector_base: // sscratch == 0: trap from S mode // sscratch != 0: trap from U mode - csrrw sp, sscratch, sp // switch sscratch and sp + csrrw sp, sscratch, sp // swap sscratch and sp bnez sp, .Ltrap_entry_u csrr sp, sscratch // put supervisor sp back diff --git a/modules/axhal/src/arch/x86_64/context.rs b/modules/axhal/src/arch/x86_64/context.rs index f30b10dee9..4be4586e80 100644 --- a/modules/axhal/src/arch/x86_64/context.rs +++ b/modules/axhal/src/arch/x86_64/context.rs @@ -1,10 +1,20 @@ +<<<<<<< HEAD use core::{arch::naked_asm, fmt}; +======= +#![allow(unused_imports)] + +use core::{arch::asm, fmt}; + +>>>>>>> 4f0c0a4 ([hal]: Add UspaceContext to enter user space on x86_64) use memory_addr::VirtAddr; +use x86_64::registers::rflags::RFlags; + +use super::gdt::GdtStruct; /// Saved registers when a trap (interrupt or exception) occurs. #[allow(missing_docs)] #[repr(C)] -#[derive(Debug, Default, Clone)] +#[derive(Debug, Default, Clone, Copy)] pub struct TrapFrame { pub rax: u64, pub rcx: u64, @@ -35,12 +45,143 @@ pub struct TrapFrame { } impl TrapFrame { + /// Gets the 0th syscall argument. + pub const fn arg0(&self) -> usize { + self.rdi as _ + } + + /// Gets the 1st syscall argument. + pub const fn arg1(&self) -> usize { + self.rsi as _ + } + + /// Gets the 2nd syscall argument. + pub const fn arg2(&self) -> usize { + self.rdx as _ + } + + /// Gets the 3rd syscall argument. + pub const fn arg3(&self) -> usize { + self.r10 as _ + } + + /// Gets the 4th syscall argument. + pub const fn arg4(&self) -> usize { + self.r8 as _ + } + + /// Gets the 5th syscall argument. + pub const fn arg5(&self) -> usize { + self.r9 as _ + } + /// Whether the trap is from userspace. pub const fn is_user(&self) -> bool { self.cs & 0b11 == 3 } } +/// Context to enter user space. +#[cfg(feature = "uspace")] +pub struct UspaceContext(TrapFrame); + +#[cfg(feature = "uspace")] +impl UspaceContext { + /// Creates an empty context with all registers set to zero. + pub const fn empty() -> Self { + unsafe { core::mem::MaybeUninit::zeroed().assume_init() } + } + + /// 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 { + Self(TrapFrame { + rdi: arg0 as _, + rip: entry as _, + cs: GdtStruct::UCODE64_SELECTOR.0 as _, + #[cfg(feature = "irq")] + rflags: RFlags::INTERRUPT_FLAG.bits(), // IOPL = 0, IF = 1 + rsp: ustack_top.as_usize() as _, + ss: GdtStruct::UDATA_SELECTOR.0 as _, + ..Default::default() + }) + } + + /// Creates a new context from the given [`TrapFrame`]. + /// + /// 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 { + let mut tf = *tf; + tf.cs = GdtStruct::UCODE64_SELECTOR.0 as _; + tf.ss = GdtStruct::UDATA_SELECTOR.0 as _; + Self(tf) + } + + /// Gets the instruction pointer. + pub const fn get_ip(&self) -> usize { + self.0.rip as _ + } + + /// Gets the stack pointer. + pub const fn get_sp(&self) -> usize { + self.0.rsp as _ + } + + /// Sets the instruction pointer. + pub const fn set_ip(&mut self, rip: usize) { + self.0.rip = rip as _; + } + + /// Sets the stack pointer. + pub const fn set_sp(&mut self, rsp: usize) { + self.0.rsp = rsp as _; + } + + /// Sets the return value register. + pub const fn set_retval(&mut self, rax: usize) { + self.0.rax = rax as _; + } + + /// Enters user space. + /// + /// It restores the user registers and jumps to the user entry point + /// (saved in `rip`). + /// When an exception or syscall occurs, the kernel stack pointer is + /// switched to `kstack_top`. + /// + /// # Safety + /// + /// This function is unsafe because it changes processor mode and the stack. + pub unsafe fn enter_uspace(&self, kstack_top: VirtAddr) -> ! { + super::disable_irqs(); + super::tss_set_rsp0(kstack_top); + asm!(" + mov rsp, {tf} + pop rax + pop rcx + pop rdx + pop rbx + pop rbp + pop rsi + pop rdi + pop r8 + pop r9 + pop r10 + pop r11 + pop r12 + pop r13 + pop r14 + pop r15 + add rsp, 16 // skip vector, error_code + swapgs + iretq", + tf = in(reg) &self.0, + options(noreturn), + ) + } +} + #[repr(C)] #[derive(Debug, Default)] struct ContextSwitchFrame { @@ -144,7 +285,13 @@ pub struct TaskContext { } impl TaskContext { - /// Creates a new default context for a new task. + /// Creates a dummy context for a new task. + /// + /// Note the context is not initialized, it will be filled by [`switch_to`] + /// (for initial tasks) and [`init`] (for regular tasks) methods. + /// + /// [`init`]: TaskContext::init + /// [`switch_to`]: TaskContext::switch_to pub const fn new() -> Self { Self { kstack_top: va!(0), diff --git a/modules/axhal/src/arch/x86_64/gdt.rs b/modules/axhal/src/arch/x86_64/gdt.rs index 8c22380856..6a040cb96f 100644 --- a/modules/axhal/src/arch/x86_64/gdt.rs +++ b/modules/axhal/src/arch/x86_64/gdt.rs @@ -1,11 +1,19 @@ use core::fmt; +use lazyinit::LazyInit; use x86_64::instructions::tables::{lgdt, load_tss}; use x86_64::registers::segmentation::{CS, Segment, SegmentSelector}; use x86_64::structures::gdt::{Descriptor, DescriptorFlags}; use x86_64::structures::{DescriptorTablePointer, tss::TaskStateSegment}; use x86_64::{PrivilegeLevel, addr::VirtAddr}; +#[no_mangle] +#[percpu::def_percpu] +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 { @@ -86,3 +94,30 @@ impl fmt::Debug for GdtStruct { .finish() } } + +/// Initializes the per-CPU TSS and GDT structures and loads them into the +/// current CPU. +pub fn init_gdt() { + unsafe { + let gdt = GDT.current_ref_raw(); + gdt.init_once(GdtStruct::new(TSS.current_ref_raw())); + gdt.load(); + gdt.load_tss(); + } +} + +/// Returns the stack pointer for privilege level 0 (RSP0) of the current TSS. +pub fn tss_get_rsp0() -> memory_addr::VirtAddr { + let tss = unsafe { TSS.current_ref_raw() }; + memory_addr::VirtAddr::from(tss.privilege_stack_table[0].as_u64() as usize) +} + +/// Sets the stack pointer for privilege level 0 (RSP0) of the current TSS. +/// +/// # Safety +/// +/// Must be called after initialization and preemption is disabled. +pub unsafe fn tss_set_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); +} diff --git a/modules/axhal/src/arch/x86_64/idt.rs b/modules/axhal/src/arch/x86_64/idt.rs index 30bfc63a21..107d1dbc35 100644 --- a/modules/axhal/src/arch/x86_64/idt.rs +++ b/modules/axhal/src/arch/x86_64/idt.rs @@ -1,11 +1,14 @@ use core::fmt; +use lazyinit::LazyInit; use x86_64::addr::VirtAddr; use x86_64::structures::DescriptorTablePointer; use x86_64::structures::idt::{Entry, HandlerFunc, InterruptDescriptorTable}; const NUM_INT: usize = 256; +static IDT: LazyInit = LazyInit::new(); + /// A wrapper of the Interrupt Descriptor Table (IDT). #[repr(transparent)] pub struct IdtStruct { @@ -33,7 +36,11 @@ impl IdtStruct { }; for i in 0..NUM_INT { #[allow(clippy::missing_transmute_annotations)] - entries[i].set_handler_fn(unsafe { core::mem::transmute(ENTRIES[i]) }); + 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 } @@ -66,3 +73,9 @@ impl fmt::Debug for IdtStruct { .finish() } } + +/// Initializes the global IDT and loads it into the current CPU. +pub fn init_idt() { + IDT.call_once(IdtStruct::new); + unsafe { IDT.load() }; +} diff --git a/modules/axhal/src/arch/x86_64/mod.rs b/modules/axhal/src/arch/x86_64/mod.rs index bced2460d2..a3b7d81156 100644 --- a/modules/axhal/src/arch/x86_64/mod.rs +++ b/modules/axhal/src/arch/x86_64/mod.rs @@ -2,6 +2,9 @@ mod context; mod gdt; mod idt; +#[cfg(feature = "uspace")] +mod syscall; + #[cfg(target_os = "none")] mod trap; @@ -12,9 +15,11 @@ use x86::{controlregs, msr, tlb}; use x86_64::instructions::interrupts; 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; +pub use self::gdt::{init_gdt, tss_get_rsp0, tss_set_rsp0, GdtStruct}; +pub use self::idt::{init_idt, IdtStruct}; + +#[cfg(feature = "uspace")] +pub use self::{context::UspaceContext, syscall::init_syscall}; /// Allows the current CPU to respond to interrupts. #[inline] @@ -116,3 +121,15 @@ pub fn read_thread_pointer() -> usize { pub unsafe fn write_thread_pointer(fs_base: usize) { unsafe { msr::wrmsr(msr::IA32_FS_BASE, fs_base as u64) } } + +/// Initializes CPU states on the current CPU. +/// +/// 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 enable the `syscall` instruction. +pub fn cpu_init() { + init_gdt(); + init_idt(); + #[cfg(feature = "uspace")] + init_syscall(); +} diff --git a/modules/axhal/src/arch/x86_64/syscall.S b/modules/axhal/src/arch/x86_64/syscall.S new file mode 100644 index 0000000000..08ac99995d --- /dev/null +++ b/modules/axhal/src/arch/x86_64/syscall.S @@ -0,0 +1,54 @@ +.section .text +syscall_entry: + swapgs // switch to kernel gs + mov gs:[offset __PERCPU_USER_RSP_OFFSET], rsp // save user rsp + mov rsp, gs:[offset __PERCPU_TSS + {tss_rsp0_offset}] // switch to kernel stack + + sub rsp, 8 // skip user ss + push gs:[offset __PERCPU_USER_RSP_OFFSET] // user rsp + push r11 // rflags + mov [rsp - 2 * 8], rcx // rip + sub rsp, 4 * 8 // skip until general registers + + push r15 + push r14 + push r13 + push r12 + push r11 + push r10 + push r9 + push r8 + push rdi + push rsi + push rbp + push rbx + push rdx + push rcx + push rax + + mov rdi, rsp + call x86_syscall_handler + + pop rax + pop rcx + pop rdx + pop rbx + pop rbp + pop rsi + pop rdi + pop r8 + pop r9 + pop r10 + pop r11 + pop r12 + pop r13 + pop r14 + pop r15 + + add rsp, 7 * 8 + mov rcx, [rsp - 5 * 8] // rip + mov r11, [rsp - 3 * 8] // rflags + mov rsp, [rsp - 2 * 8] // user rsp + + swapgs + sysretq diff --git a/modules/axhal/src/arch/x86_64/syscall.rs b/modules/axhal/src/arch/x86_64/syscall.rs new file mode 100644 index 0000000000..fb57432362 --- /dev/null +++ b/modules/axhal/src/arch/x86_64/syscall.rs @@ -0,0 +1,51 @@ +use x86_64::addr::VirtAddr; +use x86_64::registers::model_specific::{Efer, EferFlags, KernelGsBase, LStar, SFMask, Star}; +use x86_64::registers::rflags::RFlags; +use x86_64::structures::tss::TaskStateSegment; + +use super::{GdtStruct, TrapFrame}; + +#[no_mangle] +#[percpu::def_percpu] +static USER_RSP_OFFSET: usize = 0; + +core::arch::global_asm!( + include_str!("syscall.S"), + tss_rsp0_offset = const core::mem::offset_of!(TaskStateSegment, privilege_stack_table), +); + +#[no_mangle] +pub(super) fn x86_syscall_handler(tf: &mut TrapFrame) { + info!( + "syscall @ {:#x}: {} [{}, {}, {}, {}]", + tf.rip, tf.rax, tf.rdi, tf.rsi, tf.rdx, tf.rdx, + ); +} + +/// Initializes syscall support and setups the syscall handler. +pub fn init_syscall() { + extern "C" { + fn syscall_entry(); + } + unsafe { + LStar::write(VirtAddr::new(syscall_entry as usize as _)); + Star::write( + GdtStruct::UCODE64_SELECTOR, + GdtStruct::UDATA_SELECTOR, + GdtStruct::KCODE64_SELECTOR, + GdtStruct::KDATA_SELECTOR, + ) + .unwrap(); + SFMask::write( + RFlags::TRAP_FLAG + | RFlags::INTERRUPT_FLAG + | RFlags::DIRECTION_FLAG + | RFlags::IOPL_LOW + | RFlags::IOPL_HIGH + | RFlags::NESTED_TASK + | RFlags::ALIGNMENT_CHECK, + ); // TF | IF | DF | IOPL | AC | NT (0x47700) + Efer::update(|efer| *efer |= EferFlags::SYSTEM_CALL_EXTENSIONS); + KernelGsBase::write(VirtAddr::new(0)); + } +} diff --git a/modules/axhal/src/arch/x86_64/trap.rs b/modules/axhal/src/arch/x86_64/trap.rs index 08ba46efaa..3fd1072895 100644 --- a/modules/axhal/src/arch/x86_64/trap.rs +++ b/modules/axhal/src/arch/x86_64/trap.rs @@ -6,6 +6,9 @@ use super::context::TrapFrame; core::arch::global_asm!(include_str!("trap.S")); +#[cfg(feature = "uspace")] +const LEGACY_SYSCALL_VECTOR: u8 = 0x80; + const IRQ_VECTOR_START: u8 = 0x20; const IRQ_VECTOR_END: u8 = 0xff; @@ -26,8 +29,13 @@ fn handle_page_fault(tf: &TrapFrame) { } } +<<<<<<< HEAD #[unsafe(no_mangle)] fn x86_trap_handler(tf: &TrapFrame) { +======= +#[no_mangle] +fn x86_trap_handler(tf: &mut TrapFrame) { +>>>>>>> 4f0c0a4 ([hal]: Add UspaceContext to enter user space on x86_64) match tf.vector as u8 { PAGE_FAULT_VECTOR => handle_page_fault(tf), BREAKPOINT_VECTOR => debug!("#BP @ {:#x} ", tf.rip), @@ -37,6 +45,8 @@ fn x86_trap_handler(tf: &TrapFrame) { tf.rip, tf.error_code, tf ); } + #[cfg(feature = "uspace")] + LEGACY_SYSCALL_VECTOR => super::syscall::x86_syscall_handler(tf), IRQ_VECTOR_START..=IRQ_VECTOR_END => { handle_trap!(IRQ, tf.vector as _); } diff --git a/modules/axhal/src/cpu.rs b/modules/axhal/src/cpu.rs index 875aff11b8..89ca281bfa 100644 --- a/modules/axhal/src/cpu.rs +++ b/modules/axhal/src/cpu.rs @@ -81,6 +81,7 @@ pub(crate) fn init_primary(cpu_id: usize) { CPU_ID.write_current_raw(cpu_id); IS_BSP.write_current_raw(true); } + crate::arch::cpu_init(); } #[allow(dead_code)] @@ -90,4 +91,5 @@ pub(crate) fn init_secondary(cpu_id: usize) { CPU_ID.write_current_raw(cpu_id); IS_BSP.write_current_raw(false); } + crate::arch::cpu_init(); } diff --git a/modules/axhal/src/platform/aarch64_bsta1000b/mod.rs b/modules/axhal/src/platform/aarch64_bsta1000b/mod.rs index cc9cbed108..a50b2d5550 100644 --- a/modules/axhal/src/platform/aarch64_bsta1000b/mod.rs +++ b/modules/axhal/src/platform/aarch64_bsta1000b/mod.rs @@ -28,7 +28,6 @@ unsafe extern "C" { pub(crate) unsafe extern "C" fn rust_entry(cpu_id: usize, dtb: usize) { crate::mem::clear_bss(); - crate::arch::set_exception_vector_base(exception_vector_base as usize); crate::cpu::init_primary(cpu_id); dw_apb_uart::init_early(); super::aarch64_common::generic_timer::init_early(); @@ -37,7 +36,6 @@ pub(crate) unsafe extern "C" fn rust_entry(cpu_id: usize, dtb: usize) { #[cfg(feature = "smp")] pub(crate) unsafe extern "C" fn rust_entry_secondary(cpu_id: usize) { - crate::arch::set_exception_vector_base(exception_vector_base as usize); crate::cpu::init_secondary(cpu_id); rust_main_secondary(cpu_id); } diff --git a/modules/axhal/src/platform/aarch64_qemu_virt/mod.rs b/modules/axhal/src/platform/aarch64_qemu_virt/mod.rs index 13a865384d..f1d3a570eb 100644 --- a/modules/axhal/src/platform/aarch64_qemu_virt/mod.rs +++ b/modules/axhal/src/platform/aarch64_qemu_virt/mod.rs @@ -29,8 +29,6 @@ unsafe extern "C" { pub(crate) unsafe extern "C" fn rust_entry(cpu_id: usize, dtb: usize) { crate::mem::clear_bss(); - crate::arch::set_exception_vector_base(exception_vector_base as usize); - crate::arch::write_page_table_root0(0.into()); // disable low address access crate::cpu::init_primary(cpu_id); super::aarch64_common::pl011::init_early(); super::aarch64_common::generic_timer::init_early(); @@ -39,8 +37,6 @@ pub(crate) unsafe extern "C" fn rust_entry(cpu_id: usize, dtb: usize) { #[cfg(feature = "smp")] pub(crate) unsafe extern "C" fn rust_entry_secondary(cpu_id: usize) { - crate::arch::set_exception_vector_base(exception_vector_base as usize); - crate::arch::write_page_table_root0(0.into()); // disable low address access crate::cpu::init_secondary(cpu_id); rust_main_secondary(cpu_id); } diff --git a/modules/axhal/src/platform/riscv64_qemu_virt/mod.rs b/modules/axhal/src/platform/riscv64_qemu_virt/mod.rs index dc75d0c490..4ab5d50bd9 100644 --- a/modules/axhal/src/platform/riscv64_qemu_virt/mod.rs +++ b/modules/axhal/src/platform/riscv64_qemu_virt/mod.rs @@ -21,14 +21,12 @@ unsafe extern "C" { unsafe extern "C" fn rust_entry(cpu_id: usize, dtb: usize) { crate::mem::clear_bss(); crate::cpu::init_primary(cpu_id); - crate::arch::set_trap_vector_base(trap_vector_base as usize); self::time::init_early(); rust_main(cpu_id, dtb); } #[cfg(feature = "smp")] unsafe extern "C" fn rust_entry_secondary(cpu_id: usize) { - crate::arch::set_trap_vector_base(trap_vector_base as usize); crate::cpu::init_secondary(cpu_id); rust_main_secondary(cpu_id); } diff --git a/modules/axhal/src/platform/x86_pc/dtables.rs b/modules/axhal/src/platform/x86_pc/dtables.rs deleted file mode 100644 index a6af270c66..0000000000 --- a/modules/axhal/src/platform/x86_pc/dtables.rs +++ /dev/null @@ -1,37 +0,0 @@ -//! Description tables (per-CPU GDT, per-CPU ISS, IDT) - -use crate::arch::{GdtStruct, IdtStruct, TaskStateSegment}; -use lazyinit::LazyInit; - -static IDT: LazyInit = LazyInit::new(); - -#[percpu::def_percpu] -static TSS: LazyInit = LazyInit::new(); - -#[percpu::def_percpu] -static GDT: LazyInit = LazyInit::new(); - -fn init_percpu() { - unsafe { - IDT.load(); - let tss = TSS.current_ref_mut_raw(); - let gdt = GDT.current_ref_mut_raw(); - tss.init_once(TaskStateSegment::new()); - gdt.init_once(GdtStruct::new(tss)); - gdt.load(); - gdt.load_tss(); - } -} - -/// Initializes IDT, GDT on the primary CPU. -pub(super) fn init_primary() { - axlog::ax_println!("\nInitialize IDT & GDT..."); - IDT.init_once(IdtStruct::new()); - init_percpu(); -} - -/// Initializes IDT, GDT on secondary CPUs. -#[cfg(feature = "smp")] -pub(super) fn init_secondary() { - init_percpu(); -} diff --git a/modules/axhal/src/platform/x86_pc/mod.rs b/modules/axhal/src/platform/x86_pc/mod.rs index e39bc7e573..a9d45ba0f0 100644 --- a/modules/axhal/src/platform/x86_pc/mod.rs +++ b/modules/axhal/src/platform/x86_pc/mod.rs @@ -1,6 +1,5 @@ mod apic; mod boot; -mod dtables; mod uart16550; pub mod mem; @@ -38,7 +37,6 @@ unsafe extern "C" fn rust_entry(magic: usize, _mbi: usize) { crate::mem::clear_bss(); crate::cpu::init_primary(current_cpu_id()); self::uart16550::init(); - self::dtables::init_primary(); self::time::init_early(); rust_main(current_cpu_id(), 0); } @@ -49,7 +47,6 @@ unsafe extern "C" fn rust_entry_secondary(magic: usize) { #[cfg(feature = "smp")] if magic == self::boot::MULTIBOOT_BOOTLOADER_MAGIC { crate::cpu::init_secondary(current_cpu_id()); - self::dtables::init_secondary(); rust_main_secondary(current_cpu_id()); } } diff --git a/modules/axmm/src/aspace.rs b/modules/axmm/src/aspace.rs index 23165d996f..bf96eaa0c8 100644 --- a/modules/axmm/src/aspace.rs +++ b/modules/axmm/src/aspace.rs @@ -57,6 +57,21 @@ impl AddrSpace { }) } + /// Copies page table mappings from another address space. + /// + /// It copies the page table entries only rather than the memory regions, + /// usually used to copy a portion of the kernel space mapping to the + /// user space. + /// + /// Returns an error if the two address spaces overlap. + pub fn copy_mappings_from(&mut self, other: &AddrSpace) -> AxResult { + if self.va_range.overlaps(other.va_range) { + return ax_err!(InvalidInput, "address space overlap"); + } + self.pt.copy_from(&other.pt, other.base(), other.size()); + Ok(()) + } + /// Add a new linear mapping. /// /// The mapping is linear, i.e., `start_vaddr` is mapped to `start_paddr`, diff --git a/modules/axmm/src/lib.rs b/modules/axmm/src/lib.rs index cabda17104..d3015b4b20 100644 --- a/modules/axmm/src/lib.rs +++ b/modules/axmm/src/lib.rs @@ -17,6 +17,9 @@ use kspin::SpinNoIrq; use lazyinit::LazyInit; use memory_addr::{PhysAddr, va}; +const USER_ASPACE_BASE: usize = 0x1000; +const USER_ASPACE_SIZE: usize = 0x7fff_ffff_f000; + static KERNEL_ASPACE: LazyInit> = LazyInit::new(); fn paging_err_to_ax_err(err: PagingError) -> AxError { @@ -42,6 +45,17 @@ pub fn new_kernel_aspace() -> AxResult { Ok(aspace) } +/// Creates a new address space for user processes. +pub fn new_user_aspace() -> AxResult { + let mut aspace = AddrSpace::new_empty(VirtAddr::from(USER_ASPACE_BASE), USER_ASPACE_SIZE)?; + if !cfg!(target_arch = "aarch64") { + // ARMv8 use a separate page table (TTBR0_EL1) for user space, it + // doesn't need to copy the kernel portion to the user page table. + aspace.copy_mappings_from(&kernel_aspace().lock())?; + } + Ok(aspace) +} + /// Returns the globally unique kernel address space. pub fn kernel_aspace() -> &'static SpinNoIrq { &KERNEL_ASPACE From f6d8e2c904f3592267fcbe1353145f9216fd1989 Mon Sep 17 00:00:00 2001 From: Yuekai Jia Date: Sat, 27 Jul 2024 01:07:34 +0800 Subject: [PATCH 02/17] [hal]: Switch `CR3` on context switch --- modules/axhal/src/arch/x86_64/context.rs | 49 ++++++++++++++++-------- modules/axhal/src/arch/x86_64/gdt.rs | 12 ++++-- modules/axhal/src/paging.rs | 21 ++++++++++ modules/axmm/src/lib.rs | 6 +-- 4 files changed, 65 insertions(+), 23 deletions(-) diff --git a/modules/axhal/src/arch/x86_64/context.rs b/modules/axhal/src/arch/x86_64/context.rs index 4be4586e80..fd8d3ef6f9 100644 --- a/modules/axhal/src/arch/x86_64/context.rs +++ b/modules/axhal/src/arch/x86_64/context.rs @@ -1,15 +1,7 @@ -<<<<<<< HEAD -use core::{arch::naked_asm, fmt}; -======= #![allow(unused_imports)] -use core::{arch::asm, fmt}; - ->>>>>>> 4f0c0a4 ([hal]: Add UspaceContext to enter user space on x86_64) +use core::{arch::naked_asm, fmt}; use memory_addr::VirtAddr; -use x86_64::registers::rflags::RFlags; - -use super::gdt::GdtStruct; /// Saved registers when a trap (interrupt or exception) occurs. #[allow(missing_docs)] @@ -155,7 +147,7 @@ impl UspaceContext { /// This function is unsafe because it changes processor mode and the stack. pub unsafe fn enter_uspace(&self, kstack_top: VirtAddr) -> ! { super::disable_irqs(); - super::tss_set_rsp0(kstack_top); + assert_eq!(super::tss_get_rsp0(), kstack_top); asm!(" mov rsp, {tf} pop rax @@ -282,6 +274,9 @@ pub struct TaskContext { /// Extended states, i.e., FP/SIMD states. #[cfg(feature = "fp_simd")] pub ext_state: ExtendedState, + /// The `CR3` register value, i.e., the page table root. + #[cfg(feature = "uspace")] + pub cr3: PhysAddr, } impl TaskContext { @@ -292,11 +287,13 @@ impl TaskContext { /// /// [`init`]: TaskContext::init /// [`switch_to`]: TaskContext::switch_to - pub const fn new() -> Self { + pub fn new() -> Self { Self { kstack_top: va!(0), rsp: 0, fs_base: 0, + #[cfg(feature = "uspace")] + cr3: crate::paging::kernel_page_table_root(), #[cfg(feature = "fp_simd")] ext_state: ExtendedState::default(), } @@ -321,6 +318,17 @@ impl TaskContext { self.fs_base = tls_area.as_usize(); } + /// Changes the page table root (`CR3` register for x86_64). + /// + /// If not set, the kernel page table root is used (obtained by + /// [`axhal::paging::kernel_page_table_root`][1]). + /// + /// [1]: crate::paging::kernel_page_table_root + #[cfg(feature = "uspace")] + pub fn set_page_table_root(&mut self, cr3: PhysAddr) { + self.cr3 = cr3; + } + /// Switches to another task. /// /// It first saves the current task's context from CPU to this place, and then @@ -332,9 +340,16 @@ impl TaskContext { next_ctx.ext_state.restore(); } #[cfg(feature = "tls")] - { + unsafe { self.fs_base = super::read_thread_pointer(); - unsafe { super::write_thread_pointer(next_ctx.fs_base) }; + super::write_thread_pointer(next_ctx.fs_base); + } + #[cfg(feature = "uspace")] + unsafe { + super::tss_set_rsp0(next_ctx.kstack_top); + if next_ctx.cr3 != self.cr3 { + super::write_page_table_root(next_ctx.cr3); + } } unsafe { context_switch(&mut self.rsp, &next_ctx.rsp) } } @@ -342,8 +357,9 @@ impl TaskContext { #[naked] unsafe extern "C" fn context_switch(_current_stack: &mut u64, _next_stack: &u64) { - naked_asm!( - ".code64 + unsafe { + naked_asm!( + ".code64 push rbp push rbx push r12 @@ -360,5 +376,6 @@ unsafe extern "C" fn context_switch(_current_stack: &mut u64, _next_stack: &u64) pop rbx pop rbp ret", - ) + ) + } } diff --git a/modules/axhal/src/arch/x86_64/gdt.rs b/modules/axhal/src/arch/x86_64/gdt.rs index 6a040cb96f..b8c5e89845 100644 --- a/modules/axhal/src/arch/x86_64/gdt.rs +++ b/modules/axhal/src/arch/x86_64/gdt.rs @@ -7,7 +7,7 @@ use x86_64::structures::gdt::{Descriptor, DescriptorFlags}; use x86_64::structures::{DescriptorTablePointer, tss::TaskStateSegment}; use x86_64::{PrivilegeLevel, addr::VirtAddr}; -#[no_mangle] +#[unsafe(no_mangle)] #[percpu::def_percpu] static TSS: TaskStateSegment = TaskStateSegment::new(); @@ -71,8 +71,10 @@ impl GdtStruct { /// This function is unsafe because it manipulates the CPU's privileged /// states. pub unsafe fn load(&'static self) { - lgdt(&self.pointer()); - CS::set_reg(Self::KCODE64_SELECTOR); + unsafe { + lgdt(&self.pointer()); + CS::set_reg(Self::KCODE64_SELECTOR); + } } /// Loads the TSS into the CPU (executes the `ltr` instruction). @@ -82,7 +84,9 @@ impl GdtStruct { /// This function is unsafe because it manipulates the CPU's privileged /// states. pub unsafe fn load_tss(&'static self) { - load_tss(Self::TSS_SELECTOR); + unsafe { + load_tss(Self::TSS_SELECTOR); + } } } diff --git a/modules/axhal/src/paging.rs b/modules/axhal/src/paging.rs index 3d390304dc..d34ba0bd02 100644 --- a/modules/axhal/src/paging.rs +++ b/modules/axhal/src/paging.rs @@ -1,6 +1,7 @@ //! Page table manipulation. use axalloc::global_allocator; +use lazyinit::LazyInit; use page_table_multiarch::PagingHandler; use crate::mem::{MemRegionFlags, PAGE_SIZE_4K, PhysAddr, VirtAddr, phys_to_virt, virt_to_phys}; @@ -64,3 +65,23 @@ cfg_if::cfg_if! { pub type PageTable = page_table_multiarch::aarch64::A64PageTable; } } + +static KERNEL_PAGE_TABLE_ROOT: LazyInit = LazyInit::new(); + +/// Saves the root physical address of the kernel page table, which may be used +/// on context switch. +pub fn set_kernel_page_table_root(root_paddr: PhysAddr) { + KERNEL_PAGE_TABLE_ROOT.call_once(|| root_paddr); + unsafe { crate::arch::write_page_table_root(root_paddr) }; +} + +/// Get the root physical address of the kernel page table. +/// +/// # Panics +/// +/// It must be called after [`set_kernel_page_table`], otherwise it will panic. +pub fn kernel_page_table_root() -> PhysAddr { + *KERNEL_PAGE_TABLE_ROOT + .get() + .expect("kernel page table not initialized") +} diff --git a/modules/axmm/src/lib.rs b/modules/axmm/src/lib.rs index d3015b4b20..013ff0efa2 100644 --- a/modules/axmm/src/lib.rs +++ b/modules/axmm/src/lib.rs @@ -15,7 +15,7 @@ use axhal::mem::phys_to_virt; use axhal::paging::PagingError; use kspin::SpinNoIrq; use lazyinit::LazyInit; -use memory_addr::{PhysAddr, va}; +use memory_addr::{PhysAddr, VirtAddr, va}; const USER_ASPACE_BASE: usize = 0x1000; const USER_ASPACE_SIZE: usize = 0x7fff_ffff_f000; @@ -76,10 +76,10 @@ pub fn init_memory_management() { let kernel_aspace = new_kernel_aspace().expect("failed to initialize kernel address space"); debug!("kernel address space init OK: {:#x?}", kernel_aspace); KERNEL_ASPACE.init_once(SpinNoIrq::new(kernel_aspace)); - unsafe { axhal::arch::write_page_table_root(kernel_page_table_root()) }; + axhal::paging::set_kernel_page_table_root(kernel_page_table_root()); } /// Initializes kernel paging for secondary CPUs. pub fn init_memory_management_secondary() { - unsafe { axhal::arch::write_page_table_root(kernel_page_table_root()) }; + axhal::paging::set_kernel_page_table_root(kernel_page_table_root()); } From 6becbec38236e2530bccf8fe3de49341d1bcd72c Mon Sep 17 00:00:00 2001 From: Yuekai Jia Date: Wed, 31 Jul 2024 03:05:45 +0800 Subject: [PATCH 03/17] [hal]: Add syscall handler registration --- modules/axhal/linker.lds.S | 2 ++ modules/axhal/src/arch/x86_64/syscall.rs | 5 +---- modules/axhal/src/trap.rs | 14 ++++++++++++++ 3 files changed, 17 insertions(+), 4 deletions(-) diff --git a/modules/axhal/linker.lds.S b/modules/axhal/linker.lds.S index 2ec5f00a62..b8d42ee770 100644 --- a/modules/axhal/linker.lds.S +++ b/modules/axhal/linker.lds.S @@ -87,5 +87,7 @@ SECTIONS { linkm2_IRQ : { *(linkm2_IRQ) } linkme_PAGE_FAULT : { *(linkme_PAGE_FAULT) } linkm2_PAGE_FAULT : { *(linkm2_PAGE_FAULT) } + linkme_SYSCALL : { *(linkme_SYSCALL) } + linkm2_SYSCALL : { *(linkm2_SYSCALL) } } INSERT AFTER .tbss; diff --git a/modules/axhal/src/arch/x86_64/syscall.rs b/modules/axhal/src/arch/x86_64/syscall.rs index fb57432362..941ccd9ab3 100644 --- a/modules/axhal/src/arch/x86_64/syscall.rs +++ b/modules/axhal/src/arch/x86_64/syscall.rs @@ -16,10 +16,7 @@ core::arch::global_asm!( #[no_mangle] pub(super) fn x86_syscall_handler(tf: &mut TrapFrame) { - info!( - "syscall @ {:#x}: {} [{}, {}, {}, {}]", - tf.rip, tf.rax, tf.rdi, tf.rsi, tf.rdx, tf.rdx, - ); + tf.rax = crate::trap::handle_syscall(tf, tf.rax as usize) as u64; } /// Initializes syscall support and setups the syscall handler. diff --git a/modules/axhal/src/trap.rs b/modules/axhal/src/trap.rs index fcf9074d96..a38f7e543c 100644 --- a/modules/axhal/src/trap.rs +++ b/modules/axhal/src/trap.rs @@ -4,6 +4,9 @@ use linkme::distributed_slice as def_trap_handler; use memory_addr::VirtAddr; use page_table_entry::MappingFlags; +#[cfg(feature = "uspace")] +use crate::arch::TrapFrame; + pub use linkme::distributed_slice as register_trap_handler; /// A slice of IRQ handler functions. @@ -14,6 +17,11 @@ pub static IRQ: [fn(usize) -> bool]; #[def_trap_handler] pub static PAGE_FAULT: [fn(VirtAddr, MappingFlags, bool) -> bool]; +/// A slice of syscall handler functions. +#[cfg(feature = "uspace")] +#[def_trap_handler] +pub static SYSCALL: [fn(&TrapFrame, usize) -> isize]; + #[allow(unused_macros)] macro_rules! handle_trap { ($trap:ident, $($args:tt)*) => {{ @@ -29,3 +37,9 @@ macro_rules! handle_trap { } }} } + +/// Call the external syscall handler. +#[cfg(feature = "uspace")] +pub(crate) fn handle_syscall(tf: &TrapFrame, syscall_num: usize) -> isize { + SYSCALL[0](tf, syscall_num) +} From 71fda2d9883e17f90aa65a7014a9bc82678a7cf9 Mon Sep 17 00:00:00 2001 From: Yuekai Jia Date: Sat, 27 Jul 2024 14:45:08 +0800 Subject: [PATCH 04/17] [mm]: Import crate memory_set and implement linear & alloc backend --- Cargo.lock | 11 ++++ modules/axmm/Cargo.toml | 2 + modules/axmm/src/aspace.rs | 83 +++++++++++++++++++++--------- modules/axmm/src/backend/alloc.rs | 79 ++++++++++++++++++++++++++++ modules/axmm/src/backend/linear.rs | 46 +++++++++++++++++ modules/axmm/src/backend/mod.rs | 71 +++++++++++++++++++++++++ modules/axmm/src/lib.rs | 16 +++--- 7 files changed, 276 insertions(+), 32 deletions(-) create mode 100644 modules/axmm/src/backend/alloc.rs create mode 100644 modules/axmm/src/backend/linear.rs create mode 100644 modules/axmm/src/backend/mod.rs diff --git a/Cargo.lock b/Cargo.lock index b363e8d586..95ad03b9b8 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -516,6 +516,7 @@ dependencies = [ name = "axmm" version = "0.1.0" dependencies = [ + "axalloc", "axconfig", "axerrno", "axhal", @@ -523,6 +524,7 @@ dependencies = [ "lazyinit", "log", "memory_addr", + "memory_set", ] [[package]] @@ -1201,6 +1203,15 @@ version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6f769efcf10b9dfb4c913bebb409cda77b1a3f072b249bf5465e250bcb30eb49" +[[package]] +name = "memory_set" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "335675b7ab07460f532d3b2b557313be73037fdf81b65464d8c0d8bd90d2fbf9" +dependencies = [ + "memory_addr", +] + [[package]] name = "minimal-lexical" version = "0.2.1" diff --git a/modules/axmm/Cargo.toml b/modules/axmm/Cargo.toml index 4a0e702c07..faac117267 100644 --- a/modules/axmm/Cargo.toml +++ b/modules/axmm/Cargo.toml @@ -11,6 +11,7 @@ documentation = "https://arceos-org.github.io/arceos/axmm/index.html" [dependencies] axhal = { workspace = true, features = ["paging"] } +axalloc = { workspace = true } axconfig = { workspace = true } log = "=0.4.21" @@ -18,3 +19,4 @@ axerrno = "0.1" lazyinit = "0.2" memory_addr = "0.3" kspin = "0.1" +memory_set = "0.3" diff --git a/modules/axmm/src/aspace.rs b/modules/axmm/src/aspace.rs index bf96eaa0c8..a1013650b7 100644 --- a/modules/axmm/src/aspace.rs +++ b/modules/axmm/src/aspace.rs @@ -1,19 +1,20 @@ use core::fmt; use axerrno::{AxError, AxResult, ax_err}; -use axhal::{ - mem::phys_to_virt, - paging::{MappingFlags, PageTable}, -}; +use axhal::mem::phys_to_virt; +use axhal::paging::{MappingFlags, PageTable}; use memory_addr::{ - MemoryAddr, PAGE_SIZE_4K, PageIter4K, PhysAddr, VirtAddr, VirtAddrRange, is_aligned_4k, pa, + MemoryAddr, PAGE_SIZE_4K, PageIter4K, PhysAddr, VirtAddr, VirtAddrRange, is_aligned_4k, }; +use memory_set::{MemoryArea, MemorySet}; -use crate::paging_err_to_ax_err; +use crate::backend::Backend; +use crate::mapping_err_to_ax_err; /// The virtual memory address space. pub struct AddrSpace { va_range: VirtAddrRange, + areas: MemorySet, pt: PageTable, } @@ -53,6 +54,7 @@ impl AddrSpace { pub(crate) fn new_empty(base: VirtAddr, size: usize) -> AxResult { Ok(Self { va_range: VirtAddrRange::from_start_size(base, size), + areas: MemorySet::new(), pt: PageTable::try_new().map_err(|_| AxError::NoMemory)?, }) } @@ -74,8 +76,7 @@ impl AddrSpace { /// Add a new linear mapping. /// - /// The mapping is linear, i.e., `start_vaddr` is mapped to `start_paddr`, - /// and `start_vaddr + size` is mapped to `start_paddr + size`. + /// See [`Backend`] for more details about the mapping backends. /// /// The `flags` parameter indicates the mapping permissions and attributes. /// @@ -96,17 +97,39 @@ impl AddrSpace { } let offset = start_vaddr.as_usize() - start_paddr.as_usize(); - self.pt - .map_region( - start_vaddr, - |va| pa!(va.as_usize() - offset), - size, - flags, - false, // allow_huge - false, // flush_tlb_by_page - ) - .map_err(paging_err_to_ax_err)? - .flush_all(); + let area = MemoryArea::new(start_vaddr, size, flags, Backend::new_linear(offset)); + self.areas + .map(area, &mut self.pt, false) + .map_err(mapping_err_to_ax_err)?; + Ok(()) + } + + /// Add a new allocation mapping. + /// + /// See [`Backend`] for more details about the mapping backends. + /// + /// The `flags` parameter indicates the mapping permissions and attributes. + /// + /// Returns an error if the address range is out of the address space or not + /// aligned. + pub fn map_alloc( + &mut self, + start: VirtAddr, + size: usize, + flags: MappingFlags, + populate: bool, + ) -> AxResult { + if !self.contains_range(start, size) { + return ax_err!(InvalidInput, "address out of range"); + } + if !start.is_aligned_4k() || !is_aligned_4k(size) { + return ax_err!(InvalidInput, "address not aligned"); + } + + let area = MemoryArea::new(start, size, flags, Backend::new_alloc(populate)); + self.areas + .map(area, &mut self.pt, false) + .map_err(mapping_err_to_ax_err)?; Ok(()) } @@ -122,10 +145,9 @@ impl AddrSpace { return ax_err!(InvalidInput, "address not aligned"); } - self.pt - .unmap_region(start, size, true) - .map_err(paging_err_to_ax_err)? - .ignore(); + self.areas + .unmap(start, size, &mut self.pt) + .map_err(mapping_err_to_ax_err)?; Ok(()) } @@ -199,12 +221,18 @@ impl AddrSpace { return ax_err!(InvalidInput, "address not aligned"); } + // TODO self.pt .protect_region(start, size, flags, true) - .map_err(paging_err_to_ax_err)? + .map_err(|_| AxError::BadState)? .ignore(); Ok(()) } + + /// Removes all mappings in the address space. + pub fn clear(&mut self) { + self.areas.clear(&mut self.pt).unwrap(); + } } impl fmt::Debug for AddrSpace { @@ -212,6 +240,13 @@ impl fmt::Debug for AddrSpace { f.debug_struct("AddrSpace") .field("va_range", &self.va_range) .field("page_table_root", &self.pt.root_paddr()) + .field("areas", &self.areas) .finish() } } + +impl Drop for AddrSpace { + fn drop(&mut self) { + self.clear(); + } +} diff --git a/modules/axmm/src/backend/alloc.rs b/modules/axmm/src/backend/alloc.rs new file mode 100644 index 0000000000..5abb32a7f3 --- /dev/null +++ b/modules/axmm/src/backend/alloc.rs @@ -0,0 +1,79 @@ +use axalloc::global_allocator; +use axhal::mem::{phys_to_virt, virt_to_phys}; +use axhal::paging::{MappingFlags, PageSize, PageTable}; +use memory_addr::{PageIter4K, PhysAddr, VirtAddr, PAGE_SIZE_4K}; + +use super::Backend; + +fn alloc_frame(zeroed: bool) -> Option { + let vaddr = VirtAddr::from(global_allocator().alloc_pages(1, PAGE_SIZE_4K).ok()?); + if zeroed { + unsafe { core::ptr::write_bytes(vaddr.as_mut_ptr(), 0, PAGE_SIZE_4K) }; + } + let paddr = virt_to_phys(vaddr); + Some(paddr) +} + +fn dealloc_frame(frame: PhysAddr) { + let vaddr = phys_to_virt(frame); + global_allocator().dealloc_pages(vaddr.as_usize(), 1); +} + +impl Backend { + /// Creates a new allocation mapping backend. + pub const fn new_alloc(populate: bool) -> Self { + Self::Alloc { populate } + } + + pub(crate) fn map_alloc( + &self, + start: VirtAddr, + size: usize, + flags: MappingFlags, + pt: &mut PageTable, + populate: bool, + ) -> bool { + debug!( + "map_alloc: [{:#x}, {:#x}) {:?} (populate={})", + start, + start + size, + flags, + populate + ); + // allocate all possible physical frames for populated mapping. + for addr in PageIter4K::new(start, start + size).unwrap() { + if let Some(frame) = alloc_frame(true) { + if let Ok(tlb) = pt.map(addr, frame, PageSize::Size4K, flags) { + tlb.ignore(); // TLB flush on map is unnecessary, as there are no outdated mappings. + } else { + return false; + } + } + } + true + } + + pub(crate) fn unmap_alloc( + &self, + start: VirtAddr, + size: usize, + pt: &mut PageTable, + _populate: bool, + ) -> bool { + debug!("unmap_alloc: [{:#x}, {:#x})", start, start + size); + for addr in PageIter4K::new(start, start + size).unwrap() { + if let Ok((frame, page_size, tlb)) = pt.unmap(addr) { + // Deallocate the physical frame if there is a mapping in the + // page table. + if page_size.is_huge() { + return false; + } + tlb.flush(); + dealloc_frame(frame); + } else { + // Deallocation is needn't if the page is not mapped. + } + } + true + } +} diff --git a/modules/axmm/src/backend/linear.rs b/modules/axmm/src/backend/linear.rs new file mode 100644 index 0000000000..3807766590 --- /dev/null +++ b/modules/axmm/src/backend/linear.rs @@ -0,0 +1,46 @@ +use axhal::paging::{MappingFlags, PageTable}; +use memory_addr::{PhysAddr, VirtAddr}; + +use super::Backend; + +impl Backend { + /// Creates a new linear mapping backend. + pub const fn new_linear(pa_va_offset: usize) -> Self { + Self::Linear { pa_va_offset } + } + + pub(crate) fn map_linear( + &self, + start: VirtAddr, + size: usize, + flags: MappingFlags, + pt: &mut PageTable, + pa_va_offset: usize, + ) -> bool { + let va_to_pa = |va: VirtAddr| PhysAddr::from(va.as_usize() - pa_va_offset); + debug!( + "map_linear: [{:#x}, {:#x}) -> [{:#x}, {:#x}) {:?}", + start, + start + size, + va_to_pa(start), + va_to_pa(start + size), + flags + ); + pt.map_region(start, va_to_pa, size, flags, false, false) + .map(|tlb| tlb.ignore()) // TLB flush on map is unnecessary, as there are no outdated mappings. + .is_ok() + } + + pub(crate) fn unmap_linear( + &self, + start: VirtAddr, + size: usize, + pt: &mut PageTable, + _pa_va_offset: usize, + ) -> bool { + debug!("unmap_linear: [{:#x}, {:#x})", start, start + size); + pt.unmap_region(start, size, true) + .map(|tlb| tlb.ignore()) // flush each page on unmap, do not flush the entire TLB. + .is_ok() + } +} diff --git a/modules/axmm/src/backend/mod.rs b/modules/axmm/src/backend/mod.rs new file mode 100644 index 0000000000..5d92d6bfe6 --- /dev/null +++ b/modules/axmm/src/backend/mod.rs @@ -0,0 +1,71 @@ +//! Memory mapping backends. + +use axhal::paging::{MappingFlags, PageTable}; +use memory_addr::VirtAddr; +use memory_set::MappingBackend; + +mod alloc; +mod linear; + +/// A unified enum type for different memory mapping backends. +/// +/// Currently, two backends are implemented: +/// +/// - **Linear**: used for linear mappings. The target physical frames are +/// contiguous and their addresses should be known when creating the mapping. +/// - **Allocation**: used in general, or for lazy mappings. The target physical +/// frames are obtained from the global allocator. +#[derive(Clone)] +pub enum Backend { + /// Linear mapping backend. + /// + /// The offset between the virtual address and the physical address is + /// constant, which is specified by `pa_va_offset`. For example, the virtual + /// address `vaddr` is mapped to the physical address `vaddr - pa_va_offset`. + Linear { + /// `vaddr - paddr`. + pa_va_offset: usize, + }, + /// Allocation mapping backend. + /// + /// If `populate` is `true`, all physical frames are allocated when the + /// mapping is created, and no page faults are triggered during the memory + /// access. Otherwise, the physical frames are allocated on demand (by + /// handling page faults). + Alloc { + /// Whether to populate the physical frames when creating the mapping. + populate: bool, + }, +} + +impl MappingBackend for Backend { + type Addr = VirtAddr; + type Flags = MappingFlags; + type PageTable = PageTable; + fn map(&self, start: VirtAddr, size: usize, flags: MappingFlags, pt: &mut PageTable) -> bool { + match *self { + Self::Linear { pa_va_offset } => self.map_linear(start, size, flags, pt, pa_va_offset), + Self::Alloc { populate } => self.map_alloc(start, size, flags, pt, populate), + } + } + + fn unmap(&self, start: VirtAddr, size: usize, pt: &mut PageTable) -> bool { + match *self { + Self::Linear { pa_va_offset } => self.unmap_linear(start, size, pt, pa_va_offset), + Self::Alloc { populate } => self.unmap_alloc(start, size, pt, populate), + } + } + + fn protect( + &self, + start: Self::Addr, + size: usize, + new_flags: Self::Flags, + page_table: &mut Self::PageTable, + ) -> bool { + page_table + .protect_region(start, size, new_flags, true) + .map(|tlb| tlb.ignore()) + .is_ok() + } +} diff --git a/modules/axmm/src/lib.rs b/modules/axmm/src/lib.rs index 013ff0efa2..05b6e51bbf 100644 --- a/modules/axmm/src/lib.rs +++ b/modules/axmm/src/lib.rs @@ -7,29 +7,29 @@ extern crate log; extern crate alloc; mod aspace; +mod backend; pub use self::aspace::AddrSpace; +pub use self::backend::Backend; use axerrno::{AxError, AxResult}; use axhal::mem::phys_to_virt; -use axhal::paging::PagingError; use kspin::SpinNoIrq; use lazyinit::LazyInit; use memory_addr::{PhysAddr, VirtAddr, va}; +use memory_set::MappingError; const USER_ASPACE_BASE: usize = 0x1000; const USER_ASPACE_SIZE: usize = 0x7fff_ffff_f000; static KERNEL_ASPACE: LazyInit> = LazyInit::new(); -fn paging_err_to_ax_err(err: PagingError) -> AxError { - warn!("Paging error: {:?}", err); +fn mapping_err_to_ax_err(err: MappingError) -> AxError { + warn!("Mapping error: {:?}", err); match err { - PagingError::NoMemory => AxError::NoMemory, - PagingError::NotAligned => AxError::InvalidInput, - PagingError::NotMapped => AxError::NotFound, - PagingError::AlreadyMapped => AxError::AlreadyExists, - PagingError::MappedToHugePage => AxError::InvalidInput, + MappingError::InvalidParam => AxError::InvalidInput, + MappingError::AlreadyExists => AxError::AlreadyExists, + MappingError::BadState => AxError::BadState, } } From 051d269017fbbcb387e12f6c8bd76b57c6d93248 Mon Sep 17 00:00:00 2001 From: Yuekai Jia Date: Tue, 2 Jul 2024 23:16:37 +0800 Subject: [PATCH 05/17] [mm]: Implement lazy allocation & mapping --- modules/axmm/src/aspace.rs | 21 +++++++++++++++ modules/axmm/src/backend/alloc.rs | 45 +++++++++++++++++++++++++------ modules/axmm/src/backend/mod.rs | 16 +++++++++++ 3 files changed, 74 insertions(+), 8 deletions(-) diff --git a/modules/axmm/src/aspace.rs b/modules/axmm/src/aspace.rs index a1013650b7..7992c458bc 100644 --- a/modules/axmm/src/aspace.rs +++ b/modules/axmm/src/aspace.rs @@ -233,6 +233,27 @@ impl AddrSpace { pub fn clear(&mut self) { self.areas.clear(&mut self.pt).unwrap(); } + + /// Handles a page fault at the given address. + /// + /// `access_flags` indicates the access type that caused the page fault. + /// + /// Returns `true` if the page fault is handled successfully (not a real + /// fault). + pub fn handle_page_fault(&mut self, vaddr: VirtAddr, access_flags: MappingFlags) -> bool { + if !self.va_range.contains(vaddr) { + return false; + } + if let Some(area) = self.areas.find(vaddr) { + let orig_flags = area.flags(); + if orig_flags.contains(access_flags) { + return area + .backend() + .handle_page_fault(vaddr, orig_flags, &mut self.pt); + } + } + false + } } impl fmt::Debug for AddrSpace { diff --git a/modules/axmm/src/backend/alloc.rs b/modules/axmm/src/backend/alloc.rs index 5abb32a7f3..e404d718a8 100644 --- a/modules/axmm/src/backend/alloc.rs +++ b/modules/axmm/src/backend/alloc.rs @@ -40,17 +40,25 @@ impl Backend { flags, populate ); - // allocate all possible physical frames for populated mapping. - for addr in PageIter4K::new(start, start + size).unwrap() { - if let Some(frame) = alloc_frame(true) { - if let Ok(tlb) = pt.map(addr, frame, PageSize::Size4K, flags) { - tlb.ignore(); // TLB flush on map is unnecessary, as there are no outdated mappings. - } else { - return false; + if populate { + // allocate all possible physical frames for populated mapping. + for addr in PageIter4K::new(start, start + size).unwrap() { + if let Some(frame) = alloc_frame(true) { + if let Ok(tlb) = pt.map(addr, frame, PageSize::Size4K, flags) { + tlb.ignore(); // TLB flush on map is unnecessary, as there are no outdated mappings. + } else { + return false; + } } } + true + } else { + // Map to a empty entry for on-demand mapping. + let flags = MappingFlags::empty(); + pt.map_region(start, |_| 0.into(), size, flags, false, false) + .map(|tlb| tlb.ignore()) + .is_ok() } - true } pub(crate) fn unmap_alloc( @@ -76,4 +84,25 @@ impl Backend { } true } + + pub(crate) fn handle_page_fault_alloc( + &self, + vaddr: VirtAddr, + orig_flags: MappingFlags, + pt: &mut PageTable, + populate: bool, + ) -> bool { + if populate { + false // Populated mappings should not trigger page faults. + } else if let Some(frame) = alloc_frame(true) { + // Allocate a physical frame lazily and map it to the fault address. + // `vaddr` does not need to be aligned. It will be automatically + // aligned during `pt.remap` regardless of the page size. + pt.remap(vaddr, frame, orig_flags) + .map(|(_, tlb)| tlb.flush()) + .is_ok() + } else { + false + } + } } diff --git a/modules/axmm/src/backend/mod.rs b/modules/axmm/src/backend/mod.rs index 5d92d6bfe6..5df33a8908 100644 --- a/modules/axmm/src/backend/mod.rs +++ b/modules/axmm/src/backend/mod.rs @@ -69,3 +69,19 @@ impl MappingBackend for Backend { .is_ok() } } + +impl Backend { + pub(crate) fn handle_page_fault( + &self, + vaddr: VirtAddr, + orig_flags: MappingFlags, + page_table: &mut PageTable, + ) -> bool { + match *self { + Self::Linear { .. } => false, // Linear mappings should not trigger page faults. + Self::Alloc { populate } => { + self.handle_page_fault_alloc(vaddr, orig_flags, page_table, populate) + } + } + } +} From d3362f9aff7c41fefd1e26d5feba1d107b7caef3 Mon Sep 17 00:00:00 2001 From: Youjie Zheng Date: Wed, 18 Sep 2024 22:01:25 +0845 Subject: [PATCH 06/17] [hal] Save and restore tls context under uspace feature --- modules/axhal/src/arch/x86_64/context.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/axhal/src/arch/x86_64/context.rs b/modules/axhal/src/arch/x86_64/context.rs index fd8d3ef6f9..d4be98bdb4 100644 --- a/modules/axhal/src/arch/x86_64/context.rs +++ b/modules/axhal/src/arch/x86_64/context.rs @@ -339,7 +339,7 @@ impl TaskContext { self.ext_state.save(); next_ctx.ext_state.restore(); } - #[cfg(feature = "tls")] + #[cfg(any(feature = "tls", feature = "uspace"))] unsafe { self.fs_base = super::read_thread_pointer(); super::write_thread_pointer(next_ctx.fs_base); From dbe289ae9e5622642c67758f6ca2a52525be32c9 Mon Sep 17 00:00:00 2001 From: Youjie Zheng Date: Wed, 18 Sep 2024 22:06:20 +0845 Subject: [PATCH 07/17] [mm] Add find_free_area function for mmap syscalls --- modules/axmm/src/aspace.rs | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/modules/axmm/src/aspace.rs b/modules/axmm/src/aspace.rs index 7992c458bc..6341bcc94b 100644 --- a/modules/axmm/src/aspace.rs +++ b/modules/axmm/src/aspace.rs @@ -74,6 +74,20 @@ impl AddrSpace { Ok(()) } + /// Finds a free area that can accommodate the given size. + + /// The search starts from the given hint address, and the area should be within the given limit range. + + /// Returns the start address of the free area. Returns None if no such area is found. + pub fn find_free_area( + &self, + hint: VirtAddr, + size: usize, + limit: VirtAddrRange, + ) -> Option { + self.areas.find_free_area(hint, size, limit) + } + /// Add a new linear mapping. /// /// See [`Backend`] for more details about the mapping backends. From ad9d6669d5f44b66ea9bcf2ecd5db4dceae07df2 Mon Sep 17 00:00:00 2001 From: Youjie Zheng Date: Wed, 18 Sep 2024 22:12:16 +0845 Subject: [PATCH 08/17] [doc] fix doc link error at axhal/src/paging.rs --- modules/axhal/src/paging.rs | 2 +- modules/axmm/src/aspace.rs | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/modules/axhal/src/paging.rs b/modules/axhal/src/paging.rs index d34ba0bd02..f09b1d8479 100644 --- a/modules/axhal/src/paging.rs +++ b/modules/axhal/src/paging.rs @@ -79,7 +79,7 @@ pub fn set_kernel_page_table_root(root_paddr: PhysAddr) { /// /// # Panics /// -/// It must be called after [`set_kernel_page_table`], otherwise it will panic. +/// It must be called after [`set_kernel_page_table_root`], otherwise it will panic. pub fn kernel_page_table_root() -> PhysAddr { *KERNEL_PAGE_TABLE_ROOT .get() diff --git a/modules/axmm/src/aspace.rs b/modules/axmm/src/aspace.rs index 6341bcc94b..39e377e065 100644 --- a/modules/axmm/src/aspace.rs +++ b/modules/axmm/src/aspace.rs @@ -75,9 +75,9 @@ impl AddrSpace { } /// Finds a free area that can accommodate the given size. - + /// /// The search starts from the given hint address, and the area should be within the given limit range. - + /// /// Returns the start address of the free area. Returns None if no such area is found. pub fn find_free_area( &self, From b7f60e630c30642731b0095686db0afc488e9397 Mon Sep 17 00:00:00 2001 From: Youjie Zheng Date: Thu, 26 Sep 2024 14:39:19 +0845 Subject: [PATCH 09/17] [hal] Support user space for riscv64 and aarch64 --- modules/axhal/src/arch/aarch64/context.rs | 165 +++++++++++++++++++++- modules/axhal/src/arch/aarch64/mod.rs | 2 + modules/axhal/src/arch/aarch64/trap.S | 5 + modules/axhal/src/arch/aarch64/trap.rs | 5 +- modules/axhal/src/arch/riscv/context.rs | 162 ++++++++++++++++++++- modules/axhal/src/arch/riscv/mod.rs | 2 + modules/axhal/src/arch/riscv/trap.S | 12 +- modules/axhal/src/arch/riscv/trap.rs | 5 + modules/axhal/src/arch/x86_64/context.rs | 8 ++ modules/axhal/src/cpu.rs | 16 ++- 10 files changed, 367 insertions(+), 15 deletions(-) diff --git a/modules/axhal/src/arch/aarch64/context.rs b/modules/axhal/src/arch/aarch64/context.rs index ae8635d48c..23a4fe5a48 100644 --- a/modules/axhal/src/arch/aarch64/context.rs +++ b/modules/axhal/src/arch/aarch64/context.rs @@ -15,6 +15,147 @@ pub struct TrapFrame { pub spsr: u64, } +impl TrapFrame { + /// Gets the 0th syscall argument. + pub const fn arg0(&self) -> usize { + self.r[0] as _ + } + + /// Gets the 1st syscall argument. + pub const fn arg1(&self) -> usize { + self.r[1] as _ + } + + /// Gets the 2nd syscall argument. + pub const fn arg2(&self) -> usize { + self.r[2] as _ + } + + /// Gets the 3rd syscall argument. + pub const fn arg3(&self) -> usize { + self.r[3] as _ + } + + /// Gets the 4th syscall argument. + pub const fn arg4(&self) -> usize { + self.r[4] as _ + } + + /// Gets the 5th syscall argument. + pub const fn arg5(&self) -> usize { + self.r[5] as _ + } +} + +/// Context to enter user space. +#[cfg(feature = "uspace")] +pub struct UspaceContext(TrapFrame); + +#[cfg(feature = "uspace")] +impl UspaceContext { + /// Creates an empty context with all registers set to zero. + pub const fn empty() -> Self { + unsafe { core::mem::MaybeUninit::zeroed().assume_init() } + } + + /// 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 aarch64_cpu::registers::SPSR_EL1; + let mut regs = [0; 31]; + regs[0] = arg0 as _; + Self(TrapFrame { + r: regs, + usp: ustack_top.as_usize() as _, + elr: entry as _, + spsr: (SPSR_EL1::M::EL0t + + SPSR_EL1::D::Masked + + SPSR_EL1::A::Masked + + SPSR_EL1::I::Unmasked + + SPSR_EL1::F::Masked) + .value, + }) + } + + /// Creates a new context from the given [`TrapFrame`]. + pub const fn from(trap_frame: &TrapFrame) -> Self { + Self(*trap_frame) + } + + /// Gets the instruction pointer. + pub const fn get_ip(&self) -> usize { + self.0.elr as _ + } + + /// Gets the stack pointer. + pub const fn get_sp(&self) -> usize { + self.0.usp as _ + } + + /// Sets the instruction pointer. + pub const fn set_ip(&mut self, pc: usize) { + self.0.elr = pc as _; + } + + /// Sets the stack pointer. + pub const fn set_sp(&mut self, sp: usize) { + self.0.usp = sp as _; + } + + /// Sets the return value register. + pub const fn set_retval(&mut self, r0: usize) { + self.0.r[0] = r0 as _; + } + + /// Enters user space. + /// + /// It restores the user registers and jumps to the user entry point + /// (saved in `elr`). + /// When an exception or syscall occurs, the kernel stack pointer is + /// switched to `kstack_top`. + /// + /// # Safety + /// + /// This function is unsafe because it changes processor mode and the stack. + #[inline(never)] + #[no_mangle] + pub unsafe fn enter_uspace(&self, kstack_top: VirtAddr) -> ! { + super::disable_irqs(); + // We do not handle traps that occur at the current exception level, + // so the kstack ptr(`sp_el1`) will not change during running in user space. + // Then we don't need to save the `sp_el1` to the taskctx. + asm!( + " + mov sp, x1 + ldp x30, x9, [x0, 30 * 8] + ldp x10, x11, [x0, 32 * 8] + msr sp_el0, x9 + msr elr_el1, x10 + msr spsr_el1, x11 + + ldp x28, x29, [x0, 28 * 8] + ldp x26, x27, [x0, 26 * 8] + ldp x24, x25, [x0, 24 * 8] + ldp x22, x23, [x0, 22 * 8] + ldp x20, x21, [x0, 20 * 8] + ldp x18, x19, [x0, 18 * 8] + ldp x16, x17, [x0, 16 * 8] + ldp x14, x15, [x0, 14 * 8] + ldp x12, x13, [x0, 12 * 8] + ldp x10, x11, [x0, 10 * 8] + ldp x8, x9, [x0, 8 * 8] + ldp x6, x7, [x0, 6 * 8] + ldp x4, x5, [x0, 4 * 8] + ldp x2, x3, [x0, 2 * 8] + ldp x0, x1, [x0] + eret", + in("x0") &self.0, + in("x1") kstack_top.as_usize() , + options(noreturn), + ) + } +} + /// FP & SIMD registers. #[repr(C, align(16))] #[derive(Debug, Default)] @@ -47,7 +188,7 @@ impl FpState { /// and the next task restores its context from memory to CPU. #[allow(missing_docs)] #[repr(C)] -#[derive(Debug)] +#[derive(Debug, Default)] pub struct TaskContext { pub sp: u64, pub tpidr_el0: u64, @@ -63,6 +204,9 @@ pub struct TaskContext { pub r28: u64, pub r29: u64, pub lr: u64, // r30 + /// The `ttbr0_el1` register value, i.e., the page table root. + #[cfg(feature = "uspace")] + pub ttbr0_el1: memory_addr::PhysAddr, #[cfg(feature = "fp_simd")] pub fp_state: FpState, } @@ -75,8 +219,8 @@ impl TaskContext { /// /// [`init`]: TaskContext::init /// [`switch_to`]: TaskContext::switch_to - pub const fn new() -> Self { - unsafe { core::mem::MaybeUninit::zeroed().assume_init() } + pub fn new() -> Self { + Self::default() } /// Initializes the context for a new task, with the given entry point and @@ -84,9 +228,18 @@ impl TaskContext { pub fn init(&mut self, entry: usize, kstack_top: VirtAddr, tls_area: VirtAddr) { self.sp = kstack_top.as_usize() as u64; self.lr = entry as u64; + // When under `uspace` feature, kernel will not use this register. self.tpidr_el0 = tls_area.as_usize() as u64; } + /// Changes the page table root for user space (`ttbr0_el1` register for aarch64 in el1 level). + /// + /// If not set, it means that this task is a kernel task and only `ttbr1_el1` register will be used. + #[cfg(feature = "uspace")] + pub fn set_page_table_root(&mut self, ttbr0_el1: memory_addr::PhysAddr) { + self.ttbr0_el1 = ttbr0_el1; + } + /// Switches to another task. /// /// It first saves the current task's context from CPU to this place, and then @@ -94,6 +247,12 @@ impl TaskContext { pub fn switch_to(&mut self, next_ctx: &Self) { #[cfg(feature = "fp_simd")] self.fp_state.switch_to(&next_ctx.fp_state); + #[cfg(feature = "uspace")] + { + if self.ttbr0_el1 != next_ctx.ttbr0_el1 { + unsafe { super::write_page_table_root0(next_ctx.ttbr0_el1) }; + } + } unsafe { context_switch(self, next_ctx) } } } diff --git a/modules/axhal/src/arch/aarch64/mod.rs b/modules/axhal/src/arch/aarch64/mod.rs index 0d1f70530a..9e56602b2c 100644 --- a/modules/axhal/src/arch/aarch64/mod.rs +++ b/modules/axhal/src/arch/aarch64/mod.rs @@ -9,6 +9,8 @@ use aarch64_cpu::registers::{DAIF, TPIDR_EL0, TTBR0_EL1, TTBR1_EL1, VBAR_EL1}; use memory_addr::{PhysAddr, VirtAddr}; use tock_registers::interfaces::{Readable, Writeable}; +#[cfg(feature = "uspace")] +pub use self::context::UspaceContext; pub use self::context::{FpState, TaskContext, TrapFrame}; /// Allows the current CPU to respond to interrupts. diff --git a/modules/axhal/src/arch/aarch64/trap.S b/modules/axhal/src/arch/aarch64/trap.S index 7167610acb..662c0888d1 100644 --- a/modules/axhal/src/arch/aarch64/trap.S +++ b/modules/axhal/src/arch/aarch64/trap.S @@ -21,6 +21,11 @@ mrs x11, spsr_el1 stp x30, x9, [sp, 30 * 8] stp x10, x11, [sp, 32 * 8] + + # We may have interrupted userspace, or a guest, or exit-from or + # return-to either of those. So we can't trust sp_el0, and need to + # restore it. + bl {cache_current_task_ptr} .endm .macro RESTORE_REGS diff --git a/modules/axhal/src/arch/aarch64/trap.rs b/modules/axhal/src/arch/aarch64/trap.rs index 2355e2ffc7..2e8dddaa63 100644 --- a/modules/axhal/src/arch/aarch64/trap.rs +++ b/modules/axhal/src/arch/aarch64/trap.rs @@ -6,7 +6,7 @@ use tock_registers::interfaces::Readable; use super::TrapFrame; -global_asm!(include_str!("trap.S")); +global_asm!(include_str!("trap.S"), cache_current_task_ptr = sym crate::cpu::cache_current_task_ptr); #[repr(u8)] #[derive(Debug)] @@ -98,8 +98,9 @@ fn handle_sync_exception(tf: &mut TrapFrame) { let esr = ESR_EL1.extract(); let iss = esr.read(ESR_EL1::ISS); match esr.read_as_enum(ESR_EL1::EC) { + #[cfg(feature = "uspace")] Some(ESR_EL1::EC::Value::SVC64) => { - warn!("No syscall is supported currently!"); + tf.r[0] = crate::trap::handle_syscall(tf, tf.r[8] as usize) as u64; } Some(ESR_EL1::EC::Value::InstrAbortLowerEL) => handle_instruction_abort(tf, iss, true), Some(ESR_EL1::EC::Value::InstrAbortCurrentEL) => handle_instruction_abort(tf, iss, false), diff --git a/modules/axhal/src/arch/riscv/context.rs b/modules/axhal/src/arch/riscv/context.rs index 40eb9d9195..b92a24754f 100644 --- a/modules/axhal/src/arch/riscv/context.rs +++ b/modules/axhal/src/arch/riscv/context.rs @@ -4,7 +4,7 @@ use memory_addr::VirtAddr; /// General registers of RISC-V. #[allow(missing_docs)] #[repr(C)] -#[derive(Debug, Default, Clone)] +#[derive(Debug, Default, Clone, Copy)] pub struct GeneralRegisters { pub ra: usize, pub sp: usize, @@ -41,7 +41,7 @@ pub struct GeneralRegisters { /// Saved registers when a trap (interrupt or exception) occurs. #[repr(C)] -#[derive(Debug, Default, Clone)] +#[derive(Debug, Default, Clone, Copy)] pub struct TrapFrame { /// All general registers. pub regs: GeneralRegisters, @@ -51,6 +51,136 @@ pub struct TrapFrame { pub sstatus: usize, } +impl TrapFrame { + /// Gets the 0th syscall argument. + pub const fn arg0(&self) -> usize { + self.regs.a0 + } + + /// Gets the 1st syscall argument. + pub const fn arg1(&self) -> usize { + self.regs.a1 + } + + /// Gets the 2nd syscall argument. + pub const fn arg2(&self) -> usize { + self.regs.a2 + } + + /// Gets the 3rd syscall argument. + pub const fn arg3(&self) -> usize { + self.regs.a3 + } + + /// Gets the 4th syscall argument. + pub const fn arg4(&self) -> usize { + self.regs.a4 + } + + /// Gets the 5th syscall argument. + pub const fn arg5(&self) -> usize { + self.regs.a5 + } +} + +/// Context to enter user space. +#[cfg(feature = "uspace")] +pub struct UspaceContext(TrapFrame); + +#[cfg(feature = "uspace")] +impl UspaceContext { + /// Creates an empty context with all registers set to zero. + pub const fn empty() -> Self { + unsafe { core::mem::MaybeUninit::zeroed().assume_init() } + } + + /// 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 { + const SPIE: usize = 1 << 5; + const SUM: usize = 1 << 18; + Self(TrapFrame { + regs: GeneralRegisters { + a0: arg0, + sp: ustack_top.as_usize(), + ..Default::default() + }, + sepc: entry, + sstatus: SPIE | SUM, + }) + } + + /// Creates a new context from the given [`TrapFrame`]. + pub const fn from(trap_frame: &TrapFrame) -> Self { + Self(*trap_frame) + } + + /// Gets the instruction pointer. + pub const fn get_ip(&self) -> usize { + self.0.sepc + } + + /// Gets the stack pointer. + pub const fn get_sp(&self) -> usize { + self.0.regs.sp + } + + /// Sets the instruction pointer. + pub const fn set_ip(&mut self, pc: usize) { + self.0.sepc = pc; + } + + /// Sets the stack pointer. + pub const fn set_sp(&mut self, sp: usize) { + self.0.regs.sp = sp; + } + + /// Sets the return value register. + pub const fn set_retval(&mut self, a0: usize) { + self.0.regs.a0 = a0; + } + + /// Enters user space. + /// + /// It restores the user registers and jumps to the user entry point + /// (saved in `sepc`). + /// When an exception or syscall occurs, the kernel stack pointer is + /// switched to `kstack_top`. + /// + /// # Safety + /// + /// This function is unsafe because it changes processor mode and the stack. + #[inline(never)] + #[no_mangle] + pub unsafe fn enter_uspace(&self, kstack_top: VirtAddr) -> ! { + use riscv::register::{sepc, sscratch}; + + super::disable_irqs(); + sscratch::write(kstack_top.as_usize()); + sepc::write(self.0.sepc); + // Address of the top of the kernel stack after saving the trap frame. + let kernel_trap_addr = kstack_top.as_usize() - core::mem::size_of::(); + asm!(" + mv sp, {tf} + + STR gp, {kernel_trap_addr}, 2 + LDR gp, sp, 2 + + STR tp, {kernel_trap_addr}, 3 + LDR tp, sp, 3 + + LDR t0, sp, 32 + csrw sstatus, t0 + POP_GENERAL_REGS + LDR sp, sp, 1 + sret", + tf = in(reg) &(self.0), + kernel_trap_addr = in(reg) kernel_trap_addr, + options(noreturn), + ) + } +} + /// Saved hardware states of a task. /// /// The context usually includes: @@ -84,6 +214,9 @@ pub struct TaskContext { pub s11: usize, pub tp: usize, + /// The `satp` register value, i.e., the page table root. + #[cfg(feature = "uspace")] + pub satp: memory_addr::PhysAddr, // TODO: FP states } @@ -95,8 +228,12 @@ impl TaskContext { /// /// [`init`]: TaskContext::init /// [`switch_to`]: TaskContext::switch_to - pub const fn new() -> Self { - unsafe { core::mem::MaybeUninit::zeroed().assume_init() } + pub fn new() -> Self { + Self { + #[cfg(feature = "uspace")] + satp: crate::paging::kernel_page_table_root(), + ..Default::default() + } } /// Initializes the context for a new task, with the given entry point and @@ -107,6 +244,17 @@ impl TaskContext { self.tp = tls_area.as_usize(); } + /// Changes the page table root (`satp` register for riscv64). + /// + /// If not set, the kernel page table root is used (obtained by + /// [`axhal::paging::kernel_page_table_root`][1]). + /// + /// [1]: crate::paging::kernel_page_table_root + #[cfg(feature = "uspace")] + pub fn set_page_table_root(&mut self, satp: memory_addr::PhysAddr) { + self.satp = satp; + } + /// Switches to another task. /// /// It first saves the current task's context from CPU to this place, and then @@ -117,6 +265,12 @@ impl TaskContext { self.tp = super::read_thread_pointer(); unsafe { super::write_thread_pointer(next_ctx.tp) }; } + #[cfg(feature = "uspace")] + unsafe { + if self.satp != next_ctx.satp { + super::write_page_table_root(next_ctx.satp); + } + } unsafe { // TODO: switch FP states context_switch(self, next_ctx) diff --git a/modules/axhal/src/arch/riscv/mod.rs b/modules/axhal/src/arch/riscv/mod.rs index eb3bc82278..36ce91da41 100644 --- a/modules/axhal/src/arch/riscv/mod.rs +++ b/modules/axhal/src/arch/riscv/mod.rs @@ -8,6 +8,8 @@ use memory_addr::{PhysAddr, VirtAddr}; use riscv::asm; use riscv::register::{satp, sstatus, stvec}; +#[cfg(feature = "uspace")] +pub use self::context::UspaceContext; pub use self::context::{GeneralRegisters, TaskContext, TrapFrame}; /// Allows the current CPU to respond to interrupts. diff --git a/modules/axhal/src/arch/riscv/trap.S b/modules/axhal/src/arch/riscv/trap.S index 811d1aa1a4..c42780a6f7 100644 --- a/modules/axhal/src/arch/riscv/trap.S +++ b/modules/axhal/src/arch/riscv/trap.S @@ -49,18 +49,22 @@ STR t2, sp, 1 // tf.regs.sp .if \from_user == 1 - LDR t0, sp, 3 // load supervisor tp + LDR t0, sp, 2 // load supervisor gp + LDR t1, sp, 3 // load supervisor tp STR gp, sp, 2 // save user gp and tp STR tp, sp, 3 - mv tp, t0 + mv gp, t0 + mv tp, t1 .endif .endm .macro RESTORE_REGS, from_user .if \from_user == 1 - LDR gp, sp, 2 // load user gp and tp + LDR t1, sp, 2 // load user gp and tp LDR t0, sp, 3 - STR tp, sp, 3 // save supervisor tp + STR gp, sp, 2 // save supervisor gp + STR tp, sp, 3 // save supervisor gp and tp + mv gp, t1 mv tp, t0 addi t0, sp, {trapframe_size} // put supervisor sp to scratch csrw sscratch, t0 diff --git a/modules/axhal/src/arch/riscv/trap.rs b/modules/axhal/src/arch/riscv/trap.rs index 5e5fcf0d98..3867f778d4 100644 --- a/modules/axhal/src/arch/riscv/trap.rs +++ b/modules/axhal/src/arch/riscv/trap.rs @@ -38,6 +38,11 @@ fn riscv_trap_handler(tf: &mut TrapFrame, from_user: bool) { let scause = scause::read(); if let Ok(cause) = scause.cause().try_into::() { match cause { + #[cfg(feature = "uspace")] + Trap::Exception(E::UserEnvCall) => { + tf.regs.a0 = crate::trap::handle_syscall(tf, tf.regs.a7) as usize; + tf.sepc += 4; + } Trap::Exception(E::LoadPageFault) => { handle_page_fault(tf, MappingFlags::READ, from_user) } diff --git a/modules/axhal/src/arch/x86_64/context.rs b/modules/axhal/src/arch/x86_64/context.rs index d4be98bdb4..46b12d0f59 100644 --- a/modules/axhal/src/arch/x86_64/context.rs +++ b/modules/axhal/src/arch/x86_64/context.rs @@ -271,6 +271,9 @@ pub struct TaskContext { pub rsp: u64, /// Thread Local Storage (TLS). pub fs_base: usize, + /// The `gs_base` register value. + #[cfg(feature = "uspace")] + pub gs_base: usize, /// Extended states, i.e., FP/SIMD states. #[cfg(feature = "fp_simd")] pub ext_state: ExtendedState, @@ -296,6 +299,8 @@ impl TaskContext { cr3: crate::paging::kernel_page_table_root(), #[cfg(feature = "fp_simd")] ext_state: ExtendedState::default(), + #[cfg(feature = "uspace")] + gs_base: 0, } } @@ -346,6 +351,9 @@ impl TaskContext { } #[cfg(feature = "uspace")] unsafe { + // Switch gs base for user space. + self.gs_base = x86::msr::rdmsr(x86::msr::IA32_KERNEL_GSBASE) as usize; + x86::msr::wrmsr(x86::msr::IA32_KERNEL_GSBASE, next_ctx.gs_base as u64); super::tss_set_rsp0(next_ctx.kstack_top); if next_ctx.cr3 != self.cr3 { super::write_page_table_root(next_ctx.cr3); diff --git a/modules/axhal/src/cpu.rs b/modules/axhal/src/cpu.rs index 89ca281bfa..adecced8a2 100644 --- a/modules/axhal/src/cpu.rs +++ b/modules/axhal/src/cpu.rs @@ -22,6 +22,16 @@ pub fn this_cpu_is_bsp() -> bool { IS_BSP.read_current() } +/// Stores the pointer to the current task in the SP_EL0 register. +/// +/// In aarch64 architecture, we use `SP_EL0` as the read cache for +/// the current task pointer. And this function will update this cache. +#[cfg(target_arch = "aarch64")] +pub(crate) unsafe fn cache_current_task_ptr() { + use tock_registers::interfaces::Writeable; + aarch64_cpu::registers::SP_EL0.set(CURRENT_TASK_PTR.read_current_raw() as u64); +} + /// Gets the pointer to the current task with preemption-safety. /// /// Preemption may be enabled when calling this function. This function will @@ -42,6 +52,7 @@ pub fn current_task_ptr() -> *const T { #[cfg(target_arch = "aarch64")] { // on ARM64, we use `SP_EL0` to store the task pointer. + // `SP_EL0` is equivalent to the cache of CURRENT_TASK_PTR here. use tock_registers::interfaces::Readable; aarch64_cpu::registers::SP_EL0.get() as _ } @@ -68,8 +79,9 @@ pub unsafe fn set_current_task_ptr(ptr: *const T) { } #[cfg(target_arch = "aarch64")] { - use tock_registers::interfaces::Writeable; - aarch64_cpu::registers::SP_EL0.set(ptr as u64) + let _guard = kernel_guard::IrqSave::new(); + CURRENT_TASK_PTR.write_current_raw(ptr as usize); + cache_current_task_ptr(); } } From c2be8221b1c440e102a6f1a70bf2cfc46870c07f Mon Sep 17 00:00:00 2001 From: Youjie Zheng Date: Tue, 22 Oct 2024 18:08:21 +0800 Subject: [PATCH 10/17] [mm] Create new uspace with parameters passed by user --- modules/axmm/src/lib.rs | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/modules/axmm/src/lib.rs b/modules/axmm/src/lib.rs index 05b6e51bbf..d8e2668b37 100644 --- a/modules/axmm/src/lib.rs +++ b/modules/axmm/src/lib.rs @@ -19,9 +19,6 @@ use lazyinit::LazyInit; use memory_addr::{PhysAddr, VirtAddr, va}; use memory_set::MappingError; -const USER_ASPACE_BASE: usize = 0x1000; -const USER_ASPACE_SIZE: usize = 0x7fff_ffff_f000; - static KERNEL_ASPACE: LazyInit> = LazyInit::new(); fn mapping_err_to_ax_err(err: MappingError) -> AxError { @@ -46,8 +43,8 @@ pub fn new_kernel_aspace() -> AxResult { } /// Creates a new address space for user processes. -pub fn new_user_aspace() -> AxResult { - let mut aspace = AddrSpace::new_empty(VirtAddr::from(USER_ASPACE_BASE), USER_ASPACE_SIZE)?; +pub fn new_user_aspace(base: VirtAddr, size: usize) -> AxResult { + let mut aspace = AddrSpace::new_empty(base, size)?; if !cfg!(target_arch = "aarch64") { // ARMv8 use a separate page table (TTBR0_EL1) for user space, it // doesn't need to copy the kernel portion to the user page table. From cb589bb840627e294b17bdb188b6c872f2ca115d Mon Sep 17 00:00:00 2001 From: Youjie Zheng Date: Fri, 7 Feb 2025 23:10:51 +0800 Subject: [PATCH 11/17] [feat] Add support for resource namespace and monolithic plugin --- Cargo.lock | 33 +++ Cargo.toml | 2 + api/arceos_posix_api/Cargo.toml | 5 +- api/arceos_posix_api/src/imp/fd_ops.rs | 13 +- api/arceos_posix_api/src/imp/io_mpx/select.rs | 4 +- api/arceos_posix_api/src/imp/mod.rs | 19 ++ api/arceos_posix_api/src/imp/net.rs | 6 +- api/arceos_posix_api/src/imp/stdio.rs | 4 + modules/axfs/Cargo.toml | 4 + modules/axfs/src/root.rs | 13 +- modules/axfs/tests/test_fatfs.rs | 21 ++ modules/axfs/tests/test_ramfs.rs | 21 ++ modules/axhal/linker.lds.S | 10 + modules/axhal/src/arch/aarch64/context.rs | 16 +- modules/axhal/src/arch/aarch64/mod.rs | 2 +- modules/axhal/src/arch/riscv/context.rs | 14 +- modules/axhal/src/arch/riscv/mod.rs | 2 +- modules/axhal/src/arch/x86_64/context.rs | 18 +- modules/axhal/src/arch/x86_64/mod.rs | 4 +- modules/axhal/src/arch/x86_64/syscall.S | 1 + modules/axhal/src/arch/x86_64/syscall.rs | 6 +- modules/axhal/src/arch/x86_64/trap.rs | 5 - modules/axhal/src/mem.rs | 8 + .../src/platform/aarch64_qemu_virt/mod.rs | 1 - .../src/platform/riscv64_qemu_virt/mod.rs | 1 - modules/axmm/src/backend/alloc.rs | 2 +- modules/axmm/src/lib.rs | 3 + modules/axns/Cargo.toml | 24 ++ modules/axns/src/lib.rs | 279 ++++++++++++++++++ modules/axns/tests/test_global.rs | 71 +++++ modules/axns/tests/test_thread_local.rs | 85 ++++++ modules/axruntime/Cargo.toml | 1 + modules/axruntime/src/lib.rs | 2 + modules/axtask/src/run_queue.rs | 13 +- modules/axtask/src/task.rs | 8 +- 35 files changed, 659 insertions(+), 62 deletions(-) create mode 100644 modules/axns/Cargo.toml create mode 100644 modules/axns/src/lib.rs create mode 100644 modules/axns/tests/test_global.rs create mode 100644 modules/axns/tests/test_thread_local.rs diff --git a/Cargo.lock b/Cargo.lock index 95ad03b9b8..0a142db4f7 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -172,10 +172,12 @@ dependencies = [ "axio", "axlog", "axnet", + "axns", "axruntime", "axsync", "axtask", "bindgen", + "ctor_bare", "flatten_objects", "lazy_static", "spin", @@ -400,6 +402,7 @@ dependencies = [ "axfs_ramfs", "axfs_vfs", "axio", + "axns", "axsync", "axtask", "cap_access", @@ -545,6 +548,15 @@ dependencies = [ "spin", ] +[[package]] +name = "axns" +version = "0.1.0" +dependencies = [ + "axns", + "crate_interface", + "lazyinit", +] + [[package]] name = "axruntime" version = "0.1.0" @@ -561,6 +573,7 @@ dependencies = [ "axtask", "chrono", "crate_interface", + "ctor_bare", "kernel_guard", "percpu", ] @@ -852,6 +865,26 @@ version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "790eea4361631c5e7d22598ecd5723ff611904e3344ce8720784c93e3d83d40b" +[[package]] +name = "ctor_bare" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e4e5ae3c454dc1efb0e5821dc17344539849391b2de18c89596ea563f1909f93" +dependencies = [ + "ctor_bare_macros", +] + +[[package]] +name = "ctor_bare_macros" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a49d5cd78b1c748184d41407b14a58af8403c13328ff2b9f49b0a418c24e3ff" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.98", +] + [[package]] name = "defmt" version = "0.3.10" diff --git a/Cargo.toml b/Cargo.toml index 2924a174af..dfc0a37bda 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -12,6 +12,7 @@ members = [ "modules/axmm", "modules/axdma", "modules/axnet", + "modules/axns", "modules/axruntime", "modules/axsync", "modules/axtask", @@ -58,6 +59,7 @@ axhal = { path = "modules/axhal" } axlog = { path = "modules/axlog" } axmm = { path = "modules/axmm" } axnet = { path = "modules/axnet" } +axns = { path = "modules/axns" } axruntime = { path = "modules/axruntime" } axsync = { path = "modules/axsync" } axtask = { path = "modules/axtask" } diff --git a/api/arceos_posix_api/Cargo.toml b/api/arceos_posix_api/Cargo.toml index 681b6aeb71..a3dd531176 100644 --- a/api/arceos_posix_api/Cargo.toml +++ b/api/arceos_posix_api/Cargo.toml @@ -23,12 +23,13 @@ smp = ["axfeat/smp"] irq = ["axfeat/irq"] alloc = ["dep:axalloc", "axfeat/alloc"] multitask = ["axtask/multitask", "axfeat/multitask", "axsync/multitask"] -fd = ["alloc"] +fd = ["alloc", "dep:axns"] fs = ["dep:axfs", "axfeat/fs", "fd"] net = ["dep:axnet", "axfeat/net", "fd"] pipe = ["fd"] select = ["fd"] epoll = ["fd"] +uspace = ["axns/thread-local", "axfs/thread-local", "smp", "irq", "fs", "multitask", "net", "pipe", "select", "epoll"] [dependencies] # ArceOS modules @@ -42,6 +43,7 @@ axalloc = { workspace = true, optional = true } axtask = { workspace = true, optional = true } axfs = { workspace = true, optional = true } axnet = { workspace = true, optional = true } +axns = { workspace = true, optional = true } # Other crates axio = "0.1" @@ -50,6 +52,7 @@ flatten_objects = "0.2" static_assertions = "1.1.0" spin = { version = "0.9" } lazy_static = { version = "1.5", features = ["spin_no_std"] } +ctor_bare = "0.2" [build-dependencies] bindgen ={ version = "0.69" } diff --git a/api/arceos_posix_api/src/imp/fd_ops.rs b/api/arceos_posix_api/src/imp/fd_ops.rs index 53663c584a..1b75583b04 100644 --- a/api/arceos_posix_api/src/imp/fd_ops.rs +++ b/api/arceos_posix_api/src/imp/fd_ops.rs @@ -3,10 +3,10 @@ use core::ffi::c_int; use axerrno::{LinuxError, LinuxResult}; use axio::PollState; +use axns::{AxResource, def_resource}; use flatten_objects::FlattenObjects; use spin::RwLock; -use super::stdio::{stdin, stdout}; use crate::ctypes; pub const AX_FILE_LIMIT: usize = 1024; @@ -21,14 +21,9 @@ pub trait FileLike: Send + Sync { fn set_nonblocking(&self, nonblocking: bool) -> LinuxResult; } -lazy_static::lazy_static! { - static ref FD_TABLE: RwLock, AX_FILE_LIMIT>> = { - let mut fd_table = FlattenObjects::new(); - fd_table.add_at(0, Arc::new(stdin()) as _).unwrap_or_else(|_| panic!()); // stdin - fd_table.add_at(1, Arc::new(stdout()) as _).unwrap_or_else(|_| panic!()); // stdout - fd_table.add_at(2, Arc::new(stdout()) as _).unwrap_or_else(|_| panic!()); // stderr - RwLock::new(fd_table) - }; +def_resource! { + #[allow(non_camel_case_types)] + pub(crate) static FD_TABLE: AxResource, AX_FILE_LIMIT>>> = AxResource::new(); } pub fn get_file_like(fd: c_int) -> LinuxResult> { diff --git a/api/arceos_posix_api/src/imp/io_mpx/select.rs b/api/arceos_posix_api/src/imp/io_mpx/select.rs index 8a3471a672..c51d1f0513 100644 --- a/api/arceos_posix_api/src/imp/io_mpx/select.rs +++ b/api/arceos_posix_api/src/imp/io_mpx/select.rs @@ -153,13 +153,13 @@ pub unsafe fn sys_select( unsafe fn zero_fd_set(fds: *mut ctypes::fd_set, nfds: usize) { if !fds.is_null() { let nfds_usizes = nfds.div_ceil(BITS_PER_USIZE); - let dst = &mut (*fds).fds_bits[..nfds_usizes]; + let dst = &mut unsafe { *fds }.fds_bits[..nfds_usizes]; dst.fill(0); } } unsafe fn set_fd_set(fds: *mut ctypes::fd_set, fd: usize) { if !fds.is_null() { - (*fds).fds_bits[fd / BITS_PER_USIZE] |= 1 << (fd % BITS_PER_USIZE); + unsafe { *fds }.fds_bits[fd / BITS_PER_USIZE] |= 1 << (fd % BITS_PER_USIZE); } } diff --git a/api/arceos_posix_api/src/imp/mod.rs b/api/arceos_posix_api/src/imp/mod.rs index 603f934baa..0202f5c0e1 100644 --- a/api/arceos_posix_api/src/imp/mod.rs +++ b/api/arceos_posix_api/src/imp/mod.rs @@ -18,3 +18,22 @@ pub mod net; pub mod pipe; #[cfg(feature = "multitask")] pub mod pthread; + +#[ctor_bare::register_ctor] +#[cfg(feature = "fd")] +fn init_stdio() { + use crate::imp::fd_ops::FD_TABLE; + use alloc::sync::Arc; + use stdio::{stdin, stdout}; + let mut fd_table = flatten_objects::FlattenObjects::new(); + fd_table + .add_at(0, Arc::new(stdin()) as _) + .unwrap_or_else(|_| panic!()); // stdin + fd_table + .add_at(1, Arc::new(stdout()) as _) + .unwrap_or_else(|_| panic!()); // stdout + fd_table + .add_at(2, Arc::new(stdout()) as _) + .unwrap_or_else(|_| panic!()); // stderr + FD_TABLE.init_new(spin::RwLock::new(fd_table)); +} diff --git a/api/arceos_posix_api/src/imp/net.rs b/api/arceos_posix_api/src/imp/net.rs index 861e4737bb..3c43430e3c 100644 --- a/api/arceos_posix_api/src/imp/net.rs +++ b/api/arceos_posix_api/src/imp/net.rs @@ -525,10 +525,10 @@ pub unsafe fn sys_freeaddrinfo(res: *mut ctypes::addrinfo) { return; } let aibuf_ptr = res as *mut ctypes::aibuf; - let len = (*aibuf_ptr).ref_ as usize; - assert!((*aibuf_ptr).slot == 0); + let len = unsafe { *aibuf_ptr }.ref_ as usize; + assert!(unsafe { *aibuf_ptr }.slot == 0); assert!(len > 0); - let vec = Vec::from_raw_parts(aibuf_ptr, len, len); // TODO: lock + let vec = unsafe { Vec::from_raw_parts(aibuf_ptr, len, len) }; // TODO: lock drop(vec); } diff --git a/api/arceos_posix_api/src/imp/stdio.rs b/api/arceos_posix_api/src/imp/stdio.rs index 4087004f00..46ba0c2a26 100644 --- a/api/arceos_posix_api/src/imp/stdio.rs +++ b/api/arceos_posix_api/src/imp/stdio.rs @@ -16,6 +16,10 @@ fn console_read_bytes(buf: &mut [u8]) -> AxResult { } fn console_write_bytes(buf: &[u8]) -> AxResult { + #[cfg(feature = "uspace")] + // We need to copy the buffer to the kernel space because SBI can't access user space. + axhal::console::write_bytes(buf.to_vec().as_slice()); + #[cfg(not(feature = "uspace"))] axhal::console::write_bytes(buf); Ok(buf.len()) } diff --git a/modules/axfs/Cargo.toml b/modules/axfs/Cargo.toml index 13d2e141fe..b0c05346cf 100644 --- a/modules/axfs/Cargo.toml +++ b/modules/axfs/Cargo.toml @@ -10,6 +10,7 @@ repository = "https://github.com/arceos-org/arceos/tree/main/modules/axfs" documentation = "https://arceos-org.github.io/arceos/axfs/index.html" [features] +thread-local = ["axns/thread-local"] devfs = ["dep:axfs_devfs"] ramfs = ["dep:axfs_ramfs"] procfs = ["dep:axfs_ramfs"] @@ -34,6 +35,7 @@ crate_interface = { version = "0.1", optional = true } axsync = { workspace = true } axdriver = { workspace = true, features = ["block"] } axdriver_block = { git = "https://github.com/arceos-org/axdriver_crates.git", tag = "v0.1.0" } +axns = { workspace = true } [dependencies.fatfs] git = "https://github.com/rafalh/rust-fatfs" @@ -52,3 +54,5 @@ axdriver = { workspace = true, features = ["block", "ramdisk"] } axdriver_block = { git = "https://github.com/arceos-org/axdriver_crates.git", tag = "v0.1.0", features = ["ramdisk"] } axsync = { workspace = true, features = ["multitask"] } axtask = { workspace = true, features = ["test"] } +axns = { workspace = true, features = ["thread-local"] } +crate_interface = "0.1" \ No newline at end of file diff --git a/modules/axfs/src/root.rs b/modules/axfs/src/root.rs index 83de70ef63..4bcebb5307 100644 --- a/modules/axfs/src/root.rs +++ b/modules/axfs/src/root.rs @@ -5,13 +5,18 @@ use alloc::{string::String, sync::Arc, vec::Vec}; use axerrno::{AxError, AxResult, ax_err}; use axfs_vfs::{VfsNodeAttr, VfsNodeOps, VfsNodeRef, VfsNodeType, VfsOps, VfsResult}; +use axns::{AxResource, def_resource}; use axsync::Mutex; use lazyinit::LazyInit; use crate::{api::FileType, fs, mounts}; -static CURRENT_DIR_PATH: Mutex = Mutex::new(String::new()); -static CURRENT_DIR: LazyInit> = LazyInit::new(); +def_resource! { + #[allow(non_camel_case_types)] + static CURRENT_DIR_PATH: AxResource> = AxResource::new(); + #[allow(non_camel_case_types)] + static CURRENT_DIR: AxResource> = AxResource::new(); +} struct MountPoint { path: &'static str, @@ -180,8 +185,8 @@ pub(crate) fn init_rootfs(disk: crate::dev::Disk) { .expect("fail to mount sysfs at /sys"); ROOT_DIR.init_once(Arc::new(root_dir)); - CURRENT_DIR.init_once(Mutex::new(ROOT_DIR.clone())); - *CURRENT_DIR_PATH.lock() = "/".into(); + CURRENT_DIR.init_new(Mutex::new(ROOT_DIR.clone())); + CURRENT_DIR_PATH.init_new(Mutex::new("/".into())); } fn parent_node_of(dir: Option<&VfsNodeRef>, path: &str) -> VfsNodeRef { diff --git a/modules/axfs/tests/test_fatfs.rs b/modules/axfs/tests/test_fatfs.rs index b05950d507..f81c044b82 100644 --- a/modules/axfs/tests/test_fatfs.rs +++ b/modules/axfs/tests/test_fatfs.rs @@ -15,10 +15,31 @@ fn make_disk() -> std::io::Result { Ok(RamDisk::from(&data)) } +mod axns_imp { + use axns::{AxNamespace, AxNamespaceIf}; + use lazyinit::LazyInit; + thread_local! { + static NS: LazyInit = LazyInit::new(); + } + struct AxNamespaceImpl; + #[crate_interface::impl_interface] + impl AxNamespaceIf for AxNamespaceImpl { + fn current_namespace_base() -> *mut u8 { + NS.with(|ns| ns.base()) + } + } + pub(crate) fn thread_init_namespace() { + NS.with(|ns| { + ns.init_once(AxNamespace::global()); + }); + } +} + #[test] fn test_fatfs() { println!("Testing fatfs with ramdisk ..."); + axns_imp::thread_init_namespace(); let disk = make_disk().expect("failed to load disk image"); axtask::init_scheduler(); // call this to use `axsync::Mutex`. axfs::init_filesystems(AxDeviceContainer::from_one(disk)); diff --git a/modules/axfs/tests/test_ramfs.rs b/modules/axfs/tests/test_ramfs.rs index 914c245bc6..f41ac00ba8 100644 --- a/modules/axfs/tests/test_ramfs.rs +++ b/modules/axfs/tests/test_ramfs.rs @@ -41,10 +41,31 @@ fn create_init_files() -> Result<()> { Ok(()) } +mod axns_imp { + use axns::{AxNamespace, AxNamespaceIf}; + use lazyinit::LazyInit; + thread_local! { + static NS: LazyInit = LazyInit::new(); + } + struct AxNamespaceImpl; + #[crate_interface::impl_interface] + impl AxNamespaceIf for AxNamespaceImpl { + fn current_namespace_base() -> *mut u8 { + NS.with(|ns| ns.base()) + } + } + pub(crate) fn thread_init_namespace() { + NS.with(|ns| { + ns.init_once(AxNamespace::global()); + }); + } +} + #[test] fn test_ramfs() { println!("Testing ramfs ..."); + axns_imp::thread_init_namespace(); axtask::init_scheduler(); // call this to use `axsync::Mutex`. axfs::init_filesystems(AxDeviceContainer::from_one(RamDisk::default())); // dummy disk, actually not used. diff --git a/modules/axhal/linker.lds.S b/modules/axhal/linker.lds.S index b8d42ee770..ba3e7d2c3d 100644 --- a/modules/axhal/linker.lds.S +++ b/modules/axhal/linker.lds.S @@ -24,6 +24,15 @@ SECTIONS . = ALIGN(4K); _erodata = .; } + + .init_array : ALIGN(4K) { + _sinit_array = .; + __init_array_start = .; + *(.init_array .init_array.*) + __init_array_end = .; + . = ALIGN(4K); + _einit_array = .; + } .data : ALIGN(4K) { _sdata = .; @@ -89,5 +98,6 @@ SECTIONS { linkm2_PAGE_FAULT : { *(linkm2_PAGE_FAULT) } linkme_SYSCALL : { *(linkme_SYSCALL) } linkm2_SYSCALL : { *(linkm2_SYSCALL) } + axns_resource : { *(axns_resource) } } INSERT AFTER .tbss; diff --git a/modules/axhal/src/arch/aarch64/context.rs b/modules/axhal/src/arch/aarch64/context.rs index 23a4fe5a48..4ce640ed1d 100644 --- a/modules/axhal/src/arch/aarch64/context.rs +++ b/modules/axhal/src/arch/aarch64/context.rs @@ -118,14 +118,15 @@ impl UspaceContext { /// /// This function is unsafe because it changes processor mode and the stack. #[inline(never)] - #[no_mangle] + #[unsafe(no_mangle)] pub unsafe fn enter_uspace(&self, kstack_top: VirtAddr) -> ! { super::disable_irqs(); // We do not handle traps that occur at the current exception level, // so the kstack ptr(`sp_el1`) will not change during running in user space. // Then we don't need to save the `sp_el1` to the taskctx. - asm!( - " + unsafe { + core::arch::asm!( + " mov sp, x1 ldp x30, x9, [x0, 30 * 8] ldp x10, x11, [x0, 32 * 8] @@ -149,10 +150,11 @@ impl UspaceContext { ldp x2, x3, [x0, 2 * 8] ldp x0, x1, [x0] eret", - in("x0") &self.0, - in("x1") kstack_top.as_usize() , - options(noreturn), - ) + in("x0") &self.0, + in("x1") kstack_top.as_usize() , + options(noreturn), + ) + } } } diff --git a/modules/axhal/src/arch/aarch64/mod.rs b/modules/axhal/src/arch/aarch64/mod.rs index 9e56602b2c..2d99acad8c 100644 --- a/modules/axhal/src/arch/aarch64/mod.rs +++ b/modules/axhal/src/arch/aarch64/mod.rs @@ -144,7 +144,7 @@ pub unsafe fn write_thread_pointer(tpidr_el0: usize) { /// /// On AArch64, it sets the exception vector base address (`VBAR_EL1`) and `TTBR0_EL1`. pub fn cpu_init() { - extern "C" { + unsafe extern "C" { fn exception_vector_base(); } set_exception_vector_base(exception_vector_base as usize); diff --git a/modules/axhal/src/arch/riscv/context.rs b/modules/axhal/src/arch/riscv/context.rs index b92a24754f..48ed8d6e6c 100644 --- a/modules/axhal/src/arch/riscv/context.rs +++ b/modules/axhal/src/arch/riscv/context.rs @@ -151,7 +151,7 @@ impl UspaceContext { /// /// This function is unsafe because it changes processor mode and the stack. #[inline(never)] - #[no_mangle] + #[unsafe(no_mangle)] pub unsafe fn enter_uspace(&self, kstack_top: VirtAddr) -> ! { use riscv::register::{sepc, sscratch}; @@ -160,7 +160,8 @@ impl UspaceContext { sepc::write(self.0.sepc); // Address of the top of the kernel stack after saving the trap frame. let kernel_trap_addr = kstack_top.as_usize() - core::mem::size_of::(); - asm!(" + unsafe { + core::arch::asm!(" mv sp, {tf} STR gp, {kernel_trap_addr}, 2 @@ -174,10 +175,11 @@ impl UspaceContext { POP_GENERAL_REGS LDR sp, sp, 1 sret", - tf = in(reg) &(self.0), - kernel_trap_addr = in(reg) kernel_trap_addr, - options(noreturn), - ) + tf = in(reg) &(self.0), + kernel_trap_addr = in(reg) kernel_trap_addr, + options(noreturn), + ) + } } } diff --git a/modules/axhal/src/arch/riscv/mod.rs b/modules/axhal/src/arch/riscv/mod.rs index 36ce91da41..07caf58643 100644 --- a/modules/axhal/src/arch/riscv/mod.rs +++ b/modules/axhal/src/arch/riscv/mod.rs @@ -114,7 +114,7 @@ pub unsafe fn write_thread_pointer(tp: usize) { /// /// On RISC-V, it sets the trap vector base address. pub fn cpu_init() { - extern "C" { + unsafe extern "C" { fn trap_vector_base(); } set_trap_vector_base(trap_vector_base as usize); diff --git a/modules/axhal/src/arch/x86_64/context.rs b/modules/axhal/src/arch/x86_64/context.rs index 46b12d0f59..01f0271665 100644 --- a/modules/axhal/src/arch/x86_64/context.rs +++ b/modules/axhal/src/arch/x86_64/context.rs @@ -2,7 +2,6 @@ use core::{arch::naked_asm, fmt}; use memory_addr::VirtAddr; - /// Saved registers when a trap (interrupt or exception) occurs. #[allow(missing_docs)] #[repr(C)] @@ -87,6 +86,8 @@ 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::arch::GdtStruct; + use x86_64::registers::rflags::RFlags; Self(TrapFrame { rdi: arg0 as _, rip: entry as _, @@ -104,6 +105,7 @@ 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::arch::GdtStruct; let mut tf = *tf; tf.cs = GdtStruct::UCODE64_SELECTOR.0 as _; tf.ss = GdtStruct::UDATA_SELECTOR.0 as _; @@ -148,7 +150,8 @@ impl UspaceContext { pub unsafe fn enter_uspace(&self, kstack_top: VirtAddr) -> ! { super::disable_irqs(); assert_eq!(super::tss_get_rsp0(), kstack_top); - asm!(" + unsafe { + core::arch::asm!(" mov rsp, {tf} pop rax pop rcx @@ -168,9 +171,10 @@ impl UspaceContext { add rsp, 16 // skip vector, error_code swapgs iretq", - tf = in(reg) &self.0, - options(noreturn), - ) + tf = in(reg) &self.0, + options(noreturn), + ) + } } } @@ -279,7 +283,7 @@ pub struct TaskContext { pub ext_state: ExtendedState, /// The `CR3` register value, i.e., the page table root. #[cfg(feature = "uspace")] - pub cr3: PhysAddr, + pub cr3: memory_addr::PhysAddr, } impl TaskContext { @@ -330,7 +334,7 @@ impl TaskContext { /// /// [1]: crate::paging::kernel_page_table_root #[cfg(feature = "uspace")] - pub fn set_page_table_root(&mut self, cr3: PhysAddr) { + pub fn set_page_table_root(&mut self, cr3: memory_addr::PhysAddr) { self.cr3 = cr3; } diff --git a/modules/axhal/src/arch/x86_64/mod.rs b/modules/axhal/src/arch/x86_64/mod.rs index a3b7d81156..e1b348da1c 100644 --- a/modules/axhal/src/arch/x86_64/mod.rs +++ b/modules/axhal/src/arch/x86_64/mod.rs @@ -15,8 +15,8 @@ use x86::{controlregs, msr, tlb}; use x86_64::instructions::interrupts; pub use self::context::{ExtendedState, FxsaveArea, TaskContext, TrapFrame}; -pub use self::gdt::{init_gdt, tss_get_rsp0, tss_set_rsp0, GdtStruct}; -pub use self::idt::{init_idt, IdtStruct}; +pub use self::gdt::{GdtStruct, init_gdt, tss_get_rsp0, tss_set_rsp0}; +pub use self::idt::{IdtStruct, init_idt}; #[cfg(feature = "uspace")] pub use self::{context::UspaceContext, syscall::init_syscall}; diff --git a/modules/axhal/src/arch/x86_64/syscall.S b/modules/axhal/src/arch/x86_64/syscall.S index 08ac99995d..61c511ec85 100644 --- a/modules/axhal/src/arch/x86_64/syscall.S +++ b/modules/axhal/src/arch/x86_64/syscall.S @@ -1,4 +1,5 @@ .section .text +.code64 syscall_entry: swapgs // switch to kernel gs mov gs:[offset __PERCPU_USER_RSP_OFFSET], rsp // save user rsp diff --git a/modules/axhal/src/arch/x86_64/syscall.rs b/modules/axhal/src/arch/x86_64/syscall.rs index 941ccd9ab3..92dcf130a8 100644 --- a/modules/axhal/src/arch/x86_64/syscall.rs +++ b/modules/axhal/src/arch/x86_64/syscall.rs @@ -5,7 +5,7 @@ use x86_64::structures::tss::TaskStateSegment; use super::{GdtStruct, TrapFrame}; -#[no_mangle] +#[unsafe(no_mangle)] #[percpu::def_percpu] static USER_RSP_OFFSET: usize = 0; @@ -14,14 +14,14 @@ core::arch::global_asm!( tss_rsp0_offset = const core::mem::offset_of!(TaskStateSegment, privilege_stack_table), ); -#[no_mangle] +#[unsafe(no_mangle)] pub(super) fn x86_syscall_handler(tf: &mut TrapFrame) { tf.rax = crate::trap::handle_syscall(tf, tf.rax as usize) as u64; } /// Initializes syscall support and setups the syscall handler. pub fn init_syscall() { - extern "C" { + unsafe extern "C" { fn syscall_entry(); } unsafe { diff --git a/modules/axhal/src/arch/x86_64/trap.rs b/modules/axhal/src/arch/x86_64/trap.rs index 3fd1072895..7c44b60370 100644 --- a/modules/axhal/src/arch/x86_64/trap.rs +++ b/modules/axhal/src/arch/x86_64/trap.rs @@ -29,13 +29,8 @@ fn handle_page_fault(tf: &TrapFrame) { } } -<<<<<<< HEAD #[unsafe(no_mangle)] -fn x86_trap_handler(tf: &TrapFrame) { -======= -#[no_mangle] fn x86_trap_handler(tf: &mut TrapFrame) { ->>>>>>> 4f0c0a4 ([hal]: Add UspaceContext to enter user space on x86_64) match tf.vector as u8 { PAGE_FAULT_VECTOR => handle_page_fault(tf), BREAKPOINT_VECTOR => debug!("#BP @ {:#x} ", tf.rip), diff --git a/modules/axhal/src/mem.rs b/modules/axhal/src/mem.rs index e4e15747d3..4a30464eb7 100644 --- a/modules/axhal/src/mem.rs +++ b/modules/axhal/src/mem.rs @@ -88,6 +88,12 @@ fn kernel_image_regions() -> impl Iterator { flags: MemRegionFlags::RESERVED | MemRegionFlags::READ, name: ".rodata", }, + MemRegion { + paddr: virt_to_phys((_sinit_array as usize).into()), + size: _einit_array as usize - _sinit_array as usize, + flags: MemRegionFlags::RESERVED | MemRegionFlags::READ, + name: ".init_array", + }, MemRegion { paddr: virt_to_phys((_sdata as usize).into()), size: _edata as usize - _sdata as usize, @@ -151,6 +157,8 @@ unsafe extern "C" { fn _etext(); fn _srodata(); fn _erodata(); + fn _sinit_array(); + fn _einit_array(); fn _sdata(); fn _edata(); fn _sbss(); diff --git a/modules/axhal/src/platform/aarch64_qemu_virt/mod.rs b/modules/axhal/src/platform/aarch64_qemu_virt/mod.rs index f1d3a570eb..a074c8b102 100644 --- a/modules/axhal/src/platform/aarch64_qemu_virt/mod.rs +++ b/modules/axhal/src/platform/aarch64_qemu_virt/mod.rs @@ -21,7 +21,6 @@ pub mod misc { } unsafe extern "C" { - fn exception_vector_base(); fn rust_main(cpu_id: usize, dtb: usize); #[cfg(feature = "smp")] fn rust_main_secondary(cpu_id: usize); diff --git a/modules/axhal/src/platform/riscv64_qemu_virt/mod.rs b/modules/axhal/src/platform/riscv64_qemu_virt/mod.rs index 4ab5d50bd9..e599e22e64 100644 --- a/modules/axhal/src/platform/riscv64_qemu_virt/mod.rs +++ b/modules/axhal/src/platform/riscv64_qemu_virt/mod.rs @@ -12,7 +12,6 @@ pub mod irq; pub mod mp; unsafe extern "C" { - fn trap_vector_base(); fn rust_main(cpu_id: usize, dtb: usize); #[cfg(feature = "smp")] fn rust_main_secondary(cpu_id: usize); diff --git a/modules/axmm/src/backend/alloc.rs b/modules/axmm/src/backend/alloc.rs index e404d718a8..cce416ee0e 100644 --- a/modules/axmm/src/backend/alloc.rs +++ b/modules/axmm/src/backend/alloc.rs @@ -1,7 +1,7 @@ use axalloc::global_allocator; use axhal::mem::{phys_to_virt, virt_to_phys}; use axhal::paging::{MappingFlags, PageSize, PageTable}; -use memory_addr::{PageIter4K, PhysAddr, VirtAddr, PAGE_SIZE_4K}; +use memory_addr::{PAGE_SIZE_4K, PageIter4K, PhysAddr, VirtAddr}; use super::Backend; diff --git a/modules/axmm/src/lib.rs b/modules/axmm/src/lib.rs index d8e2668b37..e94858035c 100644 --- a/modules/axmm/src/lib.rs +++ b/modules/axmm/src/lib.rs @@ -37,6 +37,9 @@ pub fn new_kernel_aspace() -> AxResult { axconfig::plat::KERNEL_ASPACE_SIZE, )?; for r in axhal::mem::memory_regions() { + if r.size == 0 && r.name == ".init_array" { + continue; + } aspace.map_linear(phys_to_virt(r.paddr), r.paddr, r.size, r.flags.into())?; } Ok(aspace) diff --git a/modules/axns/Cargo.toml b/modules/axns/Cargo.toml new file mode 100644 index 0000000000..7200734ac0 --- /dev/null +++ b/modules/axns/Cargo.toml @@ -0,0 +1,24 @@ +[package] +name = "axns" +version.workspace = true +edition.workspace = true +authors = ["Yuekai Jia "] +description = "ArceOS namespaces to control system resource sharing between threads" +license.workspace = true +homepage.workspace = true +repository = "https://github.com/arceos-org/arceos/tree/main/modules/axns" +documentation = "https://arceos-org.github.io/arceos/axns/index.html" + +[features] +default = [] + +# Each thread has its individual namespace field, instead of using the global +# namespace. +thread-local = [] + +[dependencies] +lazyinit = "0.2" +crate_interface = "0.1" + +[dev-dependencies] +axns = { workspace = true, features = ["thread-local"] } diff --git a/modules/axns/src/lib.rs b/modules/axns/src/lib.rs new file mode 100644 index 0000000000..641d17b22f --- /dev/null +++ b/modules/axns/src/lib.rs @@ -0,0 +1,279 @@ +//! [ArceOS](https://github.com/arceos-org/arceos) namespaces module. +//! +//! Namespaces are used to control system resource sharing between threads. This +//! module provides a unified interface to access system resources in different +//! scenarios. +//! +//! For a unikernel, there is only one global namespace, so all threads share +//! the same system resources, such as virtual address space, working directory, +//! and file descriptors, etc. +//! +//! For a monolithic kernel, each process corresponds to a namespace, all +//! threads in the same process share the same system resources. Different +//! processes have different namespaces and isolated resources. +//! +//! For further container support, some global system resources can also be +//! grouped into a namespace. +//! +//! See the examples of [`def_resource!`] for more usage. + +#![cfg_attr(not(test), no_std)] + +extern crate alloc; + +use alloc::sync::Arc; +use core::{alloc::Layout, fmt, ops::Deref}; + +use lazyinit::LazyInit; + +unsafe extern "C" { + fn __start_axns_resource(); + fn __stop_axns_resource(); +} + +/// A namespace that contains all user-defined resources. +/// +/// There are two types of namespaces: +/// +/// - Global namespace: this namespace is globally unique and all threads share +/// the resources in it. Resources are statically collected into the +/// `axns_resource` section, and the global namespace is constructed by the base +/// address of the section ([`AxNamespace::global`]). +/// - Thread-local namespace: this namespace is per-thread, each thread should +/// call [`AxNamespace::new_thread_local()`] to allocate a memory area as its +/// namespace. Layout of resources in global and thread-local namespaces is +/// consistent. Each namespace has its own resources, which may be unique or +/// shared between threads by the [`Arc`] wrapper. +pub struct AxNamespace { + base: *mut u8, + alloc: bool, +} + +impl AxNamespace { + /// Returns the base address of the namespace, which points to the start of + /// all resources. + pub const fn base(&self) -> *mut u8 { + self.base + } + + /// Returns the size of the namespace (size of all resources). + pub fn size(&self) -> usize { + Self::section_size() + } + + /// Returns the size of the `axns_resource` section. + fn section_size() -> usize { + __stop_axns_resource as usize - __start_axns_resource as usize + } + + /// Returns the global namespace. + pub fn global() -> Self { + Self { + base: __start_axns_resource as *mut u8, + alloc: false, + } + } + + /// Constructs a new thread-local namespace. + /// + /// Each thread can have its own namespace instead of the global one, to + /// isolate resources between threads. + /// + /// This function allocates a memory area to store the thread-local resources, + /// and copies from the global namespace as the initial value. + #[cfg(feature = "thread-local")] + pub fn new_thread_local() -> Self { + let size = Self::section_size(); + let base = if size == 0 { + core::ptr::null_mut() + } else { + let layout = Layout::from_size_align(size, 64).unwrap(); + let dst = unsafe { alloc::alloc::alloc(layout) }; + let src = __start_axns_resource as *const u8; + unsafe { core::ptr::copy_nonoverlapping(src, dst, size) }; + dst + }; + Self { base, alloc: true } + } +} + +impl Drop for AxNamespace { + fn drop(&mut self) { + if self.alloc { + let size = Self::section_size(); + if size != 0 && !self.base.is_null() { + let layout = Layout::from_size_align(size, 64).unwrap(); + unsafe { alloc::alloc::dealloc(self.base, layout) }; + } + } + } +} + +/// A helper type to easily manage shared resources. +/// +/// It provides methods to lazily initialize the resource of the current thread, +/// or to share the resource with other threads. +pub struct AxResource(LazyInit>); + +impl AxResource { + /// Creates a new uninitialized resource. + pub const fn new() -> Self { + Self(LazyInit::new()) + } + + /// Returns a shared reference to the resource. + pub fn share(&self) -> Arc { + self.0.deref().clone() + } + + /// Initializes the resource and does not share with others. + pub fn init_new(&self, data: T) { + self.0.init_once(Arc::new(data)); + } + + /// Initializes the resource with the shared data. + pub fn init_shared(&self, data: Arc) { + self.0.init_once(data); + } + + /// Checks whether the value is initialized. + pub fn is_inited(&self) -> bool { + self.0.is_inited() + } +} + +impl Deref for AxResource { + type Target = T; + + fn deref(&self) -> &Self::Target { + self.0.deref() + } +} + +impl fmt::Debug for AxResource { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + self.0.fmt(f) + } +} + +/// The interfaces need to be implemented when enable thread-local namespaces. +#[cfg(feature = "thread-local")] +#[crate_interface::def_interface] +pub trait AxNamespaceIf { + /// Returns the pointer to the current namespace. + /// + /// It usually needs to be obtained from the thread local storage. + fn current_namespace_base() -> *mut u8; +} + +/// Returns the pointer to the current namespace. +/// +/// When `thread-local` feature is enabled, it returns the thread-local namespace +/// of the current thread. Otherwise, it returns the global namespace. +/// +/// # Safety +/// +/// This function is unsafe, the returned pointer should not outlive the current +/// thread. +pub unsafe fn current_namespace_base() -> *mut u8 { + #[cfg(feature = "thread-local")] + { + crate_interface::call_interface!(AxNamespaceIf::current_namespace_base) + } + #[cfg(not(feature = "thread-local"))] + { + AxNamespace::global().base() + } +} + +/// Defines a resource that managed by [`AxNamespace`]. +/// +/// Each resource will be collected into the `axns_resource` section. When +/// accessed, it is either dereferenced from the global namespace or the +/// thread-local namespace according to the `thread-local` feature. +/// +/// # Example +/// +/// ``` +/// use axns::AxResource; +/// +/// axns::def_resource! { +/// static FOO: u32 = 42; +/// static BAR: AxResource = AxResource::new(); +/// } +/// +/// BAR.init_new("hello world".to_string()); +/// assert_eq!(*FOO, 42); +/// assert_eq!(BAR.as_str(), "hello world"); +/// +/// mod imp { +/// use axns::{AxNamespace, AxNamespaceIf}; +/// +/// struct AxResourceImpl; +/// +/// #[crate_interface::impl_interface] +/// impl AxNamespaceIf for AxResourceImpl { +/// fn current_namespace_base() -> *mut u8 { +/// AxNamespace::global().base() +/// } +/// } +/// } +/// ``` +#[macro_export] +macro_rules! def_resource { + ( $( $(#[$attr:meta])* $vis:vis static $name:ident: $ty:ty = $default:expr; )+ ) => { + $( + $(#[$attr])* + $vis struct $name { __value: () } + + impl $name { + unsafe fn deref_from_base(&self, ns_base: *mut u8) -> &$ty { + unsafe extern { + fn __start_axns_resource(); + } + + #[unsafe(link_section = "axns_resource")] + static RES: $ty = $default; + + let offset = &RES as *const _ as usize - __start_axns_resource as usize; + let ptr = unsafe{ ns_base.add(offset) } as *const _; + unsafe{ &*ptr } + } + + /// Dereference the resource from the given namespace. + pub fn deref_from(&self, ns: &$crate::AxNamespace) -> &$ty { + unsafe { self.deref_from_base(ns.base()) } + } + + /// Dereference the resource from the global namespace. + pub fn deref_global(&self) -> &$ty { + self.deref_from(&$crate::AxNamespace::global()) + } + + /// Dereference the resource automatically, according whether the + /// `thread-local` feature of the `axns` crate is enabled or not. + /// + /// When the feature is enabled, it dereferences from the + /// thread-local namespace of the current thread. Otherwise, it + /// dereferences from the global namespace. + pub fn deref_auto(&self) -> &$ty { + unsafe { self.deref_from_base($crate::current_namespace_base()) } + } + } + + impl core::ops::Deref for $name { + type Target = $ty; + + #[inline(never)] + fn deref(&self) -> &Self::Target { + self.deref_auto() + } + } + + #[used] + #[doc(hidden)] + $(#[$attr])* + $vis static $name: $name = $name { __value: () }; + )+ + }; +} diff --git a/modules/axns/tests/test_global.rs b/modules/axns/tests/test_global.rs new file mode 100644 index 0000000000..020341d8c8 --- /dev/null +++ b/modules/axns/tests/test_global.rs @@ -0,0 +1,71 @@ +use std::sync::atomic::{AtomicUsize, Ordering}; +use std::sync::{Barrier, Mutex}; +use std::thread; + +use axns::{AxResource, def_resource}; + +use self::imp::thread_init_namespace; + +def_resource! { + static FOO: AxResource = AxResource::new(); + static BAR: AxResource> = AxResource::new(); +} + +static BARRIER: Barrier = Barrier::new(3); + +fn thread_fn() { + FOO.fetch_add(1, Ordering::SeqCst); + BAR.lock().unwrap().push_str(" hello"); + + BARRIER.wait(); + println!("{:?} FOO: {:?}", std::thread::current().id(), *FOO); + println!("{:?} BAR: {:?}", std::thread::current().id(), BAR.lock()); + + // all threads share the same namespace + assert_eq!(FOO.load(Ordering::SeqCst), 103); + assert_eq!(BAR.lock().unwrap().as_str(), "one hello hello hello"); +} + +#[test] +fn test_namespace() { + thread_init_namespace(); + FOO.init_new(100.into()); + BAR.init_new(Mutex::new(String::from("one"))); + + let t1 = thread::spawn(|| { + thread_init_namespace(); + thread_fn(); + }); + let t2 = thread::spawn(|| { + thread_init_namespace(); + thread_fn(); + }); + + thread_fn(); + t1.join().unwrap(); + t2.join().unwrap(); +} + +mod imp { + use axns::{AxNamespace, AxNamespaceIf}; + use lazyinit::LazyInit; + + thread_local! { + static NS: LazyInit = LazyInit::new(); + } + + struct AxNamespaceImpl; + + #[crate_interface::impl_interface] + impl AxNamespaceIf for AxNamespaceImpl { + fn current_namespace_base() -> *mut u8 { + NS.with(|ns| ns.base()) + } + } + + pub fn thread_init_namespace() { + NS.with(|ns| { + ns.init_once(AxNamespace::global()); + }); + } +} diff --git a/modules/axns/tests/test_thread_local.rs b/modules/axns/tests/test_thread_local.rs new file mode 100644 index 0000000000..dc4a99901c --- /dev/null +++ b/modules/axns/tests/test_thread_local.rs @@ -0,0 +1,85 @@ +#![feature(thread_id_value)] + +use std::sync::atomic::{AtomicUsize, Ordering}; +use std::sync::{Barrier, Mutex}; +use std::thread; + +use axns::{AxResource, def_resource}; + +use self::imp::thread_init_namespace; + +def_resource! { + static FOO: AxResource = AxResource::new(); + static BAR: AxResource> = AxResource::new(); +} + +static BARRIER: Barrier = Barrier::new(3); + +fn thread_fn() { + FOO.fetch_add(1, Ordering::SeqCst); + BAR.lock().unwrap().push_str(" hello"); + + BARRIER.wait(); + println!("{:?} FOO: {:?}", std::thread::current().id(), *FOO); + println!("{:?} BAR: {:?}", std::thread::current().id(), BAR.lock()); + + let id: u64 = thread::current().id().as_u64().into(); + if id == 2 || id == 4 { + assert_eq!(FOO.load(Ordering::SeqCst), 102); + assert_eq!(BAR.lock().unwrap().as_str(), "one hello hello"); + } else if id == 3 { + assert_eq!(FOO.load(Ordering::SeqCst), 201); + assert_eq!(BAR.lock().unwrap().as_str(), "two hello"); + } +} + +#[test] +fn test_namespace() { + thread_init_namespace(); + FOO.init_new(100.into()); + BAR.init_new(Mutex::new(String::from("one"))); + + let t0_foo = FOO.share(); + let t0_bar = BAR.share(); + + let t1 = thread::spawn(|| { + thread_init_namespace(); + FOO.init_new(200.into()); // isolated from t0 + BAR.init_new(Mutex::new(String::from("two"))); // isolated from t0 + thread_fn(); + }); + let t2 = thread::spawn(|| { + thread_init_namespace(); + FOO.init_shared(t0_foo); // shared with t0 + BAR.init_shared(t0_bar); // shared with t0 + thread_fn(); + }); + + thread_fn(); + t1.join().unwrap(); + t2.join().unwrap(); +} + +mod imp { + use axns::{AxNamespace, AxNamespaceIf}; + use lazyinit::LazyInit; + + thread_local! { + static NS: LazyInit = LazyInit::new(); + } + + struct AxNamespaceImpl; + + #[crate_interface::impl_interface] + impl AxNamespaceIf for AxNamespaceImpl { + fn current_namespace_base() -> *mut u8 { + NS.with(|ns| ns.base()) + } + } + + pub fn thread_init_namespace() { + NS.with(|ns| { + ns.init_once(AxNamespace::new_thread_local()); + }); + } +} diff --git a/modules/axruntime/Cargo.toml b/modules/axruntime/Cargo.toml index 770b85dc74..8209722d1d 100644 --- a/modules/axruntime/Cargo.toml +++ b/modules/axruntime/Cargo.toml @@ -39,5 +39,6 @@ axtask = { workspace = true, optional = true } crate_interface = "0.1" percpu = { version = "0.2", optional = true } kernel_guard = { version = "0.1", optional = true } +ctor_bare = "0.2" chrono = { version = "0.4.38", default-features = false } diff --git a/modules/axruntime/src/lib.rs b/modules/axruntime/src/lib.rs index 114db35cc0..f1d0844edc 100644 --- a/modules/axruntime/src/lib.rs +++ b/modules/axruntime/src/lib.rs @@ -186,6 +186,8 @@ pub extern "C" fn rust_main(cpu_id: usize, dtb: usize) -> ! { info!("Primary CPU {} init OK.", cpu_id); INITED_CPUS.fetch_add(1, Ordering::Relaxed); + ctor_bare::call_ctors(); + while !is_init_ok() { core::hint::spin_loop(); } diff --git a/modules/axtask/src/run_queue.rs b/modules/axtask/src/run_queue.rs index 0c5873ca73..2e68bc1559 100644 --- a/modules/axtask/src/run_queue.rs +++ b/modules/axtask/src/run_queue.rs @@ -613,13 +613,14 @@ pub(crate) fn migrate_entry(migrated_task: AxTaskRef) { /// Clear the `on_cpu` field of previous task running on this CPU. #[cfg(feature = "smp")] pub(crate) unsafe fn clear_prev_task_on_cpu() { - PREV_TASK - .current_ref_raw() - .upgrade() - .expect("Invalid prev_task pointer or prev_task has been dropped") - .set_on_cpu(false); + unsafe { + PREV_TASK + .current_ref_raw() + .upgrade() + .expect("Invalid prev_task pointer or prev_task has been dropped") + .set_on_cpu(false); + } } - pub(crate) fn init() { let cpu_id = this_cpu_id(); diff --git a/modules/axtask/src/task.rs b/modules/axtask/src/task.rs index 2b9a5bfd8c..dfb2a844c0 100644 --- a/modules/axtask/src/task.rs +++ b/modules/axtask/src/task.rs @@ -498,14 +498,18 @@ impl CurrentTask { #[cfg(feature = "tls")] axhal::arch::write_thread_pointer(init_task.tls.tls_ptr() as usize); let ptr = Arc::into_raw(init_task); - axhal::cpu::set_current_task_ptr(ptr); + unsafe { + axhal::cpu::set_current_task_ptr(ptr); + } } pub(crate) unsafe fn set_current(prev: Self, next: AxTaskRef) { let Self(arc) = prev; ManuallyDrop::into_inner(arc); // `call Arc::drop()` to decrease prev task reference count. let ptr = Arc::into_raw(next); - axhal::cpu::set_current_task_ptr(ptr); + unsafe { + axhal::cpu::set_current_task_ptr(ptr); + } } } From 5723dce1e08b7b24fc49e637ba72e14f4c9ab577 Mon Sep 17 00:00:00 2001 From: Youjie Zheng Date: Sat, 8 Feb 2025 00:05:43 +0800 Subject: [PATCH 12/17] [style] simplify ns init for unittest --- modules/axfs/tests/test_fatfs.rs | 24 ++++++------------------ modules/axfs/tests/test_ramfs.rs | 24 ++++++------------------ 2 files changed, 12 insertions(+), 36 deletions(-) diff --git a/modules/axfs/tests/test_fatfs.rs b/modules/axfs/tests/test_fatfs.rs index f81c044b82..34014b0e35 100644 --- a/modules/axfs/tests/test_fatfs.rs +++ b/modules/axfs/tests/test_fatfs.rs @@ -4,6 +4,7 @@ mod test_common; use axdriver::AxDeviceContainer; use axdriver_block::ramdisk::RamDisk; +use axns::{AxNamespace, AxNamespaceIf}; const IMG_PATH: &str = "resources/fat16.img"; @@ -15,23 +16,11 @@ fn make_disk() -> std::io::Result { Ok(RamDisk::from(&data)) } -mod axns_imp { - use axns::{AxNamespace, AxNamespaceIf}; - use lazyinit::LazyInit; - thread_local! { - static NS: LazyInit = LazyInit::new(); - } - struct AxNamespaceImpl; - #[crate_interface::impl_interface] - impl AxNamespaceIf for AxNamespaceImpl { - fn current_namespace_base() -> *mut u8 { - NS.with(|ns| ns.base()) - } - } - pub(crate) fn thread_init_namespace() { - NS.with(|ns| { - ns.init_once(AxNamespace::global()); - }); +struct AxNamespaceImpl; +#[crate_interface::impl_interface] +impl AxNamespaceIf for AxNamespaceImpl { + fn current_namespace_base() -> *mut u8 { + AxNamespace::global().base() } } @@ -39,7 +28,6 @@ mod axns_imp { fn test_fatfs() { println!("Testing fatfs with ramdisk ..."); - axns_imp::thread_init_namespace(); let disk = make_disk().expect("failed to load disk image"); axtask::init_scheduler(); // call this to use `axsync::Mutex`. axfs::init_filesystems(AxDeviceContainer::from_one(disk)); diff --git a/modules/axfs/tests/test_ramfs.rs b/modules/axfs/tests/test_ramfs.rs index f41ac00ba8..3efdb57866 100644 --- a/modules/axfs/tests/test_ramfs.rs +++ b/modules/axfs/tests/test_ramfs.rs @@ -11,6 +11,7 @@ use axfs::fops::{Disk, MyFileSystemIf}; use axfs_ramfs::RamFileSystem; use axfs_vfs::VfsOps; use axio::{Result, Write}; +use axns::{AxNamespace, AxNamespaceIf}; struct MyFileSystemIfImpl; @@ -41,23 +42,11 @@ fn create_init_files() -> Result<()> { Ok(()) } -mod axns_imp { - use axns::{AxNamespace, AxNamespaceIf}; - use lazyinit::LazyInit; - thread_local! { - static NS: LazyInit = LazyInit::new(); - } - struct AxNamespaceImpl; - #[crate_interface::impl_interface] - impl AxNamespaceIf for AxNamespaceImpl { - fn current_namespace_base() -> *mut u8 { - NS.with(|ns| ns.base()) - } - } - pub(crate) fn thread_init_namespace() { - NS.with(|ns| { - ns.init_once(AxNamespace::global()); - }); +struct AxNamespaceImpl; +#[crate_interface::impl_interface] +impl AxNamespaceIf for AxNamespaceImpl { + fn current_namespace_base() -> *mut u8 { + AxNamespace::global().base() } } @@ -65,7 +54,6 @@ mod axns_imp { fn test_ramfs() { println!("Testing ramfs ..."); - axns_imp::thread_init_namespace(); axtask::init_scheduler(); // call this to use `axsync::Mutex`. axfs::init_filesystems(AxDeviceContainer::from_one(RamDisk::default())); // dummy disk, actually not used. From aa4c2a6d31a7b56a056147fd932699598c05be0c Mon Sep 17 00:00:00 2001 From: Youjie Zheng Date: Sat, 8 Feb 2025 00:09:27 +0800 Subject: [PATCH 13/17] [style] rename AxResource as ResArc --- api/arceos_posix_api/src/imp/fd_ops.rs | 23 +++++++++++++++++++++-- api/arceos_posix_api/src/imp/mod.rs | 19 ------------------- modules/axfs/src/root.rs | 6 +++--- modules/axns/src/lib.rs | 16 ++++++++-------- modules/axns/tests/test_global.rs | 6 +++--- modules/axns/tests/test_thread_local.rs | 6 +++--- modules/axruntime/src/lib.rs | 4 ++-- 7 files changed, 40 insertions(+), 40 deletions(-) diff --git a/api/arceos_posix_api/src/imp/fd_ops.rs b/api/arceos_posix_api/src/imp/fd_ops.rs index 1b75583b04..cabfd27791 100644 --- a/api/arceos_posix_api/src/imp/fd_ops.rs +++ b/api/arceos_posix_api/src/imp/fd_ops.rs @@ -3,7 +3,7 @@ use core::ffi::c_int; use axerrno::{LinuxError, LinuxResult}; use axio::PollState; -use axns::{AxResource, def_resource}; +use axns::{ResArc, def_resource}; use flatten_objects::FlattenObjects; use spin::RwLock; @@ -23,7 +23,7 @@ pub trait FileLike: Send + Sync { def_resource! { #[allow(non_camel_case_types)] - pub(crate) static FD_TABLE: AxResource, AX_FILE_LIMIT>>> = AxResource::new(); + pub(crate) static FD_TABLE: ResArc, AX_FILE_LIMIT>>> = ResArc::new(); } pub fn get_file_like(fd: c_int) -> LinuxResult> { @@ -122,3 +122,22 @@ pub fn sys_fcntl(fd: c_int, cmd: c_int, arg: usize) -> c_int { } }) } + +#[ctor_bare::register_ctor] +#[cfg(feature = "fd")] +fn init_stdio() { + use crate::imp::fd_ops::FD_TABLE; + use crate::imp::stdio::{stdin, stdout}; + use alloc::sync::Arc; + let mut fd_table = flatten_objects::FlattenObjects::new(); + fd_table + .add_at(0, Arc::new(stdin()) as _) + .unwrap_or_else(|_| panic!()); // stdin + fd_table + .add_at(1, Arc::new(stdout()) as _) + .unwrap_or_else(|_| panic!()); // stdout + fd_table + .add_at(2, Arc::new(stdout()) as _) + .unwrap_or_else(|_| panic!()); // stderr + FD_TABLE.init_new(spin::RwLock::new(fd_table)); +} diff --git a/api/arceos_posix_api/src/imp/mod.rs b/api/arceos_posix_api/src/imp/mod.rs index 0202f5c0e1..603f934baa 100644 --- a/api/arceos_posix_api/src/imp/mod.rs +++ b/api/arceos_posix_api/src/imp/mod.rs @@ -18,22 +18,3 @@ pub mod net; pub mod pipe; #[cfg(feature = "multitask")] pub mod pthread; - -#[ctor_bare::register_ctor] -#[cfg(feature = "fd")] -fn init_stdio() { - use crate::imp::fd_ops::FD_TABLE; - use alloc::sync::Arc; - use stdio::{stdin, stdout}; - let mut fd_table = flatten_objects::FlattenObjects::new(); - fd_table - .add_at(0, Arc::new(stdin()) as _) - .unwrap_or_else(|_| panic!()); // stdin - fd_table - .add_at(1, Arc::new(stdout()) as _) - .unwrap_or_else(|_| panic!()); // stdout - fd_table - .add_at(2, Arc::new(stdout()) as _) - .unwrap_or_else(|_| panic!()); // stderr - FD_TABLE.init_new(spin::RwLock::new(fd_table)); -} diff --git a/modules/axfs/src/root.rs b/modules/axfs/src/root.rs index 4bcebb5307..8dda269cd4 100644 --- a/modules/axfs/src/root.rs +++ b/modules/axfs/src/root.rs @@ -5,7 +5,7 @@ use alloc::{string::String, sync::Arc, vec::Vec}; use axerrno::{AxError, AxResult, ax_err}; use axfs_vfs::{VfsNodeAttr, VfsNodeOps, VfsNodeRef, VfsNodeType, VfsOps, VfsResult}; -use axns::{AxResource, def_resource}; +use axns::{ResArc, def_resource}; use axsync::Mutex; use lazyinit::LazyInit; @@ -13,9 +13,9 @@ use crate::{api::FileType, fs, mounts}; def_resource! { #[allow(non_camel_case_types)] - static CURRENT_DIR_PATH: AxResource> = AxResource::new(); + static CURRENT_DIR_PATH: ResArc> = ResArc::new(); #[allow(non_camel_case_types)] - static CURRENT_DIR: AxResource> = AxResource::new(); + static CURRENT_DIR: ResArc> = ResArc::new(); } struct MountPoint { diff --git a/modules/axns/src/lib.rs b/modules/axns/src/lib.rs index 641d17b22f..95ce32ea6d 100644 --- a/modules/axns/src/lib.rs +++ b/modules/axns/src/lib.rs @@ -113,9 +113,9 @@ impl Drop for AxNamespace { /// /// It provides methods to lazily initialize the resource of the current thread, /// or to share the resource with other threads. -pub struct AxResource(LazyInit>); +pub struct ResArc(LazyInit>); -impl AxResource { +impl ResArc { /// Creates a new uninitialized resource. pub const fn new() -> Self { Self(LazyInit::new()) @@ -142,7 +142,7 @@ impl AxResource { } } -impl Deref for AxResource { +impl Deref for ResArc { type Target = T; fn deref(&self) -> &Self::Target { @@ -150,7 +150,7 @@ impl Deref for AxResource { } } -impl fmt::Debug for AxResource { +impl fmt::Debug for ResArc { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { self.0.fmt(f) } @@ -195,11 +195,11 @@ pub unsafe fn current_namespace_base() -> *mut u8 { /// # Example /// /// ``` -/// use axns::AxResource; +/// use axns::ResArc; /// /// axns::def_resource! { /// static FOO: u32 = 42; -/// static BAR: AxResource = AxResource::new(); +/// static BAR: ResArc = ResArc::new(); /// } /// /// BAR.init_new("hello world".to_string()); @@ -209,10 +209,10 @@ pub unsafe fn current_namespace_base() -> *mut u8 { /// mod imp { /// use axns::{AxNamespace, AxNamespaceIf}; /// -/// struct AxResourceImpl; +/// struct ResArcImpl; /// /// #[crate_interface::impl_interface] -/// impl AxNamespaceIf for AxResourceImpl { +/// impl AxNamespaceIf for ResArcImpl { /// fn current_namespace_base() -> *mut u8 { /// AxNamespace::global().base() /// } diff --git a/modules/axns/tests/test_global.rs b/modules/axns/tests/test_global.rs index 020341d8c8..00aaf4e430 100644 --- a/modules/axns/tests/test_global.rs +++ b/modules/axns/tests/test_global.rs @@ -2,13 +2,13 @@ use std::sync::atomic::{AtomicUsize, Ordering}; use std::sync::{Barrier, Mutex}; use std::thread; -use axns::{AxResource, def_resource}; +use axns::{ResArc, def_resource}; use self::imp::thread_init_namespace; def_resource! { - static FOO: AxResource = AxResource::new(); - static BAR: AxResource> = AxResource::new(); + static FOO: ResArc = ResArc::new(); + static BAR: ResArc> = ResArc::new(); } static BARRIER: Barrier = Barrier::new(3); diff --git a/modules/axns/tests/test_thread_local.rs b/modules/axns/tests/test_thread_local.rs index dc4a99901c..0b8eff465b 100644 --- a/modules/axns/tests/test_thread_local.rs +++ b/modules/axns/tests/test_thread_local.rs @@ -4,13 +4,13 @@ use std::sync::atomic::{AtomicUsize, Ordering}; use std::sync::{Barrier, Mutex}; use std::thread; -use axns::{AxResource, def_resource}; +use axns::{ResArc, def_resource}; use self::imp::thread_init_namespace; def_resource! { - static FOO: AxResource = AxResource::new(); - static BAR: AxResource> = AxResource::new(); + static FOO: ResArc = ResArc::new(); + static BAR: ResArc> = ResArc::new(); } static BARRIER: Barrier = Barrier::new(3); diff --git a/modules/axruntime/src/lib.rs b/modules/axruntime/src/lib.rs index f1d0844edc..1261f95116 100644 --- a/modules/axruntime/src/lib.rs +++ b/modules/axruntime/src/lib.rs @@ -183,11 +183,11 @@ pub extern "C" fn rust_main(cpu_id: usize, dtb: usize) -> ! { init_tls(); } + ctor_bare::call_ctors(); + info!("Primary CPU {} init OK.", cpu_id); INITED_CPUS.fetch_add(1, Ordering::Relaxed); - ctor_bare::call_ctors(); - while !is_init_ok() { core::hint::spin_loop(); } From 1832eade68b6ef2fe198ab1d7e1886078c764ba8 Mon Sep 17 00:00:00 2001 From: Youjie Zheng Date: Sun, 9 Feb 2025 17:35:21 +0800 Subject: [PATCH 14/17] [fix] simplify feature of uspace --- api/arceos_posix_api/Cargo.toml | 2 +- modules/axfs/Cargo.toml | 1 - modules/axhal/linker.lds.S | 2 -- modules/axhal/src/mem.rs | 9 +++++---- 4 files changed, 6 insertions(+), 8 deletions(-) diff --git a/api/arceos_posix_api/Cargo.toml b/api/arceos_posix_api/Cargo.toml index a3dd531176..2794bda372 100644 --- a/api/arceos_posix_api/Cargo.toml +++ b/api/arceos_posix_api/Cargo.toml @@ -29,7 +29,7 @@ net = ["dep:axnet", "axfeat/net", "fd"] pipe = ["fd"] select = ["fd"] epoll = ["fd"] -uspace = ["axns/thread-local", "axfs/thread-local", "smp", "irq", "fs", "multitask", "net", "pipe", "select", "epoll"] +uspace = ["axns/thread-local"] [dependencies] # ArceOS modules diff --git a/modules/axfs/Cargo.toml b/modules/axfs/Cargo.toml index b0c05346cf..5db182dc47 100644 --- a/modules/axfs/Cargo.toml +++ b/modules/axfs/Cargo.toml @@ -10,7 +10,6 @@ repository = "https://github.com/arceos-org/arceos/tree/main/modules/axfs" documentation = "https://arceos-org.github.io/arceos/axfs/index.html" [features] -thread-local = ["axns/thread-local"] devfs = ["dep:axfs_devfs"] ramfs = ["dep:axfs_ramfs"] procfs = ["dep:axfs_ramfs"] diff --git a/modules/axhal/linker.lds.S b/modules/axhal/linker.lds.S index ba3e7d2c3d..bc9422096e 100644 --- a/modules/axhal/linker.lds.S +++ b/modules/axhal/linker.lds.S @@ -26,12 +26,10 @@ SECTIONS } .init_array : ALIGN(4K) { - _sinit_array = .; __init_array_start = .; *(.init_array .init_array.*) __init_array_end = .; . = ALIGN(4K); - _einit_array = .; } .data : ALIGN(4K) { diff --git a/modules/axhal/src/mem.rs b/modules/axhal/src/mem.rs index 4a30464eb7..37a27cc239 100644 --- a/modules/axhal/src/mem.rs +++ b/modules/axhal/src/mem.rs @@ -89,8 +89,9 @@ fn kernel_image_regions() -> impl Iterator { name: ".rodata", }, MemRegion { - paddr: virt_to_phys((_sinit_array as usize).into()), - size: _einit_array as usize - _sinit_array as usize, + paddr: virt_to_phys((__init_array_start as usize).into()).align_down_4k(), + size: VirtAddr::from_usize(__init_array_end as usize).align_up_4k() + - VirtAddr::from_usize(__init_array_start as usize).align_down_4k(), flags: MemRegionFlags::RESERVED | MemRegionFlags::READ, name: ".init_array", }, @@ -157,8 +158,8 @@ unsafe extern "C" { fn _etext(); fn _srodata(); fn _erodata(); - fn _sinit_array(); - fn _einit_array(); + fn __init_array_start(); + fn __init_array_end(); fn _sdata(); fn _edata(); fn _sbss(); From 3cfc4acf02a3a0c46d93adc1f51a187c4615baac Mon Sep 17 00:00:00 2001 From: Youjie Zheng Date: Mon, 10 Feb 2025 01:47:03 +0800 Subject: [PATCH 15/17] [fix] Adjust init_array to the range of sdata-edata --- api/arceos_posix_api/src/imp/fd_ops.rs | 1 - modules/axhal/linker.lds.S | 14 +-- modules/axhal/src/arch/riscv/context.rs | 33 +++--- modules/axhal/src/arch/riscv/macros.rs | 106 +++++++++++++----- modules/axhal/src/arch/riscv/trap.S | 39 ------- modules/axhal/src/arch/riscv/trap.rs | 2 +- modules/axhal/src/arch/x86_64/context.rs | 73 ++++++------ modules/axhal/src/mem.rs | 9 -- .../src/platform/aarch64_bsta1000b/mod.rs | 1 - .../src/platform/aarch64_phytium_pi/mod.rs | 3 - .../axhal/src/platform/aarch64_raspi/mod.rs | 3 - modules/axmm/src/lib.rs | 3 - modules/axns/src/lib.rs | 2 +- 13 files changed, 142 insertions(+), 147 deletions(-) diff --git a/api/arceos_posix_api/src/imp/fd_ops.rs b/api/arceos_posix_api/src/imp/fd_ops.rs index cabfd27791..1158629968 100644 --- a/api/arceos_posix_api/src/imp/fd_ops.rs +++ b/api/arceos_posix_api/src/imp/fd_ops.rs @@ -22,7 +22,6 @@ pub trait FileLike: Send + Sync { } def_resource! { - #[allow(non_camel_case_types)] pub(crate) static FD_TABLE: ResArc, AX_FILE_LIMIT>>> = ResArc::new(); } diff --git a/modules/axhal/linker.lds.S b/modules/axhal/linker.lds.S index bc9422096e..12b9047535 100644 --- a/modules/axhal/linker.lds.S +++ b/modules/axhal/linker.lds.S @@ -24,13 +24,6 @@ SECTIONS . = ALIGN(4K); _erodata = .; } - - .init_array : ALIGN(4K) { - __init_array_start = .; - *(.init_array .init_array.*) - __init_array_end = .; - . = ALIGN(4K); - } .data : ALIGN(4K) { _sdata = .; @@ -47,6 +40,13 @@ SECTIONS _etdata = .; } + .init_array : ALIGN(4K) { + __init_array_start = .; + *(.init_array .init_array.*) + __init_array_end = .; + . = ALIGN(4K); + } + .tbss : ALIGN(0x10) { _stbss = .; *(.tbss .tbss.*) diff --git a/modules/axhal/src/arch/riscv/context.rs b/modules/axhal/src/arch/riscv/context.rs index 48ed8d6e6c..d3e4e6faea 100644 --- a/modules/axhal/src/arch/riscv/context.rs +++ b/modules/axhal/src/arch/riscv/context.rs @@ -150,7 +150,6 @@ impl UspaceContext { /// # Safety /// /// This function is unsafe because it changes processor mode and the stack. - #[inline(never)] #[unsafe(no_mangle)] pub unsafe fn enter_uspace(&self, kstack_top: VirtAddr) -> ! { use riscv::register::{sepc, sscratch}; @@ -161,20 +160,22 @@ impl UspaceContext { // Address of the top of the kernel stack after saving the trap frame. let kernel_trap_addr = kstack_top.as_usize() - core::mem::size_of::(); unsafe { - core::arch::asm!(" - mv sp, {tf} - - STR gp, {kernel_trap_addr}, 2 - LDR gp, sp, 2 - - STR tp, {kernel_trap_addr}, 3 - LDR tp, sp, 3 - - LDR t0, sp, 32 - csrw sstatus, t0 - POP_GENERAL_REGS - LDR sp, sp, 1 - sret", + core::arch::asm!( + include_asm_macros!(), + " + mv sp, {tf} + + STR gp, {kernel_trap_addr}, 2 + LDR gp, sp, 2 + + STR tp, {kernel_trap_addr}, 3 + LDR tp, sp, 3 + + LDR t0, sp, 32 + csrw sstatus, t0 + POP_GENERAL_REGS + LDR sp, sp, 1 + sret", tf = in(reg) &(self.0), kernel_trap_addr = in(reg) kernel_trap_addr, options(noreturn), @@ -283,7 +284,7 @@ impl TaskContext { #[naked] unsafe extern "C" fn context_switch(_current_task: &mut TaskContext, _next_task: &TaskContext) { naked_asm!( - include_asm_marcos!(), + include_asm_macros!(), " // save old context (callee-saved registers) STR ra, a0, 0 diff --git a/modules/axhal/src/arch/riscv/macros.rs b/modules/axhal/src/arch/riscv/macros.rs index a793036774..eebcf6829d 100644 --- a/modules/axhal/src/arch/riscv/macros.rs +++ b/modules/axhal/src/arch/riscv/macros.rs @@ -1,35 +1,87 @@ #[cfg(target_arch = "riscv32")] -macro_rules! include_asm_marcos { - () => { - r" - .ifndef XLENB - .equ XLENB, 4 - - .macro LDR rd, rs, off - lw \rd, \off*XLENB(\rs) - .endm - .macro STR rs2, rs1, off - sw \rs2, \off*XLENB(\rs1) - .endm - - .endif" - }; -} +macro_rules! __asm_macros { + () => { + r" + .ifndef XLENB + .equ XLENB, 4 + + .macro LDR rd, rs, off + lw \rd, \off*XLENB(\rs) + .endm + .macro STR rs2, rs1, off + sw \rs2, \off*XLENB(\rs1) + .endm + + .endif" + }; + } #[cfg(target_arch = "riscv64")] -macro_rules! include_asm_marcos { +macro_rules! __asm_macros { + () => { + r" + .ifndef XLENB + .equ XLENB, 8 + + .macro LDR rd, rs, off + ld \rd, \off*XLENB(\rs) + .endm + .macro STR rs2, rs1, off + sd \rs2, \off*XLENB(\rs1) + .endm + + .endif" + }; + } + +macro_rules! include_asm_macros { () => { - r" - .ifndef XLENB - .equ XLENB, 8 + concat!( + __asm_macros!(), + r" + .ifndef REGS_MACROS_FLAG + .equ REGS_MACROS_FLAG, 1 + + .macro PUSH_POP_GENERAL_REGS, op + \op ra, sp, 0 + \op t0, sp, 4 + \op t1, sp, 5 + \op t2, sp, 6 + \op s0, sp, 7 + \op s1, sp, 8 + \op a0, sp, 9 + \op a1, sp, 10 + \op a2, sp, 11 + \op a3, sp, 12 + \op a4, sp, 13 + \op a5, sp, 14 + \op a6, sp, 15 + \op a7, sp, 16 + \op s2, sp, 17 + \op s3, sp, 18 + \op s4, sp, 19 + \op s5, sp, 20 + \op s6, sp, 21 + \op s7, sp, 22 + \op s8, sp, 23 + \op s9, sp, 24 + \op s10, sp, 25 + \op s11, sp, 26 + \op t3, sp, 27 + \op t4, sp, 28 + \op t5, sp, 29 + \op t6, sp, 30 + .endm + + .macro PUSH_GENERAL_REGS + PUSH_POP_GENERAL_REGS STR + .endm - .macro LDR rd, rs, off - ld \rd, \off*XLENB(\rs) - .endm - .macro STR rs2, rs1, off - sd \rs2, \off*XLENB(\rs1) - .endm + .macro POP_GENERAL_REGS + PUSH_POP_GENERAL_REGS LDR + .endm - .endif" + .endif" + ) }; } diff --git a/modules/axhal/src/arch/riscv/trap.S b/modules/axhal/src/arch/riscv/trap.S index c42780a6f7..8a594b8ada 100644 --- a/modules/axhal/src/arch/riscv/trap.S +++ b/modules/axhal/src/arch/riscv/trap.S @@ -1,42 +1,3 @@ -.macro PUSH_POP_GENERAL_REGS, op - \op ra, sp, 0 - \op t0, sp, 4 - \op t1, sp, 5 - \op t2, sp, 6 - \op s0, sp, 7 - \op s1, sp, 8 - \op a0, sp, 9 - \op a1, sp, 10 - \op a2, sp, 11 - \op a3, sp, 12 - \op a4, sp, 13 - \op a5, sp, 14 - \op a6, sp, 15 - \op a7, sp, 16 - \op s2, sp, 17 - \op s3, sp, 18 - \op s4, sp, 19 - \op s5, sp, 20 - \op s6, sp, 21 - \op s7, sp, 22 - \op s8, sp, 23 - \op s9, sp, 24 - \op s10, sp, 25 - \op s11, sp, 26 - \op t3, sp, 27 - \op t4, sp, 28 - \op t5, sp, 29 - \op t6, sp, 30 -.endm - -.macro PUSH_GENERAL_REGS - PUSH_POP_GENERAL_REGS STR -.endm - -.macro POP_GENERAL_REGS - PUSH_POP_GENERAL_REGS LDR -.endm - .macro SAVE_REGS, from_user addi sp, sp, -{trapframe_size} PUSH_GENERAL_REGS diff --git a/modules/axhal/src/arch/riscv/trap.rs b/modules/axhal/src/arch/riscv/trap.rs index 3867f778d4..febf88e572 100644 --- a/modules/axhal/src/arch/riscv/trap.rs +++ b/modules/axhal/src/arch/riscv/trap.rs @@ -6,7 +6,7 @@ use riscv::register::{scause, stval}; use super::TrapFrame; core::arch::global_asm!( - include_asm_marcos!(), + include_asm_macros!(), include_str!("trap.S"), trapframe_size = const core::mem::size_of::(), ); diff --git a/modules/axhal/src/arch/x86_64/context.rs b/modules/axhal/src/arch/x86_64/context.rs index 01f0271665..62861e43d6 100644 --- a/modules/axhal/src/arch/x86_64/context.rs +++ b/modules/axhal/src/arch/x86_64/context.rs @@ -152,25 +152,25 @@ impl UspaceContext { assert_eq!(super::tss_get_rsp0(), kstack_top); unsafe { core::arch::asm!(" - mov rsp, {tf} - pop rax - pop rcx - pop rdx - pop rbx - pop rbp - pop rsi - pop rdi - pop r8 - pop r9 - pop r10 - pop r11 - pop r12 - pop r13 - pop r14 - pop r15 - add rsp, 16 // skip vector, error_code - swapgs - iretq", + mov rsp, {tf} + pop rax + pop rcx + pop rdx + pop rbx + pop rbp + pop rsi + pop rdi + pop r8 + pop r9 + pop r10 + pop r11 + pop r12 + pop r13 + pop r14 + pop r15 + add rsp, 16 // skip vector, error_code + swapgs + iretq", tf = in(reg) &self.0, options(noreturn), ) @@ -371,23 +371,24 @@ impl TaskContext { unsafe extern "C" fn context_switch(_current_stack: &mut u64, _next_stack: &u64) { unsafe { naked_asm!( - ".code64 - push rbp - push rbx - push r12 - push r13 - push r14 - push r15 - mov [rdi], rsp - - mov rsp, [rsi] - pop r15 - pop r14 - pop r13 - pop r12 - pop rbx - pop rbp - ret", + " + .code64 + push rbp + push rbx + push r12 + push r13 + push r14 + push r15 + mov [rdi], rsp + + mov rsp, [rsi] + pop r15 + pop r14 + pop r13 + pop r12 + pop rbx + pop rbp + ret", ) } } diff --git a/modules/axhal/src/mem.rs b/modules/axhal/src/mem.rs index 37a27cc239..e4e15747d3 100644 --- a/modules/axhal/src/mem.rs +++ b/modules/axhal/src/mem.rs @@ -88,13 +88,6 @@ fn kernel_image_regions() -> impl Iterator { flags: MemRegionFlags::RESERVED | MemRegionFlags::READ, name: ".rodata", }, - MemRegion { - paddr: virt_to_phys((__init_array_start as usize).into()).align_down_4k(), - size: VirtAddr::from_usize(__init_array_end as usize).align_up_4k() - - VirtAddr::from_usize(__init_array_start as usize).align_down_4k(), - flags: MemRegionFlags::RESERVED | MemRegionFlags::READ, - name: ".init_array", - }, MemRegion { paddr: virt_to_phys((_sdata as usize).into()), size: _edata as usize - _sdata as usize, @@ -158,8 +151,6 @@ unsafe extern "C" { fn _etext(); fn _srodata(); fn _erodata(); - fn __init_array_start(); - fn __init_array_end(); fn _sdata(); fn _edata(); fn _sbss(); diff --git a/modules/axhal/src/platform/aarch64_bsta1000b/mod.rs b/modules/axhal/src/platform/aarch64_bsta1000b/mod.rs index a50b2d5550..6dddea80b7 100644 --- a/modules/axhal/src/platform/aarch64_bsta1000b/mod.rs +++ b/modules/axhal/src/platform/aarch64_bsta1000b/mod.rs @@ -20,7 +20,6 @@ pub mod time { } unsafe extern "C" { - fn exception_vector_base(); fn rust_main(cpu_id: usize, dtb: usize); #[cfg(feature = "smp")] fn rust_main_secondary(cpu_id: usize); diff --git a/modules/axhal/src/platform/aarch64_phytium_pi/mod.rs b/modules/axhal/src/platform/aarch64_phytium_pi/mod.rs index 546b703345..73199fb121 100644 --- a/modules/axhal/src/platform/aarch64_phytium_pi/mod.rs +++ b/modules/axhal/src/platform/aarch64_phytium_pi/mod.rs @@ -26,7 +26,6 @@ pub mod misc { } unsafe extern "C" { - fn exception_vector_base(); fn rust_main(cpu_id: usize, dtb: usize); #[cfg(feature = "smp")] fn rust_main_secondary(cpu_id: usize); @@ -35,7 +34,6 @@ unsafe extern "C" { pub(crate) unsafe extern "C" fn rust_entry(cpu_id: usize, dtb: usize) { crate::mem::clear_bss(); let cpu_id = cpu_hard_id_to_logic_id(cpu_id); - crate::arch::set_exception_vector_base(exception_vector_base as usize); crate::arch::write_page_table_root0(0.into()); // disable low address access crate::cpu::init_primary(cpu_id); super::aarch64_common::pl011::init_early(); @@ -46,7 +44,6 @@ pub(crate) unsafe extern "C" fn rust_entry(cpu_id: usize, dtb: usize) { #[cfg(feature = "smp")] pub(crate) unsafe extern "C" fn rust_entry_secondary(cpu_id: usize) { let cpu_id = cpu_hard_id_to_logic_id(cpu_id); - crate::arch::set_exception_vector_base(exception_vector_base as usize); crate::arch::write_page_table_root0(0.into()); // disable low address access crate::cpu::init_secondary(cpu_id); rust_main_secondary(cpu_id); diff --git a/modules/axhal/src/platform/aarch64_raspi/mod.rs b/modules/axhal/src/platform/aarch64_raspi/mod.rs index 2357eb16ad..f6f4113e1e 100644 --- a/modules/axhal/src/platform/aarch64_raspi/mod.rs +++ b/modules/axhal/src/platform/aarch64_raspi/mod.rs @@ -26,7 +26,6 @@ pub mod misc { } unsafe extern "C" { - fn exception_vector_base(); fn rust_main(cpu_id: usize, dtb: usize); #[cfg(feature = "smp")] fn rust_main_secondary(cpu_id: usize); @@ -34,7 +33,6 @@ unsafe extern "C" { pub(crate) unsafe extern "C" fn rust_entry(cpu_id: usize, dtb: usize) { crate::mem::clear_bss(); - crate::arch::set_exception_vector_base(exception_vector_base as usize); crate::cpu::init_primary(cpu_id); super::aarch64_common::pl011::init_early(); super::aarch64_common::generic_timer::init_early(); @@ -43,7 +41,6 @@ pub(crate) unsafe extern "C" fn rust_entry(cpu_id: usize, dtb: usize) { #[cfg(feature = "smp")] pub(crate) unsafe extern "C" fn rust_entry_secondary(cpu_id: usize) { - crate::arch::set_exception_vector_base(exception_vector_base as usize); crate::cpu::init_secondary(cpu_id); rust_main_secondary(cpu_id); } diff --git a/modules/axmm/src/lib.rs b/modules/axmm/src/lib.rs index e94858035c..d8e2668b37 100644 --- a/modules/axmm/src/lib.rs +++ b/modules/axmm/src/lib.rs @@ -37,9 +37,6 @@ pub fn new_kernel_aspace() -> AxResult { axconfig::plat::KERNEL_ASPACE_SIZE, )?; for r in axhal::mem::memory_regions() { - if r.size == 0 && r.name == ".init_array" { - continue; - } aspace.map_linear(phys_to_virt(r.paddr), r.paddr, r.size, r.flags.into())?; } Ok(aspace) diff --git a/modules/axns/src/lib.rs b/modules/axns/src/lib.rs index 95ce32ea6d..b3ae46fbc3 100644 --- a/modules/axns/src/lib.rs +++ b/modules/axns/src/lib.rs @@ -223,7 +223,7 @@ pub unsafe fn current_namespace_base() -> *mut u8 { macro_rules! def_resource { ( $( $(#[$attr:meta])* $vis:vis static $name:ident: $ty:ty = $default:expr; )+ ) => { $( - $(#[$attr])* + #[allow(non_camel_case_types)] $vis struct $name { __value: () } impl $name { From 2f5c368e2b6f05d370a7e164caf7a3c69743b21a Mon Sep 17 00:00:00 2001 From: Youjie Zheng Date: Tue, 11 Feb 2025 21:02:04 +0800 Subject: [PATCH 16/17] [chore] put .init_array with rodata sections --- api/arceos_posix_api/src/imp/fd_ops.rs | 5 +-- api/arceos_posix_api/src/imp/stdio.rs | 4 --- modules/axfs/Cargo.toml | 2 +- modules/axfs/src/root.rs | 2 -- modules/axhal/linker.lds.S | 20 +++++------ modules/axhal/src/arch/aarch64/context.rs | 44 +++++++++++------------ modules/axns/src/lib.rs | 1 + 7 files changed, 35 insertions(+), 43 deletions(-) diff --git a/api/arceos_posix_api/src/imp/fd_ops.rs b/api/arceos_posix_api/src/imp/fd_ops.rs index 1158629968..7274b4cb13 100644 --- a/api/arceos_posix_api/src/imp/fd_ops.rs +++ b/api/arceos_posix_api/src/imp/fd_ops.rs @@ -8,6 +8,7 @@ use flatten_objects::FlattenObjects; use spin::RwLock; use crate::ctypes; +use crate::imp::stdio::{stdin, stdout}; pub const AX_FILE_LIMIT: usize = 1024; @@ -123,11 +124,7 @@ pub fn sys_fcntl(fd: c_int, cmd: c_int, arg: usize) -> c_int { } #[ctor_bare::register_ctor] -#[cfg(feature = "fd")] fn init_stdio() { - use crate::imp::fd_ops::FD_TABLE; - use crate::imp::stdio::{stdin, stdout}; - use alloc::sync::Arc; let mut fd_table = flatten_objects::FlattenObjects::new(); fd_table .add_at(0, Arc::new(stdin()) as _) diff --git a/api/arceos_posix_api/src/imp/stdio.rs b/api/arceos_posix_api/src/imp/stdio.rs index 46ba0c2a26..4087004f00 100644 --- a/api/arceos_posix_api/src/imp/stdio.rs +++ b/api/arceos_posix_api/src/imp/stdio.rs @@ -16,10 +16,6 @@ fn console_read_bytes(buf: &mut [u8]) -> AxResult { } fn console_write_bytes(buf: &[u8]) -> AxResult { - #[cfg(feature = "uspace")] - // We need to copy the buffer to the kernel space because SBI can't access user space. - axhal::console::write_bytes(buf.to_vec().as_slice()); - #[cfg(not(feature = "uspace"))] axhal::console::write_bytes(buf); Ok(buf.len()) } diff --git a/modules/axfs/Cargo.toml b/modules/axfs/Cargo.toml index 5db182dc47..a2efed294a 100644 --- a/modules/axfs/Cargo.toml +++ b/modules/axfs/Cargo.toml @@ -53,5 +53,5 @@ axdriver = { workspace = true, features = ["block", "ramdisk"] } axdriver_block = { git = "https://github.com/arceos-org/axdriver_crates.git", tag = "v0.1.0", features = ["ramdisk"] } axsync = { workspace = true, features = ["multitask"] } axtask = { workspace = true, features = ["test"] } -axns = { workspace = true, features = ["thread-local"] } +axns = { workspace = true, features = [] } crate_interface = "0.1" \ No newline at end of file diff --git a/modules/axfs/src/root.rs b/modules/axfs/src/root.rs index 8dda269cd4..afa3f937a2 100644 --- a/modules/axfs/src/root.rs +++ b/modules/axfs/src/root.rs @@ -12,9 +12,7 @@ use lazyinit::LazyInit; use crate::{api::FileType, fs, mounts}; def_resource! { - #[allow(non_camel_case_types)] static CURRENT_DIR_PATH: ResArc> = ResArc::new(); - #[allow(non_camel_case_types)] static CURRENT_DIR: ResArc> = ResArc::new(); } diff --git a/modules/axhal/linker.lds.S b/modules/axhal/linker.lds.S index 12b9047535..aa3b3c9ca3 100644 --- a/modules/axhal/linker.lds.S +++ b/modules/axhal/linker.lds.S @@ -16,15 +16,22 @@ SECTIONS _etext = .; } + _srodata = .; .rodata : ALIGN(4K) { - _srodata = .; *(.rodata .rodata.*) *(.srodata .srodata.*) *(.sdata2 .sdata2.*) - . = ALIGN(4K); - _erodata = .; } + .init_array : ALIGN(0x10) { + __init_array_start = .; + *(.init_array .init_array.*) + __init_array_end = .; + } + + . = ALIGN(4K); + _erodata = .; + .data : ALIGN(4K) { _sdata = .; *(.data.boot_page_table) @@ -40,13 +47,6 @@ SECTIONS _etdata = .; } - .init_array : ALIGN(4K) { - __init_array_start = .; - *(.init_array .init_array.*) - __init_array_end = .; - . = ALIGN(4K); - } - .tbss : ALIGN(0x10) { _stbss = .; *(.tbss .tbss.*) diff --git a/modules/axhal/src/arch/aarch64/context.rs b/modules/axhal/src/arch/aarch64/context.rs index 4ce640ed1d..ddd39f2fa2 100644 --- a/modules/axhal/src/arch/aarch64/context.rs +++ b/modules/axhal/src/arch/aarch64/context.rs @@ -127,29 +127,29 @@ impl UspaceContext { unsafe { core::arch::asm!( " - mov sp, x1 - ldp x30, x9, [x0, 30 * 8] - ldp x10, x11, [x0, 32 * 8] - msr sp_el0, x9 - msr elr_el1, x10 - msr spsr_el1, x11 + mov sp, x1 + ldp x30, x9, [x0, 30 * 8] + ldp x10, x11, [x0, 32 * 8] + msr sp_el0, x9 + msr elr_el1, x10 + msr spsr_el1, x11 - ldp x28, x29, [x0, 28 * 8] - ldp x26, x27, [x0, 26 * 8] - ldp x24, x25, [x0, 24 * 8] - ldp x22, x23, [x0, 22 * 8] - ldp x20, x21, [x0, 20 * 8] - ldp x18, x19, [x0, 18 * 8] - ldp x16, x17, [x0, 16 * 8] - ldp x14, x15, [x0, 14 * 8] - ldp x12, x13, [x0, 12 * 8] - ldp x10, x11, [x0, 10 * 8] - ldp x8, x9, [x0, 8 * 8] - ldp x6, x7, [x0, 6 * 8] - ldp x4, x5, [x0, 4 * 8] - ldp x2, x3, [x0, 2 * 8] - ldp x0, x1, [x0] - eret", + ldp x28, x29, [x0, 28 * 8] + ldp x26, x27, [x0, 26 * 8] + ldp x24, x25, [x0, 24 * 8] + ldp x22, x23, [x0, 22 * 8] + ldp x20, x21, [x0, 20 * 8] + ldp x18, x19, [x0, 18 * 8] + ldp x16, x17, [x0, 16 * 8] + ldp x14, x15, [x0, 14 * 8] + ldp x12, x13, [x0, 12 * 8] + ldp x10, x11, [x0, 10 * 8] + ldp x8, x9, [x0, 8 * 8] + ldp x6, x7, [x0, 6 * 8] + ldp x4, x5, [x0, 4 * 8] + ldp x2, x3, [x0, 2 * 8] + ldp x0, x1, [x0] + eret", in("x0") &self.0, in("x1") kstack_top.as_usize() , options(noreturn), diff --git a/modules/axns/src/lib.rs b/modules/axns/src/lib.rs index b3ae46fbc3..f1a679c08f 100644 --- a/modules/axns/src/lib.rs +++ b/modules/axns/src/lib.rs @@ -223,6 +223,7 @@ pub unsafe fn current_namespace_base() -> *mut u8 { macro_rules! def_resource { ( $( $(#[$attr:meta])* $vis:vis static $name:ident: $ty:ty = $default:expr; )+ ) => { $( + $(#[$attr])* #[allow(non_camel_case_types)] $vis struct $name { __value: () } From 7e421c8f9778d335c5d7028ca5bf55f9b8c4e560 Mon Sep 17 00:00:00 2001 From: Youjie Zheng Date: Tue, 11 Feb 2025 21:30:05 +0800 Subject: [PATCH 17/17] [bug] error for unit test --- modules/axfs/Cargo.toml | 2 -- modules/axfs/tests/test_fatfs.rs | 9 --------- modules/axfs/tests/test_ramfs.rs | 9 --------- 3 files changed, 20 deletions(-) diff --git a/modules/axfs/Cargo.toml b/modules/axfs/Cargo.toml index a2efed294a..441a923817 100644 --- a/modules/axfs/Cargo.toml +++ b/modules/axfs/Cargo.toml @@ -53,5 +53,3 @@ axdriver = { workspace = true, features = ["block", "ramdisk"] } axdriver_block = { git = "https://github.com/arceos-org/axdriver_crates.git", tag = "v0.1.0", features = ["ramdisk"] } axsync = { workspace = true, features = ["multitask"] } axtask = { workspace = true, features = ["test"] } -axns = { workspace = true, features = [] } -crate_interface = "0.1" \ No newline at end of file diff --git a/modules/axfs/tests/test_fatfs.rs b/modules/axfs/tests/test_fatfs.rs index 34014b0e35..b05950d507 100644 --- a/modules/axfs/tests/test_fatfs.rs +++ b/modules/axfs/tests/test_fatfs.rs @@ -4,7 +4,6 @@ mod test_common; use axdriver::AxDeviceContainer; use axdriver_block::ramdisk::RamDisk; -use axns::{AxNamespace, AxNamespaceIf}; const IMG_PATH: &str = "resources/fat16.img"; @@ -16,14 +15,6 @@ fn make_disk() -> std::io::Result { Ok(RamDisk::from(&data)) } -struct AxNamespaceImpl; -#[crate_interface::impl_interface] -impl AxNamespaceIf for AxNamespaceImpl { - fn current_namespace_base() -> *mut u8 { - AxNamespace::global().base() - } -} - #[test] fn test_fatfs() { println!("Testing fatfs with ramdisk ..."); diff --git a/modules/axfs/tests/test_ramfs.rs b/modules/axfs/tests/test_ramfs.rs index 3efdb57866..914c245bc6 100644 --- a/modules/axfs/tests/test_ramfs.rs +++ b/modules/axfs/tests/test_ramfs.rs @@ -11,7 +11,6 @@ use axfs::fops::{Disk, MyFileSystemIf}; use axfs_ramfs::RamFileSystem; use axfs_vfs::VfsOps; use axio::{Result, Write}; -use axns::{AxNamespace, AxNamespaceIf}; struct MyFileSystemIfImpl; @@ -42,14 +41,6 @@ fn create_init_files() -> Result<()> { Ok(()) } -struct AxNamespaceImpl; -#[crate_interface::impl_interface] -impl AxNamespaceIf for AxNamespaceImpl { - fn current_namespace_base() -> *mut u8 { - AxNamespace::global().base() - } -} - #[test] fn test_ramfs() { println!("Testing ramfs ...");