Interrupts Tutorial

From OSDev.wiki
Revision as of 17:46, 17 September 2018 by osdev>Klakap (repair bug in code)
Jump to navigation Jump to search
Difficulty level

Beginner

Every operating system that wants to work with the hardware must have a set of the interrupts. At first, you must understand how interrupts work. http://www.osdever.net/tutorials/view/irqs is good for it. In this tutorial you will learn how to set interrupts.

IDT table

When the interrupts is handled, the cpu looks in the IDT table, which method is calling. We must therefore make a array that will IDT table and load it with the command lidt.

struct IDT_entry{
	unsigned short int offset_lowerbits;
	unsigned short int selector;
	unsigned char zero;
	unsigned char type_attr;
	unsigned short int offset_higherbits;
};

struct IDT_entry IDT[286];

Now we must remap PIC and fill the IDT with correct values:

void idt_init(void) {
        extern int load_idt();
        extern int irq0();
        extern int irq1();
        extern int irq2();
        extern int irq3();
        extern int irq4();
        extern int irq5();
        extern int irq6();
        extern int irq7();
        extern int irq8();
        extern int irq9();
        extern int irq10();
        extern int irq11();
        extern int irq12();
        extern int irq13();
        extern int irq14();
        extern int irq15();

	unsigned long irq0_address;
        unsigned long irq1_address;
        unsigned long irq2_address;
        unsigned long irq3_address;          
        unsigned long irq4_address; 
        unsigned long irq5_address;
        unsigned long irq6_address;
        unsigned long irq7_address;
        unsigned long irq8_address;
        unsigned long irq9_address;          
        unsigned long irq10_address;
        unsigned long irq11_address;
        unsigned long irq12_address;
        unsigned long irq13_address;
        unsigned long irq14_address;          
        unsigned long irq15_address;         
	unsigned long idt_address;
	unsigned long idt_ptr[2];

        /* remapping the PIC */
	outb(0x20, 0x11);
        outb(0xA0, 0x11);
        outb(0x21, 0x20);
        outb(0xA1, 40);
        outb(0x21, 0x04);
        outb(0xA1, 0x02);
        outb(0x21, 0x01);
        outb(0xA1, 0x01);
        outb(0x21, 0x0);
        outb(0xA1, 0x0);

	irq0_address = (unsigned long)irq0; 
	IDT[32].offset_lowerbits = irq0_address & 0xffff;
	IDT[32].selector = 0x08; /* KERNEL_CODE_SEGMENT_OFFSET */
	IDT[32].zero = 0;
	IDT[32].type_attr = 0x8e; /* INTERRUPT_GATE */
	IDT[32].offset_higherbits = (irq0_address & 0xffff0000) >> 16;

	irq1_address = (unsigned long)irq1; 
	IDT[33].offset_lowerbits = irq1_address & 0xffff;
	IDT[33].selector = 0x08; /* KERNEL_CODE_SEGMENT_OFFSET */
	IDT[33].zero = 0;
	IDT[33].type_attr = 0x8e; /* INTERRUPT_GATE */
	IDT[33].offset_higherbits = (irq1_address & 0xffff0000) >> 16;

	irq2_address = (unsigned long)irq2; 
	IDT[34].offset_lowerbits = irq2_address & 0xffff;
	IDT[34].selector = 0x08; /* KERNEL_CODE_SEGMENT_OFFSET */
	IDT[34].zero = 0;
	IDT[34].type_attr = 0x8e; /* INTERRUPT_GATE */
	IDT[34].offset_higherbits = (irq2_address & 0xffff0000) >> 16;

	irq3_address = (unsigned long)irq3; 
	IDT[35].offset_lowerbits = irq3_address & 0xffff;
	IDT[35].selector = 0x08; /* KERNEL_CODE_SEGMENT_OFFSET */
	IDT[35].zero = 0;
	IDT[35].type_attr = 0x8e; /* INTERRUPT_GATE */
	IDT[35].offset_higherbits = (irq3_address & 0xffff0000) >> 16;

	irq4_address = (unsigned long)irq4; 
	IDT[36].offset_lowerbits = irq4_address & 0xffff;
	IDT[36].selector = 0x08; /* KERNEL_CODE_SEGMENT_OFFSET */
	IDT[36].zero = 0;
	IDT[36].type_attr = 0x8e; /* INTERRUPT_GATE */
	IDT[36].offset_higherbits = (irq4_address & 0xffff0000) >> 16;

	irq5_address = (unsigned long)irq5; 
	IDT[37].offset_lowerbits = irq5_address & 0xffff;
	IDT[37].selector = 0x08; /* KERNEL_CODE_SEGMENT_OFFSET */
	IDT[37].zero = 0;
	IDT[37].type_attr = 0x8e; /* INTERRUPT_GATE */
	IDT[37].offset_higherbits = (irq5_address & 0xffff0000) >> 16;

	irq6_address = (unsigned long)irq6; 
	IDT[38].offset_lowerbits = irq6_address & 0xffff;
	IDT[38].selector = 0x08; /* KERNEL_CODE_SEGMENT_OFFSET */
	IDT[38].zero = 0;
	IDT[38].type_attr = 0x8e; /* INTERRUPT_GATE */
	IDT[38].offset_higherbits = (irq6_address & 0xffff0000) >> 16;

	irq7_address = (unsigned long)irq7; 
	IDT[39].offset_lowerbits = irq7_address & 0xffff;
	IDT[39].selector = 0x08; /* KERNEL_CODE_SEGMENT_OFFSET */
	IDT[39].zero = 0;
	IDT[39].type_attr = 0x8e; /* INTERRUPT_GATE */
	IDT[39].offset_higherbits = (irq7_address & 0xffff0000) >> 16;

	irq8_address = (unsigned long)irq8; 
	IDT[40].offset_lowerbits = irq8_address & 0xffff;
	IDT[40].selector = 0x08; /* KERNEL_CODE_SEGMENT_OFFSET */
	IDT[40].zero = 0;
	IDT[40].type_attr = 0x8e; /* INTERRUPT_GATE */
	IDT[40].offset_higherbits = (irq8_address & 0xffff0000) >> 16;

	irq9_address = (unsigned long)irq9; 
	IDT[41].offset_lowerbits = irq9_address & 0xffff;
	IDT[41].selector = 0x08; /* KERNEL_CODE_SEGMENT_OFFSET */
	IDT[41].zero = 0;
	IDT[41].type_attr = 0x8e; /* INTERRUPT_GATE */
	IDT[41].offset_higherbits = (irq9_address & 0xffff0000) >> 16;

	irq10_address = (unsigned long)irq10; 
	IDT[42].offset_lowerbits = irq10_address & 0xffff;
	IDT[42].selector = 0x08; /* KERNEL_CODE_SEGMENT_OFFSET */
	IDT[42].zero = 0;
	IDT[42].type_attr = 0x8e; /* INTERRUPT_GATE */
	IDT[42].offset_higherbits = (irq10_address & 0xffff0000) >> 16;

	irq11_address = (unsigned long)irq11; 
	IDT[43].offset_lowerbits = irq11_address & 0xffff;
	IDT[43].selector = 0x08; /* KERNEL_CODE_SEGMENT_OFFSET */
	IDT[43].zero = 0;
	IDT[43].type_attr = 0x8e; /* INTERRUPT_GATE */
	IDT[43].offset_higherbits = (irq11_address & 0xffff0000) >> 16;

	irq12_address = (unsigned long)irq12; 
	IDT[44].offset_lowerbits = irq12_address & 0xffff;
	IDT[44].selector = 0x08; /* KERNEL_CODE_SEGMENT_OFFSET */
	IDT[44].zero = 0;
	IDT[44].type_attr = 0x8e; /* INTERRUPT_GATE */
	IDT[44].offset_higherbits = (irq12_address & 0xffff0000) >> 16;

	irq13_address = (unsigned long)irq13; 
	IDT[45].offset_lowerbits = irq13_address & 0xffff;
	IDT[45].selector = 0x08; /* KERNEL_CODE_SEGMENT_OFFSET */
	IDT[45].zero = 0;
	IDT[45].type_attr = 0x8e; /* INTERRUPT_GATE */
	IDT[45].offset_higherbits = (irq13_address & 0xffff0000) >> 16;

	irq14_address = (unsigned long)irq14; 
	IDT[46].offset_lowerbits = irq14_address & 0xffff;
	IDT[46].selector = 0x08; /* KERNEL_CODE_SEGMENT_OFFSET */
	IDT[46].zero = 0;
	IDT[46].type_attr = 0x8e; /* INTERRUPT_GATE */
	IDT[46].offset_higherbits = (irq14_address & 0xffff0000) >> 16;

        irq15_address = (unsigned long)irq15; 
	IDT[47].offset_lowerbits = irq15_address & 0xffff;
	IDT[47].selector = 0x08; /* KERNEL_CODE_SEGMENT_OFFSET */
	IDT[47].zero = 0;
	IDT[47].type_attr = 0x8e; /* INTERRUPT_GATE */
	IDT[47].offset_higherbits = (irq15_address & 0xffff0000) >> 16;

	/* fill the IDT descriptor */
	idt_address = (unsigned long)IDT ;
	idt_ptr[0] = (sizeof (struct IDT_entry) * 286) + ((idt_address & 0xffff) << 16);
	idt_ptr[1] = idt_address >> 16 ;



	load_idt(idt_ptr);

}

