Page 1 of 4

[SOLVED] converting 32-bit IDT to 64-bit IDT

Posted: Mon Nov 30, 2020 2:51 pm
by austanss
Ever since I ported my OS to 64-bit, I haven't used interrupts. I only used UEFI's IDT, which does just about nothing but the nice-looking exception register dumps.

Now I need to fix it, and I can't use my old code, because I tested it and it doesn't work.

The old code I actually don't understand, because I didn't write it, because it comes from a time before microNET existed, which was the project that I poured hours into, that was an OSS project, that I ended up basing microNET off of.

Anyway, I have an IDT written, yet I have no IDT struct or proper loading of IDT.

Can anyone point me in the right direction?

(Yes, I understand that IDT issues are the most common and newbie issues to ask about.)

Re: converting 32-bit IDT to 64-bit IDT

Posted: Mon Nov 30, 2020 3:40 pm
by Octocontrabass
rizxt wrote:The old code I actually don't understand,
How will you update it for 64-bit mode if you don't understand it?
rizxt wrote:Can anyone point me in the right direction?
Have you tried the Intel or AMD manuals? :wink:

In 64-bit mode, the IDT entry size doubles: they are now sixteen bytes long instead of eight bytes long. Four of the new eight bytes are the upper 32 bits of the ISR address, and the other four must be zero. If you're only using trap and interrupt gates, nothing else in your IDT needs to change.

Re: converting 32-bit IDT to 64-bit IDT

Posted: Mon Nov 30, 2020 3:42 pm
by austanss
There isn't much on 64-bit IDTs compared to their 32-bit relatives.

Re: converting 32-bit IDT to 64-bit IDT

Posted: Mon Nov 30, 2020 3:53 pm
by austanss
So I read the Intel manuals sections 6.9-6.12, and it gives next to nothing on the structure of the IDT. It doesn't give a diagram or anything of the like. Also, most of the documentation written in that section is for IA-32, or maybe I'm reading the wrong manual. This is the only useful information I gathered:
Intel manual wrote: 6.10 INTERRUPT DESCRIPTOR TABLE (IDT)The interrupt descriptor table (IDT) associates each exception or interrupt vector with a gate descriptor for the procedure or task used to service the associated exception or interrupt. Like the GDT and LDTs, the IDT is an array of 8-byte descriptors (in protected mode). Unlike the GDT, the first entry of the IDT may contain a descriptor. To form an index into the IDT, the processor scales the exception or interrupt vector by eight (the number of bytes in a gate descriptor). Because there are only 256 interrupt or exception vectors, the IDT need not contain more than 256 descriptors. It can contain fewer than 256 descriptors, because descriptors are required only for the interrupt and exception vectors that may occur. All empty descriptor slots in the IDT should have the present flag for the descriptor set to 0.The base addresses of the IDT should be aligned on an 8-byte boundary to maximize performance of cache line fills. The limit value is expressed in bytes and is added to the base address to get the address of the last valid byte. A limit value of 0 results in exactly 1 valid byte. Because IDT entries are always eight bytes long, the limit should always be one less than an integral multiple of eight (that is, 8N – 1).The IDT may reside anywhere in the linear address space. As shown in Figure 6-1, the processor locates the IDT using the IDTR register. This register holds both a 32-bit base address and 16-bit limit for the IDT.

Re: converting 32-bit IDT to 64-bit IDT

Posted: Mon Nov 30, 2020 4:22 pm
by Octocontrabass
rizxt wrote:It doesn't give a diagram or anything of the like.
Isn't figure 6-2 a diagram of the IDT descriptors? (Figure 6-8 is for 64-bit mode.)
rizxt wrote:Also, most of the documentation written in that section is for IA-32, or maybe I'm reading the wrong manual.
Try section 6.14.

Re: converting 32-bit IDT to 64-bit IDT

