Page 1 of 2

I'm not getting any IRQ's from my PIC

Posted: Sun Jun 09, 2013 2:38 pm
by Gogeta70
Hey guys. I wrote my own routine to initialize and set up the PIC, and that wasn't working. So i temporarily copied code from the pic article on the osdev wiki, but i'm still not getting any IRQ's :(

I've zeroed out the interrupt mask register, and i've tested that my software interrupts are working (i put an int3 breakpoint in my code), and i even mapped every interrupt to the same function in my idt to see if the irq's were being sent to the wrong interrupt, but i'm still getting nothing. What am i doing wrong?

I'm testing to see if i get IRQ's by typing on my keyboard.

pic_remap function:

Code: Select all

#define PIC1		0x20		/* IO base address for master PIC */
#define PIC2		0xA0		/* IO base address for slave PIC */
#define PIC1_COMMAND	PIC1
#define PIC1_DATA	(PIC1+1)
#define PIC2_COMMAND	PIC2
#define PIC2_DATA	(PIC2+1)

#define ICW1_ICW4	0x01		/* ICW4 (not) needed */
#define ICW1_SINGLE	0x02		/* Single (cascade) mode */
#define ICW1_INTERVAL4	0x04		/* Call address interval 4 (8) */
#define ICW1_LEVEL	0x08		/* Level triggered (edge) mode */
#define ICW1_INIT	0x10		/* Initialization - required! */
 
#define ICW4_8086	0x01		/* 8086/88 (MCS-80/85) mode */
#define ICW4_AUTO	0x02		/* Auto (normal) EOI */
#define ICW4_BUF_SLAVE	0x08		/* Buffered mode/slave */
#define ICW4_BUF_MASTER	0x0C		/* Buffered mode/master */
#define ICW4_SFNM	0x10		/* Special fully nested (not) */

void PIC_remap(unsigned char offset1, unsigned char offset2)
{
//	unsigned char a1, a2;
 
//	a1 = inb(PIC1_DATA);                        // save masks
//	a2 = inb(PIC2_DATA);
 
	outb(PIC1_COMMAND, ICW1_INIT+ICW1_ICW4);  // starts the initialization sequence (in cascade mode)
	io_wait();
	outb(PIC2_COMMAND, ICW1_INIT+ICW1_ICW4);
	io_wait();
	outb(PIC1_DATA, offset1);                 // ICW2: Master PIC vector offset
	io_wait();
	outb(PIC2_DATA, offset2);                 // ICW2: Slave PIC vector offset
	io_wait();
	outb(PIC1_DATA, 4);                       // ICW3: tell Master PIC that there is a slave PIC at IRQ2 (0000 0100)
	io_wait();
	outb(PIC2_DATA, 2);                       // ICW3: tell Slave PIC its cascade identity (0000 0010)
	io_wait();
 
	outb(PIC1_DATA, ICW4_8086);
	io_wait();
	outb(PIC2_DATA, ICW4_8086);
	io_wait();
 
//	outb(PIC1_DATA, a1);   // restore saved masks.
//	outb(PIC2_DATA, a2);
	
	outb(PIC1_DATA, 0); // zero out the pic mask
	outb(PIC2_DATA, 0);
}
IDT Initialization function:

Code: Select all

