Gigasoft wrote:The error shown in QEmu indicates a stack buffer overflow. The text "int15: func " overwrites a return frame.
Thanks. According to the Intel manuals, if bit 0 is set in the error code it means that the problem was with an external interrupt, if bit 1 is set it means that it was the IDT and if bit 2 is set, it means it was the GDT. If none of those are set, it's the LDT (I googled that, apparently it's a "Local Descriptor Table," but I don't know what that is, so I probably don't have one). I checked like this:
Code: Select all
#include <plx/isr.h>
struct {
char* message;
boolean has_errcode;
} exceptions[] = {
{"Division by zero", False},
{"Debug exception", False},
{"Non-maskable interrupt", False},
{"Breakpoint exception", False},
{"Into dected overflow", False},
{"Out-of-bounds exception", False},
{"Illegal instruction (invalid opcode)", False},
{"No coprocessor exception", False},
{"Double fault", True },
{"Coprocessor segment overrun exception", False},
{"Bad TSS", True },
{"Segment not present", True },
{"Stack fault", True },
{"General protection fault", True },
{"Page fault", True },
{"Unknown interrupt exception", False},
{"Coprocessor fault", False},
{"Alignment check exception", False},
{"Machine check exception", False}
};
isr_t interrupt_handlers[256];
void register_interrupt_handler(uint8 n, isr_t handler)
{
interrupt_handlers[n] = handler;
}
void irq_handler(struct reg regs)
{
/* Send EOI to the PICs */
if (regs.intno >= 40)
outb(0xA0, 0x20); /* Slave */
outb(0x20, 0x20); /* Master */
if (interrupt_handlers[regs.intno] != 0) {
isr_t handler = interrupt_handlers[regs.intno];
handler(regs);
}
}
void isr_handler(struct reg regs)
{
if (regs.intno <= 18) {
printk("CPU internal interrupt 0x%x: %s",
regs.intno, exceptions[regs.intno].message);
if (exceptions[regs.intno].has_errcode == True && regs.errcode != 0)
printk(" (errcode: 0x%x)", regs.errcode);
/* According to the Intel manuals; in errcode:
* +-----+-----+---------+
* | bit + set + not set |
* +-----+-----+---------+
* | 0 + EXT + - |
* +-----+-----+---------+
* | 1 + IDT + - |
* +-----+-----+---------+
* | 2 + GDT + LDT |
* +-----+-----+---------+
* Where EXT is an external device, IDT is the interrupt descriptor
* table, GDT is the global descriptor table and LDT is the local
* descriptor table.
*/
/* Here we check which bits are set with BIT_IS_SET (plx/common.h) */
if (BIT_IS_SET(regs.errcode, 0))
printk(" EXT\n");
else if (BIT_IS_SET(regs.errcode, 1))
printk(" IDT\n");
else if (BIT_IS_SET(regs.errcode, 2))
printk(" GDT\n");
else
printk(" LDT\n");
}
}
but I'm getting "LDT," annoyingly enough. It has to be the one thing I can't fix on my own...
The GPF could be a result of returning to an address with an incorrect segment from the exception handler. The exception handler for 0x0D must have an add esp, 4 before the iret to get rid of the error code. You should also check that any functions that the exception handler calls have the correct parameter byte count in their ret instruction.
Well, as I say, I'm basing everything I've written on JamesM's tutorial. The handler does add esp, 8 before re-enabling interrupts and then doing iret. I assume it's 8 and not 4 because the interrupt number is also pushed, so that's two 32-bit integers and therefore 8 bytes:
Code: Select all
;==============================================================================;
extern isr_handler ; C isr handler
extern irq_handler ; C irq handler
%macro ISR_NO_ERRCODE 1
global isr%1
isr%1:
cli
push byte 0 ; Push dummy error code
push byte %1
jmp isr_stub
%endmacro
;------------------------------------------------------------------------------;
%macro ISR_ERRCODE 1
global isr%1
isr%1:
cli
push byte %1
jmp isr_stub
%endmacro
;------------------------------------------------------------------------------;
%macro IRQ 2
global irq%1
irq%1:
cli
push byte 0
push byte %2
jmp irq_stub
%endmacro
;==============================================================================;
;==============================================================================;
ISR_NO_ERRCODE 0
ISR_NO_ERRCODE 1
ISR_NO_ERRCODE 2
ISR_NO_ERRCODE 3
ISR_NO_ERRCODE 4
ISR_NO_ERRCODE 5
ISR_NO_ERRCODE 6
ISR_NO_ERRCODE 7
ISR_ERRCODE 8
ISR_NO_ERRCODE 9
ISR_ERRCODE 10
ISR_ERRCODE 11
ISR_ERRCODE 12
ISR_ERRCODE 13
ISR_ERRCODE 14
ISR_NO_ERRCODE 15
ISR_NO_ERRCODE 16
ISR_NO_ERRCODE 17
ISR_ERRCODE 18
ISR_NO_ERRCODE 19
ISR_NO_ERRCODE 20
ISR_NO_ERRCODE 21
ISR_NO_ERRCODE 22
ISR_NO_ERRCODE 23
ISR_NO_ERRCODE 24
ISR_NO_ERRCODE 25
ISR_NO_ERRCODE 26
ISR_NO_ERRCODE 27
ISR_NO_ERRCODE 28
ISR_NO_ERRCODE 29
ISR_NO_ERRCODE 30
ISR_NO_ERRCODE 31
IRQ 0, 32
IRQ 1, 33
IRQ 2, 34
IRQ 3, 35
IRQ 4, 36
IRQ 5, 37
IRQ 6, 38
IRQ 7, 39
IRQ 8, 40
IRQ 9, 41
IRQ 10, 42
IRQ 11, 43
IRQ 12, 44
IRQ 13, 45
IRQ 14, 46
IRQ 15, 47
;------------------------------------------------------------------------------;
isr_stub:
pusha
mov ax, ds
push eax
mov ax, 0x10
mov ds, ax
mov es, ax
mov fs, ax
mov gs, ax
call isr_handler
pop eax
mov ds, ax
mov es, ax
mov fs, ax
mov gs, ax
popa
add esp, 8
sti
iret
;------------------------------------------------------------------------------;
irq_stub:
pusha
mov ax, ds
push eax
mov ax, 0x10
mov ds, ax
mov es, ax
mov fs, ax
mov gs, ax
call irq_handler
pop ebx
mov ds, ax
mov es, ax
mov fs, ax
mov gs, ax
popa
add esp, 8
sti
iret
;==============================================================================;
What does your exception handler look like?
At the moment, it's just the above. I will extend it obviously, but I don't have processes running, so I can't do anything except tell myself what happened. If