Posted: Mon Nov 30, 2020 7:35 pm
by bzt
Octocontrabass wrote:In 64-bit mode, the IDT entry size doubles: they are now sixteen bytes long instead of eight bytes long. Four of the new eight bytes are the upper 32 bits of the ISR address, and the other four must be zero.
Not exactly, there are new fields in the old 8 bytes as well (IST). Our wiki has a C structure for it.
rizxt wrote:maybe I'm reading the wrong manual.
Looks like it.

Cheers,
bzt

Re: converting 32-bit IDT to 64-bit IDT

Posted: Mon Nov 30, 2020 9:48 pm
by austanss
Still a bit lost on these descriptors. What are the members of the structure, and what do they represent?

These Intel manuals are not going into enough detail, and it's using big words. (TSS, LDT, etc.)

Re: converting 32-bit IDT to 64-bit IDT

Posted: Mon Nov 30, 2020 11:04 pm
by Octocontrabass
rizxt wrote:These Intel manuals are not going into enough detail, and it's using big words. (TSS, LDT, etc.)
Look them up in the index. You need to know at least a little bit about all of those things.

You might want to check the AMD manuals too. Sometimes they explain things better than the Intel manuals.

Re: converting 32-bit IDT to 64-bit IDT

Posted: Mon Nov 30, 2020 11:27 pm
by austanss
Please stop referring me to the manuals. I've read everything in the manuals about the IDT and the AMD manuals are even worse at documenting it.

Re: converting 32-bit IDT to 64-bit IDT

Posted: Mon Nov 30, 2020 11:53 pm
by Octocontrabass
So... what do you need explained that the manuals and the wiki don't cover well enough?

Re: converting 32-bit IDT to 64-bit IDT

Posted: Tue Dec 01, 2020 8:24 am
by austanss

Code: Select all

struct IDTDescr {
   uint16_t offset_1; // offset bits 0..15
   uint16_t selector; // a code segment selector in GDT or LDT
   uint8_t ist;       // bits 0..2 holds Interrupt Stack Table offset, rest of bits zero.
   uint8_t type_attr; // type and attributes
   uint16_t offset_2; // offset bits 16..31
   uint32_t offset_3; // offset bits 32..63
   uint32_t zero;     // reserved
};
What are the offsets? What does the selector point to? How many do I need for a complete table? What is the IST? What are the types and attributes?

Re: converting 32-bit IDT to 64-bit IDT

Posted: Tue Dec 01, 2020 11:38 am
by nullplan
rizxt wrote:What are the offsets? What does the selector point to? How many do I need for a complete table? What is the IST? What are the types and attributes?
What? Did you just look at the pretty pictures in the manuals and failed to actually read the description? I invite you to open page 6-17 of volume 3A of the Intel SDM. And read the stuff below the picture.

To make it short, all offset fields put together will name the "offset" of the interrupt service routine. Since that is an offset relative to a segment whose base is ignored, it is literally just the address of the interrupt service routine. The selector field contains the CS selector for that ISR, so you just put the number there that you are also putting into CS after loading the GDT. The IST is an interrupt stack switching mechanism that allows you to always load RSP with a certain value upon that interrupt, even if you are already executing in ring 0 (normally, stack switching occurs only when switching to higher privilege). That is necessary if the interrupt in question can occur while in ring 0, but the registers aren't all set up correctly, as is the case when you support the SYSCALL instruction. Linux only puts NMI, Machine Check, and Double Fault on IST stacks. Other faults in kernel mode will likely lead to a kernel panic anyway (especially if they occur in the entry code), so this is enough. Finally, type and attributes is an 8 bit field, where the highest bit is the P bit, which must be set or else the gate counts as not present, then come two bits for the DPL. If, like most normal people, you only ever use ring 0 and ring 3, then only 0 and 3 are sensible values, and they describe the access level a user of the "int" instruction must have to call that interrupt as a software interrupt. So you set that to 0 for every interrupt you don't want userspace to call, and to 3 for every interrupt that you do. Bear in mind, however, that SYSCALL is the far faster mechanism. So much so that an exclusively interrupt based system call mechanism performs worse on contemporary hardware than on older hardware that lacked the SYSCALL instruction. So there is not much reason to use software interrupts. The DPL is ignored when a gate is invoked as part of an exception or external interrupt.

