【问题标题】:Crash when adding or removing data in code on an IRQ在 IRQ 上的代码中添加或删除数据时崩溃
【发布时间】:2021-12-18 22:00:40
【问题描述】:

我正在开发一个操作系统作为一种爱好,我在处理键盘 IRQ 时遇到了一个奇怪的问题,我不知道为什么,但是当字符串中出现的字符串太少时,我得到了一个无效的操作码 ISR代码,以及添加更多字符串时的断点 ISR。

如果我添加足够数量的字符串,代码就可以正常工作,不会出错,但我不能只是在代码中添加字符串,不理解就忽略问题...

这是我的 ISR/IRQ 处理程序(宏的第一个参数是“isr”或“irq”,第二个参数是 c 处理程序,第三个参数仅在处理 irq 时出现)

%macro handler_macro 2-3
    handle_%+%1:
        pusha

        mov ax, ds
        push eax    ; save data segment
        mov esi, eax

        mov ax, 0x10
        mov ds, ax
        mov es, ax
        mov gs, ax
        mov fs, ax  ; set data segments

        push esp ; push the stack for the first arg of the c function
        call %2
        pop esp ; pop the pushed stack
        %if %0 = 3
            pop ebx
            mov ebx, esi
            mov ds, bx
            mov es, bx
            mov gs, bx
            mov fs, bx
        %else
            pop eax
            mov eax, esi
            mov ds, ax
            mov es, ax
            mov gs, ax
            mov fs, ax  ; restore data segments
        %endif

        popa

        add esp, 8  ; Remove the 2 params that's left in the stack
        sti          ; reenable interrupts
        iret
%endmacro

还有用于 irq 的 C 处理程序

/*
Structure in .h:
typedef struct reg_s {
    unsigned int eax_save;
    unsigned int edi, esi, ebp, esp, ebx, edx, ecx, eax;
    unsigned int code;
    unsigned int error;
    //unsigned int eip, cs, eflags, useresp, ss;
} reg_t;
*/

void irq_handler(reg_t *test)
    // Getting ISR 6 when all printfs are commented
    //printf("IRQ: %d\n", test->code); Uncommenting this one adds keeps on ISR 6
    //printf("");                      Same
    //printf("");                      Same
    //printf("");                      Uncommenting this one make the program works
    if (handler_fun[test->code] != NULL) {
        void (*fun)(void) = (void (*)(void)) handler_fun[test->code];
        //printf("Fun: %p\n", fun);
        fun();
    }
    pic_send_eio(test->code);
}

如果这可以帮助 fun 指针的值不会从一个测试更改为另一个测试,并且可能(也许?)改变某些东西的区别是,通过取消注释 4 个 printfs,从磁盘读取的扇区数从 45 切换到 46

【问题讨论】:

    标签: c assembly x86 osdev irq


    【解决方案1】:

    使用调试器(例如,可能是 Bochs 中内置的调试器)来检查 krenel 是否正确加载(并且在加载后和执行之前没有损坏);确定导致无效操作码异常的地址;和/或单步执行您的中断处理程序并弄清楚发生了什么。

    除此之外(与症状无关):

    a) 我觉得奇怪的是,您的程序集存根保存和恢复 ds 而不是 es 或其他数据段寄存器。要么根本不打扰(如果用户空间是“平面内存,没有分段,用户空间没有理由首先修改 DS”)或者做所有的段寄存器(如果用户空间预计使用段,这不是常见的设计选择)。

    b) IRQ 处理程序永远不需要有关被中断代码状态的任何信息(例如void irq_handler(void)),因此(对于 IRQ 处理程序)在您的程序集存根中完成了不必要的工作(包括将“被调用者保留”寄存器保存在 @987654324 @ IRQ 处理程序不需要访问)。异常处理程序不是 IRQ 处理程序(并且可能需要考虑一个错误代码,可能需要保存 CR2 以防止第二个页面错误破坏第一个页面错误的 CR2 内容,可能需要作为任务门来防止“内核堆栈错误”问题,可能必须解决诸如恢复标志之类的怪癖等);一般来说,异常处理程序可能根本不应该使用通用处理程序(或通用宏),因为它们的程序集存根有很多差异。

    c) 用于 PIC 芯片;很高兴在 IRQ 的程序集存根中检测到“虚假 IRQ 7”和“虚假 IRQ 15”,并且如果 IRQ 是真正的 IRQ,则仅调用(更高级别的)IRQ 处理程序。这使得更高级别的代码更简洁,同时也提高了虚假 IRQ 的效率(如果您不调用任何 C 代码,则无需遵守 C 调用约定)。

    d) 在 IRQ 处理程序中 iret 之前执行 sti 是没有意义的(因为 iret 无论如何都会从堆栈中加载 EFLAGS)。对于 IRQ 处理程序(除非先前启用了中断/IRQ,否则无法启动),可能有理由执行sti 然后nop 然后iret(以鼓励待处理的第二个 IRQ 在 CPU 仍在运行时中断在 CPL=0 时,避免切换到 CPL=3,然后必须立即再次切换回 CPL=0);但是这不会发生在sti 紧跟iret 的情况下,因为sti 导致在下一条指令之后(iret 之后)而不是sti 本身之后启用 IRQ。请注意,sti 然后nop 然后iret(还有sti 然后iret)对于异常处理程序是危险的,因为在必须保持禁用 IRQ 的情况下可能会发生异常。

    【讨论】:

      猜你喜欢
      • 2011-11-18
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-01-12
      • 1970-01-01
      • 2017-02-06
      相关资源
      最近更新 更多