x86nasmcpu-architecturecpu-registersgdt

What are the sizes of GDTR, LDTR, IDTR and TR registers?


I've searched up on a lot on the net and every time I can only find the sizes of GDT, LDT and IDT.

Here I'm not asking for the size of the table, but I'm asking about the sizes of the registers

I know that GDTR is a 64 bit register, but clueless about the others.


Solution

  • IDTR and GDTR appear to be 10 bytes each.

    IDTR can also point anywhere in virtual address space, so obviously its base has to be 64-bit as well. lidt and lgdt take the same limit / base structure format, and the pseudocode shows that in 64-bit mode they do:

                        GDTR(Limit) ← SRC[0:15];
                        GDTR(Base) ← SRC[16:79];
    

    The limit (max byte offset) should be considered part of GDTR / IDTR. The documentation for sidt also says "store IDTR", and what it stores is the same 2 + 8 byte struct that has limit + base. (The GDTR internally uses the limit to check segment selectors before looking them up in the GDT, so memory within 64k of the address you used with lgdt can be used for other things, if it's past the limit.)

    It seems lidt / lgdt don't check for the GDT/IDT base being a canonical address. The documentation says they #GP(0) If the memory address is in a non-canonical form. but I think that's talking about the addressing mode to reach the 10-byte memory operand, not the base address.

    (If it isn't possible to get a non-canonical address into the GDTR or IDTR, the CPU could internally store only the significant 48 bits (or 57 with PML5), bringing the size down to 6 + 2 = 8 bytes. And do the sign extension back to 64-bit as part of sidt / sgdt. But probably it is possible to round-trip an arbitrary 64-bit value through GDTR, just make sure the CPU doesn't need to use the GDT for anything before you put in a valid address!)


    LDTR holds a segment selector and a whole descriptor

    The documentation for lldt indicates that if there's no error:

        LDTR(SegmentSelector) ← SRC;
        LDTR(SegmentDescriptor) ← GDTSegmentDescriptor;
    

    This indicates that the internal LDTR keeps the 16-bit segment selector (the actual operand to ldtr, e.g. ldtr ax), as well as loading the selected GDT entry and keeping that. A GDT entry is 8 bytes, but it may decode that entry into some internal format. (Perhaps not including the type field, which was already checked and required to be of type == LDT)

    This implies that like segment registers such as DS or SS, if you change the GDT contents after running lldt, the base/limit of the selected entry at the time you ran lldt will continue to apply.

    TR appears similar to LDTR

    Again, the documentation shows:

    TaskRegister(SegmentSelector) ← SRC;
    TaskRegister(SegmentDescriptor) ← TSSSegmentDescriptor;
    

    As with LDTR, you can only retrieve the segment selector, not the actual descriptor it was storing. str r/m16 and sldt r/m16 only write a 16-bit destination operand.

    But the actual internal register needs to hold onto the whole segment descriptor, not re-index the current GDT with the selector.