void idt_init()
{
	idt_location.offset = (unsigned long) &idt;
	idt_location.size = (sizeof(struct IDT_ENTRY) * 8) - 1;
	
	unsigned char *zero_idt = (unsigned char*) idt;
	
	for(unsigned long i = 0; i < idt_location.size; i++)
		zero_idt[i] = 0;
	
	unsigned char flags = 0x8E; // standard 32 bit interrupt gate
	unsigned long offset = 0;
	
	
	for(int i = 0; i < 256; i++)
	{
		switch(i)
		{
			case 0:
			{
				offset = (unsigned long) isr_divide_by_zero;
			}
			break;
			
			case 1:
			{
				offset = (unsigned long) isr_debugger;
			}
			break;
			
			case 2:
			{
				offset = (unsigned long) isr_nmi;
			}
			break;
			
			case 3:
			{
				offset = (unsigned long) isr_breakpoint;
			}
			break;
			
			case 4:
			{
				offset = (unsigned long) isr_overflow;
			}
			break;
			
			case 5:
			{
				offset = (unsigned long) isr_bounds;
			}
			break;
			
			case 6:
			{
				offset = (unsigned long) isr_invalid_opcode;
			}
			break;
			
			case 7:
			{
				offset = (unsigned long) isr_coprocessor_not_avail;
			}
			break;
			
			case 8:
			{
				offset = (unsigned long) isr_double_fault;
			}
			break;
			
			case 9:
			{
				offset = (unsigned long) isr_coprocessor_segment_overrun;
			}
			break;
			
			case 10:
			{
				offset = (unsigned long) isr_invalid_tss;
			}
			break;
			
			case 11:
			{
				offset = (unsigned long) isr_segment_not_present;
			}
			break;
			
			case 12:
			{
				offset = (unsigned long) isr_stack_fault;
			}
			break;
			
			case 13:
			{
				offset = (unsigned long) isr_general_protection_fault;
			}
			break;
			
			case 14:
			{
				offset = (unsigned long) isr_page_fault;
			}
			break;
			
			case 15:
			{
				offset = (unsigned long) isr_reserved;
			}
			break;
			
			case 16:
			{
				offset = (unsigned long) isr_math_fault;
			}
			break;
			
			case 17:
			{
				offset = (unsigned long) isr_alignment_check;
			}
			break;
			
			case 18:
			{
				offset = (unsigned long) isr_machine_check;
			}
			break;
			
			case 19:
			{
				offset = (unsigned long) isr_simd_floating_point_exception;
			}
			break;
			
			case 0x21: // keyboard
			{
				offset = (unsigned long) isr_keyboard;
			}
			break;
			
			default:
				continue;
			
		}
	
		idt[i].offset_low = (offset & 0xFFFF);
		idt[i].offset_high = ((offset >> 16) & 0xFFFF);
		idt[i].selector = 0x08;
		idt[i].zero = 0;
		idt[i].type_attr = flags;
	}
	
	install_idt();
}
kernel_main function:

Code: Select all

void kernel_main()
{
	k_clearscreen(DEFAULT_TEXT);
	
	idt_init(); // install interrupt descriptor table
	PIC_remap(0x20, 0x28);
	
	k_putstring("PIC Initialized.\n", DEFAULT_TEXT);
	
}

Re: I'm not getting any IRQ's from my PIC

