Page 2 of 6

Re: Issues with interrupts

Posted: Fri Aug 05, 2022 10:31 am
by Octocontrabass
Looks like your IDT should work well enough to at least catch exceptions, although it definitely was not created by the code on your Github. Check your GDT with "info gdt" next.
Techflash wrote:Perhaps I should fill out the entire rest of the IDT with stub entries instead of just zeroes?
Eventually, yes, but you don't need it right now.

Re: Issues with interrupts

Posted: Fri Aug 05, 2022 11:52 am
by Techflash
nullplan wrote:you have 2 bytes for the push and at most 5 for the jump, makes 7 in total, aligned to 8
This seems like a major hassle, and could possibly change with newer versions of GCC. I vaguely remember there being some way to force (a section?) to align to x bytes no matter what using GNU AS directives (I think it was something along the lines of ".align"?) I would need to look into that more.

In regards to your other statement:
nullpan wrote:This defines a new symbol, "external interrupt", that handles interrupt 32. You need to define your own function "interrupt_common". It is entered with an IRET frame and the interrupt number less 128 on the stack.
I think I already have such a function like "interrupt_common": ISRCommonStub for ISRs and IRQCommonStub for IRQs (both defined in [IRQ/ISR]ASM.S. However I'm not certain that they work correctly, as I have yet to know when they trigger.

Re: Issues with interrupts

Posted: Fri Aug 05, 2022 12:08 pm
by Demindiro
nullplan wrote:
Techflash wrote:Perhaps I should fill out the entire rest of the IDT with stub entries instead of just zeroes?
That is generally a good idea. Between IOAPIC, local APIC, and MSI, you can generally not know how many interrupts you will have ahead of time these days. Filling the entire IDT can be done as simply as:

Code: Select all

.align 8
.global external_interrupt
external_interrupt:
.set interrupt,32
.rept 256-32
  pushq interrupt-128
  jmpq interrupt_common
.align 8
.set interrupt, interrupt+1
.endr
This defines a new symbol, "external interrupt", that handles interrupt 32. You need to define your own function "interrupt_common". It is entered with an IRET frame and the interrupt number less 128 on the stack. The encoding is chosen such that for all possible values of "interrupt", the encoding of the push instruction is the short encoding. This means that all interrupt handlers are exactly 8 bytes apart from one another (you have 2 bytes for the push and at most 5 for the jump, makes 7 in total, aligned to 8). So now you can define in C:

Code: Select all

extern const char external_interrupt[];
for (int i = 0; i < 256-32; i++)
  set_idt_entry(i + 32, external_interrupt + 8 * i);
Your "interrupt_common" function will then need to save all registers, probably fix up the interrupt number on stack, then call whatever interrupt handler you need to. Then restore registers and do whatever work is required when returning from interrupt.
You can save some code with two tricks:
  • Instead of using push + jmp, you can use call and calculate the index from the offset ($rip - external_interrupt) (example). Each call instruction is always exactly 5 bytes.
  • You can avoid the setup loop at runtime by putting the table at a known location and calculating the offset from a constant. Personally, I put it at the start address of the kernel. (example link.ld, example compile time loop)

Re: Issues with interrupts

Posted: Fri Aug 05, 2022 7:08 pm
by Techflash
UPDATE: new IDT with max entries sent to a stub ISR handler. bochs 'info idt' can be found here: https://gist.github.com/techflashYT/65e ... 2d0d8af37d

Still crashes though. Surely something is horribly wrong then. Even at the instruction that caused the crash, 'info idt' is the same, and 'sreg' shows the CS as still at 0x08, and 64-bit.

Re: Issues with interrupts

Posted: Fri Aug 05, 2022 7:09 pm
by Octocontrabass
As I mentioned in my previous post, that suggests a problem with your GDT. Use "info gdt" to check it.

Re: Issues with interrupts

Posted: Fri Aug 05, 2022 7:11 pm
by Techflash
Octocontrabass wrote:As I mentioned in my previous post, that suggests a problem with your GDT. Use "info gdt" to check it.
#-o Looks like I read it wrong, my bad! I thought you had said to run "info IDT". Will update with results in a sec.
UPDATE: WOAAAH! That's all sorts of wrong:

Code: Select all

Global Descriptor Table (base=0xffffffffffe07120, limit=39):
GDT[0x0000]=??? descriptor hi=0x00000000, lo=0x00000000
GDT[0x0008]=Code segment, base=0x00000000, limit=0xffffffff, Execute/Read, Non-Conforming, 32-bit
GDT[0x0010]=Data segment, base=0x00000000, limit=0xffffffff, Read/Write, Accessed
GDT[0x0018]=Code segment, base=0x00000000, limit=0xffffffff, Execute/Read, Non-Conforming, 32-bit
GDT[0x0020]=Data segment, base=0x00000000, limit=0xffffffff, Read/Write
You can list individual entries with 'info gdt [NUM]' or groups with 'info gdt [NUM] [NUM]'
Can't really tell myself, but it looks like it's basically reading a bunch of 0x0s and 0xFs at random. Likely the GDT pointer is messed up? Either that or something went horribly wrong while creating it.

Re: Issues with interrupts

Posted: Fri Aug 05, 2022 7:21 pm
by Octocontrabass
Looks mostly correct to me. The only problem with your GDT is that your code segments are 32-bit instead of 64-bit.

This also means that your code to reload the segment registers is not reloading CS, since the CPU doesn't switch to 32-bit mode!

Re: Issues with interrupts

Posted: Fri Aug 05, 2022 7:41 pm
by Techflash
Octocontrabass wrote:Looks mostly correct to me. The only problem with your GDT is that your code segments are 32-bit instead of 64-bit.

This also means that your code to reload the segment registers is not reloading CS, since the CPU doesn't switch to 32-bit mode!
Huh. I can't seem to figure out why it's 32-bit. No matter what I change it always breaks more. Also all of the "base" parts are 64-bit.

Re: Issues with interrupts

Posted: Fri Aug 05, 2022 7:52 pm
by Octocontrabass
Techflash wrote:I can't seem to figure out why it's 32-bit.
You've set the granularity byte to 0xCF. A 64-bit code segment would use the value 0xAF here.

You should also take a closer look at this instruction since it isn't loading CS.

Re: Issues with interrupts

Posted: Fri Aug 05, 2022 9:21 pm
by Techflash
Octocontrabass wrote:
Techflash wrote:I can't seem to figure out why it's 32-bit.
You've set the granularity byte to 0xCF. A 64-bit code segment would use the value 0xAF here.

You should also take a closer look at this instruction since it isn't loading CS.
After simply fixing the granularity byte, it does some goofy stuff.:
Out of nowhere after the IDT init it randomly jumps to 0x000..023? (an illegal instruction)
From there it jumps back to where it was
After letting it go, it barfs: "fetch_raw_descriptor: GDT: index (37) 6 > limit (27)" into the log a bunch then triple faults. Update to this: After tinkering with interrupt handlers it gives "access_read_linear(): canonical failure" every other interrupt handler..... weird.
I also noticed that it somehow managed to print "I4" onto the screen? (found out it's calling my ISRHandler function, and it's just because I have a very borked printf implementation)

Also what do you mean that the instruction isn't loading CS?

Re: Issues with interrupts

Posted: Fri Aug 05, 2022 10:20 pm
by Octocontrabass
Techflash wrote:Out of nowhere after the IDT init it randomly jumps to 0x000..023? (an illegal instruction)
You're receiving an IRQ, and your IDT says that's where the handler is. No idea which IRQ though, you still haven't initialized the interrupt controllers.
Techflash wrote:Also what do you mean that the instruction isn't loading CS?
It's not a far JMP, so CS doesn't change - it still contains the old descriptor from the bootloader's GDT. It's easier to see that it's not a far JMP after translating it to Intel syntax. There's no way to encode a far JMP with a 64-bit destination in long mode, so you'll have to use something else (far RET is the usual choice).

Re: Issues with interrupts

Posted: Fri Aug 05, 2022 10:46 pm
by Techflash
Octocontrabass wrote:
Techflash wrote:Out of nowhere after the IDT init it randomly jumps to 0x000..023? (an illegal instruction)
You're receiving an IRQ, and your IDT says that's where the handler is. No idea which IRQ though, you still haven't initialized the interrupt controllers.
Techflash wrote:Also what do you mean that the instruction isn't loading CS?
It's not a far JMP, so CS doesn't change - it still contains the old descriptor from the bootloader's GDT. It's easier to see that it's not a far JMP after translating it to Intel syntax. There's no way to encode a far JMP with a 64-bit destination in long mode, so you'll have to use something else (far RET is the usual choice).
Just got code working for the PIC. Live on GitHub now. Still crashes with the same junk though.

Re: Issues with interrupts

Posted: Fri Aug 05, 2022 11:08 pm
by Octocontrabass

Re: Issues with interrupts

Posted: Fri Aug 05, 2022 11:33 pm
by Techflash
Octocontrabass wrote:That's because you're still filling your IDT with something that isn't the addresses of your handlers.
Crap. I hate it when old parts of the project that were build off of a previous design come back and bite me in the @$$. Hang on let me go fix that.

UPDATE: NEW JUNK!!!
Bochs log (shortened a lot, "..." means the above repeats a lot)

Code: Select all

07416545692e[CPU0  ] iret64: return CS selector null
07416545750e[CPU0  ] RET_Op64: canonical RIP violation
07416545772e[CPU0  ] access_read_linear(): canonical failure
...
*the following repeat A LOT*
07421046755p[APIC0 ] >>PANIC<< APIC read at address 0x0000fee000b0 spans 32-bit boundary !
...
07421046763p[APIC0 ] >>PANIC<< APIC write with len=8 (should be 4)
...
07421046787p[APIC0 ] >>PANIC<< APIC read at address 0x0000fee00030 spans 32-bit boundary !
*end of huge repeat*
07421292285e[HPET  ] read: timer id out of range
07421292285e[HPET  ] write: timer id out of range
...

Re: Issues with interrupts

Posted: Fri Aug 05, 2022 11:47 pm
by nullplan
Far jump in AT&T syntax uses a different mnemonic:

Code: Select all

ljmp $<seg>, <label>
So for example:

Code: Select all

ljmp $8, 1f
1:
Indirect far jump uses the same mnemonic, but has a memory reference as operand, and requires a * to be placed in front of that operand (like indirect near jump).