首页 文章

在qemu中的irq处理程序崩溃:尝试在RAM或ROM之外执行代码

提问于
浏览
4

我一直在看内核开发教程,特别是bran's kernel,我用qemu测试,用 -kernel-cdrom 我遇到了崩溃 . 据我所知,这里发生了什么:
一旦我启用了中断( sti ),坑中断就会被我的irq处理程序捕获 . 我've reused the code from Bran'的例子:

.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

现在,当我启用qemu跟踪选项( -d cpu,exec,in_asm )时,我看到:

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

最后让我检查确实是我的

void irq_default_handler(isr_stack_frame_t sf);

C函数位于我期望的地址:

00100e81 <irq_default_handler>

eax和eip的值看起来很奇怪,但之前的mov显示了C函数的正确地址 . 也许缺少的最后一个信息是以下结构:

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;

所以从那里不确定去哪里 . 我不知道如何从通话中得到一个不正确的值 . 特别是因为我也试过直接调用C函数 . 任何建议或评论都是最受欢迎的 .

1 回答

  • 1

    我认为你的结构是关闭的 .

    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;
    

    pushad 指令以操作码编码顺序(eax,ecx,edx,ebx,esp,ebp,esi,edi)推送所有8个GPR . (现在查看链接的教程,Bran也有 esp . )

    你缺少的另一件事是 mov eax,esp 之前的 mov eax,esp 和调用:你传递给IRQ处理程序的 isr_stack_frame_t * 指针在你的情况下是错误的 . 您需要传递实际的帧地址 - 这是堆栈的顶部(井底),因为您只是使用推送将其组装在那里 .

    但是2使用 .intel_syntax noprefix - 我并不孤单! \ O /

    至于“好的英特尔风格”,有点像@FrankKotler已经说过:永远不要写 mov eax,foo ;总是写 mov eax,offset foomov eax,[foo] ,不留任何意外混淆的余地 .

    另一件事:你没有 sti 直接在 iret 之前:EI / DI(中断)标志存储在EFLAGS中,无论如何都由 iret 恢复 . 所以,只需删除该行(Bran也没有) .

相关问题