Posted: Sun Jun 09, 2013 4:49 pm
by Gogeta70
28 Views and not a single reply? I'd just like some insight as to why i'm not getting any IRQs :(

Re: I'm not getting any IRQ's from my PIC

Posted: Sun Jun 09, 2013 4:55 pm
by sortie
Do you actually enable interrupts?

Re: I'm not getting any IRQ's from my PIC

Posted: Sun Jun 09, 2013 5:04 pm
by Gogeta70
Yes, in the assembly stub that calls my kernel_main function, i explicitly do:

Code: Select all

sti
call kernel_main

halt:
hlt
jmp halt

Re: I'm not getting any IRQ's from my PIC

Posted: Sun Jun 09, 2013 6:39 pm
by Nable
What about debugging your code in Bochs?

Also, one more IMHO-notice: array of pointers would be much concise than this awful loop with switch-case.

Re: I'm not getting any IRQ's from my PIC

Posted: Sun Jun 09, 2013 7:25 pm
by Gogeta70
In bochs, the machine instantly resets. In the debugger, here's the output:

Code: Select all

00259117923i[BIOS ] Booting from 07c0:0000
00259384192i[BIOS ] int13_harddisk: function 41, unmapped device for ELDL=80
00259387873i[BIOS ] int13_harddisk: function 08, unmapped device for ELDL=80
00259391534i[BIOS ] *** int 15h function AX=00c0, BX=0000 not yet supported!
00275538964i[MEM0 ] allocate_block: block=0x1 used 0x3 of 0x20
00275792775e[CPU0 ] interrupt(): vector must be within IDT table limits, IDT.lim
it = 0x3f
00275792775e[CPU0 ] interrupt(): vector must be within IDT table limits, IDT.lim
it = 0x3f
00275792775e[CPU0 ] interrupt(): vector must be within IDT table limits, IDT.lim
it = 0x3f
00275792775i[CPU0 ] CPU is in protected mode (active)
00275792775i[CPU0 ] CS.mode = 32 bit
00275792775i[CPU0 ] SS.mode = 32 bit
00275792775i[CPU0 ] EFER   = 0x00000000
00275792775i[CPU0 ] | EAX=00000039  EBX=00000096  ECX=000b806f  EDX=00100dcc
00275792775i[CPU0 ] | ESP=00107780  EBP=00067e7c  ESI=00100dcc  EDI=000b8000
00275792775i[CPU0 ] | IOPL=0 id vip vif ac vm RF nt of df IF tf sf zf af PF cf
00275792775i[CPU0 ] | SEG sltr(index|ti|rpl)     base    limit G D
00275792775i[CPU0 ] |  CS:0008( 0001| 0|  0) 00000000 ffffffff 1 1
00275792775i[CPU0 ] |  DS:0010( 0002| 0|  0) 00000000 ffffffff 1 1
00275792775i[CPU0 ] |  SS:0010( 0002| 0|  0) 00000000 ffffffff 1 1
00275792775i[CPU0 ] |  ES:0010( 0002| 0|  0) 00000000 ffffffff 1 1
00275792775i[CPU0 ] |  FS:0010( 0002| 0|  0) 00000000 ffffffff 1 1
00275792775i[CPU0 ] |  GS:0010( 0002| 0|  0) 00000000 ffffffff 1 1
00275792775i[CPU0 ] | EIP=00100c40 (00100c40)
00275792775i[CPU0 ] | CR0=0x60000011 CR2=0x00000000
00275792775i[CPU0 ] | CR3=0x00000000 CR4=0x00000000
(0).[275792775] [0x000000100c40] 0008:0000000000100c40 (unk. ctxt): inc eax
              ; 40
00275792775e[CPU0 ] exception(): 3rd (13) exception with no resolution, shutdown
 status is 00h, resetting
00275792775i[SYS  ] bx_pc_system_c::Reset(HARDWARE) called
00275792775i[CPU0 ] cpu hardware reset
Considering 2 of those exceptions happen before it enters protected mode, there's nothing i can do about that. My OS is booted by GRUB. It appears the 3rd exception happens while i'm printing a string to the screen. :/

I think that's a good idea, i'll change that switch into an array of pointers. ^_^

If you want me to post more source code, i can upload the source files. I appreciate any help!

Re: I'm not getting any IRQ's from my PIC

Posted: Sun Jun 09, 2013 8:58 pm
by pcmattman
Considering 2 of those exceptions happen before it enters protected mode
I think you mis-read the Bochs output - the "CPU is in protected mode" string is the beginning of the triple fault dump.

Code: Select all

interrupt(): vector must be within IDT table limits, IDT.limit = 0x3f
This here is your actual problem.

Code: Select all

idt_location.size = (sizeof(struct IDT_ENTRY) * 8) - 1;
Now let's have a think about what this does, why it might perhaps be inadequate, and how it relates to the Bochs debug output.

Re: I'm not getting any IRQ's from my PIC

Posted: Mon Jun 10, 2013 3:12 am
by sortie
Gogeta70 wrote:Yes, in the assembly stub that calls my kernel_main function, i explicitly do:

Code: Select all

sti
call kernel_main

halt:
hlt
jmp halt
Are you ready for interrupts when kernel_main is run? No, you are not. Because you haven't initialized the IDT yet. An interrupt could come along before you are ready and bad things will happen depending on what the IDT looks like after the kernel has been loaded.

You need to wait to enable interrupts until you have initialized the IDT and are ready to handle them.

Re: I'm not getting any IRQ's from my PIC

Posted: Mon Jun 10, 2013 9:05 am
by Gogeta70
Ok, i set the idt size to 256 entries. That seems to have done the trick. It's still not working properly, but i am getting activity from my PIC now. It seems to be stuck servicing an interrupt. Both the IRR and ISR registers in the PIC are 0x01 - which i have set up an interrupt for. Upon stepping the code in bochs, after it executes "IRET", eip equals 0. So i guess somehow, my stack is getting messed up :/

You have a good point. I'll move that STI instruction to after my LIDT instruction.

Re: I'm not getting any IRQ's from my PIC

Posted: Mon Jun 10, 2013 11:18 am
by Gogeta70
So i've been stepping through my code in bochs to see where EIP gets set to 0x00. I'm not sure if it's happening inside of an interrupt or not. I set a breakpoint on the return of my kernel_main() function, which after it returns, halts the processor. Here's the output i'm getting from bochs when stepping through my code:

Code: Select all

(0) Breakpoint 2, 0x000000000010008b in ?? ()
Next at t=28673756
(0) [0x00000010008b] 0008:000000000010008b (unk. ctxt): ret ;-- return from my kernel_main function
  ; c3
<bochs:8> s
Next at t=28673757
(0) [0x000000100055] 0008:0000000000100055 (unk. ctxt): hlt
  ; f4
<bochs:9>
Next at t=28673758
(0) [0x000000100056] 0008:0000000000100056 (unk. ctxt): jmp .-3 (0x00100055)
  ; ebfd
<bochs:10>
00028786168i[CPU0 ] LOCK prefix unallowed (op1=0x53, modrm=0x00)
Next at t=28786169
(0) [0x000000000001] 0008:0000000000000001 (unk. ctxt): inc dword ptr ds:[eax]
  ; ff00
<bochs:11> s
00028786169i[CPU0 ] LOCK prefix unallowed (op1=0x53, modrm=0x00)
Next at t=28786170
(0) [0x000000000003] 0008:0000000000000003 (unk. ctxt): lock push ebx
  ; f053
<bochs:12> s
00028786170i[CPU0 ] LOCK prefix unallowed (op1=0x53, modrm=0x00)
Next at t=28786171
(0) [0x000000100c66] 0008:0000000000100c66 (unk. ctxt): iretd
  ; cf
<bochs:13> s
Next at t=28786172
(0) [0x000000000003] 0008:0000000000000003 (unk. ctxt): lock push ebx
  ; f053
<bochs:14>

Code: Select all

	call kernel_main
.hang:
	hlt
	jmp .hang
I can see after the jump that EIP starts executing from address 0x00, but the jmp only jumps back to the hlt instruction? So is this happening inside of an interrupt? O_o

Sorry for all the dumb questions. I'm new to OS development :/

Re: I'm not getting any IRQ's from my PIC

Posted: Mon Jun 10, 2013 12:06 pm
by Combuster
Next at t=28673758
Next at t=28786169
Spot the delay. Remember that the HLT instruction only returns if there is an interrupt to be taken.

Also, you could just go ask the debugger what's in the IDT before you even hit the HLT to see if it makes sense and possibly where the problem is.

Re: I'm not getting any IRQ's from my PIC

Posted: Mon Jun 10, 2013 1:28 pm
by Gogeta70
So i had bochs dump IDT. This is the first 8 bytes of the IDT, aka, the first entry:

0x0000000000103020 <bogus+ 0>: [0x99 0x0c] [0x08 0x00] [0x00] [0x8e] [0x10 0x00]

I used unsigned shorts for the 2 byte values in the IDT. It's storing them in little-endian format. Could that be the issue, or is it supposed to be stored that way in the IDT?

After the 22nd entry in the IDT, the entries start looking like this:

[0x00 0x00] [0x08 0x00] [0x00] [0x8e] [0x00 0x00]

Which isn't right. I think this may be my problem.

Re: I'm not getting any IRQ's from my PIC

Posted: Mon Jun 10, 2013 5:05 pm
by Gogeta70
Ok, so i have things working a lot better now. But, i'm only receiving my keyboard interrupt once. I receive a system timer interrupt (IRQ0) constantly though :/
I haven't programmed the PIT. Should i be getting any IRQ0's?

Just to see what would happen, i masked out IRQ0 by setting the PIC's mask register to 1. It stopped the constant flood of timer interrupts, but i still only get a single keyboard interrupt. I have a loop in my kernel_main function that constantly updates the screen with the ISR and IRR registers from the PIC - it's not waiting on an interrupt to complete. Both registers are constantly 0.

So, what am i doing wrong now? :? This is frustrating.

interrupt.c

Code: Select all

void idt_init()
{
	idt_location.offset = (unsigned long) &idt;
	idt_location.size = (sizeof(struct IDT_ENTRY) * 256) - 1;
	
	unsigned char *zero_idt = (unsigned char*) idt;
	
	for(unsigned long i = 0; i <= idt_location.size; i++)
		zero_idt[i] = 0;
	
	unsigned char flags = 0x8E; // standard 32 bit interrupt gate
	unsigned long offset[256] = {0};
	
	offset[0] = (unsigned long) isr_divide_by_zero;
	offset[1] = (unsigned long) isr_debugger;
	offset[2] = (unsigned long) isr_nmi;
	offset[3] = (unsigned long) isr_breakpoint;
	offset[4] = (unsigned long) isr_overflow;
	offset[5] = (unsigned long) isr_bounds;
	offset[6] = (unsigned long) isr_invalid_opcode;
	offset[7] = (unsigned long) isr_coprocessor_not_avail;
	offset[8] = (unsigned long) isr_double_fault;
	offset[9] = (unsigned long) isr_coprocessor_segment_overrun;
	offset[10] = (unsigned long) isr_invalid_tss;
	offset[11] = (unsigned long) isr_segment_not_present;
	offset[12] = (unsigned long) isr_stack_fault;
	offset[13] = (unsigned long) isr_general_protection_fault;
	offset[14] = (unsigned long) isr_page_fault;
	offset[15] = (unsigned long) isr_reserved;
	offset[16] = (unsigned long) isr_math_fault;
	offset[17] = (unsigned long) isr_alignment_check;
	offset[18] = (unsigned long) isr_machine_check;
	offset[19] = (unsigned long) isr_simd_floating_point_exception;
	
	for(int i = 20; i < 256; i++)
		offset[i] = (unsigned long) isr_reserved;
	
	offset[32] = (unsigned long) isr_sys_timer;
	offset[33] = (unsigned long) isr_keyboard;
	offset[34] = (unsigned long) isr_9;
	offset[35] = (unsigned long) isr_3;
	offset[36] = (unsigned long) isr_4;
	offset[37] = (unsigned long) isr_5;
	offset[38] = (unsigned long) isr_6;
	offset[39] = (unsigned long) isr_7;
	offset[40] = (unsigned long) isr_8;
	offset[41] = (unsigned long) isr_9;
	offset[42] = (unsigned long) isr_10;
	offset[43] = (unsigned long) isr_11;
	offset[44] = (unsigned long) isr_12;
	offset[45] = (unsigned long) isr_13;
	offset[46] = (unsigned long) isr_14;
	offset[47] = (unsigned long) isr_15;
	
	for(int i = 0; i < 256; i++)
	{
	
		idt[i].offset_low = (offset[i] & 0xFFFF);
		idt[i].offset_high = ((offset[i] >> 16) & 0xFFFF);
		idt[i].selector = 0x08;
		idt[i].zero = 0;
		idt[i].type_attr = flags;
	}
	
	install_idt();
}

void breakpoint()
{
	unsigned char clr = MAKE_ATTRIB(RED, BLACK);
	k_puts_line("            INT3 BREAKPOINT INTERRUPT CALLED - INTERRUPT TEST PASSED", 15, clr);
}

void keyboard()
{
	k_putstring("Keyboard Input!!\n", DEFAULT_TEXT);
	*intCounter += 1;
	
	pic_eoi();
}

void sys_timer()
{
//	k_putstring("sys timer\n", DEFAULT_TEXT);
//	*intCounter += 1;
	pic_eoi();
}

void irq3(void) // the other irq functions are just like this, except irq8-15 functions have pic2_eoi() as well.
{
	*intCounter += 1;
	pic_eoi();
}
interrupt.asm

Code: Select all

global isr_simd_floating_point_exception
isr_simd_floating_point_exception:       ; all the intel interrupts look like this, except the ones that have an error code pop the error code, then return.
	cli
	sti
	iret

extern keyboard
global isr_keyboard
isr_keyboard:
	cli
	pushad
	call keyboard
	popad
	sti
	iret

global isr_sys_timer
extern sys_timer
isr_sys_timer:
	cli
	pushad
	push 0xff00 ; i used this as a test to make sure i was seeing the right interrupt in bochs debugger
	call sys_timer
	pop eax
	popad
	sti
	iret


extern irq3
extern irq4
extern irq5
extern irq6
extern irq7
extern irq8
extern irq9
extern irq10
extern irq11
extern irq12
extern irq13
extern irq14
extern irq15

global isr_3
isr_3:             ; all the irq functions look like this.
	cli
	pushad
	call irq3
	popad
	sti
	iret
I hope someone still has the patience to help me. Thanks everyone for your input so far!

Re: I'm not getting any IRQ's from my PIC

Posted: Mon Jun 10, 2013 11:14 pm
by Combuster
As per the FAQ, One interrupt typically means no EOI and that's the one function you didn't post. Also, you are not reading the keyboard buffer yet either so it might just fill up and go dead as well.


Other things:

Code: Select all

isr_3:             ; all the irq functions look like this.
Some exceptions push error codes. If you don't treat them specifically, things will break later

Code: Select all

cli
(...)
sti
An interrupt gate clears interrupts (whereas a trap gate does not). IRET restores it to its previous value. Therefore CLI-STI is redundant (and in general, plain cli-sti pairs don't nest well).

Re: I'm not getting any IRQ's from my PIC

Posted: Tue Jun 11, 2013 9:45 am
by Gogeta70
I know some of the standard interrupts (numbers 0x00 - 0x1f) have error codes, but do any of the interrupts generated by the PIC have an error code associated with them?

Here are my pic_eoi functions:

Code: Select all

global pic_eoi
pic_eoi:
	
	mov	al, 0x20	; set bit 4 of OCW 2
	out	0x20, al	; write to primary PIC command register
	
	ret

global pic2_eoi
pic2_eoi:
	
	mov	al, 0x20	; set bit 4 of OCW 2
	out	0xA0, al	; write to secondary PIC command register
	
	ret
I think you may be right. I never read from the keyboard's output buffer, so maybe it's not sending any more interrupts? I'll try it and see.