Page 1 of 2

Trouble setting up ISRs

Posted: Mon Nov 29, 2010 6:23 pm
by RoadRiverRail
I realize this is a newbie question and that the wiki has an article about setting up ISRs, but it's still not enough to get me over the hump, and I've tried to resolve this on my own.

The problem in short: I have written an ISR for interrupt 0 (divide by zero exception). I have made an interrupt descriptor for it, I have made an IDT in memory, and I have a pointer to that IDT. I hand the pointer over to the lidt instruction. I can breakpoint near the lidt instruction and confirm that the idtr shows the base and limit as I expect it to. I divide by zero and, rather than running my ISR, Bochs appears to immediately reset. The kernel's address base is 0xf0000000 and I am not currently using paging. I am using packing pragmas on my IDT-related structs and I have confirmed their size to be correct.

My IDT-manipulating functions are as follows:

Code: Select all

void reset_idt() {
		memset(idt, 0, sizeof(IDTDescr)*256);
		idt_struct.base = (uint32_t)idt;
		idt_struct.limit = 256*(sizeof(IDTDescr)-1);
		idtp = &idt_struct;		
}

void set_idt_entry(uint8_t number, void (*handler)(), uint16_t type) {
	uint32_t handler_addr = (uint32_t)handler;
	idt[number].offset_1 = LOW_OFFSET(handler_addr);
	idt[number].offset_2 = HIGH_OFFSET(handler_addr);
	idt[number].selector = GD_KT;
	idt[number].type_attr = type;
	idt[number].zero = 0;
}

void load_idt() {
    asm volatile("LIDT (%0) ": :"p" (idtp));
    asm("cli");
}
Now, I suspect this has to do with the kernel base being high, and I've read in the wiki article to make sure I'm using the IDT pointer's linear address, which I believe I am, since I'm not explicitly subtracting the kernel base address and an objdump shows the high address being used in the lidt instruction. Also, I see the expected values in the idtr registers when I use the Bochs debugger. This still doesn't seem to be enough, though, and Bochs just automatically resets after the faulting divide-by-zero instruction.

I don't know what I should check next. Any thoughts?

Re: Trouble setting up ISRs

Posted: Mon Nov 29, 2010 6:37 pm
by gerryg400
I don't really understand what you are saying. But just to be clear, as is currently being discussed in another thread, the LIDT and LGDT instructions do NOT take a linear address as their operand. The operand is a normal memory address using DS as the default segment.

The memory that's pointed to must hold the size and linear address of the IDT. Is that clear ?

Re: Trouble setting up ISRs

Posted: Mon Nov 29, 2010 6:51 pm
by RoadRiverRail
gerryg400 wrote:I don't really understand what you are saying. But just to be clear, as is currently being discussed in another thread, the LIDT and LGDT instructions do NOT take a linear address as their operand. The operand is a normal memory address using DS as the default segment.

The memory that's pointed to must hold the size and linear address of the IDT. Is that clear ?
Yes. That is clear, I believe, though I think I may be experiencing some confusion about "linear address" in this context since I'm used to using that term only in a context of paging and I'm not yet using paging. That said, looking at the value of my GDT in Bochs versus what's in the symbol dump of the kernel does make some things clear and I do need to adjust for my kernel symbols being linked at addresses 0xf0000000 and higher.

Just correcting the base in my IDT struct isn't enough, though. Perhaps I'm not using the correct address for the entry point in the ISR? Should it likewise be a linear address?

Re: Trouble setting up ISRs

Posted: Mon Nov 29, 2010 8:30 pm
by gerryg400
Fingers crossed that I have this correct.....

Only the GDTR and LDTR (hidden) registers contain linear addresses. For the purpose of the explanation, let's have the following

Code: Select all

0xf0000000 is the base address of your kernel data and code segments.
0x00001000 (linear address 0xf0001000) is the address of your IDT
0x00002000 (linear address 0xf0002000) will be used as a scratch area to load the IDTR from
0x00008000 (linear address 0xf0008000) is the address of your interrupt service routine
In this code idt (the address of the IDT) equals 0x1000 and handler will be 0x8000

Code: Select all

