From f4b0f67cf6e58e58eab0e9fa52c5219b82673544 Mon Sep 17 00:00:00 2001 From: Ingo Weinhold Date: Tue, 7 Feb 2006 02:50:32 +0000 Subject: [PATCH] Added [arch_]int_post_device_manager() which is invoked after the device manager is initialized. For x86 it does nothing, but for PPC it searches for a supported interrupt controller and remembers it for later use. arch_int_{enable,disable}_io_interrupt() are implemented as well as handling of external exceptions (aka as I/O interrupts). We'll see later how well that works. git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@16271 a95241bf-73f2-0310-859d-f6bbb57e9c96 --- headers/private/kernel/arch/int.h | 1 + headers/private/kernel/int.h | 1 + src/system/kernel/arch/ppc/arch_int.cpp | 253 +++++++++++++++++++++++- src/system/kernel/arch/x86/arch_int.c | 7 + src/system/kernel/int.c | 7 + src/system/kernel/main.c | 2 + 6 files changed, 265 insertions(+), 6 deletions(-) diff --git a/headers/private/kernel/arch/int.h b/headers/private/kernel/arch/int.h index 854f215604..0cab718079 100644 --- a/headers/private/kernel/arch/int.h +++ b/headers/private/kernel/arch/int.h @@ -19,6 +19,7 @@ extern "C" { status_t arch_int_init(kernel_args *args); status_t arch_int_init_post_vm(kernel_args *args); +status_t arch_int_init_post_device_manager(struct kernel_args *args); void arch_int_enable_interrupts(void); int arch_int_disable_interrupts(void); diff --git a/headers/private/kernel/int.h b/headers/private/kernel/int.h index 42f2afa670..133b5dd68c 100644 --- a/headers/private/kernel/int.h +++ b/headers/private/kernel/int.h @@ -21,6 +21,7 @@ extern "C" { status_t int_init(struct kernel_args *args); status_t int_init_post_vm(struct kernel_args *args); +status_t int_init_post_device_manager(struct kernel_args *args); int int_io_interrupt_handler(int vector); bool interrupts_enabled(void); diff --git a/src/system/kernel/arch/ppc/arch_int.cpp b/src/system/kernel/arch/ppc/arch_int.cpp index f5a3e5c6db..aeb7daf219 100644 --- a/src/system/kernel/arch/ppc/arch_int.cpp +++ b/src/system/kernel/arch/ppc/arch_int.cpp @@ -12,13 +12,16 @@ #include -#include - #include +#include +#include #include +#include #include #include #include +#include +#include #include #include #include @@ -40,18 +43,30 @@ static ppc_cpu_exception_context sCPUExceptionContexts[SMP_MAX_CPUS]; // threads yet. struct iframe_stack gBootFrameStack; +// interrupt controller interface (initialized +// in arch_int_init_post_device_manager()) +static struct interrupt_controller_module_info *sPIC; +static void *sPICCookie; + void arch_int_enable_io_interrupt(int irq) { - return; + if (!sPIC) + return; + + // TODO: I have no idea, what IRQ type is appropriate. + sPIC->enable_io_interrupt(sPICCookie, irq, IRQ_TYPE_LEVEL); } void arch_int_disable_io_interrupt(int irq) { - return; + if (!sPIC) + return; + + sPIC->disable_io_interrupt(sPICCookie, irq); } @@ -150,9 +165,24 @@ ppc_exception_entry(int vector, struct iframe *iframe) } break; } + case 0x500: // external interrupt - panic("external interrrupt exception: unimplemented\n"); + { + if (!sPIC) { + panic("ppc_exception_entry(): external interrupt although we " + "don't have a PIC driver!"); + ret = B_HANDLED_INTERRUPT; + break; + } + +dprintf("handling I/O interrupts...\n"); + int irq; + while ((irq = sPIC->acknowledge_io_interrupt(sPICCookie)) >= 0) + ret = int_io_interrupt_handler(irq); +dprintf("handling I/O interrupts done\n"); break; + } + case 0x600: // alignment exception panic("alignment exception: unimplemented\n"); break; @@ -177,7 +207,7 @@ ppc_exception_entry(int vector, struct iframe *iframe) case 0xf00: // performance monitor exception panic("performance monitor exception: unimplemented\n"); break; - case 0xf20: // alitivec unavailable exception + case 0xf20: // altivec unavailable exception panic("alitivec unavailable exception: unimplemented\n"); break; case 0x1000: @@ -275,6 +305,217 @@ arch_int_init_post_vm(kernel_args *args) } +template +struct Module : DoublyLinkedListLinkImpl > { + Module(ModuleInfo *module) + : module(module) + { + } + + ~Module() + { + if (module) + put_module(((module_info*)module)->name); + } + + ModuleInfo *module; +}; + +typedef Module PICModule; + +struct PICModuleList : DoublyLinkedList { + ~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; + + // get first child + device_node_handle child = NULL; + if (fDeviceManager->get_next_child_device(fNode, &child, NULL) + == B_OK) { + // move to the child node + if (fParent != NULL) + fDeviceManager->put_device_node(fParent); + fParent = fNode; + fNode = child; + + // no more children; backtrack to find the next sibling + } else { + while (fParent != NULL) { + if (fDeviceManager->get_next_child_device(fParent, &fNode, NULL) + == B_OK) { + // get_next_child_device() always puts the node + break; + } + fNode = fParent; + fParent = fDeviceManager->get_parent(fNode); + } + + // if we hit the root node again, we're done + 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) { + // an interrupt controller module? + if (length <= namePrefixLen + || strncmp(name, namePrefix, namePrefixLen) != 0) { + continue; + } + + // get the module + interrupt_controller_module_info *moduleInfo; + if (get_module(name, (module_info**)&moduleInfo) != B_OK) + continue; + + // add it to the list + 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; +} + + +status_t +arch_int_init_post_device_manager(struct kernel_args *args) +{ + // get the interrupt controller driver modules + 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; + } + + // get the device manager module + 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 _deviceManager(deviceManager); // auto put + + // iterate through the device tree and probe the interrupt controllers + DeviceTreeIterator iterator(deviceManager); + while (device_node_handle node = iterator.Next()) + probe_pic_device(node, picModules); + + // iterate through the tree again and get an interrupt controller node + 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; + } + } + } + } + + // no PIC found + panic("arch_int_init_post_device_manager(): Found no supported PIC!"); + + return B_ENTRY_NOT_FOUND; +} + + // #pragma mark - struct ppc_cpu_exception_context * diff --git a/src/system/kernel/arch/x86/arch_int.c b/src/system/kernel/arch/x86/arch_int.c index b0cb635cab..a209605070 100644 --- a/src/system/kernel/arch/x86/arch_int.c +++ b/src/system/kernel/arch/x86/arch_int.c @@ -618,3 +618,10 @@ arch_int_init_post_vm(kernel_args *args) return area >= B_OK ? B_OK : area; } + + +status_t +arch_int_init_post_device_manager(struct kernel_args *args) +{ + return B_OK; +} diff --git a/src/system/kernel/int.c b/src/system/kernel/int.c index 05b42e296f..608b9fa572 100644 --- a/src/system/kernel/int.c +++ b/src/system/kernel/int.c @@ -126,6 +126,13 @@ int_init_post_vm(kernel_args *args) } +status_t +int_init_post_device_manager(kernel_args *args) +{ + return arch_int_init_post_device_manager(args); +} + + /** Install a handler to be called when an interrupt is triggered * for the given interrupt number with \a data as the argument. */ diff --git a/src/system/kernel/main.c b/src/system/kernel/main.c index 6c6e24ef22..4231eaadcf 100644 --- a/src/system/kernel/main.c +++ b/src/system/kernel/main.c @@ -218,6 +218,8 @@ main2(void *unused) // ToDo: device manager starts here, bus_init()/dev_init() won't be necessary anymore, // but instead, the hardware and drivers are rescanned then. + int_init_post_device_manager(&sKernelArgs); + TRACE(("Mount boot file system\n")); vfs_mount_boot_file_system(&sKernelArgs);