Page 1 of 3
Problem writing interrupts handler?
Posted: Sat Feb 27, 2016 11:28 pm
by chris13524
This is my first time writing an OS, and I'm really stuck on the interrupt part. I originally started out with polling for key presses, but am trying to switch to interrupts for obvious reasons...
The wiki seems to very vague on how to do this, it has a table of Standard ISA IRQs (keyboard is #1). I took this to mean that I can make an array, and put my descriptor thing there. Reading a bit more, it seems that the first 32 slots are for exceptions, so I put my descriptor at 33. This doesn't work either.
I published my code to GitHub here (testing of interrupts is temporally in that file, it will be moved to a more appropriate location later):
https://github.com/neon-orb/project-asi ... l/kernel.c
I tried to write a bunch of comments so you guys know what I was thinking.
If it is written correctly, it should print out "keyboard interrupt!" when you hit a key.
Any help would be greatly appreciated because I've been at this for more than 2 weeks.
Re: Problem writing interrupts handler?
Posted: Sat Feb 27, 2016 11:39 pm
by SpyderTL
By default, the primary Programmable Interrupt Controller (PIC) routes IRQ 1 to CPU interrupt 1, but in 32-bit mode, this interrupt is already being used by the CPU. Either your interrupt 1 handler has to be smart enough to figure out where the interrupt actually came from, or you have to reprogram the PiC to route IRQ 1 to a different interrupt.
Most people use interrupt 32, since that is the first interrupt not in use by the CPU.
When the CPU receives an interrupt, it looks up the interrupt handler in the IDT table, and jumps to it. You are responsible for putting your interrupt handler addresses in each of the 255 slots in the IDT before enabling interrupts in 32-bit mode.
Let us know if you have any other questions.
Re: Problem writing interrupts handler?
Posted: Sat Feb 27, 2016 11:50 pm
by chris13524
So I have to fill each index of an array of size 255 with a valid handler? Can I leave ones blank and not enable them in the mask?
Does the 255 array contain exception handlers (and the keyboard would be at 33)?
Could you look at my code under the
function and see if I'm doing it correctly?
Re: Problem writing interrupts handler?
Posted: Sat Feb 27, 2016 11:51 pm
by nakst
Looking at your code it appears you have a structure packing issue.
Code: Select all
struct IDT{ // http://wiki.osdev.org/Interrupt_Descriptor_Table#Location_and_Size
uint16_t limit; // Defines the length of the IDT in bytes - 1 (minimum value is 100h, a value of 1000h means 200h interrupts).
uint32_t base; // This 32 bits are the linear address where the IDT starts (INT 0)
};
Your compiler will automatically align the 4 byte member by creating 2 bytes of padding between it and the previous member. Since all the interrupt related structured are a bit funky, you'll need to tell your compiler not to do this. Add
to the structure definition, and any other structures you need to do it with. They should also probably be marked as volatile just in case your compiler wants to do some weird optimisations.
Re: Problem writing interrupts handler?
Posted: Sun Feb 28, 2016 12:23 am
by SpyderTL
chris13524 wrote:So I have to fill each index of an array of size 255 with a valid handler? Can I leave ones blank and not enable them in the mask?
Does the 255 array contain exception handlers (and the keyboard would be at 33)?
Yes, yes and yes.
Technically you don't need a handler for an interrupt that will never be called, but you might as well put something in each slot...
You can put the same address in each slot if you want. They don't all have to be different.
And just to be clear, exceptions will use 0-31, so timer IRQ 0 will use 32 and keyboard IRQ 1 will use 33.
EDIT: Fixed...
Re: Problem writing interrupts handler?
Posted: Sun Feb 28, 2016 7:09 am
by chris13524
so keyboard IRQ 0 will use 32.
According to
http://wiki.osdev.org/Interrupts, IRQ 0 is the Programmable Interrupt Timer Interrupt. Is that the keyboard, or should I be using 33 instead?
Re: Problem writing interrupts handler?
Posted: Sun Feb 28, 2016 10:16 am
by BrightLight
chris13524 wrote:should I be using 33 instead?
Yes. If you're using the old PIC and map it at interrupt 32, the keyboard IRQ handler would be at interrupt 33. Be sure to have a handler for PIT IRQ 0 (INT 32) too, because it will generate an interrupt immediately after you do (STI).
Re: Problem writing interrupts handler?
Posted: Sun Feb 28, 2016 10:24 am
by SpyderTL
chris13524 wrote:so keyboard IRQ 0 will use 32.
According to
http://wiki.osdev.org/Interrupts, IRQ 0 is the Programmable Interrupt Timer Interrupt. Is that the keyboard, or should I be using 33 instead?
You are correct. The timer is on IRQ 0, and the PS/2 controller for the keyboard is on IRQ 1. I've fixed my post above...
Re: Problem writing interrupts handler?
Posted: Sun Feb 28, 2016 10:41 am
by chris13524
Yes. If you're using the old PIC and map it at interrupt 32, the keyboard IRQ handler would be at interrupt 33. Be sure to have a handler for PIT IRQ 0 (INT 32) too, because it will generate an interrupt immediately after you do (STI).
I'm remapping the master to 0x20 and the slave to 0x28 and then filling an array of size 127 (bigger crashes or something) with the same handler and sending it with lidt. I'm also clearing each mask from 0 up to 127.
Maybe there is something wrong with my handler table?
Code: Select all
#define IDT_TABLE_LENGTH 127
struct IDTDescr idt_table[IDT_TABLE_LENGTH];
void initialize_idt_table() {
// keyboard
// first 32 are exceptions, #1 (31) is Programmable Interrupt Timer Interrupt, #2 (33) is Keyboard Interrupt - http://wiki.osdev.org/Interrupts#Interrupt_Overview
for (char i = 0; i < IDT_TABLE_LENGTH; i++) {
idt_table[i] = (struct IDTDescr ) {
(uint16_t) &handle_keyboard_interrupt, // lower-half of pointer - TODO is this how you correctly truncate?
((uint16_t) &(idt_table[i])) << 3 | 0b00000001, // selector - http://wiki.osdev.org/Selector
0, // always zero
0b1110 // 80386 32-bit interrupt gate
| 0b1000 << 4, // no idea what I'm doing here
((uint32_t) &handle_keyboard_interrupt) >> 16 // upper-half of pointer - TODO is this how you correctly truncate?
};
}
// tell CPU about descriptor table
struct IDT idt = { sizeof(idt_table) - 1, &idt_table };
asm ("lidt (%0)" :: ""(idt));
}
I would assume I need to be doing something for interrupts to work, so after setting up interrupts, I have this:
Your compiler will automatically align the 4 byte member by creating 2 bytes of padding between it and the previous member. Since all the interrupt related structured are a bit funky, you'll need to tell your compiler not to do this. Add
Code:
__attribute__((packed))
to the structure definition, and any other structures you need to do it with. They should also probably be marked as volatile just in case your compiler wants to do some weird optimisations.
I added that to the structure like this:
Code: Select all
volatile struct IDTDescr { // http://wiki.osdev.org/Interrupt_Descriptor_Table#Structure
__attribute__((packed))
uint16_t offset_1; // offset bits 0..15
uint16_t selector; // a code segment selector in GDT or LDT
uint8_t zero; // unused, set to 0
uint8_t type_attr; // type and attributes, see below
uint16_t offset_2; // offset bits 16..31
};
volatile struct IDT { // http://wiki.osdev.org/Interrupt_Descriptor_Table#Location_and_Size
__attribute__((packed))
uint16_t limit; // Defines the length of the IDT in bytes - 1 (minimum value is 100h, a value of 1000h means 200h interrupts).
uint32_t base; // This 32 bits are the linear address where the IDT starts (INT 0)
};
Re: Problem writing interrupts handler?
Posted: Sun Feb 28, 2016 1:40 pm
by SpyderTL
I don't see where you are enabling interrupts on the CPU, using the STI instruction. I am also assuming that you disabled them earlier somewhere using the CLI instruction.
Your comment that states that you don't know what you are doing may be a good place to focus...
Also you should be able to put 255 entries in this table, so if that is causing problems, that may be another clue.
Edit: Your "selector" value looks like it may be completely wrong. It should probably just be set to 0x08 or 0x10, if you are using a flat memory model. How many entries are in your GDT?
Edit2: Your interrupt handler does not call IRET at the end. You also have to preserve all of your registers and restore them before calling IRET. You should probably be using pure ASM for your interrupt handlers. Using C is going to be somewhere between difficult and impossible. You can read about the issues with ISRs in C here:
Interrupt Service Routines
Re: Problem writing interrupts handler?
Posted: Sun Feb 28, 2016 7:31 pm
by chris13524
How many entries are in your GDT?
I'm not managing that, I'm using QEMU to test, and in production I think it will be doing GRUB. As far as I know, these two things will set up my GDT for me, no? In that case, I have no idea how many entries are in my GDT.
I never disabled my interrupts, if I add STI after setting up the interrupts, it causes what appears to be a boot loop, print out my hello world text, and then BIOS stuff, then my text etc. When I remove the while (true) loop, it doesn't do this.
Your interrupt handler does not call IRET at the end. You also have to preserve all of your registers and restore them before calling IRET. You should probably be using pure ASM for your interrupt handlers. Using C is going to be somewhere between difficult and impossible. You can read about the issues with ISRs in C here: Interrupt Service Routines
I knew I would have to do that, but I figured I could not do that for the time being until I got the message to print (so I know that the table works and don't have to question if my handler is written correctly). Should my handler still work without doing that for now?
Re: Problem writing interrupts handler?
Posted: Sun Feb 28, 2016 11:09 pm
by SpyderTL
Probably not. If you don't disable interrupts while you are setting the IDT address with LIDT, you'll probably get random results. It may work one time out of 3...
But the missing IRET at the end of your interrupt handler will never work. That link that I posted above describes a way that you can cheat and write your handlers in C instead of ASM, but I wouldn't recommend it.
GRUB will set up a GDT, but it will be a minimal setup that you probably don't want to use. You should set up your own GDT as soon as possible, then some of those IDT fields will start to make sense.
QEMU, on the other hand, will not set up a GDT for you, as far as I know.
By the way, the "boot loop" that you are experiencing is because the machine automatically resets itself if it gets three exceptions in a row. This is called a "triple fault", and it happens when there is an exception, which runs an interrupt handler, which causes another exception, which calls another handler, which throws another exception. At this point, the CPU gives up, and reboots the system.
Your code is simply doing that over and over. This happens when, say, you reference a segment that doesn't exist in your GDT. And, just to get it out of the way, zero is not a valid selector.
Re: Problem writing interrupts handler?
Posted: Mon Feb 29, 2016 3:37 pm
by chris13524
I updated my code, disabling interrupts at the start of the kernel, and enabling them at the end of the setup.
It seems that when I add a while(true){} loop to the end of my main function, it does the triple fault, but when that isn't there, it is fine (but still doesn't handle interrupts). I guess this is expected because when returning from kernel_main, it does cli, hlt, and a jmp loop...nevermind.
At this point, I'm copying code from CakeOS to try to get it to work (which it doesn't). Once I get it working, I will legitimize it with my own code.
I've added the GDT and IDT code from CakeOS to my OS, as well as the proper ASM handler code.
It doesn't appear that the problem is in my interrupt handler, but rather in the IDT. I put a message and a hlt instruction in my handler, but it is still triple faulting (without the message).
Does anybody have an idea of why this triple fault may be happening? The source code is here:
https://github.com/neon-orb/project-asiago
Re: Problem writing interrupts handler?
Posted: Mon Feb 29, 2016 6:12 pm
by BASICFreak
1. You are destroying your stack!
You pop error code and int number, but never push them
2. You are probably interrupted in your "interrupt" function
Don't do cli and sti in an ISR.
3.
Code: Select all
for (uint16_t c = 1; c < 256; c++) { // FIXME c should be 8 bit or char but GCC gives a warning..try it
This is 0 to 255 (NOT 1-255), and an uint8 will always be true as it only goes from 0-255, which is why GCC warns you.
NOTE: I only quickly glanced at your code.
Best Regards,
B!
Re: Problem writing interrupts handler?
Posted: Mon Feb 29, 2016 7:25 pm
by kzinti
Code: Select all
for (uint16_t c = 1; c < 256; c++) { // FIXME c should be 8 bit or char but GCC gives a warning..try it
It should be an 'int', not a "uint8_t" or a "uint16_t" or anything else.