Question about which tools to use, bugs, the best way to implement a function, etc should go here. Don't forget to see if your question is answered in the wiki first! When in doubt post here.
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)!
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.
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:
#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?
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.
Project: OZone
Source: GitHub
Current Task: LIB/OBJ file support
"The more they overthink the plumbing, the easier it is to stop up the drain." - Montgomery Scott
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!
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.
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.).
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.
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.
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.
Project: OZone
Source: GitHub
Current Task: LIB/OBJ file support
"The more they overthink the plumbing, the easier it is to stop up the drain." - Montgomery Scott
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.