在 IRQ 上添加或删除代码中的数据时发生崩溃

问题描述 投票:0回答:2

我正在开发一个操作系统作为一种爱好,并且在处理键盘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
2个回答
1
投票

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

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

a)我觉得很奇怪,你的程序集存根保存和恢复

ds
但不保存和恢复
es
或其他数据段寄存器。要么根本不打扰(如果用户空间是“平面内存,没有分段,用户空间没有理由首先修改 DS”),要么执行所有段寄存器(如果用户空间预计使用段) ,这不是常见的设计选择)。

b) IRQ 处理程序永远不需要任何有关中断代码状态的信息(例如

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

c) 对于PIC芯片;最好能够在 IRQ 的程序集存根中检测“虚假 IRQ 7”和“虚假 IRQ 15”,并且仅在 IRQ 是真实 IRQ 时才调用(更高级别)IRQ 处理程序。这使得更高级别的代码更干净,同时还提高了虚假 IRQ 的效率(如果不调用任何 C 代码,则无需遵守 C 调用约定)。

d) 在 IRQ 处理程序中,在

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


0
投票

万一有人偶然发现这个,我找到了它的来源。

仅根据我在内存中加载内核的位置来看,这是一个非常糟糕的错误。

基本上,当我添加一些字节时,gcc 优化了程序以在内存中正确对齐函数的地址,这导致程序稍微增长。

内核大小的轻微增加使内核稍微覆盖了堆栈,从而导致系统出现恐慌。

因此,对于任何偶然发现同样事情的人:只需检查您的内核和堆栈是否正确放置(并且在执行远跳转之前,您的内核不会错误地覆盖引导加载程序)

我给出的另一个建议是为了避免这种愚蠢的错误:只需编写一个多阶段引导加载程序,这样您就可以解锁更多内存,将您的内核置于 1MB 裸露之上,并且此外还有更多高级引导加载程序,您不会遇到这种错误。

© www.soinside.com 2019 - 2024. All rights reserved.