Finally the type, the low 4 bits of the type_attr field. The type is explained in table 3-2, and that text is actually a link there. The only types that have any business being in a 64-bit IDT are types 14 and 15. They are identical except for their treatment of the interrupt flag. Type 15, trap gate, will leave the interrupt flag unchanged when entering the ISR, while type 14, interrupt gate, will disable that flag. In both cases, the old value is preserved in the interrupt frame. I have yet to find a use for type 15. Just use type 14 for everything. If you want an interruptible kernel, the correct way is to use type 14, then check if you have enough stack left to field another interrupt, and only then enable interrupts again.

All of this is information I learned from the SDM and AMD's APM, and not by badgering poor innocent forum users while refusing their advice. If you think this stuff is complicated, it is not. Not in comparison to the hell all of the real hardware will put you through.

Re: converting 32-bit IDT to 64-bit IDT

Posted: Tue Dec 01, 2020 12:07 pm
by austanss
Thank you for that information.

However, AMD's APM? I was reading the BKDG...

I apologize for coming off as ignorant. I just wasn't understanding what I was reading in the Intel manual, and I wasn't reading the right AMD manual, either...

Re: converting 32-bit IDT to 64-bit IDT

Posted: Tue Dec 01, 2020 2:00 pm
by austanss
Aight. So, I did all that. Let me share my source:
https://github.com/microNET-OS/microCOR ... interrupts

I'm getting the same behavior as before.
Not using UEFI IDT, not faulting, but not doing anything...

This is a registers dump via QEMU

Code: Select all

