* Copyright 2003-2011, Haiku, Inc. All rights reserved.
* Distributed under the terms of the MIT License.
*
* Authors:
* Axel Dörfler <axeld@pinc-software.de>
* Ingo Weinhold <bonefish@cs.tu-berlin.de>
* François Revol <revol@free.fr>
*
* Copyright 2001, Travis Geiselbrecht. All rights reserved.
* Distributed under the terms of the NewOS License.
*/
#include <interrupts.h>
#include <arch_platform.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>
#warning M68K: writeme!
#ifdef TRACE_ARCH_INT
# define TRACE(x) dprintf x
#else
# define TRACE(x) ;
#endif
typedef void (*m68k_exception_handler)(void);
#define M68K_EXCEPTION_VECTOR_COUNT 256
#warning M68K: align on 4 ?
m68k_exception_handler *gExceptionVectors;
extern "C" void __m68k_exception_noop(void);
extern "C" void __m68k_exception_common(void);
extern int __irqvec_start;
extern int __irqvec_end;
extern"C" void m68k_exception_tail(void);
addr_t gFaultHandler;
struct iframe_stack gBootFrameStack;
void
arch_int_enable_io_interrupt(int32 irq)
{
M68KPlatform::Default()->EnableIOInterrupt(irq);
}
void
arch_int_disable_io_interrupt(int32 irq)
{
M68KPlatform::Default()->DisableIOInterrupt(irq);
}
int32
arch_int_assign_to_cpu(int32 irq, int32 cpu)
{
return 0;
}
static void
print_iframe(struct iframe *frame)
{
dprintf("iframe at %p:\n", frame);
dprintf(" d0 0x%08lx d1 0x%08lx d2 0x%08lx d3 0x%08lx\n",
frame->d[0], frame->d[1], frame->d[2], frame->d[3]);
kprintf(" d4 0x%08lx d5 0x%08lx d6 0x%08lx d7 0x%08lx\n",
frame->d[4], frame->d[5], frame->d[6], frame->d[7]);
kprintf(" a0 0x%08lx a1 0x%08lx a2 0x%08lx a3 0x%08lx\n",
frame->a[0], frame->a[1], frame->a[2], frame->a[3]);
kprintf(" a4 0x%08lx a5 0x%08lx a6 0x%08lx ""\n",
frame->a[4], frame->a[5], frame->a[6]);
frame->pc, frame->ccr);*/
kprintf(" pc 0x%08lx sr 0x%04x\n",
frame->cpu.pc, frame->cpu.sr);
#if 0
dprintf("r0-r3: 0x%08lx 0x%08lx 0x%08lx 0x%08lx\n", frame->d0, frame->d1, frame->d2, frame->d3);
dprintf("r4-r7: 0x%08lx 0x%08lx 0x%08lx 0x%08lx\n", frame->d4, frame->d5, frame->d6, frame->d7);
dprintf("r8-r11: 0x%08lx 0x%08lx 0x%08lx 0x%08lx\n", frame->a0, frame->a1, frame->a2, frame->a3);
dprintf("r12-r15: 0x%08lx 0x%08lx 0x%08lx 0x%08lx\n", frame->a4, frame->a5, frame->a6, frame->a7);
dprintf(" pc 0x%08lx sr 0x%08lx\n", frame->pc, frame->sr);
#endif
}
static addr_t
fault_address(struct iframe *iframe)
{
switch (iframe->cpu.type) {
case 0x0:
case 0x1:
return 0;
case 0x2:
return iframe->cpu.type_2.instruction_address;
case 0x3:
return iframe->cpu.type_3.effective_address;
case 0x7:
return iframe->cpu.type_7.effective_address;
case 0x9:
return iframe->cpu.type_9.instruction_address;
case 0xa:
return iframe->cpu.type_a.fault_address;
case 0xb:
return iframe->cpu.type_b.fault_address;
default:
return 0;
}
}
static bool
fault_was_write(struct iframe *iframe)
{
switch (iframe->cpu.type) {
case 0x7:
return !iframe->cpu.type_7.ssw.rw;
case 0xa:
return !iframe->cpu.type_a.ssw.rw;
case 0xb:
return !iframe->cpu.type_b.ssw.rw;
default:
panic("can't determine r/w from iframe type %d\n",
iframe->cpu.type);
return false;
}
}
extern "C" void m68k_exception_entry(struct iframe *iframe);
void
m68k_exception_entry(struct iframe *iframe)
{
int vector = iframe->cpu.vector >> 2;
bool hardwareInterrupt = false;
if (vector != -1) {
dprintf("m68k_exception_entry: time %lld vector 0x%x, iframe %p, "
"pc: %p\n", system_time(), vector, iframe, (void*)iframe->cpu.pc);
}
Thread *thread = thread_get_current_thread();
if (thread)
m68k_push_iframe(&thread->arch_info.iframes, iframe);
else
m68k_push_iframe(&gBootFrameStack, iframe);
switch (vector) {
case 0:
panic("system reset exception\n");
break;
case 2:
case 3:
{
bool kernelDebugger = debug_debugger_running();
if (kernelDebugger) {
if (thread && thread->fault_handler != 0) {
iframe->cpu.pc = reinterpret_cast<addr_t>(thread->fault_handler);
break;
}
panic("page fault in debugger without fault handler! Touching "
"address %p from ip %p\n", (void *)fault_address(iframe),
(void *)iframe->cpu.pc);
break;
} else if ((iframe->cpu.sr & SR_IP_MASK) != 0) {
if (thread && thread->fault_handler != 0) {
iframe->cpu.pc = reinterpret_cast<addr_t>(thread->fault_handler);
return;
}
panic("page fault, but interrupts were disabled. Touching "
"address %p from ip %p\n", (void *)fault_address(iframe),
(void *)iframe->cpu.pc);
break;
} else if (thread != NULL && thread->page_faults_allowed < 1) {
panic("page fault not allowed at this place. Touching address "
"%p from ip %p\n", (void *)fault_address(iframe),
(void *)iframe->cpu.pc);
}
enable_interrupts();
addr_t newip;
vm_page_fault(fault_address(iframe), iframe->cpu.pc,
fault_was_write(iframe),
false,
iframe->cpu.sr & SR_S,
&newip);
if (newip != 0) {
iframe->cpu.pc = newip;
}
break;
}
case 24:
dprintf("spurious interrupt\n");
break;
case 25:
case 26:
case 27:
case 28:
case 29:
case 30:
case 31:
{
#if 0
if (!sPIC) {
panic("m68k_exception_entry(): external interrupt although we "
"don't have a PIC driver!");
break;
}
#endif
M68KPlatform::Default()->AcknowledgeIOInterrupt(vector);
dprintf("handling I/O interrupts...\n");
io_interrupt_handler(vector, true);
#if 0
while ((irq = sPIC->acknowledge_io_interrupt(sPICCookie)) >= 0) {
io_interrupt_handler(irq, true);
}
#endif
dprintf("handling I/O interrupts done\n");
hardwareInterrupt = true;
break;
}
case 9:
default:
if (vector >= 64) {
if (M68KPlatform::Default()->AcknowledgeIOInterrupt(vector)) {
io_interrupt_handler(vector, true);
break;
}
}
dprintf("unhandled exception type 0x%x\n", vector);
print_iframe(iframe);
panic("unhandled exception type\n");
}
int state = disable_interrupts();
if (hardwareInterrupt && 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);
}
if (thread)
m68k_pop_iframe(&thread->arch_info.iframes);
else
m68k_pop_iframe(&gBootFrameStack);
}
status_t
arch_int_init(kernel_args *args)
{
status_t err;
addr_t vbr;
int i;
gExceptionVectors = (m68k_exception_handler *)args->arch_args.vir_vbr;
for (i = 0; i < M68K_EXCEPTION_VECTOR_COUNT; i++)
gExceptionVectors[i] = &__m68k_exception_common;
vbr = args->arch_args.phys_vbr;
asm volatile ("movec %0,%%vbr" : : "r"(vbr):);
return B_OK;
}
status_t
arch_int_init_post_vm(kernel_args *args)
{
status_t err;
err = M68KPlatform::Default()->InitPIC(args);
return err;
}
status_t
arch_int_init_io(kernel_args* args)
{
return B_OK;
}
#if 0
template<typename ModuleInfo>
struct Module : DoublyLinkedListLinkImpl<Module<ModuleInfo> > {
Module(ModuleInfo *module)
: module(module)
{
}
~Module()
{
if (module)
put_module(((module_info*)module)->name);
}
ModuleInfo *module;
};
typedef Module<interrupt_controller_module_info> PICModule;
struct PICModuleList : DoublyLinkedList<PICModule> {
~PICModuleList()
{
while (PICModule *module = First()) {
Remove(module);
delete module;
}
}
};
class DeviceTreeIterator {
public:
DeviceTreeIterator(device_manager_info *deviceManager)
: fDeviceManager(deviceManager),
fNode(NULL),
fParent(NULL)
{
Rewind();
}
~DeviceTreeIterator()
{
if (fParent != NULL)
fDeviceManager->put_device_node(fParent);
if (fNode != NULL)
fDeviceManager->put_device_node(fNode);
}
void Rewind()
{
fNode = fDeviceManager->get_root();
}
bool HasNext() const
{
return (fNode != NULL);
}
device_node_handle Next()
{
if (fNode == NULL)
return NULL;
device_node_handle foundNode = fNode;
device_node_handle child = NULL;
if (fDeviceManager->get_next_child_device(fNode, &child, NULL)
== B_OK) {
if (fParent != NULL)
fDeviceManager->put_device_node(fParent);
fParent = fNode;
fNode = child;
} else {
while (fParent != NULL) {
if (fDeviceManager->get_next_child_device(fParent, &fNode, NULL)
== B_OK) {
break;
}
fNode = fParent;
fParent = fDeviceManager->get_parent(fNode);
}
if (fParent == NULL) {
fDeviceManager->put_device_node(fNode);
fNode = NULL;
}
}
return foundNode;
}
private:
device_manager_info *fDeviceManager;
device_node_handle fNode;
device_node_handle fParent;
};
static void
get_interrupt_controller_modules(PICModuleList &list)
{
const char *namePrefix = "interrupt_controllers/";
size_t namePrefixLen = strlen(namePrefix);
char name[B_PATH_NAME_LENGTH];
size_t length;
uint32 cookie = 0;
while (get_next_loaded_module_name(&cookie, name, &(length = sizeof(name)))
== B_OK) {
if (length <= namePrefixLen
|| strncmp(name, namePrefix, namePrefixLen) != 0) {
continue;
}
interrupt_controller_module_info *moduleInfo;
if (get_module(name, (module_info**)&moduleInfo) != B_OK)
continue;
PICModule *module = new(nothrow) PICModule(moduleInfo);
if (!module) {
put_module(((module_info*)moduleInfo)->name);
continue;
}
list.Add(module);
}
}
static bool
probe_pic_device(device_node_handle node, PICModuleList &picModules)
{
for (PICModule *module = picModules.Head();
module;
module = picModules.GetNext(module)) {
bool noConnection;
if (module->module->info.supports_device(node, &noConnection) > 0) {
if (module->module->info.register_device(node) == B_OK)
return true;
}
}
return false;
}
#endif
status_t
arch_int_init_post_device_manager(struct kernel_args *args)
{
#if 0
PICModuleList picModules;
get_interrupt_controller_modules(picModules);
if (picModules.IsEmpty()) {
panic("arch_int_init_post_device_manager(): Found no PIC modules!");
return B_ENTRY_NOT_FOUND;
}
device_manager_info *deviceManager;
status_t error = get_module(B_DEVICE_MANAGER_MODULE_NAME,
(module_info**)&deviceManager);
if (error != B_OK) {
panic("arch_int_init_post_device_manager(): Failed to get device "
"manager: %s", strerror(error));
return error;
}
Module<device_manager_info> _deviceManager(deviceManager);
DeviceTreeIterator iterator(deviceManager);
while (device_node_handle node = iterator.Next())
probe_pic_device(node, picModules);
iterator.Rewind();
while (device_node_handle node = iterator.Next()) {
char *deviceType;
if (deviceManager->get_attr_string(node, B_DRIVER_DEVICE_TYPE,
&deviceType, false) == B_OK) {
bool isPIC
= (strcmp(deviceType, B_INTERRUPT_CONTROLLER_DRIVER_TYPE) == 0);
free(deviceType);
if (isPIC) {
driver_module_info *driver;
void *driverCookie;
error = deviceManager->init_driver(node, NULL, &driver,
&driverCookie);
if (error == B_OK) {
sPIC = (interrupt_controller_module_info *)driver;
sPICCookie = driverCookie;
return B_OK;
}
}
}
}
#endif
panic("arch_int_init_post_device_manager(): Found no supported PIC!");
return B_ENTRY_NOT_FOUND;
}
#if 0
struct m68k_cpu_exception_context *
m68k_get_cpu_exception_context(int cpu)
{
return sCPUExceptionContexts + cpu;
}
void
m68k_set_current_cpu_exception_context(struct m68k_cpu_exception_context *context)
{
addr_t physicalPage;
addr_t inPageOffset = (addr_t)context & (B_PAGE_SIZE - 1);
status_t error = vm_get_page_mapping(VMAddressSpace::KernelID(),
(addr_t)context - inPageOffset, &physicalPage);
if (error != B_OK) {
panic("m68k_set_current_cpu_exception_context(): Failed to get physical "
"address!");
return;
}
asm volatile("mtsprg0 %0" : : "r"(physicalPage + inPageOffset));
}
#endif