c - 向 ps2 键盘发送启用中断 (0xF4) 命令会导致系统崩溃。操作系统开发
问题描述
我刚刚完成了 IDT 的初始化并开始了 ps2 的东西。我得到了 ps2 控制器初始化程序,现在我想在第一个 ps2 端口或键盘中启用中断。这是我初始化 ps2 控制器的代码:
uint8_t initPS2()
{
// Start by disabling devices
disableFirstPS2Port();
disableSecondPS2Port();
// Flush the input buffer
inb(PS2DATA);
// Set the state of the status register.
outb(PS2COMMAND, 0x20);
uint8_t status = inb(PS2DATA);
uint8_t dual0 = status & 0b00100000 == 0b00100000; // if dual = 0, there is no second channel
status |= 0b01000011;
outb(PS2COMMAND, 0x60);
outb(PS2DATA, status);
// Perform self test
outb(PS2COMMAND, 0xAA);
uint8_t response = inb(PS2DATA);
if (response == 0xFC)
return 0xFC;
outb(PS2COMMAND, 0x60);
outb(PS2DATA, status);
// test dual channel controller
uint8_t dual = 0xFF;
if (dual0 == 1)
{
outb(PS2COMMAND, 0xAE);
outb(PS2COMMAND, 0x20);
status = inb(PS2DATA);
uint8_t dual1 = status & 0b00100000 == 0b00100000;
// if dual1 == 0, dual channel exists
outb(PS2COMMAND, 0xAD);
dual = !dual1;
}
else
{
dual = 0;
}
// Perform interface tests
outb(PS2COMMAND, 0xAB);
uint8_t responseIT1 = inb(PS2DATA);
uint8_t responseIT2;
if (dual == 1)
{
outb(PS2COMMAND, 0xA9);
responseIT2 = inb(PS2DATA);
}
if (responseIT1 != 0x0)
return responseIT1;
if (responseIT2 != 0x0 && dual == 1)
return responseIT2;
// Enable devices
enableFirstPS2Port();
if (dual == 1)
enableSecondPS2Port();
writeToFirstPS2Port(0xFF);
response = readPS2DataPort();
if (response == 0xFC)
return 0xFC;
writeToSecondPS2Port(0xFF);
response = readPS2DataPort();
if (response == 0xFC)
return 0xFC;
return 0;
}
我按照 OSDev wiki 上的教程来初始化 ps2 控制器。
现在我尝试使用 0xf4 命令在键盘上启用扫描:
uint8_t commandKeyboard(unsigned char byt)
{
writeToFirstPS2Port(byt);
uint8_t response = readPS2DataPort();
if (response == 0xFE)
{
writeToFirstPS2Port(byt);
response = readPS2DataPort();
if (response == 0xFE)
{
writeToFirstPS2Port(byt);
response = readPS2DataPort();
}
}
return response;
}
uint8_t initKeyboard()
{
uint8_t res = commandKeyboard(0xF4);
return res;
}
请注意,writeToFirstPS2Port
和其他类似的 ps2 函数确实具有适当的 controllerWait 函数,可确保数据端口是可写/可读的。
sti
我在加载我的 idt 之后调用此代码:
idt_flush(¤tIDT);
uint8_t ps2init = initPS2();
printk("PS2 Initalized, returned with 0x%x\n", ps2init);
while (1)
;
uint8_t keyboardinit = initKeyboard();
printk("PS2 Initalized, returned with 0x%x\n", keyboardinit);
outb(PIC1_DATA, 0);
outb(PIC2_DATA, 0);
//int d = 5 / 0;
asm volatile("sti");
这也是我的 IRQ 处理程序:
void irq1_handler(interrupt_frame_t *frame)
{
inb(0x60);
default_irq_handler(frame);
}
我认为键盘控制器需要我在发送另一个中断之前读取 0x60 端口,所以我认为这可能会有所帮助。
使用此代码会发生什么。我成功初始化了IDT,然后调用了initializePS2,它返回0,表示它成功了。之后,我开始了 initKeyboard 调用,它甚至没有返回,更不用说成功了。当我将命令发送到键盘时,系统崩溃了。我正在使用 qemu 运行它,我首先认为这是一个三重错误,所以我添加-d int
以查看引发了什么样的异常,但似乎在系统关闭之前没有引发异常。
这是该项目的github链接:
https://github.com/Danyy427/NEWOSDEV/tree/master/new%2064%20bit
这些文件位于 src/Kernel/Hardware/PS2 和 .../Hardware/Keyboard
IDT 在 src/Kernel/IDT 下
任何帮助表示赞赏。谢谢你。
解决方案
首先在您的中断处理程序上添加中断属性:
void __attribute__((interrupt)) Irq1Handler(interrupt_frame* frame);
这将导致编译器从堆栈中获取参数,而不是使用 __cdecl 调用约定获取它们。并且还执行 iret(中断返回)而不是正常的 ret(返回)。在编译中断文件“-mgeneral-regs-only”时,还要向 gcc 添加一个参数。这将阻止编译器使用 sse 寄存器并生成警告。
推荐阅读
- windows - 有没有办法读取设置计算机本地策略设置?
- php - 如何从 JSON 解码数组中获取数据?
- javascript - 无法将多行字符串输出到 HTML
- swift - 如何快速将 UInt 转换为 Int?
- javascript - 在 safari 中,位置为:fixed 的 body 返回 scrollLeft 0
- swift - ios13 中 UITextfield 的 PlaceholderColor 未设置为 PlaceHolderLabel
- c# - 24bit RGB 图像,但没有蓝色通道。我怎样才能访问它?
- php - 在mysql和php中存储注册表单数据和多个文件
- css - 是否可以使用 SASS for 循环遍历多个媒体查询?
- regex - 使用 grep 或 sed 的正则表达式不起作用