* Copyright 2003-2011, Haiku, Inc. All rights reserved.
* Distributed under the terms of the MIT License.
*
* Authors:
* Adrien Destugues, pulkomandy@pulkomandy.tk
*/
#include <interrupts.h>
#include <cpu.h>
#include <thread.h>
#include <vm/vm_priv.h>
#include <ksyscalls.h>
#include <syscall_numbers.h>
#include <arch_cpu_defs.h>
#include <arch_thread_types.h>
#include <arch/debug.h>
#include <util/AutoLock.h>
#include <Htif.h>
#include <Plic.h>
#include <Clint.h>
#include <AutoDeleterDrivers.h>
#include <ScopeExit.h>
#include "RISCV64VMTranslationMap.h"
#include <algorithm>
static uint32 sPlicContexts[SMP_MAX_CPUS];
static void
SendSignal(debug_exception_type type, uint32 signalNumber, int32 signalCode,
addr_t signalAddress = 0, int32 signalError = B_ERROR)
{
if (SstatusReg{.val = Sstatus()}.spp == modeU) {
struct sigaction action;
Thread* thread = thread_get_current_thread();
enable_interrupts();
if ((sigaction(signalNumber, NULL, &action) == 0
&& action.sa_handler != SIG_DFL
&& action.sa_handler != SIG_IGN)
|| user_debug_exception_occurred(type, signalNumber)) {
Signal signal(signalNumber, signalCode, signalError,
thread->team->id);
signal.SetAddress((void*)signalAddress);
send_signal_to_thread(thread, signal, 0);
}
} else {
panic("Unexpected exception occurred in kernel mode!");
}
}
static void
AfterInterrupt()
{
if (debug_debugger_running())
return;
Thread* thread = thread_get_current_thread();
cpu_status state = disable_interrupts();
if (thread->post_interrupt_callback != NULL) {
void (*callback)(void*) = thread->post_interrupt_callback;
void* data = thread->post_interrupt_data;
thread->post_interrupt_callback = NULL;
thread->post_interrupt_data = NULL;
restore_interrupts(state);
callback(data);
} else if (thread->cpu->invoke_scheduler) {
SpinLocker schedulerLocker(thread->scheduler_lock);
scheduler_reschedule(B_THREAD_READY);
schedulerLocker.Unlock();
restore_interrupts(state);
}
}
static bool
SetAccessedFlags(addr_t addr, bool isWrite)
{
VMAddressSpacePutter addressSpace;
if (IS_KERNEL_ADDRESS(addr))
addressSpace.SetTo(VMAddressSpace::GetKernel());
else if (IS_USER_ADDRESS(addr))
addressSpace.SetTo(VMAddressSpace::GetCurrent());
if(!addressSpace.IsSet())
return false;
RISCV64VMTranslationMap* map
= (RISCV64VMTranslationMap*)addressSpace->TranslationMap();
phys_addr_t physAdr;
uint32 pageFlags;
map->QueryInterrupt(addr, &physAdr, &pageFlags);
if ((PAGE_PRESENT & pageFlags) == 0)
return false;
if (isWrite) {
if (
((B_WRITE_AREA | B_KERNEL_WRITE_AREA) & pageFlags) != 0
&& ((PAGE_ACCESSED | PAGE_MODIFIED) & pageFlags)
!= (PAGE_ACCESSED | PAGE_MODIFIED)
) {
map->SetFlags(addr, PAGE_ACCESSED | PAGE_MODIFIED);
return true;
}
} else {
if (
((B_READ_AREA | B_KERNEL_READ_AREA) & pageFlags) != 0
&& (PAGE_ACCESSED & pageFlags) == 0
) {
map->SetFlags(addr, PAGE_ACCESSED);
return true;
}
}
return false;
}
extern "C" void
STrap(iframe* frame)
{
switch (frame->cause) {
case causeExecPageFault:
case causeLoadPageFault:
case causeStorePageFault: {
if (SetAccessedFlags(Stval(), frame->cause == causeStorePageFault))
return;
}
}
if (SstatusReg{.val = frame->status}.spp == modeU) {
thread_get_current_thread()->arch_info.userFrame = frame;
thread_get_current_thread()->arch_info.oldA0 = frame->a0;
thread_at_kernel_entry(system_time());
}
const auto& kernelExit = ScopeExit([&]() {
if (SstatusReg{.val = frame->status}.spp == modeU) {
disable_interrupts();
atomic_and(&thread_get_current_thread()->flags, ~THREAD_FLAGS_SYSCALL_RESTARTED);
if ((thread_get_current_thread()->flags
& (THREAD_FLAGS_SIGNALS_PENDING
| THREAD_FLAGS_DEBUG_THREAD
| THREAD_FLAGS_TRAP_FOR_CORE_DUMP)) != 0) {
enable_interrupts();
thread_at_kernel_exit();
} else {
thread_at_kernel_exit_no_signals();
}
if ((THREAD_FLAGS_RESTART_SYSCALL & thread_get_current_thread()->flags) != 0) {
atomic_and(&thread_get_current_thread()->flags, ~THREAD_FLAGS_RESTART_SYSCALL);
atomic_or(&thread_get_current_thread()->flags, THREAD_FLAGS_SYSCALL_RESTARTED);
frame->a0 = thread_get_current_thread()->arch_info.oldA0;
frame->epc -= 4;
}
thread_get_current_thread()->arch_info.userFrame = NULL;
}
});
switch (frame->cause) {
case causeIllegalInst: {
return SendSignal(B_INVALID_OPCODE_EXCEPTION, SIGILL, ILL_ILLOPC,
frame->epc);
}
case causeExecMisalign:
case causeLoadMisalign:
case causeStoreMisalign: {
return SendSignal(B_ALIGNMENT_EXCEPTION, SIGBUS, BUS_ADRALN,
Stval());
}
case causeBreakpoint: {
if (SstatusReg{.val = frame->status}.spp == modeU) {
user_debug_breakpoint_hit(false);
} else {
panic("hit kernel breakpoint");
}
return;
}
case causeExecAccessFault:
case causeLoadAccessFault:
case causeStoreAccessFault: {
return SendSignal(B_SEGMENT_VIOLATION, SIGBUS, BUS_ADRERR,
Stval());
}
case causeExecPageFault:
case causeLoadPageFault:
case causeStorePageFault: {
uint64 stval = Stval();
if (debug_debugger_running()) {
Thread* thread = thread_get_current_thread();
if (thread != NULL) {
cpu_ent* cpu = &gCPU[smp_get_current_cpu()];
if (cpu->fault_handler != 0) {
debug_set_page_fault_info(stval, frame->epc,
(frame->cause == causeStorePageFault)
? DEBUG_PAGE_FAULT_WRITE : 0);
frame->epc = cpu->fault_handler;
frame->sp = cpu->fault_handler_stack_pointer;
return;
}
if (thread->fault_handler != 0) {
kprintf("ERROR: thread::fault_handler used in kernel "
"debugger!\n");
debug_set_page_fault_info(stval, frame->epc,
frame->cause == causeStorePageFault
? DEBUG_PAGE_FAULT_WRITE : 0);
frame->epc = (addr_t)thread->fault_handler;
return;
}
}
panic("page fault in debugger without fault handler! Touching "
"address %p from ip %p\n", (void*)stval, (void*)frame->epc);
return;
}
if (SstatusReg{.val = frame->status}.pie == 0) {
Thread* thread = thread_get_current_thread();
if (thread != NULL && thread->fault_handler != 0) {
addr_t handler = (addr_t)(thread->fault_handler);
if (frame->epc != handler) {
frame->epc = handler;
return;
}
}
panic("page fault with interrupts disabled@!dump_virt_page %#" B_PRIx64, stval);
}
addr_t newIP = 0;
enable_interrupts();
vm_page_fault(stval, frame->epc, frame->cause == causeStorePageFault,
frame->cause == causeExecPageFault,
SstatusReg{.val = frame->status}.spp == modeU, &newIP);
if (newIP != 0)
frame->epc = newIP;
return;
}
case causeInterrupt + sSoftInt: {
ClearBitsSip(1 << sSoftInt);
smp_intercpu_interrupt_handler(smp_get_current_cpu());
AfterInterrupt();
return;
}
case causeInterrupt + sTimerInt: {
ClearBitsSie(1 << sTimerInt);
timer_interrupt();
AfterInterrupt();
return;
}
case causeInterrupt + sExternInt: {
uint64 irq = gPlicRegs->contexts[sPlicContexts[smp_get_current_cpu()]].claimAndComplete;
io_interrupt_handler(irq, true);
gPlicRegs->contexts[sPlicContexts[smp_get_current_cpu()]].claimAndComplete = irq;
AfterInterrupt();
return;
}
case causeUEcall: {
frame->epc += 4;
uint64 syscall = frame->t0;
uint64 args[20];
if (syscall < (uint64)kSyscallCount) {
uint32 argCnt = kExtendedSyscallInfos[syscall].parameter_count;
memcpy(&args[0], &frame->a0,
sizeof(uint64)*std::min<uint32>(argCnt, 8));
if (argCnt > 8) {
if (status_t res = user_memcpy(&args[8], (void*)frame->sp,
sizeof(uint64)*(argCnt - 8)) < B_OK) {
dprintf("can't read syscall arguments on user "
"stack\n");
frame->a0 = res;
return;
}
}
}
enable_interrupts();
uint64 returnValue = 0;
syscall_dispatcher(syscall, (void*)args, &returnValue);
frame->a0 = returnValue;
return;
}
}
panic("unhandled STrap");
}
status_t
arch_int_init(kernel_args* args)
{
dprintf("arch_int_init()\n");
for (uint32 i = 0; i < args->num_cpus; i++) {
dprintf(" CPU %" B_PRIu32 ":\n", i);
dprintf(" hartId: %" B_PRIu32 "\n", args->arch_args.hartIds[i]);
dprintf(" plicContext: %" B_PRIu32 "\n", args->arch_args.plicContexts[i]);
}
for (uint32 i = 0; i < args->num_cpus; i++)
sPlicContexts[i] = args->arch_args.plicContexts[i];
reserve_io_interrupt_vectors(128, 0, INTERRUPT_TYPE_IRQ);
for (uint32 i = 0; i < args->num_cpus; i++)
gPlicRegs->contexts[sPlicContexts[i]].priorityThreshold = 0;
return B_OK;
}
status_t
arch_int_init_post_vm(kernel_args* args)
{
return B_OK;
}
status_t
arch_int_init_post_device_manager(struct kernel_args* args)
{
return B_OK;
}
status_t
arch_int_init_io(kernel_args* args)
{
return B_OK;
}
void
arch_int_enable_io_interrupt(int32 irq)
{
dprintf("arch_int_enable_io_interrupt(%" B_PRId32 ")\n", irq);
gPlicRegs->priority[irq] = 1;
gPlicRegs->enable[sPlicContexts[0]][irq / 32] |= 1 << (irq % 32);
}
void
arch_int_disable_io_interrupt(int32 irq)
{
dprintf("arch_int_disable_io_interrupt(%" B_PRId32 ")\n", irq);
gPlicRegs->priority[irq] = 0;
gPlicRegs->enable[sPlicContexts[0]][irq / 32] &= ~(1 << (irq % 32));
}
int32
arch_int_assign_to_cpu(int32 irq, int32 cpu)
{
return 0;
}
#undef arch_int_enable_interrupts
#undef arch_int_disable_interrupts
#undef arch_int_restore_interrupts
#undef arch_int_are_interrupts_enabled
extern "C" void
arch_int_enable_interrupts()
{
arch_int_enable_interrupts_inline();
}
extern "C" int
arch_int_disable_interrupts()
{
return arch_int_disable_interrupts_inline();
}
extern "C" void
arch_int_restore_interrupts(int oldState)
{
arch_int_restore_interrupts_inline(oldState);
}
extern "C" bool
arch_int_are_interrupts_enabled()
{
return arch_int_are_interrupts_enabled_inline();
}