Files
haiku-beta6/src/system/kernel/arch/arm/arch_int.cpp
T
David Karoly 36d65a7890 kernel/arch/arm/int: add initialization code for GICv2
Change-Id: I4f98a4e0277ce37e758cabbb04806cf13f351f1e
Reviewed-on: https://review.haiku-os.org/c/haiku/+/4674
Tested-by: Commit checker robot <[email protected]>
Reviewed-by: Adrien Destugues <[email protected]>
2021-12-07 10:58:24 +00:00

380 lines
9.2 KiB
C++

/*
* Copyright 2003-2021, Haiku Inc. All rights reserved.
* Distributed under the terms of the MIT License.
*
* Authors:
* Axel Dörfler <[email protected]>
* Ingo Weinhold <[email protected]>
* François Revol <[email protected]>
* Ithamar R. Adema <[email protected]>
*
* Copyright 2001, Travis Geiselbrecht. All rights reserved.
* Distributed under the terms of the NewOS License.
*/
#include <int.h>
#include <arch/smp.h>
#include <boot/kernel_args.h>
#include <device_manager.h>
#include <kscheduler.h>
#include <interrupt_controller.h>
#include <smp.h>
#include <thread.h>
#include <timer.h>
#include <util/AutoLock.h>
#include <util/DoublyLinkedList.h>
#include <util/kernel_cpp.h>
#include <vm/vm.h>
#include <vm/vm_priv.h>
#include <vm/VMAddressSpace.h>
#include <string.h>
#include <drivers/bus/FDT.h>
#include "arch_int_gicv2.h"
#include "soc.h"
#include "soc_pxa.h"
#include "soc_omap3.h"
#define TRACE_ARCH_INT
#ifdef TRACE_ARCH_INT
# define TRACE(x) dprintf x
#else
# define TRACE(x) ;
#endif
#define VECTORPAGE_SIZE 64
#define USER_VECTOR_ADDR_LOW 0x00000000
#define USER_VECTOR_ADDR_HIGH 0xffff0000
extern int _vectors_start;
extern int _vectors_end;
static area_id sVectorPageArea;
static void *sVectorPageAddress;
static area_id sUserVectorPageArea;
static void *sUserVectorPageAddress;
//static fdt_module_info *sFdtModule;
// An iframe stack used in the early boot process when we don't have
// threads yet.
struct iframe_stack gBootFrameStack;
void
arch_int_enable_io_interrupt(int irq)
{
TRACE(("arch_int_enable_io_interrupt(%d)\n", irq));
InterruptController *ic = InterruptController::Get();
if (ic != NULL)
ic->EnableInterrupt(irq);
}
void
arch_int_disable_io_interrupt(int irq)
{
TRACE(("arch_int_disable_io_interrupt(%d)\n", irq));
InterruptController *ic = InterruptController::Get();
if (ic != NULL)
ic->DisableInterrupt(irq);
}
/* arch_int_*_interrupts() and friends are in arch_asm.S */
int32
arch_int_assign_to_cpu(int32 irq, int32 cpu)
{
// Not yet supported.
return 0;
}
static void
print_iframe(const char *event, struct iframe *frame)
{
if (event)
dprintf("Exception: %s\n", event);
dprintf("R00=%08lx R01=%08lx R02=%08lx R03=%08lx\n"
"R04=%08lx R05=%08lx R06=%08lx R07=%08lx\n",
frame->r0, frame->r1, frame->r2, frame->r3,
frame->r4, frame->r5, frame->r6, frame->r7);
dprintf("R08=%08lx R09=%08lx R10=%08lx R11=%08lx\n"
"R12=%08lx SP=%08lx LR=%08lx PC=%08lx CPSR=%08lx\n",
frame->r8, frame->r9, frame->r10, frame->r11,
frame->r12, frame->svc_sp, frame->svc_lr, frame->pc, frame->spsr);
}
status_t
arch_int_init(kernel_args *args)
{
return B_OK;
}
extern "C" void arm_vector_init(void);
#if 0
static struct fdt_device_info intc_table[] = {
{
.compatible = "marvell,pxa-intc",
.init = PXAInterruptController::Init,
}, {
.compatible = "ti,omap3-intc",
.init = OMAP3InterruptController::Init,
}
};
static int intc_count = sizeof(intc_table) / sizeof(struct fdt_device_info);
#endif
status_t
arch_int_init_post_vm(kernel_args *args)
{
// create a read/write kernel area
sVectorPageArea = create_area("vectorpage", (void **)&sVectorPageAddress,
B_ANY_ADDRESS, VECTORPAGE_SIZE, B_FULL_LOCK,
B_KERNEL_WRITE_AREA | B_KERNEL_READ_AREA);
if (sVectorPageArea < 0)
panic("vector page could not be created!");
// clone it at a fixed address with user read/only permissions
sUserVectorPageAddress = (addr_t*)USER_VECTOR_ADDR_HIGH;
sUserVectorPageArea = clone_area("user_vectorpage",
(void **)&sUserVectorPageAddress, B_EXACT_ADDRESS,
B_READ_AREA | B_EXECUTE_AREA, sVectorPageArea);
if (sUserVectorPageArea < 0)
panic("user vector page @ %p could not be created (%lx)!",
sVectorPageAddress, sUserVectorPageArea);
// copy vectors into the newly created area
memcpy(sVectorPageAddress, &_vectors_start, VECTORPAGE_SIZE);
arm_vector_init();
// see if high vectors are enabled
if ((mmu_read_c1() & (1 << 13)) != 0)
dprintf("High vectors already enabled\n");
else {
mmu_write_c1(mmu_read_c1() | (1 << 13));
if ((mmu_read_c1() & (1 << 13)) == 0)
dprintf("Unable to enable high vectors!\n");
else
dprintf("Enabled high vectors\n");
}
if (strncmp(args->arch_args.interrupt_controller.kind, INTC_KIND_GICV2,
sizeof(args->arch_args.interrupt_controller.kind)) == 0) {
InterruptController *ic = new(std::nothrow) GICv2InterruptController(
args->arch_args.interrupt_controller.regs1.start,
args->arch_args.interrupt_controller.regs2.start);
if (ic == NULL)
return B_NO_MEMORY;
} else {
panic("No interrupt controllers found!\n");
}
return B_OK;
}
status_t
arch_int_init_io(kernel_args* args)
{
return B_OK;
}
status_t
arch_int_init_post_device_manager(struct kernel_args *args)
{
return B_ENTRY_NOT_FOUND;
}
// Little helper class for handling the
// iframe stack as used by KDL.
class IFrameScope {
public:
IFrameScope(struct iframe *iframe) {
fThread = thread_get_current_thread();
if (fThread)
arm_push_iframe(&fThread->arch_info.iframes, iframe);
else
arm_push_iframe(&gBootFrameStack, iframe);
}
virtual ~IFrameScope() {
// pop iframe
if (fThread)
arm_pop_iframe(&fThread->arch_info.iframes);
else
arm_pop_iframe(&gBootFrameStack);
}
private:
Thread* fThread;
};
extern "C" void
arch_arm_undefined(struct iframe *iframe)
{
print_iframe("Undefined Instruction", iframe);
IFrameScope scope(iframe); // push/pop iframe
panic("not handled!");
}
extern "C" void
arch_arm_syscall(struct iframe *iframe)
{
print_iframe("Software interrupt", iframe);
IFrameScope scope(iframe); // push/pop iframe
}
extern "C" void
arch_arm_data_abort(struct iframe *frame)
{
Thread *thread = thread_get_current_thread();
bool isUser = (frame->spsr & 0x1f) == 0x10;
addr_t far = arm_get_far();
bool isWrite = true;
addr_t newip = 0;
#ifdef TRACE_ARCH_INT
print_iframe("Data Abort", frame);
dprintf("FAR: %08lx, thread: %s\n", far, thread->name);
#endif
IFrameScope scope(frame);
if (debug_debugger_running()) {
// If this CPU or this thread has a fault handler, we're allowed to be
// here.
if (thread != NULL) {
cpu_ent* cpu = &gCPU[smp_get_current_cpu()];
if (cpu->fault_handler != 0) {
debug_set_page_fault_info(far, frame->pc,
isWrite ? DEBUG_PAGE_FAULT_WRITE : 0);
frame->svc_sp = cpu->fault_handler_stack_pointer;
frame->pc = cpu->fault_handler;
return;
}
if (thread->fault_handler != 0) {
kprintf("ERROR: thread::fault_handler used in kernel "
"debugger!\n");
debug_set_page_fault_info(far, frame->pc,
isWrite ? DEBUG_PAGE_FAULT_WRITE : 0);
frame->pc = reinterpret_cast<uintptr_t>(thread->fault_handler);
return;
}
}
// otherwise, not really
panic("page fault in debugger without fault handler! Touching "
"address %p from pc %p\n", (void *)far, (void *)frame->pc);
return;
} else if ((frame->spsr & (1 << 7)) != 0) {
// interrupts disabled
// If a page fault handler is installed, we're allowed to be here.
// TODO: Now we are generally allowing user_memcpy() with interrupts
// disabled, which in most cases is a bug. We should add some thread
// flag allowing to explicitly indicate that this handling is desired.
uintptr_t handler = reinterpret_cast<uintptr_t>(thread->fault_handler);
if (thread && thread->fault_handler != 0) {
if (frame->pc != handler) {
frame->pc = handler;
return;
}
// The fault happened at the fault handler address. This is a
// certain infinite loop.
panic("page fault, interrupts disabled, fault handler loop. "
"Touching address %p from pc %p\n", (void*)far,
(void*)frame->pc);
}
// If we are not running the kernel startup the page fault was not
// allowed to happen and we must panic.
panic("page fault, but interrupts were disabled. Touching address "
"%p from pc %p\n", (void *)far, (void *)frame->pc);
return;
} else if (thread != NULL && thread->page_faults_allowed < 1) {
panic("page fault not allowed at this place. Touching address "
"%p from pc %p\n", (void *)far, (void *)frame->pc);
return;
}
enable_interrupts();
vm_page_fault(far, frame->pc, isWrite, false, isUser, &newip);
if (newip != 0) {
// the page fault handler wants us to modify the iframe to set the
// IP the cpu will return to to be this ip
frame->pc = newip;
}
}
extern "C" void
arch_arm_prefetch_abort(struct iframe *iframe)
{
print_iframe("Prefetch Abort", iframe);
IFrameScope scope(iframe);
panic("not handled!");
}
extern "C" void
arch_arm_irq(struct iframe *iframe)
{
IFrameScope scope(iframe);
InterruptController *ic = InterruptController::Get();
if (ic != NULL)
ic->HandleInterrupt();
Thread* thread = thread_get_current_thread();
cpu_status state = disable_interrupts();
if (thread->cpu->invoke_scheduler) {
SpinLocker schedulerLocker(thread->scheduler_lock);
scheduler_reschedule(B_THREAD_READY);
schedulerLocker.Unlock();
restore_interrupts(state);
} else 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);
}
}
extern "C" void
arch_arm_fiq(struct iframe *iframe)
{
IFrameScope scope(iframe);
panic("FIQ not implemented yet!");
}