QEMU keyboard interrupt troubles

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.
isaacwoods
Member
Member
Posts: 47
Joined: Sun Sep 06, 2015 5:40 am

QEMU keyboard interrupt troubles

Post 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

Code: Select all

asm("int $33");
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)!
Octocontrabass
Member
Member
Posts: 6298
Joined: Mon Mar 25, 2013 7:01 pm

Re: QEMU keyboard interrupt troubles

Post by Octocontrabass »

BaconWraith wrote:I am pretty sure that the GDT, IDT, PIC and the required IRQ is setup correctly, because if I add

Code: Select all

asm("int $33");
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.
isaacwoods
Member
Member
Posts: 47
Joined: Sun Sep 06, 2015 5:40 am

Re: QEMU keyboard interrupt troubles

Post 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?
Octocontrabass
Member
Member
Posts: 6298
Joined: Mon Mar 25, 2013 7:01 pm

Re: QEMU keyboard interrupt troubles

Post by Octocontrabass »

Where is the code that unmasks IRQ1?
isaacwoods
Member
Member
Posts: 47
Joined: Sun Sep 06, 2015 5:40 am

Re: QEMU keyboard interrupt troubles

Post 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.
User avatar
SpyderTL
Member
Member
Posts: 1074
Joined: Sun Sep 19, 2010 10:05 pm

Re: QEMU keyboard interrupt troubles

Post 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.
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
Octocontrabass
Member
Member
Posts: 6298
Joined: Mon Mar 25, 2013 7:01 pm

Re: QEMU keyboard interrupt troubles

Post 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.
isaacwoods
Member
Member
Posts: 47
Joined: Sun Sep 06, 2015 5:40 am

Re: QEMU keyboard interrupt troubles

Post 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! :D
Octocontrabass
Member
Member
Posts: 6298
Joined: Mon Mar 25, 2013 7:01 pm

Re: QEMU keyboard interrupt troubles

Post by Octocontrabass »

BaconWraith wrote:keyboard handler
No, I said keyboard controller. Are you initializing it correctly?
Kevin
Member
Member
Posts: 1074
Joined: Sun Feb 01, 2009 6:11 am
Location: Germany
Contact:

Re: QEMU keyboard interrupt troubles

Post 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.
Developer of tyndur - community OS of Lowlevel (German)
isaacwoods
Member
Member
Posts: 47
Joined: Sun Sep 06, 2015 5:40 am

Re: QEMU keyboard interrupt troubles

Post 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 :wink: ), 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:
  1. Initialise the USB controller
  2. Actually see if the controller exists
  3. Disable devices
  4. Flush the output buffer
  5. Set the 'Controller Configuration Byte'
  6. Perform the self test on the controller
  7. Check if it's dual channel
  8. Perform interface tests
  9. 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.).
Kevin
Member
Member
Posts: 1074
Joined: Sun Feb 01, 2009 6:11 am
Location: Germany
Contact:

Re: QEMU keyboard interrupt troubles

Post 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.
Developer of tyndur - community OS of Lowlevel (German)
isaacwoods
Member
Member
Posts: 47
Joined: Sun Sep 06, 2015 5:40 am

Re: QEMU keyboard interrupt troubles

Post 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.
User avatar
SpyderTL
Member
Member
Posts: 1074
Joined: Sun Sep 19, 2010 10:05 pm

Re: QEMU keyboard interrupt troubles

Post 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.
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
emaadmanzoor
Posts: 1
Joined: Tue Sep 29, 2015 6:19 pm

Re: QEMU keyboard interrupt troubles

Post 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.
Post Reply