1 /*
2 * Copyright 2003-2011, Haiku, Inc. All rights reserved.
3 * Distributed under the terms of the MIT License.
4 *
5 * Authors:
6 * Axel Dörfler <axeld@pinc-software.de>
7 * Ingo Weinhold <bonefish@cs.tu-berlin.de>
8 * François Revol <revol@free.fr>
9 *
10 * Copyright 2001, Travis Geiselbrecht. All rights reserved.
11 * Distributed under the terms of the NewOS License.
12 */
13
14
15 #include <int.h>
16
17 #include <arch_platform.h>
18 #include <arch/smp.h>
19 #include <boot/kernel_args.h>
20 #include <device_manager.h>
21 #include <kscheduler.h>
22 #include <interrupt_controller.h>
23 #include <smp.h>
24 #include <thread.h>
25 #include <timer.h>
26 #include <util/AutoLock.h>
27 #include <util/DoublyLinkedList.h>
28 #include <util/kernel_cpp.h>
29 #include <vm/vm.h>
30 #include <vm/vm_priv.h>
31 #include <vm/VMAddressSpace.h>
32 #include <string.h>
33
34 #warning M68K: writeme!
35
36
37 //#define TRACE_ARCH_INT
38 #ifdef TRACE_ARCH_INT
39 # define TRACE(x) dprintf x
40 #else
41 # define TRACE(x) ;
42 #endif
43
44 typedef void (*m68k_exception_handler)(void);
45 #define M68K_EXCEPTION_VECTOR_COUNT 256
46 #warning M68K: align on 4 ?
47 //m68k_exception_handler gExceptionVectors[M68K_EXCEPTION_VECTOR_COUNT];
48 m68k_exception_handler *gExceptionVectors;
49
50 // defined in arch_exceptions.S
51 extern "C" void __m68k_exception_noop(void);
52 extern "C" void __m68k_exception_common(void);
53
54 extern int __irqvec_start;
55 extern int __irqvec_end;
56
57 extern"C" void m68k_exception_tail(void);
58
59 // current fault handler
60 addr_t gFaultHandler;
61
62 // An iframe stack used in the early boot process when we don't have
63 // threads yet.
64 struct iframe_stack gBootFrameStack;
65
66 // interrupt controller interface (initialized
67 // in arch_int_init_post_device_manager())
68 //static struct interrupt_controller_module_info *sPIC;
69 //static void *sPICCookie;
70
71
72 void
arch_int_enable_io_interrupt(int32 irq)73 arch_int_enable_io_interrupt(int32 irq)
74 {
75 //if (!sPIC)
76 // return;
77
78 // TODO: I have no idea, what IRQ type is appropriate.
79 //sPIC->enable_io_interrupt(sPICCookie, irq, IRQ_TYPE_LEVEL);
80 M68KPlatform::Default()->EnableIOInterrupt(irq);
81 }
82
83
84 void
arch_int_disable_io_interrupt(int32 irq)85 arch_int_disable_io_interrupt(int32 irq)
86 {
87 //if (!sPIC)
88 // return;
89
90 //sPIC->disable_io_interrupt(sPICCookie, irq);
91 M68KPlatform::Default()->DisableIOInterrupt(irq);
92 }
93
94
95 /* arch_int_*_interrupts() and friends are in arch_asm.S */
96
97
98 int32
arch_int_assign_to_cpu(int32 irq,int32 cpu)99 arch_int_assign_to_cpu(int32 irq, int32 cpu)
100 {
101 // Not yet supported.
102 return 0;
103 }
104
105
106 static void
print_iframe(struct iframe * frame)107 print_iframe(struct iframe *frame)
108 {
109 dprintf("iframe at %p:\n", frame);
110 dprintf(" d0 0x%08lx d1 0x%08lx d2 0x%08lx d3 0x%08lx\n",
111 frame->d[0], frame->d[1], frame->d[2], frame->d[3]);
112 kprintf(" d4 0x%08lx d5 0x%08lx d6 0x%08lx d7 0x%08lx\n",
113 frame->d[4], frame->d[5], frame->d[6], frame->d[7]);
114 kprintf(" a0 0x%08lx a1 0x%08lx a2 0x%08lx a3 0x%08lx\n",
115 frame->a[0], frame->a[1], frame->a[2], frame->a[3]);
116 kprintf(" a4 0x%08lx a5 0x%08lx a6 0x%08lx "/*"a7 0x%08lx (sp)"*/"\n",
117 frame->a[4], frame->a[5], frame->a[6]/*, frame->a[7]*/);
118
119 /*kprintf(" pc 0x%08lx ccr 0x%02x\n",
120 frame->pc, frame->ccr);*/
121 kprintf(" pc 0x%08lx sr 0x%04x\n",
122 frame->cpu.pc, frame->cpu.sr);
123 #if 0
124 dprintf("r0-r3: 0x%08lx 0x%08lx 0x%08lx 0x%08lx\n", frame->d0, frame->d1, frame->d2, frame->d3);
125 dprintf("r4-r7: 0x%08lx 0x%08lx 0x%08lx 0x%08lx\n", frame->d4, frame->d5, frame->d6, frame->d7);
126 dprintf("r8-r11: 0x%08lx 0x%08lx 0x%08lx 0x%08lx\n", frame->a0, frame->a1, frame->a2, frame->a3);
127 dprintf("r12-r15: 0x%08lx 0x%08lx 0x%08lx 0x%08lx\n", frame->a4, frame->a5, frame->a6, frame->a7);
128 dprintf(" pc 0x%08lx sr 0x%08lx\n", frame->pc, frame->sr);
129 #endif
130 }
131
132
133 static addr_t
fault_address(struct iframe * iframe)134 fault_address(struct iframe *iframe)
135 {
136 switch (iframe->cpu.type) {
137 case 0x0:
138 case 0x1:
139 return 0;
140 case 0x2:
141 return iframe->cpu.type_2.instruction_address;
142 case 0x3:
143 return iframe->cpu.type_3.effective_address;
144 case 0x7:
145 return iframe->cpu.type_7.effective_address;
146 case 0x9:
147 return iframe->cpu.type_9.instruction_address;
148 case 0xa:
149 return iframe->cpu.type_a.fault_address;
150 case 0xb:
151 return iframe->cpu.type_b.fault_address;
152 default:
153 return 0;
154 }
155 }
156
157
158 static bool
fault_was_write(struct iframe * iframe)159 fault_was_write(struct iframe *iframe)
160 {
161 switch (iframe->cpu.type) {
162 case 0x7:
163 return !iframe->cpu.type_7.ssw.rw;
164 case 0xa:
165 return !iframe->cpu.type_a.ssw.rw;
166 case 0xb:
167 return !iframe->cpu.type_b.ssw.rw;
168 default:
169 panic("can't determine r/w from iframe type %d\n",
170 iframe->cpu.type);
171 return false;
172 }
173 }
174
175
176 extern "C" void m68k_exception_entry(struct iframe *iframe);
177 void
m68k_exception_entry(struct iframe * iframe)178 m68k_exception_entry(struct iframe *iframe)
179 {
180 int vector = iframe->cpu.vector >> 2;
181 bool hardwareInterrupt = false;
182
183 if (vector != -1) {
184 dprintf("m68k_exception_entry: time %lld vector 0x%x, iframe %p, "
185 "pc: %p\n", system_time(), vector, iframe, (void*)iframe->cpu.pc);
186 }
187
188 Thread *thread = thread_get_current_thread();
189
190 // push iframe
191 if (thread)
192 m68k_push_iframe(&thread->arch_info.iframes, iframe);
193 else
194 m68k_push_iframe(&gBootFrameStack, iframe);
195
196 switch (vector) {
197 case 0: // system reset
198 panic("system reset exception\n");
199 break;
200 case 2: // bus error
201 case 3: // address error
202 {
203 bool kernelDebugger = debug_debugger_running();
204
205 if (kernelDebugger) {
206 // if this thread has a fault handler, we're allowed to be here
207 if (thread && thread->fault_handler != 0) {
208 iframe->cpu.pc = reinterpret_cast<addr_t>(thread->fault_handler);
209 break;
210 }
211
212
213 // otherwise, not really
214 panic("page fault in debugger without fault handler! Touching "
215 "address %p from ip %p\n", (void *)fault_address(iframe),
216 (void *)iframe->cpu.pc);
217 break;
218 } else if ((iframe->cpu.sr & SR_IP_MASK) != 0) {
219 // interrupts disabled
220
221 // If a page fault handler is installed, we're allowed to be here.
222 // TODO: Now we are generally allowing user_memcpy() with interrupts
223 // disabled, which in most cases is a bug. We should add some thread
224 // flag allowing to explicitly indicate that this handling is desired.
225 if (thread && thread->fault_handler != 0) {
226 iframe->cpu.pc = reinterpret_cast<addr_t>(thread->fault_handler);
227 return;
228 }
229
230 // if the interrupts were disabled, and we are not running the
231 // kernel startup the page fault was not allowed to happen and
232 // we must panic
233 panic("page fault, but interrupts were disabled. Touching "
234 "address %p from ip %p\n", (void *)fault_address(iframe),
235 (void *)iframe->cpu.pc);
236 break;
237 } else if (thread != NULL && thread->page_faults_allowed < 1) {
238 panic("page fault not allowed at this place. Touching address "
239 "%p from ip %p\n", (void *)fault_address(iframe),
240 (void *)iframe->cpu.pc);
241 }
242
243 enable_interrupts();
244
245 addr_t newip;
246
247 vm_page_fault(fault_address(iframe), iframe->cpu.pc,
248 fault_was_write(iframe), // store or load
249 false,
250 iframe->cpu.sr & SR_S, // was the system in user or supervisor
251 &newip);
252 if (newip != 0) {
253 // the page fault handler wants us to modify the iframe to set the
254 // IP the cpu will return to to be this ip
255 iframe->cpu.pc = newip;
256 }
257 break;
258 }
259
260 case 24: // spurious interrupt
261 dprintf("spurious interrupt\n");
262 break;
263 case 25: // autovector interrupt
264 case 26: // autovector interrupt
265 case 27: // autovector interrupt
266 case 28: // autovector interrupt
267 case 29: // autovector interrupt
268 case 30: // autovector interrupt
269 case 31: // autovector interrupt
270 {
271 #if 0
272 if (!sPIC) {
273 panic("m68k_exception_entry(): external interrupt although we "
274 "don't have a PIC driver!");
275 break;
276 }
277 #endif
278 M68KPlatform::Default()->AcknowledgeIOInterrupt(vector);
279
280 dprintf("handling I/O interrupts...\n");
281 int_io_interrupt_handler(vector, true);
282 #if 0
283 while ((irq = sPIC->acknowledge_io_interrupt(sPICCookie)) >= 0) {
284 // TODO: correctly pass level-triggered vs. edge-triggered to the handler!
285 int_io_interrupt_handler(irq, true);
286 }
287 #endif
288 dprintf("handling I/O interrupts done\n");
289 hardwareInterrupt = true;
290 break;
291 }
292
293 case 9: // trace
294 default:
295 // vectors >= 64 are user defined vectors, used for IRQ
296 if (vector >= 64) {
297 if (M68KPlatform::Default()->AcknowledgeIOInterrupt(vector)) {
298 int_io_interrupt_handler(vector, true);
299 break;
300 }
301 }
302 dprintf("unhandled exception type 0x%x\n", vector);
303 print_iframe(iframe);
304 panic("unhandled exception type\n");
305 }
306
307 int state = disable_interrupts();
308 if (hardwareInterrupt && thread->post_interrupt_callback != NULL) {
309 void (*callback)(void*) = thread->post_interrupt_callback;
310 void* data = thread->post_interrupt_data;
311
312 thread->post_interrupt_callback = NULL;
313 thread->post_interrupt_data = NULL;
314
315 restore_interrupts(state);
316
317 callback(data);
318 } else if (thread->cpu->invoke_scheduler) {
319 SpinLocker schedulerLocker(thread->scheduler_lock);
320 scheduler_reschedule(B_THREAD_READY);
321 schedulerLocker.Unlock();
322 restore_interrupts(state);
323 }
324
325 // pop iframe
326 if (thread)
327 m68k_pop_iframe(&thread->arch_info.iframes);
328 else
329 m68k_pop_iframe(&gBootFrameStack);
330 }
331
332
333 status_t
arch_int_init(kernel_args * args)334 arch_int_init(kernel_args *args)
335 {
336 status_t err;
337 addr_t vbr;
338 int i;
339
340 gExceptionVectors = (m68k_exception_handler *)args->arch_args.vir_vbr;
341
342 /* fill in the vector table */
343 for (i = 0; i < M68K_EXCEPTION_VECTOR_COUNT; i++)
344 gExceptionVectors[i] = &__m68k_exception_common;
345
346 vbr = args->arch_args.phys_vbr;
347 /* point VBR to the new table */
348 asm volatile ("movec %0,%%vbr" : : "r"(vbr):);
349
350 return B_OK;
351 }
352
353
354 status_t
arch_int_init_post_vm(kernel_args * args)355 arch_int_init_post_vm(kernel_args *args)
356 {
357 status_t err;
358 err = M68KPlatform::Default()->InitPIC(args);
359 return err;
360 }
361
362
363 status_t
arch_int_init_io(kernel_args * args)364 arch_int_init_io(kernel_args* args)
365 {
366 return B_OK;
367 }
368
369
370 #if 0 /* PIC modules */
371 template<typename ModuleInfo>
372 struct Module : DoublyLinkedListLinkImpl<Module<ModuleInfo> > {
373 Module(ModuleInfo *module)
374 : module(module)
375 {
376 }
377
378 ~Module()
379 {
380 if (module)
381 put_module(((module_info*)module)->name);
382 }
383
384 ModuleInfo *module;
385 };
386
387 typedef Module<interrupt_controller_module_info> PICModule;
388
389 struct PICModuleList : DoublyLinkedList<PICModule> {
390 ~PICModuleList()
391 {
392 while (PICModule *module = First()) {
393 Remove(module);
394 delete module;
395 }
396 }
397 };
398
399
400 class DeviceTreeIterator {
401 public:
402 DeviceTreeIterator(device_manager_info *deviceManager)
403 : fDeviceManager(deviceManager),
404 fNode(NULL),
405 fParent(NULL)
406 {
407 Rewind();
408 }
409
410 ~DeviceTreeIterator()
411 {
412 if (fParent != NULL)
413 fDeviceManager->put_device_node(fParent);
414 if (fNode != NULL)
415 fDeviceManager->put_device_node(fNode);
416 }
417
418 void Rewind()
419 {
420 fNode = fDeviceManager->get_root();
421 }
422
423 bool HasNext() const
424 {
425 return (fNode != NULL);
426 }
427
428 device_node_handle Next()
429 {
430 if (fNode == NULL)
431 return NULL;
432
433 device_node_handle foundNode = fNode;
434
435 // get first child
436 device_node_handle child = NULL;
437 if (fDeviceManager->get_next_child_device(fNode, &child, NULL)
438 == B_OK) {
439 // move to the child node
440 if (fParent != NULL)
441 fDeviceManager->put_device_node(fParent);
442 fParent = fNode;
443 fNode = child;
444
445 // no more children; backtrack to find the next sibling
446 } else {
447 while (fParent != NULL) {
448 if (fDeviceManager->get_next_child_device(fParent, &fNode, NULL)
449 == B_OK) {
450 // get_next_child_device() always puts the node
451 break;
452 }
453 fNode = fParent;
454 fParent = fDeviceManager->get_parent(fNode);
455 }
456
457 // if we hit the root node again, we're done
458 if (fParent == NULL) {
459 fDeviceManager->put_device_node(fNode);
460 fNode = NULL;
461 }
462 }
463
464 return foundNode;
465 }
466
467 private:
468 device_manager_info *fDeviceManager;
469 device_node_handle fNode;
470 device_node_handle fParent;
471 };
472
473
474 static void
475 get_interrupt_controller_modules(PICModuleList &list)
476 {
477 const char *namePrefix = "interrupt_controllers/";
478 size_t namePrefixLen = strlen(namePrefix);
479
480 char name[B_PATH_NAME_LENGTH];
481 size_t length;
482 uint32 cookie = 0;
483 while (get_next_loaded_module_name(&cookie, name, &(length = sizeof(name)))
484 == B_OK) {
485 // an interrupt controller module?
486 if (length <= namePrefixLen
487 || strncmp(name, namePrefix, namePrefixLen) != 0) {
488 continue;
489 }
490
491 // get the module
492 interrupt_controller_module_info *moduleInfo;
493 if (get_module(name, (module_info**)&moduleInfo) != B_OK)
494 continue;
495
496 // add it to the list
497 PICModule *module = new(nothrow) PICModule(moduleInfo);
498 if (!module) {
499 put_module(((module_info*)moduleInfo)->name);
500 continue;
501 }
502 list.Add(module);
503 }
504 }
505
506
507 static bool
508 probe_pic_device(device_node_handle node, PICModuleList &picModules)
509 {
510 for (PICModule *module = picModules.Head();
511 module;
512 module = picModules.GetNext(module)) {
513 bool noConnection;
514 if (module->module->info.supports_device(node, &noConnection) > 0) {
515 if (module->module->info.register_device(node) == B_OK)
516 return true;
517 }
518 }
519
520 return false;
521 }
522 #endif /* PIC modules */
523
524 status_t
arch_int_init_post_device_manager(struct kernel_args * args)525 arch_int_init_post_device_manager(struct kernel_args *args)
526 {
527 #if 0 /* PIC modules */
528 // get the interrupt controller driver modules
529 PICModuleList picModules;
530 get_interrupt_controller_modules(picModules);
531 if (picModules.IsEmpty()) {
532 panic("arch_int_init_post_device_manager(): Found no PIC modules!");
533 return B_ENTRY_NOT_FOUND;
534 }
535
536 // get the device manager module
537 device_manager_info *deviceManager;
538 status_t error = get_module(B_DEVICE_MANAGER_MODULE_NAME,
539 (module_info**)&deviceManager);
540 if (error != B_OK) {
541 panic("arch_int_init_post_device_manager(): Failed to get device "
542 "manager: %s", strerror(error));
543 return error;
544 }
545 Module<device_manager_info> _deviceManager(deviceManager); // auto put
546
547 // iterate through the device tree and probe the interrupt controllers
548 DeviceTreeIterator iterator(deviceManager);
549 while (device_node_handle node = iterator.Next())
550 probe_pic_device(node, picModules);
551
552 // iterate through the tree again and get an interrupt controller node
553 iterator.Rewind();
554 while (device_node_handle node = iterator.Next()) {
555 char *deviceType;
556 if (deviceManager->get_attr_string(node, B_DRIVER_DEVICE_TYPE,
557 &deviceType, false) == B_OK) {
558 bool isPIC
559 = (strcmp(deviceType, B_INTERRUPT_CONTROLLER_DRIVER_TYPE) == 0);
560 free(deviceType);
561
562 if (isPIC) {
563 driver_module_info *driver;
564 void *driverCookie;
565 error = deviceManager->init_driver(node, NULL, &driver,
566 &driverCookie);
567 if (error == B_OK) {
568 sPIC = (interrupt_controller_module_info *)driver;
569 sPICCookie = driverCookie;
570 return B_OK;
571 }
572 }
573 }
574 }
575
576 #endif /* PIC modules */
577
578 // no PIC found
579 panic("arch_int_init_post_device_manager(): Found no supported PIC!");
580
581 return B_ENTRY_NOT_FOUND;
582 }
583
584
585 #if 0//PPC
586 // #pragma mark -
587
588 struct m68k_cpu_exception_context *
589 m68k_get_cpu_exception_context(int cpu)
590 {
591 return sCPUExceptionContexts + cpu;
592 }
593
594
595 void
596 m68k_set_current_cpu_exception_context(struct m68k_cpu_exception_context *context)
597 {
598 // translate to physical address
599 addr_t physicalPage;
600 addr_t inPageOffset = (addr_t)context & (B_PAGE_SIZE - 1);
601 status_t error = vm_get_page_mapping(VMAddressSpace::KernelID(),
602 (addr_t)context - inPageOffset, &physicalPage);
603 if (error != B_OK) {
604 panic("m68k_set_current_cpu_exception_context(): Failed to get physical "
605 "address!");
606 return;
607 }
608
609 asm volatile("mtsprg0 %0" : : "r"(physicalPage + inPageOffset));
610 }
611
612 #endif
613