Page 1 of 2
QEMU keyboard interrupt troubles
Posted: Sun Sep 06, 2015 5:48 am
by isaacwoods
I am relatively new to OS development (and only 14, so please bear with me a little) and am having troubles with getting the keyboard to actually interrupt using the QEMU emulator. I am pretty sure that the GDT, IDT, PIC and the required IRQ is setup correctly, because if I add
to my code, the IRQ is executed correctly. However, when I enter screen-grab mode and hit a key on my physical keyboard, the IRQ is not run at all! I did consider that QEMU wasn't listening to my keyboard correctly, but I can successfully interact with GRUB2 (my chosen bootloader) with the keyboard before my OS if even booted, so I think QEMU must be working correctly.
Does this sound like a problem with my code, or with my emulator, and how can I go about attempting to fix it? Thanks in advance (and sorry if this is a stupid question)!
Re: QEMU keyboard interrupt troubles
Posted: Sun Sep 06, 2015 7:27 am
by Octocontrabass
BaconWraith wrote:I am pretty sure that the GDT, IDT, PIC and the required IRQ is setup correctly, because if I add
to my code, the IRQ is executed correctly.
That only proves that the GDT, IDT, and IRQ handler are set up well enough to receive that interrupt. You could still have an issue with the PIC or the keyboard controller. (Plus, if you followed a tutorial to get to this point,
you probably copied the tutorial's mistakes.)
You should take another look at the code that programs the PIC and the keyboard controller.
Re: QEMU keyboard interrupt troubles
Posted: Sun Sep 06, 2015 8:30 am
by isaacwoods
Thank you for your reply. I hadn't realised that inducing the interrupt in software still meant that the PIC could be set up incorrectly, but it makes sense in hindsight. However, I don't think there is anything wrong with how I setup my PIC, I picked and chose between tutorials (and the Intel manuals) to get to this point, and so my PIC is set up like so:
Code: Select all
#define PIC_MASTER_COMMAND 0x20
#define PIC_MASTER_DATA 0x21
#define PIC_SLAVE_COMMAND 0xA0
#define PIC_SLAVE_DATA 0xA1
// Get the PIC's masks
unsigned char mask1 = inb(PIC_MASTER_DATA);
unsigned char mask2 = inb(PIC_SLAVE_DATA);
// Init ICW1
// Initialise both the master and slave PICs
outb(PIC_MASTER_COMMAND, 0x11);
outb(PIC_SLAVE_COMMAND, 0x11);
// Init ICW2
// Tell the PICs their respective vector offsets
outb(PIC_MASTER_DATA, 0x20);
outb(PIC_SLAVE_DATA, 0x70); // TODO: some sources say we should set this to 0x28
// Init ICW3
outb(PIC_MASTER_DATA, 0x04); // Tell the master PIC that there is a slave PIC at IRQ2
outb(PIC_SLAVE_DATA, 0x02); // Tell the slave PIC its cascade identity
// Init ICW4
// Give additional information about the PIC's environment
outb(PIC_MASTER_DATA, 0x01);
outb(PIC_SLAVE_DATA, 0x01);
// Mask interrupts
// Restore the previously saved masks
outb(PIC_MASTER_DATA, mask1);
outb(PIC_SLAVE_DATA, mask2);
#undef PIC_MASTER_COMMAND
#undef PIC_MASTER_DATA
#undef PIC_SLAVE_COMMAND
#undef PIC_SLAVE_DATA
Sorry if I'm missing something obvious, but I think I've set up the vector offset correctly. The keyboard is IRQ1, so 1 + 0x20 = 33. Interrupt 33 in the IDT is set to be handled correctly. Please may you have a look to see if I'm missing anything?
Re: QEMU keyboard interrupt troubles
Posted: Sun Sep 06, 2015 10:10 am
by Octocontrabass
Where is the code that unmasks IRQ1?
Re: QEMU keyboard interrupt troubles
Posted: Mon Sep 07, 2015 11:06 am
by isaacwoods
Sorry, what do you mean by unmasking IRQ1? If you mean the last two bytes sent during initialisation:
Code: Select all
// Restore the previously saved masks
outb(PIC_MASTER_DATA, mask1);
outb(PIC_SLAVE_DATA, mask2);
If I pass 0x0 instead, it also doesn't work, so unless that isn't unmasking it, I'm not sure what you mean.
Re: QEMU keyboard interrupt troubles
Posted: Tue Sep 08, 2015 1:09 am
by SpyderTL
You saved the current value of these registers, and you restored these values at the end, but you never actually changed the value of these registers.
Re: QEMU keyboard interrupt troubles
Posted: Tue Sep 08, 2015 9:37 am
by Octocontrabass
If unmasking IRQ1 in the PIT doesn't help, the other thing to check is your initialization of the keyboard controller.
Also, there is
another thread where the problem was caused by doing things in the wrong order.
Re: QEMU keyboard interrupt troubles
Posted: Tue Sep 08, 2015 11:23 am
by isaacwoods
You saved the current value of these registers, and you restored these values at the end, but you never actually changed the value of these registers.
But if I ignore the masks and just use 0x0 (hopefully to mask absolutely nothing) it still doesn't work.
the other thing to check is your initialization of the keyboard controller.
What could be wrong there? Since it works when Interrupt 33 is manually invoked using the 'int' instruction, I'm thinking the keyboard handler is fine and it's definitely the PIC that is not behaving as I would like it to. Would you be so kind as to look at the project as a whole, as the code is all nicely hosted
here. As far as I can tell, it's all fine, but obviously not, and I think a more-trained eye would be better suited to picking out any potential problems. Thanks!

Re: QEMU keyboard interrupt troubles
Posted: Tue Sep 08, 2015 11:32 am
by Octocontrabass
BaconWraith wrote:keyboard handler
No, I said
keyboard controller. Are you initializing it correctly?
Re: QEMU keyboard interrupt troubles
Posted: Wed Sep 09, 2015 5:03 am
by Kevin
BaconWraith wrote:I am relatively new to OS development (and only 14, so please bear with me a little)
Age doesn't matter. I've seen decent OS developers at 14 and others that were much older, but just didn't get it. What matters is that you're willing to try fixing things on your own, and give a good description of your problem and what you already tried when you finally ask for help. I think your posting is okay in this respect.
Anyway... Is the interrupt flag of the CPU even enabled, i.e. did you execute a "sti" instruction? Without it, hardware interrupts are ignored.
Other typical problems for missing keyboard IRQs (though usually the first interrupt works and only the second one never arrives) are forgetting to send EOI for an earlier IRQ or not reading out the keyboard buffer during initialisation. The keyboard controller only triggers an interrupt if the buffer was empty before new data arrived.
Re: QEMU keyboard interrupt troubles
Posted: Wed Sep 09, 2015 10:50 am
by isaacwoods
I execute a 'sti' after GRUB boots me, before anything else. I've also checked to see if interrupts are enabled after that, which they are. And in that case, I think it may be the keyboard controller (not handler

), because I'm not initializing it. I hadn't realised that I would have to (it doesn't say anything about the controller on the PS/2 Keyboard page, which is where I was looking). So looking at the
this page, I need to do the following things:
- Initialise the USB controller
- Actually see if the controller exists
- Disable devices
- Flush the output buffer
- Set the 'Controller Configuration Byte'
- Perform the self test on the controller
- Check if it's dual channel
- Perform interface tests
- Enable devices and then reset them
Could you give some guidance on how I should accomplish this? Especially on how I should handle the USB controller (as there appear to be a number of different standards and respective interfaces etc.).
Re: QEMU keyboard interrupt troubles
Posted: Wed Sep 09, 2015 1:01 pm
by Kevin
Forget about USB, that's definitely not for beginners. If the BIOS has some PS/2 emulation for USB keyboards, that's actually helpful for you because you won't have a USB keyboard driver that soon. Let it just do its job.
Like that section says in the beginning, doing all of these points is a little excessive. In practice, just flushing the output buffer seems to be enough, but it might be nicer to do all of steps 3-9. Doing a reset of the keyboard sounds in theory desirable as well (just like resetting any hardware before using it is a good idea because it brings it into a defined state), but I remember that tyndur removed the reset again because it seemed to break some keyboards.
Re: QEMU keyboard interrupt troubles
Posted: Thu Sep 10, 2015 12:47 pm
by isaacwoods
Okay, I've attempted to write a function to initialise the PS/2 controller:
Code: Select all
void initPS2(void)
{
asm("cli");
// Disable PS/2 devices
outb(0x64, 0xAD);
outb(0x64, 0xA7);
// Flush the output buffer
inb(0x60);
// Setup the Controller Configuration Byte
outb(0x64, 0x20);
while ((inb(0x64) & 1) == 0);
uint8_t configByte = inb(0x60);
printf("Controller Configuration Byte: %u\n", configByte);
configByte &= ~(1 << 0);
configByte &= ~(1 << 1);
configByte &= ~(1 << 6);
printf("Controller Configuration Byte: %u\n", configByte);
outb(0x60, configByte);
// Perform Controller Self Test
outb(0x64, 0xAA);
while ((inb(0x64) & 1) == 0);
if (inb(0x60) != 0x55)
printf("Self check failed!\n");
// TODO: see if two ports exist
// Perform interface tests
outb(0x64, 0xAB);
while ((inb(0x64) & 1) == 0);
printf("Interface test: %u\n", inb(0x60));
outb(0x64, 0xAE);
// outb(0x64, 0xA8); // TODO: only do this if a second port exists
asm("sti");
}
Suffice to say, it doesn't work. When I get the Controller Configuration Byte, the System Flag appears to be 0 (the wiki just says "your OS shouldn't be running" - very helpful). The self check then fails, and the keyboard still doesn't work. I'm using QEMU, so I'm pretty much certain that this isn't a hardware fault.
Re: QEMU keyboard interrupt troubles
Posted: Fri Sep 11, 2015 3:01 pm
by SpyderTL
Try waiting for bit 1 (0x02) to be clear on 0x64 before writing any commands to 0x64 or any data to 0x60.
Code: Select all
while ((inb(0x64) & 2) == 2);
outb(0x64, 0xAD);
while ((inb(0x64) & 2) == 2);
outb(0x64, 0xA7);
...
You will eventually need to send command 0x60 to the controller to enable interrupts, and send 0xFF to reset the keyboard, as well.
Re: QEMU keyboard interrupt troubles
Posted: Tue Sep 29, 2015 6:27 pm
by emaadmanzoor
BaconWraith wrote:Sorry, what do you mean by unmasking IRQ1? If you mean the last two bytes sent during initialisation:
Code: Select all
// Restore the previously saved masks
outb(PIC_MASTER_DATA, mask1);
outb(PIC_SLAVE_DATA, mask2);
If I pass 0x0 instead, it also doesn't work, so unless that isn't unmasking it, I'm not sure what you mean.
I'm testing my kernel with QEMU too. What worked for me was first masking all IRQ lines after initialising my PICs:
Code: Select all
outb(PIC_MASTER_DATA, 0xff);
outb(PIC_SLAVE_DATA, 0xff);
Then enabling keyboard interrupts by unmasking the keyboard IRQ line (IRQ1 in my case):
Code: Select all
outb(PIC_MASTER_DATA, ~(0x2)); // mask = 1111 1101
My keystrokes in QEMU are being caught by my interrupt handlers now (though this is the only interrupt I am dealing with at the moment). I didn't need to do any PS/2 initialisation or QEMU hardware setup.