【发布时间】:2019-09-06 14:37:43
【问题描述】:
我有自己正在开发的操作系统,我之前也问过一些问题。目前,该操作系统在 QEMU 上运行良好,但在真实硬件上(在带有 IDE 驱动器的旧 AMD athlon64 系统和英特尔核心 i5 系统上测试),它无法处理任何键盘中断,而是收到一个 @ 987654324@ 异常。
我已经尝试打印中断处理程序收到的错误代码,它被设置为 0,这意味着它不是一个段错误。我在两台不同的机器上测试过,结果都一样。
以下是一些相关代码:
; Common ISR code
isr_common_stub:
; 1. Save CPU state
pusha ; Pushes edi,esi,ebp,esp,ebx,edx,ecx,eax
mov ax, ds ; Lower 16-bits of eax = ds.
push eax ; save the data segment descriptor
mov ax, 0x10 ; kernel data segment descriptor
mov ds, ax
mov es, ax
mov fs, ax
mov gs, ax
push esp
; 2. Call C handler
call isr_handler
pop eax
; 3. Restore state
pop eax
mov ds, ax
mov es, ax
mov fs, ax
mov gs, ax
popa
add esp, 8 ; Cleans up the pushed error code and pushed ISR number
sti
iret ; pops 5 things at once: CS, EIP, EFLAGS, SS, and ESP
; Common IRQ code. Identical to ISR code except for the 'call'
; and the 'pop ebx'
irq_common_stub:
pusha
mov ax, ds
push eax
mov ax, 0x10 ;0x10
mov ds, ax
mov es, ax
mov fs, ax
mov gs, ax
push esp ; At this point ESP is a pointer to where DS (and the rest
; of the interrupt handler state resides)
; Push ESP as 1st parameter as it's a
; pointer to a registers_t
call irq_handler
pop ebx ; Remove the saved ESP on the stack. Efficient to just pop it
; into any register. You could have done: add esp, 4 as well
pop ebx
mov ds, bx
mov es, bx
mov fs, bx
mov gs, bx
popa
add esp, 8
sti
iret
static void keyboard_callback(registers_t regs) {
/* The PIC leaves us the scancode in port 0x60 */
uint8_t scancode = port_byte_in(0x60);
//kprint_int(scancode);
bool iskeyup = false;
if (scancode >= KEYUPOFFSET) {
iskeyup = true;
scancode -= KEYUPOFFSET;
}
key_handler(scancode, iskeyup);
UNUSED(regs);
}
void isr_handler(registers_t *r) {
kprint("received interrupt: ");
char s[3];
int_to_ascii(r->int_no, s);
kprint(s);
kprint("\n");
kprint("error code: ");
char e[3];
int_to_ascii(r->err_code, s);
kprint(s);
kprint("\n");
kprint(exception_messages[r->int_no]);
kprint("\n");
}
void irq_handler(registers_t *r) {
/* After every interrupt we need to send an EOI to the PICs
* or they will not send another interrupt again */
if (r->int_no >= 40) port_byte_out(0xA0, 0x20); /* slave */
port_byte_out(0x20, 0x20); /* master */
/* Handle the interrupt in a more modular way */
if (interrupt_handlers[r->int_no] != 0) {
isr_t handler = interrupt_handlers[r->int_no];
handler(r);
}
else {
if (loaded == 1) {
kprint("Error! Unhandled interrupt!");
}
}
}
以及用于构建的存储库: https://github.com/Menotdan/DripOS/tree/dev
我希望键盘输入应该在真实硬件上工作,但不确定为什么不能。模拟器以x86_64 运行,硬件也是如此。但操作系统并没有工作,而是以尽可能快地处理中断的速度接收[GP#0]。
编辑:对链接间距感到抱歉,出于某种原因,StackOverflow 认为它是代码,所以我不得不在它们之间添加行,这样它就不会抱怨了:/
【问题讨论】:
-
问题需要在问题文本(minimal reproducible example)中包含最重要的信息,而不是在外部链接后面
-
堆栈溢出问题需要自包含。从代码中删除重现问题所需的所有内容,然后将简化的代码放入您的问题中。您的问题必须是可以回答的,无需点击任何链接。
-
你不应该假设这里有人会挖掘你的整个代码来找到你的错误。 Stackoverflow 社区不会因回答问题而获得报酬。尝试自己找到您的错误,或者至少删除代码以使其适合问题。另见how to ask
-
旁白:
cli和sti使用的指令在文件interrupt.asm中无关紧要。当发生中断时,无论如何都会清除IF控制位,并且iret会弹出标志寄存器,立即覆盖您设置该标志的任何内容。对于这个问题,我建议编写一个专门探索键盘处理程序的小测试代码。 -
注意:您在
rti假设段寄存器都包含相同的值,但情况可能并非如此。另外,您是否应该使用pushad和popad而不是pusha和popa?
标签: c assembly x86 x86-64 osdev