pic8259.c revision 677833bc953b6cb418c701facbdcf4aa18d6c44e
/*
* Basic support for controlling the 8259 Programmable Interrupt Controllers.
*
* Initially written by Michael Brown (mcb30).
*/
#include <etherboot.h>
#include "pic8259.h"
#include "realmode.h"
#ifdef DEBUG_IRQ
#else
#define DBG(...)
#endif
/* State of trivial IRQ handler */
static uint16_t trivial_irq_previous_trigger_count = 0;
/* The actual trivial IRQ handler
*
* Note: we depend on the C compiler not realising that we're putting
* variables in the ".text16" section and therefore not forcing them
* back to the ".data" section. I don't see any reason to expect this
* behaviour to change.
*
* These must *not* be the first variables to appear in this file; the
* first variable to appear gets the ".data" directive.
*/
#ifdef VBOX
"pushw %bx\n\t"
"call 1f\n1:\tpopw %bx\n\t" /* PIC access to variables */
"popw %bx\n\t"
"iret\n\t"
"\n\t"
"\n\t"
"\n\t"
);
#else /* !VBOX */
"pushw %bx\n\t"
"call 1f\n1:\tpopw %bx\n\t" /* PIC access to variables */
"incw %cs:(_trivial_irq_trigger_count-1b)(%bx)\n\t"
"popw %bx\n\t"
"iret\n\t"
"\n\t"
".globl _trivial_irq_trigger_count\n\t"
"_trivial_irq_trigger_count: .short 0\n\t"
"\n\t"
".globl _trivial_irq_chain_to\n\t"
"_trivial_irq_chain_to: .short 0,0\n\t"
"\n\t"
".globl _trivial_irq_chain\n\t"
"_trivial_irq_chain: .byte 0\n\t"
);
#endif /* !VBOX */
extern volatile uint16_t _trivial_irq_trigger_count;
extern segoff_t _trivial_irq_chain_to;
extern int8_t _trivial_irq_chain;
/* Current locations of trivial IRQ handler. These will change at
* runtime when relocation is used; the handler needs to be copied to
* base memory before being installed.
*/
void (*trivial_irq_handler)P((void)) = _trivial_irq_handler;
/* Install a handler for the specified IRQ. Address of previous
* of IRQ will be preserved across call, therefore if the handler does
* chaining, ensure that either (a) IRQ is disabled before call, or
* (b) previous_handler points directly to the place that the handler
* picks up its chain-to address.
*/
segoff_t *previous_handler ) {
DBG ( "Invalid IRQ number %d\n" );
return 0;
}
DBG ( "Installing handler at %hx:%hx for IRQ %d (vector 0000:%hx),"
" leaving %s\n",
DBG ( "...(previous handler at %hx:%hx)\n",
return 1;
}
/* Remove handler for the specified IRQ. Routine checks that another
* handler has not been installed that chains to handler before
* restored to that specified by previously_enabled.
*/
segoff_t *previous_handler ) {
DBG ( "Invalid IRQ number %d\n" );
return 0;
}
DBG ( "Cannot remove handler for IRQ %d\n" );
return 0;
}
disable_irq ( irq );
return 1;
}
/* Install the trivial IRQ handler. This routine installs the
* handler, tests it and enables the IRQ.
*/
if ( trivial_irq_installed_on != IRQ_NONE ) {
DBG ( "Can install trivial IRQ handler only once\n" );
return 0;
}
DBG ( "Trivial IRQ handler not in base memory\n" );
return 0;
}
return 0;
DBG ( "Testing trivial IRQ handler\n" );
disable_irq ( irq );
if ( ! trivial_irq_triggered ( irq ) ) {
DBG ( "Installation of trivial IRQ handler failed\n" );
return 0;
}
/* Send EOI just in case there was a leftover interrupt */
send_specific_eoi ( irq );
DBG ( "Trivial IRQ handler installed successfully\n" );
enable_irq ( irq );
return 1;
}
/* Remove the trivial IRQ handler.
*/
if ( irq != trivial_irq_installed_on ) {
DBG ( "Cannot uninstall trivial IRQ handler from IRQ %d; "
"is installed on IRQ %d\n", irq,
return 0;
}
return 0;
if ( trivial_irq_triggered ( trivial_irq_installed_on ) ) {
DBG ( "Sending EOI for unwanted trivial IRQ\n" );
}
return 1;
}
/* Safe method to detect whether or not trivial IRQ has been
* triggered. Using this call avoids potential race conditions. This
* call will return success only once per trigger.
*/
int triggered = ( trivial_irq_this_trigger_count -
/* irq is not used at present, but we have it in the API for
* future-proofing; in case we want the facility to have
* multiple trivial IRQ handlers installed simultaneously.
*
* Avoid compiler warning about unused variable.
*/
return triggered ? 1 : 0;
}
/* Copy trivial IRQ handler to a new location. Typically used to copy
* the handler into base memory; when relocation is being used we need
* to do this before installing the handler.
*
* Call with target=NULL in order to restore the handler to its
* original location.
*/
target - (void*)_trivial_irq_handler );
DBG ( "Insufficient space to copy trivial IRQ handler\n" );
return 0;
}
if ( currently_installed_on != IRQ_NONE ) {
DBG ("WARNING: relocating trivial IRQ handler while in use\n");
if ( ! remove_trivial_irq_handler ( currently_installed_on ) )
return 0;
}
/* Do the actual copy */
DBG ( "Copying trivial IRQ handler to %hx:%hx\n",
} else {
DBG ( "Restoring trivial IRQ handler to original location\n" );
}
/* Update all the pointers to structures within the handler */
trivial_irq_handler = ( void (*)P((void)) )
( (void*)_trivial_irq_handler + offset );
( (void*)&_trivial_irq_trigger_count + offset );
( (void*)&_trivial_irq_chain_to + offset );
trivial_irq_chain = (uint8_t*)
( (void*)&_trivial_irq_chain + offset );
if ( currently_installed_on != IRQ_NONE ) {
if ( ! install_trivial_irq_handler ( currently_installed_on ) )
return 0;
}
return 1;
}
/* Send non-specific EOI(s). This seems to be inherently unsafe.
*/
if ( irq >= IRQ_PIC_CUTOFF ) {
}
}
/* Send specific EOI(s).
*/
if ( irq >= IRQ_PIC_CUTOFF ) {
ICR_REG(CHAINED_IRQ) );
}
}
/* Fake an IRQ
*/
struct {
/* Convert IRQ to INT number:
*
* subb $0x08,%cl Invert bit 3, set bits 4-7 iff irq < 8
* xorb $0x70,%cl Invert bits 4-6
* andb $0x7f,%cl Clear bit 7
*
* No, it's not the most intuitive method, but I was proud to
* get it down to three lines of assembler when this routine
* was originally implemented in pcbios.S.
*/
"popw %ax\n\t" /* %ax = INT number */
"call 1f\n1:\tpop %bx\n\t"
"movb %al, %cs:(2f-1b+1)(%bx)\n\t" /* Overwrite INT number..*/
"\n2:\tint $0x00\n\t" /* ..in this instruction */
);
}
/* Dump current 8259 status: enabled IRQs and handler addresses.
*/
#ifdef DEBUG_IRQ
void dump_irq_status ( void ) {
int irq = 0;
if ( irq_enabled ( irq ) ) {
}
}
}
#endif