You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

pic8259.c 9.9KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331
  1. /*
  2. * Basic support for controlling the 8259 Programmable Interrupt Controllers.
  3. *
  4. * Initially written by Michael Brown (mcb30).
  5. */
  6. #include <etherboot.h>
  7. #include "pic8259.h"
  8. #include "realmode.h"
  9. #ifdef DEBUG_IRQ
  10. #define DBG(...) printf ( __VA_ARGS__ )
  11. #else
  12. #define DBG(...)
  13. #endif
  14. /* State of trivial IRQ handler */
  15. irq_t trivial_irq_installed_on = IRQ_NONE;
  16. static uint16_t trivial_irq_previous_trigger_count = 0;
  17. /* The actual trivial IRQ handler
  18. *
  19. * Note: we depend on the C compiler not realising that we're putting
  20. * variables in the ".text16" section and therefore not forcing them
  21. * back to the ".data" section. I don't see any reason to expect this
  22. * behaviour to change.
  23. *
  24. * These must *not* be the first variables to appear in this file; the
  25. * first variable to appear gets the ".data" directive.
  26. */
  27. RM_FRAGMENT(_trivial_irq_handler,
  28. "pushw %bx\n\t"
  29. "call 1f\n1:\tpopw %bx\n\t" /* PIC access to variables */
  30. "incw %cs:(_trivial_irq_trigger_count-1b)(%bx)\n\t"
  31. "popw %bx\n\t"
  32. "iret\n\t"
  33. "\n\t"
  34. ".globl _trivial_irq_trigger_count\n\t"
  35. "_trivial_irq_trigger_count: .short 0\n\t"
  36. "\n\t"
  37. ".globl _trivial_irq_chain_to\n\t"
  38. "_trivial_irq_chain_to: .short 0,0\n\t"
  39. "\n\t"
  40. ".globl _trivial_irq_chain\n\t"
  41. "_trivial_irq_chain: .byte 0\n\t"
  42. );
  43. extern volatile uint16_t _trivial_irq_trigger_count;
  44. extern segoff_t _trivial_irq_chain_to;
  45. extern int8_t _trivial_irq_chain;
  46. /* Current locations of trivial IRQ handler. These will change at
  47. * runtime when relocation is used; the handler needs to be copied to
  48. * base memory before being installed.
  49. */
  50. void (*trivial_irq_handler)P((void)) = _trivial_irq_handler;
  51. uint16_t volatile *trivial_irq_trigger_count = &_trivial_irq_trigger_count;
  52. segoff_t *trivial_irq_chain_to = &_trivial_irq_chain_to;
  53. uint8_t *trivial_irq_chain = &_trivial_irq_chain;
  54. /* Install a handler for the specified IRQ. Address of previous
  55. * handler will be stored in previous_handler. Enabled/disabled state
  56. * of IRQ will be preserved across call, therefore if the handler does
  57. * chaining, ensure that either (a) IRQ is disabled before call, or
  58. * (b) previous_handler points directly to the place that the handler
  59. * picks up its chain-to address.
  60. */
  61. int install_irq_handler ( irq_t irq, segoff_t *handler,
  62. uint8_t *previously_enabled,
  63. segoff_t *previous_handler ) {
  64. segoff_t *irq_vector = IRQ_VECTOR ( irq );
  65. *previously_enabled = irq_enabled ( irq );
  66. if ( irq > IRQ_MAX ) {
  67. DBG ( "Invalid IRQ number %d\n" );
  68. return 0;
  69. }
  70. previous_handler->segment = irq_vector->segment;
  71. previous_handler->offset = irq_vector->offset;
  72. if ( *previously_enabled ) disable_irq ( irq );
  73. DBG ( "Installing handler at %hx:%hx for IRQ %d (vector 0000:%hx),"
  74. " leaving %s\n",
  75. handler->segment, handler->offset, irq, virt_to_phys(irq_vector),
  76. ( *previously_enabled ? "enabled" : "disabled" ) );
  77. DBG ( "...(previous handler at %hx:%hx)\n",
  78. previous_handler->segment, previous_handler->offset );
  79. irq_vector->segment = handler->segment;
  80. irq_vector->offset = handler->offset;
  81. if ( *previously_enabled ) enable_irq ( irq );
  82. return 1;
  83. }
  84. /* Remove handler for the specified IRQ. Routine checks that another
  85. * handler has not been installed that chains to handler before
  86. * uninstalling handler. Enabled/disabled state of the IRQ will be
  87. * restored to that specified by previously_enabled.
  88. */
  89. int remove_irq_handler ( irq_t irq, segoff_t *handler,
  90. uint8_t *previously_enabled,
  91. segoff_t *previous_handler ) {
  92. segoff_t *irq_vector = IRQ_VECTOR ( irq );
  93. if ( irq > IRQ_MAX ) {
  94. DBG ( "Invalid IRQ number %d\n" );
  95. return 0;
  96. }
  97. if ( ( irq_vector->segment != handler->segment ) ||
  98. ( irq_vector->offset != handler->offset ) ) {
  99. DBG ( "Cannot remove handler for IRQ %d\n" );
  100. return 0;
  101. }
  102. DBG ( "Removing handler for IRQ %d\n", irq );
  103. disable_irq ( irq );
  104. irq_vector->segment = previous_handler->segment;
  105. irq_vector->offset = previous_handler->offset;
  106. if ( *previously_enabled ) enable_irq ( irq );
  107. return 1;
  108. }
  109. /* Install the trivial IRQ handler. This routine installs the
  110. * handler, tests it and enables the IRQ.
  111. */
  112. int install_trivial_irq_handler ( irq_t irq ) {
  113. segoff_t trivial_irq_handler_segoff = SEGOFF(trivial_irq_handler);
  114. if ( trivial_irq_installed_on != IRQ_NONE ) {
  115. DBG ( "Can install trivial IRQ handler only once\n" );
  116. return 0;
  117. }
  118. if ( SEGMENT(trivial_irq_handler) > 0xffff ) {
  119. DBG ( "Trivial IRQ handler not in base memory\n" );
  120. return 0;
  121. }
  122. DBG ( "Installing trivial IRQ handler on IRQ %d\n", irq );
  123. if ( ! install_irq_handler ( irq, &trivial_irq_handler_segoff,
  124. trivial_irq_chain,
  125. trivial_irq_chain_to ) )
  126. return 0;
  127. trivial_irq_installed_on = irq;
  128. DBG ( "Testing trivial IRQ handler\n" );
  129. disable_irq ( irq );
  130. *trivial_irq_trigger_count = 0;
  131. trivial_irq_previous_trigger_count = 0;
  132. fake_irq ( irq );
  133. if ( ! trivial_irq_triggered ( irq ) ) {
  134. DBG ( "Installation of trivial IRQ handler failed\n" );
  135. remove_trivial_irq_handler ( irq );
  136. return 0;
  137. }
  138. /* Send EOI just in case there was a leftover interrupt */
  139. send_specific_eoi ( irq );
  140. DBG ( "Trivial IRQ handler installed successfully\n" );
  141. enable_irq ( irq );
  142. return 1;
  143. }
  144. /* Remove the trivial IRQ handler.
  145. */
  146. int remove_trivial_irq_handler ( irq_t irq ) {
  147. segoff_t trivial_irq_handler_segoff = SEGOFF(trivial_irq_handler);
  148. if ( trivial_irq_installed_on == IRQ_NONE ) return 1;
  149. if ( irq != trivial_irq_installed_on ) {
  150. DBG ( "Cannot uninstall trivial IRQ handler from IRQ %d; "
  151. "is installed on IRQ %d\n", irq,
  152. trivial_irq_installed_on );
  153. return 0;
  154. }
  155. if ( ! remove_irq_handler ( irq, &trivial_irq_handler_segoff,
  156. trivial_irq_chain,
  157. trivial_irq_chain_to ) )
  158. return 0;
  159. if ( trivial_irq_triggered ( trivial_irq_installed_on ) ) {
  160. DBG ( "Sending EOI for unwanted trivial IRQ\n" );
  161. send_specific_eoi ( trivial_irq_installed_on );
  162. }
  163. trivial_irq_installed_on = IRQ_NONE;
  164. return 1;
  165. }
  166. /* Safe method to detect whether or not trivial IRQ has been
  167. * triggered. Using this call avoids potential race conditions. This
  168. * call will return success only once per trigger.
  169. */
  170. int trivial_irq_triggered ( irq_t irq ) {
  171. uint16_t trivial_irq_this_trigger_count = *trivial_irq_trigger_count;
  172. int triggered = ( trivial_irq_this_trigger_count -
  173. trivial_irq_previous_trigger_count );
  174. /* irq is not used at present, but we have it in the API for
  175. * future-proofing; in case we want the facility to have
  176. * multiple trivial IRQ handlers installed simultaneously.
  177. *
  178. * Avoid compiler warning about unused variable.
  179. */
  180. if ( irq == IRQ_NONE ) {};
  181. trivial_irq_previous_trigger_count = trivial_irq_this_trigger_count;
  182. return triggered ? 1 : 0;
  183. }
  184. /* Copy trivial IRQ handler to a new location. Typically used to copy
  185. * the handler into base memory; when relocation is being used we need
  186. * to do this before installing the handler.
  187. *
  188. * Call with target=NULL in order to restore the handler to its
  189. * original location.
  190. */
  191. int copy_trivial_irq_handler ( void *target, size_t target_size ) {
  192. irq_t currently_installed_on = trivial_irq_installed_on;
  193. uint32_t offset = ( target == NULL ? 0 :
  194. target - (void*)_trivial_irq_handler );
  195. if (( target != NULL ) && ( target_size < TRIVIAL_IRQ_HANDLER_SIZE )) {
  196. DBG ( "Insufficient space to copy trivial IRQ handler\n" );
  197. return 0;
  198. }
  199. if ( currently_installed_on != IRQ_NONE ) {
  200. DBG ("WARNING: relocating trivial IRQ handler while in use\n");
  201. if ( ! remove_trivial_irq_handler ( currently_installed_on ) )
  202. return 0;
  203. }
  204. /* Do the actual copy */
  205. if ( target != NULL ) {
  206. DBG ( "Copying trivial IRQ handler to %hx:%hx\n",
  207. SEGMENT(target), OFFSET(target) );
  208. memcpy ( target, _trivial_irq_handler,
  209. TRIVIAL_IRQ_HANDLER_SIZE );
  210. } else {
  211. DBG ( "Restoring trivial IRQ handler to original location\n" );
  212. }
  213. /* Update all the pointers to structures within the handler */
  214. trivial_irq_handler = ( void (*)P((void)) )
  215. ( (void*)_trivial_irq_handler + offset );
  216. trivial_irq_trigger_count = (uint16_t*)
  217. ( (void*)&_trivial_irq_trigger_count + offset );
  218. trivial_irq_chain_to = (segoff_t*)
  219. ( (void*)&_trivial_irq_chain_to + offset );
  220. trivial_irq_chain = (uint8_t*)
  221. ( (void*)&_trivial_irq_chain + offset );
  222. if ( currently_installed_on != IRQ_NONE ) {
  223. if ( ! install_trivial_irq_handler ( currently_installed_on ) )
  224. return 0;
  225. }
  226. return 1;
  227. }
  228. /* Send non-specific EOI(s). This seems to be inherently unsafe.
  229. */
  230. void send_nonspecific_eoi ( irq_t irq ) {
  231. DBG ( "Sending non-specific EOI for IRQ %d\n", irq );
  232. if ( irq >= IRQ_PIC_CUTOFF ) {
  233. outb ( ICR_EOI_NON_SPECIFIC, PIC2_ICR );
  234. }
  235. outb ( ICR_EOI_NON_SPECIFIC, PIC1_ICR );
  236. }
  237. /* Send specific EOI(s).
  238. */
  239. void send_specific_eoi ( irq_t irq ) {
  240. DBG ( "Sending specific EOI for IRQ %d\n", irq );
  241. outb ( ICR_EOI_SPECIFIC | ICR_VALUE(irq), ICR_REG(irq) );
  242. if ( irq >= IRQ_PIC_CUTOFF ) {
  243. outb ( ICR_EOI_SPECIFIC | ICR_VALUE(CHAINED_IRQ),
  244. ICR_REG(CHAINED_IRQ) );
  245. }
  246. }
  247. /* Fake an IRQ
  248. */
  249. void fake_irq ( irq_t irq ) {
  250. struct {
  251. uint16_t int_number;
  252. } PACKED in_stack;
  253. /* Convert IRQ to INT number:
  254. *
  255. * subb $0x08,%cl Invert bit 3, set bits 4-7 iff irq < 8
  256. * xorb $0x70,%cl Invert bits 4-6
  257. * andb $0x7f,%cl Clear bit 7
  258. *
  259. * No, it's not the most intuitive method, but I was proud to
  260. * get it down to three lines of assembler when this routine
  261. * was originally implemented in pcbios.S.
  262. */
  263. in_stack.int_number = ( ( irq - 8 ) ^ 0x70 ) & 0x7f;
  264. RM_FRAGMENT(rm_fake_irq,
  265. "popw %ax\n\t" /* %ax = INT number */
  266. "call 1f\n1:\tpop %bx\n\t"
  267. "movb %al, %cs:(2f-1b+1)(%bx)\n\t" /* Overwrite INT number..*/
  268. "\n2:\tint $0x00\n\t" /* ..in this instruction */
  269. );
  270. real_call ( rm_fake_irq, &in_stack, NULL );
  271. }
  272. /* Dump current 8259 status: enabled IRQs and handler addresses.
  273. */
  274. #ifdef DEBUG_IRQ
  275. void dump_irq_status ( void ) {
  276. int irq = 0;
  277. for ( irq = 0; irq < 16; irq++ ) {
  278. if ( irq_enabled ( irq ) ) {
  279. printf ( "IRQ%d enabled, ISR at %hx:%hx\n", irq,
  280. IRQ_VECTOR(irq)->segment,
  281. IRQ_VECTOR(irq)->offset );
  282. }
  283. }
  284. }
  285. #endif