void set_idt_entry(uint8_t number, void (*handler)(), uint16_t type) {
   uint32_t handler_addr = (uint32_t)handler;
   idt[number].offset_1 = LOW_OFFSET(handler_addr);
   idt[number].offset_2 = HIGH_OFFSET(handler_addr);
   idt[number].selector = GD_KT;
   idt[number].type_attr = type;
   idt[number].zero = 0;
}
In this code descp will be 0x2000 BUT idt_lin will be the linear address. Note that descp is relative to DS while the value stored is linear.

Code: Select all

	desc_p.addr = 0xf0001000;    // linear address of IDT
	desc_p.leng = 0xfff;
	lidt(&desc_p);                      // &desc_p == 0x2000
At this point the IDTR register contains the linear address and length of your IDT.

When an interrupt 0 occurs the processor will do a bunch of stuff and end up vectoring to the selector (GD_KT) and offset (handler_address) that is strored in the 0th entry of the IDT. The base (GDKT == 0xf0000000) will be added to the offset (handler_addr == 0x8000) and the processor will enter the isr at linear address 0xf0008000. Remember however that EIP will be 0x8000 !!

NOTE: This explanation will test 2 things. Whether I understand what happens and whether I'm any good at explaining.

Re: Trouble setting up ISRs

Posted: Tue Nov 30, 2010 1:31 am
by Combuster
Some minor issue:

Code: Select all

void load_idt() {
    asm volatile("LIDT (%0) ": :"p" (idtp));
    asm("cli");
}
This does not enable interrupts...

Re: Trouble setting up ISRs

Posted: Tue Nov 30, 2010 1:43 am
by RoadRiverRail
Combuster wrote:Some minor issue:
(...)
This does not enable interrupts...
Clearly, I have got turned around somewhere, then. I was under the impression that that was sufficient to test out a single exception handler, which is all I've coded up so far. I take it there's more I should be doing?

Re: Trouble setting up ISRs

Posted: Tue Nov 30, 2010 1:56 am
by RoadRiverRail
gerryg400 wrote:Fingers crossed that I have this correct.....
NOTE: This explanation will test 2 things. Whether I understand what happens and whether I'm any good at explaining.
Actually, it explains a number of things. I inherited the GDT setup from some example boot code I borrowed and I'm starting to suspect I don't actually know what's properly going on in there. So, I need to review what's really set up for the kernel code and data descriptors before I keep going. Thank you for taking some time to lay this out.

One question-- if I use the & operator in C to get the address of something, should I end up with the linear address or the address relative to the current segment?

Re: Trouble setting up ISRs

Posted: Tue Nov 30, 2010 2:15 am
by gerryg400
gcc knows nothing about segments, though some other compilers do. So with gcc all addresses are relative the the segment base. Your loader is responsible for putting your non-zero-based segments at the correct linear address.

As far as I can tell the only change you need is in this func

Code: Select all

void reset_idt() {
      memset(idt, 0, sizeof(IDTDescr)*256);
      idt_struct.base = (uint32_t)idt;                <<<<<======== This should be the linear address
      idt_struct.limit = 256*(sizeof(IDTDescr)-1);
      idtp = &idt_struct;      
}

Re: Trouble setting up ISRs

Posted: Tue Nov 30, 2010 2:31 am
by RoadRiverRail
gerryg400 wrote:gcc knows nothing about segments, though some other compilers do. So with gcc all addresses are relative the the segment base. Your loader is responsible for putting your non-zero-based segments at the correct linear address.

As far as I can tell the only change you need is in this func

Code: Select all

void reset_idt() {
      memset(idt, 0, sizeof(IDTDescr)*256);
      idt_struct.base = (uint32_t)idt;                <<<<<======== This should be the linear address
      idt_struct.limit = 256*(sizeof(IDTDescr)-1);
      idtp = &idt_struct;      
}
Funny...whenever I use my kernel's printf() to get the address of idt, it's starting with 0xf, which would seem to me to indicate the linear address, since the kernel's code segment starts at 0xf0000000. I'm using gcc.

Re: Trouble setting up ISRs

