Question

I've been having a look at kernel development tutorials, especially bran's kernel, that i test with qemu, either with -kernel or -cdrom I'm getting a crash. As far as I understand here is what happens:
once I've enabled the interrupts (sti), the pit interrupts are caught by my irq handler. I've reused the code from Bran's example:

.intel_syntax noprefix
.global irq00
.extern_default_handler
irq00:
    cli
    push 0
    push 32
    jmp irq_common_stub
irq_common_stub:
    pushad
    push ds
    push es
    push fs
    push gs
    mov ax, 0x10
    mov ds, ax
    mov es, ax
    mov fs, ax
    mov gs, ax
    push eax
    mov eax, irq_default_handler
    call eax
    etc...
    add esp, 8
    sti
    iret

now when I enable qemu trace options (-d cpu,exec,in_asm) I see this:

IN:
0x0010121b: mov   %eax, %fs
0x0010121d: mov   %eax, %gs
0x0010121f: mov   %esp, %eax
0x00101222: mov   0x100e81, %eax
0x00101227: call  *%eax

Trace 0xb3aa3360 [0010121b]
EAX=83535657 EBX=00009500 ECX=0000000f EDX=00000000
ESI=00000000 EDI=0010a000 EBP=00000000 ESP=00107fc8
EIP=83535657 EFL=00000046 [---Z-P-] CPL=0 II=0 A20=1 SMM=0 HLT=0
ES =0010 00000000 ffffffff 00cf9300 DPL=0 DS   [-WA]
CS =0008 00000000 ffffffff 00cf9300 DPL=0 CS32 [-R-]
SS =0008 00000000 ffffffff 00cf9300 DPL=0 DS   [-WA]
DS =0008 00000000 ffffffff 00cf9300 DPL=0 DS   [-WA]
FS =0008 00000000 ffffffff 00cf9300 DPL=0 DS   [-WA]
GS =0008 00000000 ffffffff 00cf9300 DPL=0 DS   [-WA]
LDT=0000 00000000 0000ffff 00008200 DPL=0 LDT
TR =0000 00000000 0000ffff 00008b00 DPL=0 TSS32-busy
GDT=     00108060 0000001f 
IDT=     001080a0 0000071f
CR0=00000011 CR2=00000000 CR3=00000000 CR4=00000000
DR0=00000000 DR1=00000000 DR2=00000000 DR3=00000000 
DR6=ffff0ff0 DR7=00000400 
CCS=00000000 CCD=00000000 CCO=SUBL
EFER=0000000000000000
qemu fatal: trying to execute code outside RAM or ROM at 0x83535657

finaly an objdump -d kernel.bin let me check that indeed my

void irq_default_handler(isr_stack_frame_t sf);

C function is at the address I expect:

00100e81 <irq_default_handler>

the value of eax and eip looks strange, but the mov just before shows the correct address of the C function. maybe the last information missing is the following structure:

 typedef struct {
     uint32_t gs, ds, es, ds;
     uint32_t edi, esi, ebp, ebx, edx, ecx, eax;
     uint32_t int_number, err_code;
     uint32_t eip, cs, eflags, useresp, ss;
 } isr_stack_frame_t;  

so from there not sure where to go. not sure how I end up with an incorrect value in eip from my call. especially since I also tried calling directly the C function. any advise or comment would be most welcome.

Was it helpful?

Solution

I think your struct is off.

typedef struct {
     uint32_t gs, ds, es, ds;
     uint32_t edi, esi, ebp, ebx, edx, ecx, eax;
                           ↑↑
     uint32_t int_number, err_code;
     uint32_t eip, cs, eflags, useresp, ss;
} isr_stack_frame_t;

The pushad instruction pushes all eight GPRs in the opcode encoding order (eax, ecx, edx, ebx, esp, ebp, esi, edi). (Now looking at the tutorial linked, Bran also has esp there.)

The other thing you are missing is the mov eax,esp before push eax and the call: the isr_stack_frame_t * pointer you are passing to the IRQ handler is wrong in your case. You need to pass the actual frame address – which is the top (well bottom) of the stack since you just assembled it there using pushes.

But +2 for using .intel_syntax noprefix – I'm not alone! \o/

As for “good Intel style” and, somewhat like @FrankKotler already said: never write mov eax,foo; always write either mov eax,offset foo or mov eax,[foo] to leave no room for accidental confusion.

Another thing: you do not sti directly before the iret: The EI/DI (Interrupt) flag is stored in EFLAGS, which is restored by iret anyway. So, just remove that line (Bran also doesn't have it).

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top