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

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.
Gogeta70
Member
Member
Posts: 28
Joined: Thu Jun 06, 2013 11:15 am

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

Post 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);
	
}
Gogeta70
Member
Member
Posts: 28
Joined: Thu Jun 06, 2013 11:15 am

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

Post by Gogeta70 »

28 Views and not a single reply? I'd just like some insight as to why i'm not getting any IRQs :(
User avatar
sortie
Member
Member
Posts: 931
Joined: Wed Mar 21, 2012 3:01 pm
Libera.chat IRC: sortie

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

Post by sortie »

Do you actually enable interrupts?
Gogeta70
Member
Member
Posts: 28
Joined: Thu Jun 06, 2013 11:15 am

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

Post 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
Nable
Member
Member
Posts: 453
Joined: Tue Nov 08, 2011 11:35 am

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

Post 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.
Gogeta70
Member
Member
Posts: 28
Joined: Thu Jun 06, 2013 11:15 am

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

Post 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!
pcmattman
Member
Member
Posts: 2566
Joined: Sun Jan 14, 2007 9:15 pm
Libera.chat IRC: miselin
Location: Sydney, Australia (I come from a land down under!)
Contact:

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

Post 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.
User avatar
sortie
Member
Member
Posts: 931
Joined: Wed Mar 21, 2012 3:01 pm
Libera.chat IRC: sortie

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

Post 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.
Gogeta70
Member
Member
Posts: 28
Joined: Thu Jun 06, 2013 11:15 am

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

Post 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.
Gogeta70
Member
Member
Posts: 28
Joined: Thu Jun 06, 2013 11:15 am

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

Post 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 :/
User avatar
Combuster
Member
Member
Posts: 9301
Joined: Wed Oct 18, 2006 3:45 am
Libera.chat IRC: [com]buster
Location: On the balcony, where I can actually keep 1½m distance
Contact:

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

Post 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.
"Certainly avoid yourself. He is a newbie and might not realize it. You'll hate his code deeply a few years down the road." - Sortie
[ My OS ] [ VDisk/SFS ]
Gogeta70
Member
Member
Posts: 28
Joined: Thu Jun 06, 2013 11:15 am

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

Post 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.
Gogeta70
Member
Member
Posts: 28
Joined: Thu Jun 06, 2013 11:15 am

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

Post 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!
User avatar
Combuster
Member
Member
Posts: 9301
Joined: Wed Oct 18, 2006 3:45 am
Libera.chat IRC: [com]buster
Location: On the balcony, where I can actually keep 1½m distance
Contact:

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

Post 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).
"Certainly avoid yourself. He is a newbie and might not realize it. You'll hate his code deeply a few years down the road." - Sortie
[ My OS ] [ VDisk/SFS ]
Gogeta70
Member
Member
Posts: 28
Joined: Thu Jun 06, 2013 11:15 am

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

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