(qemu) info registers
RAX=0000000000000000 RBX=0000000000000000 RCX=000000000000001d RDX=0000000000000001
RSI=0000000000dddddd RDI=0000000000115020 RBP=000000003ff07990 RSP=000000000010f000
R8 =000000000000000a R9 =00000000000000d2 R10=0000000000000122 R11=0000000080000000
R12=0000000000000000 R13=0000000000000001 R14=0000000000000004 R15=000000003f0efe18
RIP=0000000000100095 RFL=00000202 [-------] CPL=0 II=0 A20=1 SMM=0 HLT=1
ES =0030 0000000000000000 ffffffff 00cf9300 DPL=0 DS   [-WA]
CS =0038 0000000000000000 ffffffff 00af9a00 DPL=0 CS64 [-R-]
SS =0030 0000000000000000 ffffffff 00cf9300 DPL=0 DS   [-WA]
DS =0030 0000000000000000 ffffffff 00cf9300 DPL=0 DS   [-WA]
FS =0030 0000000000000000 ffffffff 00cf9300 DPL=0 DS   [-WA]
GS =0030 0000000000000000 ffffffff 00cf9300 DPL=0 DS   [-WA]
LDT=0000 0000000000000000 0000ffff 00008200 DPL=0 LDT
TR =0000 0000000000000000 0000ffff 00008b00 DPL=0 TSS64-busy
GDT=     00000000001008b9 00000017
IDT=     000000000010f040 00000fff
CR0=80010033 CR2=0000000000000000 CR3=000000003fc01000 CR4=00000668
DR0=0000000000000000 DR1=0000000000000000 DR2=0000000000000000 DR3=0000000000000000 
DR6=00000000ffff0ff0 DR7=0000000000000400
EFER=0000000000000d00
FCW=037f FSW=0000 [ST=0] FTW=00 MXCSR=00001f80
FPR0=0000000000000000 0000 FPR1=0000000000000000 0000
FPR2=0000000000000000 0000 FPR3=0000000000000000 0000
FPR4=0000000000000000 0000 FPR5=0000000000000000 0000
FPR6=0000000000000000 0000 FPR7=0000000000000000 0000
XMM00=0000000000fa00000000000080000000 XMM01=00000a000000064000000a000000001d
XMM02=00000000000000000000000000000000 XMM03=00000000000000000000000000000000
XMM04=00000000000000000000000000000000 XMM05=00000000000000000000000000000000
XMM06=00000000000000000000000000000000 XMM07=00000000000000000000000000000000
XMM08=00000000000000000000000000000000 XMM09(qemu) info registers
RAX=0000000000000000 RBX=0000000000000000 RCX=000000000000001d RDX=0000000000000001
RSI=0000000000dddddd RDI=0000000000115020 RBP=000000003ff07990 RSP=000000000010f000
R8 =000000000000000a R9 =00000000000000d2 R10=0000000000000122 R11=0000000080000000
R12=0000000000000000 R13=0000000000000001 R14=0000000000000004 R15=000000003f0efe18
RIP=0000000000100095 RFL=00000202 [-------] CPL=0 II=0 A20=1 SMM=0 HLT=1
ES =0030 0000000000000000 ffffffff 00cf9300 DPL=0 DS   [-WA]
CS =0038 0000000000000000 ffffffff 00af9a00 DPL=0 CS64 [-R-]
SS =0030 0000000000000000 ffffffff 00cf9300 DPL=0 DS   [-WA]
DS =0030 0000000000000000 ffffffff 00cf9300 DPL=0 DS   [-WA]
FS =0030 0000000000000000 ffffffff 00cf9300 DPL=0 DS   [-WA]
GS =0030 0000000000000000 ffffffff 00cf9300 DPL=0 DS   [-WA]
LDT=0000 0000000000000000 0000ffff 00008200 DPL=0 LDT
TR =0000 0000000000000000 0000ffff 00008b00 DPL=0 TSS64-busy
GDT=     00000000001008b9 00000017
IDT=     000000000010f040 00000fff
CR0=80010033 CR2=0000000000000000 CR3=000000003fc01000 CR4=00000668
DR0=0000000000000000 DR1=0000000000000000 DR2=0000000000000000 DR3=0000000000000000 
DR6=00000000ffff0ff0 DR7=0000000000000400
EFER=0000000000000d00
FCW=037f FSW=0000 [ST=0] FTW=00 MXCSR=00001f80
FPR0=0000000000000000 0000 FPR1=0000000000000000 0000
FPR2=0000000000000000 0000 FPR3=0000000000000000 0000
FPR4=0000000000000000 0000 FPR5=0000000000000000 0000
FPR6=0000000000000000 0000 FPR7=0000000000000000 0000
XMM00=0000000000fa00000000000080000000 XMM01=00000a000000064000000a000000001d
XMM02=00000000000000000000000000000000 XMM03=00000000000000000000000000000000
XMM04=00000000000000000000000000000000 XMM05=00000000000000000000000000000000
XMM06=00000000000000000000000000000000 XMM07=00000000000000000000000000000000
XMM08=00000000000000000000000000000000 XMM09=00000000000000000000000000000000
XMM10=00000000000000000000000000000000 XMM11=00000000000000000000000000000000
XMM12=00000000000000000000000000000000 XMM13=00000000000000000000000000000000
XMM14=00000000000000000000000000000000 XMM15=00000000000000000000000000000000
=00000000000000000000000000000000
XMM10=00000000000000000000000000000000 XMM11=00000000000000000000000000000000
XMM12=00000000000000000000000000000000 XMM13=00000000000000000000000000000000
XMM14=00000000000000000000000000000000 XMM15=00000000000000000000000000000000
taken at a random moment when nothing appears to be happening.
I checked the IDTR address...
2020-12-01_14-59.png
and my hex editor went apeshit.

I'm bamboozled.

Re: converting 32-bit IDT to 64-bit IDT

Posted: Tue Dec 01, 2020 2:32 pm
by Octocontrabass
Perhaps try "info idt" instead of a broken hex editor.

Don't enable interrupts yet. Exception handlers are more important, and yours are very broken. Divide error (#DE) is usually the easiest one to start with; you can trigger it with asm("div %ah"); or similar.