summaryrefslogtreecommitdiffstats
path: root/src/arch/i386/transitions/librm_mgmt.c
diff options
context:
space:
mode:
authorMichael Brown2014-05-27 15:23:49 +0200
committerMichael Brown2014-05-27 15:49:25 +0200
commitf3d423b26b2baa3e4c3919c5a2e12d793043f354 (patch)
tree12fa70a3b0d8c94fc9e93ae623d7d3c323027847 /src/arch/i386/transitions/librm_mgmt.c
parent[ipv6] Avoid potentially copying from a NULL pointer in ipv6_tx() (diff)
downloadipxe-f3d423b26b2baa3e4c3919c5a2e12d793043f354.tar.gz
ipxe-f3d423b26b2baa3e4c3919c5a2e12d793043f354.tar.xz
ipxe-f3d423b26b2baa3e4c3919c5a2e12d793043f354.zip
[librm] Allow for the PIC interrupt vector offset to be changed
Some external code (observed with FreeBSD's bootloader) will continue to make INT 13 calls after reconfiguring the 8259 PIC to change the vector offsets for IRQs. If an IRQ (e.g. the timer IRQ) subsequently occurs while iPXE is in protected mode, this will cause a general protection fault since the corresponding IDT entry is empty. A general protection fault is INT 0x0d, which happens to overlap with the original IRQ5. We therefore do have an ISR set up to handle a general protection fault, but this ISR simply reflects the interrupt down to the real-mode INT 0x0d and then attempts to return. Since our ISR is expecting a hardware interrupt rather than a general protection fault, it doesn't remove the error code from the stack before issuing the iret instruction; it therefore attempts to return to a garbage address. Since the segment part of this address is likely to be invalid, a second general protection fault occurs. This cycle continues until we run out of stack space and triple fault. Fix by reflecting all INTs down to real mode. This actually reduces the code size by four bytes (but increases the bss size by almost 2kB). Reported-by: Brian Rak <dn@devicenull.org> Signed-off-by: Michael Brown <mcb30@ipxe.org>
Diffstat (limited to 'src/arch/i386/transitions/librm_mgmt.c')
-rw-r--r--src/arch/i386/transitions/librm_mgmt.c11
1 files changed, 6 insertions, 5 deletions
diff --git a/src/arch/i386/transitions/librm_mgmt.c b/src/arch/i386/transitions/librm_mgmt.c
index 89668978..cc4765de 100644
--- a/src/arch/i386/transitions/librm_mgmt.c
+++ b/src/arch/i386/transitions/librm_mgmt.c
@@ -21,7 +21,7 @@ FILE_LICENCE ( GPL2_OR_LATER );
extern char interrupt_wrapper[];
/** The interrupt vectors */
-static struct interrupt_vector intr_vec[ IRQ_MAX + 1 ];
+static struct interrupt_vector intr_vec[NUM_INT];
/** The interrupt descriptor table */
struct interrupt_descriptor idt[NUM_INT] __attribute__ (( aligned ( 16 ) ));
@@ -90,13 +90,11 @@ void set_interrupt_vector ( unsigned int intr, void *vector ) {
*/
void init_idt ( void ) {
struct interrupt_vector *vec;
- unsigned int irq;
unsigned int intr;
/* Initialise the interrupt descriptor table and interrupt vectors */
- for ( irq = 0 ; irq <= IRQ_MAX ; irq++ ) {
- intr = IRQ_INT ( irq );
- vec = &intr_vec[irq];
+ for ( intr = 0 ; intr < NUM_INT ; intr++ ) {
+ vec = &intr_vec[intr];
vec->pushal = PUSHAL_INSN;
vec->movb = MOVB_INSN;
vec->intr = intr;
@@ -105,6 +103,9 @@ void init_idt ( void ) {
( uint32_t ) vec->next );
set_interrupt_vector ( intr, vec );
}
+ DBGC ( &intr_vec[0], "INTn vector at %p+%xn (phys %#lx+%xn)\n",
+ intr_vec, sizeof ( intr_vec[0] ),
+ virt_to_phys ( intr_vec ), sizeof ( intr_vec[0] ) );
/* Initialise the interrupt descriptor table register */
idtr.base = virt_to_phys ( idt );