From 91e7785915fd4ac207070c5946e4bc9bab080fbc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=8E=8B=E8=8B=B1=E6=B3=B0?= <2253457010@qq.com> Date: Tue, 8 Jul 2025 16:45:56 +0800 Subject: [PATCH] Add the cpu section of LoongArch in OSTD and kernel --- kernel/src/arch/loongarch/cpu.rs | 188 +++++++++++++ kernel/src/arch/loongarch/mod.rs | 4 + kernel/src/arch/loongarch/signal.rs | 19 ++ ostd/src/arch/loongarch/cpu/context.rs | 365 +++++++++++++++++++++++++ ostd/src/arch/loongarch/cpu/local.rs | 15 + ostd/src/arch/loongarch/cpu/mod.rs | 6 + 6 files changed, 597 insertions(+) create mode 100644 kernel/src/arch/loongarch/cpu.rs create mode 100644 kernel/src/arch/loongarch/mod.rs create mode 100644 kernel/src/arch/loongarch/signal.rs create mode 100644 ostd/src/arch/loongarch/cpu/context.rs create mode 100644 ostd/src/arch/loongarch/cpu/local.rs create mode 100644 ostd/src/arch/loongarch/cpu/mod.rs diff --git a/kernel/src/arch/loongarch/cpu.rs b/kernel/src/arch/loongarch/cpu.rs new file mode 100644 index 000000000..b7abd4290 --- /dev/null +++ b/kernel/src/arch/loongarch/cpu.rs @@ -0,0 +1,188 @@ +// SPDX-License-Identifier: MPL-2.0 + +use alloc::{format, string::String}; + +use ostd::{ + cpu::context::{CpuExceptionInfo, GeneralRegs, UserContext}, + user::UserContextApi, + Pod, +}; + +use crate::{cpu::LinuxAbi, thread::exception::PageFaultInfo, vm::perms::VmPerms}; + +impl LinuxAbi for UserContext { + fn syscall_num(&self) -> usize { + self.a7() + } + + fn set_syscall_num(&mut self, num: usize) { + self.set_a7(num); + } + + fn syscall_ret(&self) -> usize { + self.a0() + } + + fn set_syscall_ret(&mut self, ret: usize) { + self.set_a0(ret) + } + + fn syscall_args(&self) -> [usize; 6] { + [ + self.a0(), + self.a1(), + self.a2(), + self.a3(), + self.a4(), + self.a5(), + ] + } + + fn set_tls_pointer(&mut self, tls: usize) { + self.set_tp(tls); + } + + fn tls_pointer(&self) -> usize { + self.tp() + } +} + +/// Represents the context of a signal handler. +/// +/// This contains the context saved before a signal handler is invoked and restored by `sys_rt_sigreturn`. +/// +/// Reference: +#[repr(C)] +#[repr(align(16))] +#[derive(Clone, Copy, Debug, Default, Pod)] +pub struct SigContext { + pub pc: usize, + pub zero: usize, + pub ra: usize, + pub tp: usize, + pub sp: usize, + pub a0: usize, + pub a1: usize, + pub a2: usize, + pub a3: usize, + pub a4: usize, + pub a5: usize, + pub a6: usize, + pub a7: usize, + pub t0: usize, + pub t1: usize, + pub t2: usize, + pub t3: usize, + pub t4: usize, + pub t5: usize, + pub t6: usize, + pub t7: usize, + pub t8: usize, + pub r21: usize, + pub fp: usize, + pub s0: usize, + pub s1: usize, + pub s2: usize, + pub s3: usize, + pub s4: usize, + pub s5: usize, + pub s6: usize, + pub s7: usize, + pub s8: usize, + pub flags: u32, + // In LoongArch, the signal stack layout places the FPU context directly + // after the general-purpose registers. + // Reference: +} + +macro_rules! copy_gp_regs { + ($src: ident, $dst: ident) => { + $dst.zero = $src.zero; + $dst.ra = $src.ra; + $dst.tp = $src.tp; + $dst.sp = $src.sp; + $dst.a0 = $src.a0; + $dst.a1 = $src.a1; + $dst.a2 = $src.a2; + $dst.a3 = $src.a3; + $dst.a4 = $src.a4; + $dst.a5 = $src.a5; + $dst.a6 = $src.a6; + $dst.a7 = $src.a7; + $dst.t0 = $src.t0; + $dst.t1 = $src.t1; + $dst.t2 = $src.t2; + $dst.t3 = $src.t3; + $dst.t4 = $src.t4; + $dst.t5 = $src.t5; + $dst.t6 = $src.t6; + $dst.t7 = $src.t7; + $dst.t8 = $src.t8; + $dst.r21 = $src.r21; + $dst.fp = $src.fp; + $dst.s0 = $src.s0; + $dst.s1 = $src.s1; + $dst.s2 = $src.s2; + $dst.s3 = $src.s3; + $dst.s4 = $src.s4; + $dst.s5 = $src.s5; + $dst.s6 = $src.s6; + $dst.s7 = $src.s7; + $dst.s8 = $src.s8; + }; +} + +impl SigContext { + pub fn copy_user_regs_to(&self, dst: &mut UserContext) { + let gp_regs = dst.general_regs_mut(); + copy_gp_regs!(self, gp_regs); + dst.set_instruction_pointer(self.pc); + } + + pub fn copy_user_regs_from(&mut self, src: &UserContext) { + let gp_regs = src.general_regs(); + copy_gp_regs!(gp_regs, self); + self.pc = src.instruction_pointer(); + } +} + +impl TryFrom<&CpuExceptionInfo> for PageFaultInfo { + // [`Err`] indicates that the [`CpuExceptionInfo`] is not a page fault, + // with no additional error information. + type Error = (); + + fn try_from(value: &CpuExceptionInfo) -> Result { + use loongArch64::register::estat::Exception; + + let required_perms = match value.cpu_exception() { + Exception::FetchPageFault => VmPerms::EXEC, + Exception::LoadPageFault => VmPerms::READ, + Exception::StorePageFault => VmPerms::WRITE, + _ => return Err(()), + }; + + Ok(PageFaultInfo { + address: value.page_fault_addr, + required_perms, + }) + } +} + +/// CPU Information structure. +// TODO: Implement CPU information retrieval on LoongArch platforms. +pub struct CpuInfo { + processor: u32, +} + +impl CpuInfo { + pub fn new(processor_id: u32) -> Self { + Self { + processor: processor_id, + } + } + + /// Collect and format CPU information into a `String`. + pub fn collect_cpu_info(&self) -> String { + format!("processor\t: {}\n", self.processor) + } +} diff --git a/kernel/src/arch/loongarch/mod.rs b/kernel/src/arch/loongarch/mod.rs new file mode 100644 index 000000000..e2a78f3c5 --- /dev/null +++ b/kernel/src/arch/loongarch/mod.rs @@ -0,0 +1,4 @@ +// SPDX-License-Identifier: MPL-2.0 + +pub mod cpu; +pub mod signal; diff --git a/kernel/src/arch/loongarch/signal.rs b/kernel/src/arch/loongarch/signal.rs new file mode 100644 index 000000000..5390dbf20 --- /dev/null +++ b/kernel/src/arch/loongarch/signal.rs @@ -0,0 +1,19 @@ +// SPDX-License-Identifier: MPL-2.0 + +use ostd::cpu::context::{CpuExceptionInfo, UserContext}; + +use crate::process::signal::{sig_num::SigNum, signals::fault::FaultSignal, SignalContext}; + +impl SignalContext for UserContext { + fn set_arguments(&mut self, sig_num: SigNum, siginfo_addr: usize, ucontext_addr: usize) { + self.set_a0(sig_num.as_u8() as usize); + self.set_a1(siginfo_addr); + self.set_a2(ucontext_addr); + } +} + +impl From<&CpuExceptionInfo> for FaultSignal { + fn from(trap_info: &CpuExceptionInfo) -> Self { + unimplemented!() + } +} diff --git a/ostd/src/arch/loongarch/cpu/context.rs b/ostd/src/arch/loongarch/cpu/context.rs new file mode 100644 index 000000000..eeedcb37f --- /dev/null +++ b/ostd/src/arch/loongarch/cpu/context.rs @@ -0,0 +1,365 @@ +// SPDX-License-Identifier: MPL-2.0 + +//! CPU execution context control. + +use core::{arch::asm, fmt::Debug}; + +use loongArch64::register::estat::{self, Exception, Interrupt, Trap}; + +use crate::{ + arch::{ + mm::tlb_flush_addr, + trap::{RawUserContext, TrapFrame}, + }, + trap::call_irq_callback_functions, + user::{ReturnReason, UserContextApi, UserContextApiInternal}, +}; + +/// General registers +#[derive(Debug, Default, Clone, Copy)] +#[repr(C)] +#[expect(missing_docs)] +pub struct GeneralRegs { + pub zero: usize, + pub ra: usize, + pub tp: usize, + pub sp: usize, + pub a0: usize, + pub a1: usize, + pub a2: usize, + pub a3: usize, + pub a4: usize, + pub a5: usize, + pub a6: usize, + pub a7: usize, + pub t0: usize, + pub t1: usize, + pub t2: usize, + pub t3: usize, + pub t4: usize, + pub t5: usize, + pub t6: usize, + pub t7: usize, + pub t8: usize, + pub r21: usize, + pub fp: usize, + pub s0: usize, + pub s1: usize, + pub s2: usize, + pub s3: usize, + pub s4: usize, + pub s5: usize, + pub s6: usize, + pub s7: usize, + pub s8: usize, +} + +/// CPU exception information. +// +// TODO: Refactor the struct into an enum (similar to x86's `CpuException`). +#[derive(Clone, Copy, Debug)] +#[repr(C)] +pub struct CpuExceptionInfo { + /// The type of the exception. + pub code: Exception, + /// The page fault address. + pub page_fault_addr: usize, + /// The error code associated with the exception. + pub error_code: usize, // TODO +} + +impl Default for CpuExceptionInfo { + fn default() -> Self { + CpuExceptionInfo { + code: Exception::AddressNotAligned, + page_fault_addr: 0, + error_code: 0, + } + } +} + +impl CpuExceptionInfo { + /// Gets corresponding CPU exception. + pub fn cpu_exception(&self) -> CpuException { + self.code + } +} + +/// Userspace CPU context, including general-purpose registers and exception information. +#[derive(Clone, Copy, Debug)] +#[repr(C)] +pub struct UserContext { + user_context: RawUserContext, + trap: Trap, + cpu_exception_info: Option, +} + +impl Default for UserContext { + fn default() -> Self { + UserContext { + user_context: RawUserContext::default(), + trap: Trap::Unknown, + cpu_exception_info: None, + } + } +} + +impl UserContext { + /// Returns a reference to the general registers. + pub fn general_regs(&self) -> &GeneralRegs { + &self.user_context.general + } + + /// Returns a mutable reference to the general registers + pub fn general_regs_mut(&mut self) -> &mut GeneralRegs { + &mut self.user_context.general + } + + /// Returns the trap information. + pub fn take_exception(&mut self) -> Option { + self.cpu_exception_info.take() + } + + /// Sets thread-local storage pointer. + pub fn set_tls_pointer(&mut self, tls: usize) { + self.set_tp(tls) + } + + /// Gets thread-local storage pointer. + pub fn tls_pointer(&self) -> usize { + self.tp() + } + + /// Activates thread-local storage pointer on the current CPU. + pub fn activate_tls_pointer(&self) { + // No-op + } + + /// Enables floating-point unit. + pub fn enable_fpu(&mut self) { + self.user_context.euen = 0x1; + } +} + +impl UserContextApiInternal for UserContext { + fn execute(&mut self, mut has_kernel_event: F) -> ReturnReason + where + F: FnMut() -> bool, + { + let ret = loop { + self.user_context.run(); + + let cause = loongArch64::register::estat::read().cause(); + let badv = loongArch64::register::badv::read().raw(); + let badi = loongArch64::register::badi::read().raw(); + let era = loongArch64::register::era::read().raw(); + + match cause { + Trap::Exception(exception) => match exception { + Exception::Syscall => { + self.user_context.era += 4; + break ReturnReason::UserSyscall; + } + Exception::LoadPageFault + | Exception::StorePageFault + | Exception::FetchPageFault + | Exception::PageModifyFault + | Exception::PageNonReadableFault + | Exception::PageNonExecutableFault + | Exception::PagePrivilegeIllegal => { + // Handle page fault + // Disable the badv in TLB. + tlb_flush_addr(badv); + self.cpu_exception_info = Some(CpuExceptionInfo { + code: exception, + page_fault_addr: badv, + error_code: 0, // TODO: Set error code if needed + }); + break ReturnReason::UserException; + } + Exception::FetchInstructionAddressError + | Exception::MemoryAccessAddressError + | Exception::AddressNotAligned + | Exception::BoundsCheckFault + | Exception::Breakpoint + | Exception::InstructionNotExist + | Exception::InstructionPrivilegeIllegal => { + // Handle other exceptions + self.cpu_exception_info = Some(CpuExceptionInfo { + code: exception, + page_fault_addr: 0, + error_code: 0, // TODO: Set error code if needed + }); + log::debug!( + "Exception {exception:?} occurred, badv: {badv:#x?}, badi: {badi:#x?}, era: {era:#x?}" + ); + break ReturnReason::UserException; + } + Exception::FloatingPointUnavailable => { + log::debug!( + "Floating point unit is not available, badv: {badv:#x?}, badi: {badi:#x?}, era: {era:#x?}" + ); + // TODO: Add FPU support and enable it when this exception occurs. + break ReturnReason::UserException; + } + Exception::TLBRFill => unreachable!(), + }, + Trap::Interrupt(interrupt) => match interrupt { + Interrupt::SWI0 => todo!(), + Interrupt::SWI1 => todo!(), + Interrupt::HWI0 + | Interrupt::HWI1 + | Interrupt::HWI2 + | Interrupt::HWI3 + | Interrupt::HWI4 + | Interrupt::HWI5 + | Interrupt::HWI6 + | Interrupt::HWI7 => { + log::debug!("Handling hardware interrupt: {:?}", interrupt); + while let Some(irq) = crate::arch::kernel::irq::claim() { + // Call the IRQ callback functions for the claimed interrupt + call_irq_callback_functions(&self.as_trap_frame(), irq as _); + } + } + Interrupt::PMI => todo!(), + Interrupt::Timer => todo!(), + Interrupt::IPI => todo!(), + }, + Trap::MachineError(machine_error) => panic!( + "Machine error: {machine_error:?}, badv: {badv:#x?}, badi: {badi:#x?}, era: {era:#x?}" + ), + Trap::Unknown => panic!( + "Unknown trap, badv: {badv:#x?}, badi: {badi:#x?}, era: {era:#x?}" + ), + } + + if has_kernel_event() { + break ReturnReason::KernelEvent; + } + }; + + crate::arch::irq::enable_local(); + ret + } + + fn as_trap_frame(&self) -> TrapFrame { + TrapFrame { + general: self.user_context.general, + prmd: self.user_context.prmd, + era: self.user_context.era, + euen: self.user_context.euen, + } + } +} + +impl UserContextApi for UserContext { + fn trap_number(&self) -> usize { + todo!() + } + + fn trap_error_code(&self) -> usize { + todo!() + } + + fn instruction_pointer(&self) -> usize { + self.user_context.era + } + + fn set_instruction_pointer(&mut self, ip: usize) { + self.user_context.era = ip; + } + + fn stack_pointer(&self) -> usize { + self.sp() + } + + fn set_stack_pointer(&mut self, sp: usize) { + self.set_sp(sp); + } +} + +macro_rules! cpu_context_impl_getter_setter { + ( $( [ $field: ident, $setter_name: ident] ),*) => { + impl UserContext { + $( + #[doc = concat!("Gets the value of ", stringify!($field))] + #[inline(always)] + pub fn $field(&self) -> usize { + self.user_context.general.$field + } + + #[doc = concat!("Sets the value of ", stringify!($field))] + #[inline(always)] + pub fn $setter_name(&mut self, $field: usize) { + self.user_context.general.$field = $field; + } + )* + } + }; +} + +cpu_context_impl_getter_setter!( + [ra, set_ra], + [tp, set_tp], + [sp, set_sp], + [a0, set_a0], + [a1, set_a1], + [a2, set_a2], + [a3, set_a3], + [a4, set_a4], + [a5, set_a5], + [a6, set_a6], + [a7, set_a7], + [t0, set_t0], + [t1, set_t1], + [t2, set_t2], + [t3, set_t3], + [t4, set_t4], + [t5, set_t5], + [t6, set_t6], + [t7, set_t7], + [t8, set_t8], + [r21, set_r21], + [fp, set_fp], + [s0, set_s0], + [s1, set_s1], + [s2, set_s2], + [s3, set_s3], + [s4, set_s4], + [s5, set_s5], + [s6, set_s6], + [s7, set_s7], + [s8, set_s8] +); + +/// CPU exception. +pub type CpuException = Exception; + +/// The FPU context of user task. +/// Reference: +// FIXME: Implement FPU context on LoongArch64 platforms. +#[derive(Clone, Copy, Debug, Default)] +pub struct FpuContext; + +impl FpuContext { + /// Creates a new FPU context. + pub fn new() -> Self { + Self + } + + /// Saves CPU's current FPU context to this instance, if needed. + pub fn save(&mut self) {} + + /// Loads CPU's FPU context from this instance, if needed. + pub fn load(&mut self) {} + + /// Returns the FPU context as a byte slice. + pub fn as_bytes(&self) -> &[u8] { + &[] + } + + /// Returns the FPU context as a mutable byte slice. + pub fn as_bytes_mut(&mut self) -> &mut [u8] { + &mut [] + } +} diff --git a/ostd/src/arch/loongarch/cpu/local.rs b/ostd/src/arch/loongarch/cpu/local.rs new file mode 100644 index 000000000..861ddecea --- /dev/null +++ b/ostd/src/arch/loongarch/cpu/local.rs @@ -0,0 +1,15 @@ +// SPDX-License-Identifier: MPL-2.0 + +//! Architecture dependent CPU-local information utilities. + +pub(crate) fn get_base() -> u64 { + let mut gp; + unsafe { + core::arch::asm!( + "move {gp}, $r21", + gp = out(reg) gp, + options(preserves_flags, nostack) + ); + } + gp +} diff --git a/ostd/src/arch/loongarch/cpu/mod.rs b/ostd/src/arch/loongarch/cpu/mod.rs new file mode 100644 index 000000000..bf160e756 --- /dev/null +++ b/ostd/src/arch/loongarch/cpu/mod.rs @@ -0,0 +1,6 @@ +// SPDX-License-Identifier: MPL-2.0 + +//! CPU context & state control and CPU local memory. + +pub mod context; +pub mod local;