There is irq handlers in assembler:

global irq0
global irq1
global irq2
global irq3
global irq4
global irq5
global irq6
global irq7
global irq8
global irq9
global irq10
global irq11
global irq12
global irq13
global irq14
global irq15

global load_idt

global irq0_handler
global irq1_handler
global irq2_handler
global irq3_handler
global irq4_handler
global irq5_handler
global irq6_handler
global irq7_handler
global irq8_handler
global irq9_handler
global irq10_handler
global irq11_handler
global irq12_handler
global irq13_handler
global irq14_handler
global irq15_handler

extern irq0_handler
extern irq1_handler
extern irq2_handler
extern irq3_handler
extern irq4_handler
extern irq5_handler
extern irq6_handler
extern irq7_handler
extern irq8_handler
extern irq9_handler
extern irq10_handler
extern irq11_handler
extern irq12_handler
extern irq13_handler
extern irq14_handler
extern irq15_handler

irq0:
  pusha
  call irq0_handler
  popa
  iret

irq1:
  pusha
  call irq1_handler
  popa
  iret

irq2:
  pusha
  call irq2_handler
  popa
  iret

irq3:
  pusha
  call irq3_handler
  popa
  iret

irq4:
  pusha
  call irq4_handler
  popa
  iret

irq5:
  pusha
  call irq5_handler
  popa
  iret

