irq.c 4.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173
  1. /* vim: tabstop=4 shiftwidth=4 noexpandtab
  2. * This file is part of ToaruOS and is released under the terms
  3. * of the NCSA / University of Illinois License - see LICENSE.md
  4. * Copyright (C) 2011-2018 K. Lange
  5. * Copyright (C) 2015 Dale Weiler
  6. *
  7. * Interrupt Requests
  8. *
  9. */
  10. #include <kernel/system.h>
  11. #include <kernel/logging.h>
  12. #include <kernel/module.h>
  13. #include <kernel/printf.h>
  14. /* Programmable interrupt controller */
  15. #define PIC1 0x20
  16. #define PIC1_COMMAND PIC1
  17. #define PIC1_OFFSET 0x20
  18. #define PIC1_DATA (PIC1+1)
  19. #define PIC2 0xA0
  20. #define PIC2_COMMAND PIC2
  21. #define PIC2_OFFSET 0x28
  22. #define PIC2_DATA (PIC2+1)
  23. #define PIC_EOI 0x20
  24. #define ICW1_ICW4 0x01
  25. #define ICW1_INIT 0x10
  26. #define PIC_WAIT() \
  27. do { \
  28. /* May be fragile */ \
  29. asm volatile("jmp 1f\n\t" \
  30. "1:\n\t" \
  31. " jmp 2f\n\t" \
  32. "2:"); \
  33. } while (0)
  34. /* Interrupts */
  35. static volatile int sync_depth = 0;
  36. #define SYNC_CLI() asm volatile("cli")
  37. #define SYNC_STI() asm volatile("sti")
  38. void int_disable(void) {
  39. /* Check if interrupts are enabled */
  40. uint32_t flags;
  41. asm volatile("pushf\n\t"
  42. "pop %%eax\n\t"
  43. "movl %%eax, %0\n\t"
  44. : "=r"(flags)
  45. :
  46. : "%eax");
  47. /* Disable interrupts */
  48. SYNC_CLI();
  49. /* If interrupts were enabled, then this is the first call depth */
  50. if (flags & (1 << 9)) {
  51. sync_depth = 1;
  52. } else {
  53. /* Otherwise there is now an additional call depth */
  54. sync_depth++;
  55. }
  56. }
  57. void int_resume(void) {
  58. /* If there is one or no call depths, reenable interrupts */
  59. if (sync_depth == 0 || sync_depth == 1) {
  60. SYNC_STI();
  61. } else {
  62. sync_depth--;
  63. }
  64. }
  65. void int_enable(void) {
  66. sync_depth = 0;
  67. SYNC_STI();
  68. }
  69. /* Interrupt Requests */
  70. #define IRQ_CHAIN_SIZE 16
  71. #define IRQ_CHAIN_DEPTH 4
  72. static void (*irqs[IRQ_CHAIN_SIZE])(void);
  73. static irq_handler_chain_t irq_routines[IRQ_CHAIN_SIZE * IRQ_CHAIN_DEPTH] = { NULL };
  74. static char * _irq_handler_descriptions[IRQ_CHAIN_SIZE * IRQ_CHAIN_DEPTH] = { NULL };
  75. char * get_irq_handler(int irq, int chain) {
  76. if (irq >= IRQ_CHAIN_SIZE) return NULL;
  77. if (chain >= IRQ_CHAIN_DEPTH) return NULL;
  78. return _irq_handler_descriptions[IRQ_CHAIN_SIZE * chain + irq];
  79. }
  80. void irq_install_handler(size_t irq, irq_handler_chain_t handler, char * desc) {
  81. /* Disable interrupts when changing handlers */
  82. SYNC_CLI();
  83. for (size_t i = 0; i < IRQ_CHAIN_DEPTH; i++) {
  84. if (irq_routines[i * IRQ_CHAIN_SIZE + irq])
  85. continue;
  86. irq_routines[i * IRQ_CHAIN_SIZE + irq] = handler;
  87. _irq_handler_descriptions[i * IRQ_CHAIN_SIZE + irq ] = desc;
  88. break;
  89. }
  90. SYNC_STI();
  91. }
  92. void irq_uninstall_handler(size_t irq) {
  93. /* Disable interrupts when changing handlers */
  94. SYNC_CLI();
  95. for (size_t i = 0; i < IRQ_CHAIN_DEPTH; i++)
  96. irq_routines[i * IRQ_CHAIN_SIZE + irq] = NULL;
  97. SYNC_STI();
  98. }
  99. static void irq_remap(void) {
  100. /* Cascade initialization */
  101. outportb(PIC1_COMMAND, ICW1_INIT|ICW1_ICW4); PIC_WAIT();
  102. outportb(PIC2_COMMAND, ICW1_INIT|ICW1_ICW4); PIC_WAIT();
  103. /* Remap */
  104. outportb(PIC1_DATA, PIC1_OFFSET); PIC_WAIT();
  105. outportb(PIC2_DATA, PIC2_OFFSET); PIC_WAIT();
  106. /* Cascade identity with slave PIC at IRQ2 */
  107. outportb(PIC1_DATA, 0x04); PIC_WAIT();
  108. outportb(PIC2_DATA, 0x02); PIC_WAIT();
  109. /* Request 8086 mode on each PIC */
  110. outportb(PIC1_DATA, 0x01); PIC_WAIT();
  111. outportb(PIC2_DATA, 0x01); PIC_WAIT();
  112. }
  113. static void irq_setup_gates(void) {
  114. for (size_t i = 0; i < IRQ_CHAIN_SIZE; i++) {
  115. idt_set_gate(32 + i, irqs[i], 0x08, 0x8E);
  116. }
  117. }
  118. void irq_install(void) {
  119. char buffer[16];
  120. for (int i = 0; i < IRQ_CHAIN_SIZE; i++) {
  121. sprintf(buffer, "_irq%d", i);
  122. irqs[i] = symbol_find(buffer);
  123. }
  124. irq_remap();
  125. irq_setup_gates();
  126. }
  127. void irq_ack(size_t irq_no) {
  128. if (irq_no >= 8) {
  129. outportb(PIC2_COMMAND, PIC_EOI);
  130. }
  131. outportb(PIC1_COMMAND, PIC_EOI);
  132. }
  133. void irq_handler(struct regs *r) {
  134. /* Disable interrupts when handling */
  135. int_disable();
  136. if (r->int_no <= 47 && r->int_no >= 32) {
  137. for (size_t i = 0; i < IRQ_CHAIN_DEPTH; i++) {
  138. irq_handler_chain_t handler = irq_routines[i * IRQ_CHAIN_SIZE + (r->int_no - 32)];
  139. if (!handler) break;
  140. if (handler(r)) {
  141. goto done;
  142. }
  143. }
  144. irq_ack(r->int_no - 32);
  145. }
  146. done:
  147. int_resume();
  148. }