Add the cpu section of LoongArch in OSTD and kernel
This commit is contained in:
parent
ce22374b50
commit
91e7785915
|
|
@ -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: <https://elixir.bootlin.com/linux/v6.15.7/source/arch/loongarch/include/uapi/asm/sigcontext.h#L20>
|
||||
#[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: <https://elixir.bootlin.com/linux/v6.15.7/source/arch/loongarch/kernel/signal.c#L861>
|
||||
}
|
||||
|
||||
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<Self, ()> {
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,4 @@
|
|||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
pub mod cpu;
|
||||
pub mod signal;
|
||||
|
|
@ -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!()
|
||||
}
|
||||
}
|
||||
|
|
@ -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<CpuExceptionInfo>,
|
||||
}
|
||||
|
||||
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<CpuExceptionInfo> {
|
||||
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<F>(&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: <https://elixir.bootlin.com/linux/v6.15.7/source/arch/loongarch/include/uapi/asm/sigcontext.h#L37>
|
||||
// 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 []
|
||||
}
|
||||
}
|
||||
|
|
@ -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
|
||||
}
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
//! CPU context & state control and CPU local memory.
|
||||
|
||||
pub mod context;
|
||||
pub mod local;
|
||||
Loading…
Reference in New Issue