irq6:
  pusha
  call irq6_handler
  popa
  iret

irq7:
  pusha
  call irq7_handler
  popa
  iret

irq8:
  pusha
  call irq8_handler
  popa
  iret

irq9:
  pusha
  call irq9_handler
  popa
  iret

irq10:
  pusha
  call irq10_handler
  popa
  iret

irq11:
  pusha
  call irq11_handler
  popa
  iret

irq12:
  pusha
  call irq12_handler
  popa
  iret

irq13:
  pusha
  call irq13_handler
  popa
  iret

irq14:
  pusha
  call irq14_handler
  popa
  iret

irq15:
  pusha
  call irq15_handler
  popa
  iret

load_idt:
	mov edx, [esp + 4]
	lidt [edx]
	sti
	ret

And there is irq handlers in C that is called from assembler handlers. We must send EOI(End Of Interrupt) otherwise never any interrupts called.

void irq0_handler(void) {
          outb(0x20, 0x20); //EOI
}

void irq1_handler(void) {
	  outb(0x20, 0x20); //EOI
}

void irq2_handler(void) {
          outb(0x20, 0x20); //EOI
}

void irq3_handler(void) {
          outb(0x20, 0x20); //EOI
}

void irq4_handler(void) {
          outb(0x20, 0x20); //EOI
}

void irq5_handler(void) {
          outb(0x20, 0x20); //EOI
}

void irq6_handler(void) {
          outb(0x20, 0x20); //EOI
}

void irq7_handler(void) {
          outb(0x20, 0x20); //EOI
}

void irq8_handler(void) {
          outb(0xA0, 0x20);
          outb(0x20, 0x20); //EOI          
}

void irq9_handler(void) {
          outb(0xA0, 0x20);
          outb(0x20, 0x20); //EOI
}

void irq10_handler(void) {
          outb(0xA0, 0x20);
          outb(0x20, 0x20); //EOI
}

void irq11_handler(void) {
          outb(0xA0, 0x20);
          outb(0x20, 0x20); //EOI
}

void irq12_handler(void) {
          outb(0xA0, 0x20);
          outb(0x20, 0x20); //EOI
}

void irq13_handler(void) {
          outb(0xA0, 0x20);
          outb(0x20, 0x20); //EOI
}

void irq14_handler(void) {
          outb(0xA0, 0x20);
          outb(0x20, 0x20); //EOI
}

void irq15_handler(void) {
          outb(0xA0, 0x20);
          outb(0x20, 0x20); //EOI
}

Now you can write your handler methods!

Interrupts in GRUB

It happens that when the boot your operating system with GRUB, after the setting of the IDT table emulators get fatal error. Mostly this is due to the fact that you haven't set up your GDT table. Here is the code how to set it correctly in assembler:

gdt:

gdt_null:
   dq 0
gdt_code:
   dw 0FFFFh
   dw 0
db 0
db 10011010b
db 11001111b
db 0
gdt_data:
   dw 0FFFFh
   dw 0
db 0
db 10010010b
db 11001111b
db 0
gdt_end

gdt_desc:
   dw gdt_end - gdt - 1
   dd gdt

lgdt [gdt_desc]
jmp 0x0008:fix_cs
fix_cs:
mov ax, 0x0010
mov ds, ax
mov es, ax
mov fs, ax
mov gs, ax
mov ss, ax
mov esp, stack_space   ;set stack pointer

I tried that this code works on Virtualbox and Qemu. Good luck!

Klakap

See also

Threads

References

Let’s write a Kernel with keyboard and screen support]