Posted: Tue Nov 30, 2010 3:09 am
by Combuster
if you are not using paging, the address used is 0xf0000000, and the segment's base is 0xf0000000, it implies that the location in memory is base + address (truncated to 32 bits) = 0x(1) e000 0000 - which often happens to be your video card's VRAM. That is not good - you are probably giving us incorrect information.

Re: Trouble setting up ISRs

Posted: Tue Nov 30, 2010 3:18 am
by RoadRiverRail
Combuster wrote:if you are not using paging, the address used is 0xf0000000, and the segment's base is 0xf0000000, it implies that the location in memory is base + address (truncated to 32 bits) = 0x(1) e000 0000 - which often happens to be your video card's VRAM. That is not good - you are probably giving us incorrect information.
You're right. I am, in no small part because I'm still pretty new to segmentation and I inherited the GDT setup code so I'm still grasping what's happening. Regardless, Bochs does not lie:

Code: Select all

es:0x0010, dh=0x10cf9300, dl=0x0000ffff, valid=1
	Data segment, base=0x10000000, limit=0xffffffff, Read/Write, Accessed
cs:0x0008, dh=0x10cf9b00, dl=0x0000ffff, valid=1
	Code segment, base=0x10000000, limit=0xffffffff, Execute/Read, Accessed, 32-bit
ss:0x0010, dh=0x10cf9300, dl=0x0000ffff, valid=7
	Data segment, base=0x10000000, limit=0xffffffff, Read/Write, Accessed
ds:0x0010, dh=0x10cf9300, dl=0x0000ffff, valid=7
	Data segment, base=0x10000000, limit=0xffffffff, Read/Write, Accessed
fs:0x0010, dh=0x00cf9300, dl=0x0000ffff, valid=1
	Data segment, base=0x00000000, limit=0xffffffff, Read/Write, Accessed
gs:0x0010, dh=0x00cf9300, dl=0x0000ffff, valid=1
	Data segment, base=0x00000000, limit=0xffffffff, Read/Write, Accessed

Re: Trouble setting up ISRs

Posted: Tue Nov 30, 2010 3:25 am
by Combuster
So, you are using the GDT trick to put your kernel at 0xf0000000 virtual and 0x00000000 linear/physical. That means the argument to LIDT/LGDT should have a value of the form like 0x000?????. As mentioned earlier, if it starts with 0xf00, you forgot to add the segment base to get the real linear address.

Re: Trouble setting up ISRs

Posted: Tue Nov 30, 2010 3:29 am
by gerryg400

Code: Select all

Code segment, base=0x10000000, limit=0xffffffff, Execute/Read, Accessed, 32-bit
I thought your base was 0xf0000000.

Re: Trouble setting up ISRs

Posted: Tue Nov 30, 2010 3:35 am
by RoadRiverRail
gerryg400 wrote:

Code: Select all

Code segment, base=0x10000000, limit=0xffffffff, Execute/Read, Accessed, 32-bit
I thought your base was 0xf0000000.
Indeed, I thought so, too, and the reason I thought so was because I was wrong. As for the reason I was wrong, I believe it's because the GDT setup code put the selectors together with a macro using a symbol called "KERNBASE" which is set to 0xf00000. Genius that I am, I have repeatedly misread its use in the macro...it's not "KERNBASE" but "-KERNBASE", which some back-of-the-envelope playing with bits turns up 0x10000000, which Bochs reports.

I apologize for being wrong about this.

Re: Trouble setting up ISRs

Posted: Tue Nov 30, 2010 3:43 am
by RoadRiverRail
Combuster wrote:So, you are using the GDT trick to put your kernel at 0xf0000000 virtual and 0x00000000 linear/physical. That means the argument to LIDT/LGDT should have a value of the form like 0x000?????. As mentioned earlier, if it starts with 0xf00, you forgot to add the segment base to get the real linear address.
This has been very instructive and I appreciate your explaining this to me, as well as why it seems that the linear addresses are always lining up with physical ones, as well as why subtracting the KERNBASE (a symbol set to 0xf0000000) is magically giving me physical addresses.

I'm getting much further down the road. "info idt" in Bochs is at least starting to try and parse through my interrupt descriptor table. It's showing a ??? for even the 0 entry, but I am guessing this is some sort of error in how I'm building the table.