Page 1 of 1

[SOLVED] Flood of PS2 keyboard IRQs (PS2 driver error)

Posted: Sat Apr 06, 2024 5:12 pm
by austanss
My operating system has a functioning scheduler, and (I confidently presume) I've solidly configured my interrupt handling and the IOAPIC/LAPIC/PIC/IDT shenanigans. For context, my LAPIC timer interrupt handling works fine, and delivers interrupts at the expected frequency, so I have no reason to believe that my IOAPIC, LAPIC, or IDT configuration is bugged. I am not attempting to make a kernel shell with kernel keyboard handling. Instead, I have a "pid 0" process named 'frame' which contains a rudimentary startup shell and terminal, all isolated from the kernel and running happily in a contained userspace process. My LAPIC timer is used to coordinate context switches, albeit at a relatively low frequency (and also there is no real "switch" because there is only one process).

I have properly implemented the SYSCALL instruction, and I am confident that they work properly because my lone user process is able to properly acquire framebuffer access from the kernel, as is evidenced by the functional userspace terminal.

Basically, I've all but completely isolated the issue to the actual keyboard handling code on the kernel side. I've debugged the userspace keyboard handler ad nauseam, and found that what mainly happens is the first keyboard interrupt is handled correctly, and I do hit the breakpoints in the user code that handles the interrupts. However, after that interrupt returns, I am able to see the EOI being sent to the LAPIC, but after the interrupt ends I receive a spam of interrupts 0x22 (the keyboard IRQ vector). This spam is not triggered until a key is pressed. The flood is large enough that it probably prevents the process from having enough CPU time to execute properly. However, further debugging demonstrated that the interrupts do not trample each other, and each and every IRQ I successfully handles and proceeds to send an EOI.

Yet, it's not throwing any exceptions. I'm kind of at a loss. I'll keep debugging.

Re: Flood of PS2 keyboard IRQs (all other IRQs work fine)

Posted: Sat Apr 06, 2024 6:10 pm
by austanss
I've isolated the issue to the ps2_kbd_sync_leds() call in the IRQ handler. When I sync the LEDs, I use the ps2_kbd_send_command() function, which is a very thorough function that polls the PS/2 controller and waits for ACKs, deals with resends, and detects errors. I know it is unwise to send commands in the middle of an interrupt, so I commented the line and it worked fine. I do want the keyboard lights to be synchronized with the key states, though, and I wonder how else I could do that effectively without causing this weird behavior.

Re: Flood of PS2 keyboard IRQs (all other IRQs work fine)

Posted: Sat Apr 06, 2024 9:13 pm
by Octocontrabass
If you're getting an IRQ flood from the PS/2 controller, you're not reading port 0x60 to acknowledge the IRQ. I haven't figured out why that might be happening, but I did see you're sometimes reading port 0x60 more than once per IRQ, which is also wrong. The PS/2 controller raises an IRQ every time there's a byte available for you to read from port 0x60 - scan code or otherwise.

Re: Flood of PS2 keyboard IRQs (all other IRQs work fine)

Posted: Sun Apr 07, 2024 6:59 am
by austanss
I think it's possible I've made a slight design flaw. Whenever I press and release the A key, I get three interrupts, as logged by QEMU. Whenever I press and release the RIGHT ALT key, I get five interrupts. Clearly, from referring to the scan code set 2 reference, I am not getting one interrupt on press and one on release as I originally anticipated, but rather an interrupt for every scan code byte.

(The A key sends one byte on press, 0x1C; and two bytes on release, 0xF0 and 0x1C.)
(The RIGHT ALT key sends two bytes on press: 0xE0 and 0x11. It sends three on release: 0xE0, 0xF0, and 0x11.)

My code is designed with the idea that every IRQ corresponds to a press or release, but that's not the case.

Re: Flood of PS2 keyboard IRQs (all other IRQs work fine)

Posted: Sun Apr 07, 2024 7:25 am
by austanss
Sure enough, I was able to restructure my IRQ handler in a more sensible way that actually corresponds with controller behavior, and now keyboard input works perfectly. I'm not sure how I made this oversight, but I guess in all the documentation I've read about PS/2 input it was never really emphasized.

Re: [SOLVED] Flood of PS2 keyboard IRQs (PS2 driver error)

Posted: Sun Apr 07, 2024 9:48 am
by iansjack
It all depends upon which scan code set you are using: https://wiki.osdev.org/PS/2_Keyboard

It’s probably best to consider receiving and acknowledging the codes as separate from interpreting them. The interrupt handler can just read the code, put it in a queue and acknowledge it. The driver can then read codes from the queue and interpret them.

Re: [SOLVED] Flood of PS2 keyboard IRQs (PS2 driver error)

Posted: Sun Apr 07, 2024 10:56 am
by austanss
iansjack wrote:The interrupt handler can just read the code, put it in a queue and acknowledge it.
That's basically what my code does now. The kernel driver is designed to interpret scancodes into simpler byte-sized key-codes. It then sends those codes into a buffer, a buffer which is read by the sole user process that is assigned to handle and process keycodes. The userspace manager then sends the keycodes to whatever it wants, which in my case is just the basic user shell. Then the shell processes the actual keycodes and handles presses and releases and decodes the keys into characters. Then the userspace tty writes the characters to the screen, and the shell saves the character to the command buffer.

The interrupt handler though, is what I was doing wrong. Every time I got an interrupt, I was attempting to retrieve the whole scan code. What I do instead, now, is only read one scan code byte, save it to the buffer, see if the scancode is finished (with a large switch block that handles the scancode set 2), and if it is, then I interpret the scan code and send out the keycode to the processes, and clear the buffer. If the scan code is still incomplete, then I simply save the new scancode to the buffer, increment the buffer iterator, and send the EOI.