use crate::arch::current_pid;
use crate::arch::exception::RiscvException;
use crate::arch::mem::MemoryMapping;
use crate::arch::process::{Process as ArchProcess, RETURN_FROM_EXCEPTION_HANDLER};
use crate::arch::process::{Thread, EXIT_THREAD, RETURN_FROM_ISR};
use crate::services::SystemServices;
use riscv::register::{scause, sepc, sstatus, stval, vexriscv::sim, vexriscv::sip};
use xous_kernel::{SysCall, PID, TID};
extern "Rust" {
fn _xous_syscall_return_result(result: &xous_kernel::Result, context: &Thread) -> !;
}
static mut SIM_BACKING: usize = 0;
pub fn disable_all_irqs() {
unsafe { SIM_BACKING = sim::read() };
sim::write(0x0);
}
#[export_name = "_enable_all_irqs"]
pub extern "C" fn enable_all_irqs() {
sim::write(unsafe { SIM_BACKING });
}
pub fn enable_irq(irq_no: usize) {
sim::write(sim::read() | (1 << irq_no));
}
pub fn disable_irq(irq_no: usize) -> Result<(), xous_kernel::Error> {
sim::write(sim::read() & !(1 << irq_no));
Ok(())
}
static mut PREVIOUS_PAIR: Option<(PID, TID)> = None;
pub unsafe fn set_isr_return_pair(pid: PID, tid: TID) {
PREVIOUS_PAIR = Some((pid, tid));
}
#[cfg(feature = "gdb-stub")]
pub unsafe fn take_isr_return_pair() -> Option<(PID, TID)> {
PREVIOUS_PAIR.take()
}
fn generate_exception_args(ex: &RiscvException) -> Option<[usize; 3]> {
match *ex {
RiscvException::InstructionAddressMisaligned(epc, addr) => Some([
xous_kernel::ExceptionType::InstructionAddressMisaligned as usize,
epc,
addr,
]),
RiscvException::InstructionAccessFault(epc, addr) => Some([
xous_kernel::ExceptionType::InstructionAccessFault as usize,
epc,
addr,
]),
RiscvException::IllegalInstruction(epc, instruction) => Some([
xous_kernel::ExceptionType::IllegalInstruction as usize,
epc,
instruction,
]),
RiscvException::LoadAddressMisaligned(epc, addr) => Some([
xous_kernel::ExceptionType::LoadAddressMisaligned as usize,
epc,
addr,
]),
RiscvException::LoadAccessFault(epc, addr) => Some([
xous_kernel::ExceptionType::LoadAccessFault as usize,
epc,
addr,
]),
RiscvException::StoreAddressMisaligned(epc, addr) => Some([
xous_kernel::ExceptionType::StoreAddressMisaligned as usize,
epc,
addr,
]),
RiscvException::StoreAccessFault(epc, addr) => Some([
xous_kernel::ExceptionType::StoreAccessFault as usize,
epc,
addr,
]),
RiscvException::InstructionPageFault(epc, addr) => Some([
xous_kernel::ExceptionType::InstructionPageFault as usize,
epc,
addr,
]),
RiscvException::LoadPageFault(epc, addr) => Some([
xous_kernel::ExceptionType::LoadPageFault as usize,
epc,
addr,
]),
RiscvException::StorePageFault(epc, addr) => Some([
xous_kernel::ExceptionType::StorePageFault as usize,
epc,
addr,
]),
_ => None,
}
}
#[export_name = "_start_trap_rust"]
pub extern "C" fn trap_handler(
a0: usize,
a1: usize,
a2: usize,
a3: usize,
a4: usize,
a5: usize,
a6: usize,
a7: usize,
) -> ! {
let sc = scause::read();
if cfg!(target_arch = "riscv32")
&& sstatus::read().spp() == sstatus::SPP::Supervisor
&& sc.bits() == 0xf
{
let pid = current_pid();
let ex = RiscvException::from_regs(sc.bits(), sepc::read(), stval::read());
print!("KERNEL({}): RISC-V fault: {} - ", pid, ex);
panic!("Maybe ran out of kernel stack?");
}
let pid = current_pid();
if (sc.bits() == 9) || (sc.bits() == 8) {
let tid = ArchProcess::with_current_mut(|p| {
p.current_thread_mut().sepc += 4;
p.current_tid()
});
let call = SysCall::from_args(a0, a1, a2, a3, a4, a5, a6, a7).unwrap_or_else(|_| {
ArchProcess::with_current_mut(|p| unsafe {
_xous_syscall_return_result(
&xous_kernel::Result::Error(xous_kernel::Error::UnhandledSyscall),
p.current_thread(),
)
})
});
let response = crate::syscall::handle(pid, tid, unsafe { PREVIOUS_PAIR.is_some() }, call)
.unwrap_or_else(xous_kernel::Result::Error);
ArchProcess::with_current_mut(|p| {
let thread = p.current_thread();
if response == xous_kernel::Result::ResumeProcess {
crate::arch::syscall::resume(current_pid().get() == 1, thread);
} else {
unsafe { _xous_syscall_return_result(&response, thread) };
}
});
}
let ex = RiscvException::from_regs(sc.bits(), sepc::read(), stval::read());
if sc.is_exception() {
match ex {
RiscvException::StorePageFault(_pc, addr)
| RiscvException::LoadPageFault(_pc, addr) => {
#[cfg(all(feature = "debug-print", feature = "print-panics"))]
print!(
"KERNEL({}): RISC-V fault: {} @ {:08x}, addr {:08x} - ",
pid, ex, _pc, addr
);
crate::arch::mem::ensure_page_exists_inner(addr)
.map(|_new_page| {
#[cfg(all(feature = "debug-print", feature = "print-panics"))]
klog!("Handing page {:08x} to process", _new_page);
ArchProcess::with_current_mut(|process| {
crate::arch::syscall::resume(
current_pid().get() == 1,
process.current_thread(),
)
});
})
.ok(); }
RiscvException::InstructionPageFault(RETURN_FROM_EXCEPTION_HANDLER, _offset) => {
SystemServices::with_mut(|ss| {
ss.finish_exception_handler_and_resume(pid)
.expect("unable to finish exception handler")
});
ArchProcess::with_current_mut(|p| {
let pc_adjust = a0 as isize;
if pc_adjust < 0 {
p.current_thread_mut().sepc -= pc_adjust.abs() as usize;
} else {
p.current_thread_mut().sepc += pc_adjust.abs() as usize;
}
crate::arch::syscall::resume(pid.get() == 1, p.current_thread())
});
}
RiscvException::InstructionPageFault(EXIT_THREAD, _offset) => {
let tid = ArchProcess::with_current(|process| process.current_tid());
if SystemServices::with_mut(|ss| ss.destroy_thread(pid, tid)).unwrap() {
crate::syscall::reset_switchto_caller();
}
ArchProcess::with_current_mut(|p| {
crate::arch::syscall::resume(current_pid().get() == 1, p.current_thread())
});
}
RiscvException::InstructionPageFault(RETURN_FROM_ISR, _offset) => {
let (previous_pid, previous_context) = unsafe {
PREVIOUS_PAIR
.take()
.expect("got RETURN_FROM_ISR with no previous PID")
};
SystemServices::with_mut(|ss| {
ss.finish_callback_and_resume(previous_pid, previous_context)
.expect("unable to resume previous PID")
});
enable_all_irqs();
ArchProcess::with_current_mut(|process| {
crate::arch::syscall::resume(current_pid().get() == 1, process.current_thread())
});
}
_ => (),
}
if let Some(args) = generate_exception_args(&ex) {
if let Some(handler) = SystemServices::with_mut(|ss| ss.begin_exception_handler(pid)) {
klog!("Exception handler for process exists ({:x?})", handler);
klog!(
"At start of exception, current thread was: {}",
SystemServices::with(|ss| ss.get_process(pid).unwrap().current_thread)
);
ArchProcess::with_current_mut(|process| {
crate::arch::syscall::invoke(
process.thread_mut(crate::arch::process::EXCEPTION_TID),
current_pid().get() == 1,
handler.pc,
handler.sp,
RETURN_FROM_EXCEPTION_HANDLER,
&args,
);
crate::arch::syscall::resume(
current_pid().get() == 1,
process.thread(crate::arch::process::EXCEPTION_TID),
)
});
}
}
let is_kernel_failure = sstatus::read().spp() == sstatus::SPP::Supervisor;
println!(
"{}: CPU Exception on PID {}: {}",
if is_kernel_failure {
"!!! KERNEL FAILURE !!!"
} else {
"PROGRAM HALT"
},
pid,
ex
);
ArchProcess::with_current(|process| {
println!("Current thread {}:", process.current_tid());
process.print_current_thread();
});
MemoryMapping::current().print_map();
if is_kernel_failure {
#[allow(clippy::empty_loop)]
loop {}
}
SystemServices::with_mut(|ss| {
ss.terminate_process(pid)
.expect("couldn't terminate current process");
crate::syscall::reset_switchto_caller();
});
ArchProcess::with_current_mut(|process| {
crate::arch::syscall::resume(current_pid().get() == 1, process.current_thread())
})
} else {
let irqs_pending = sip::read();
unsafe {
if PREVIOUS_PAIR.is_none() {
let tid = crate::arch::process::current_tid();
PREVIOUS_PAIR = Some((pid, tid));
}
}
crate::irq::handle(irqs_pending).expect("Couldn't handle IRQ");
ArchProcess::with_current_mut(|process| {
crate::arch::syscall::resume(current_pid().get() == 1, process.current_thread())
})
}
}