Page 1 of 1

LTR causes #GP

Posted: Wed Jun 02, 2021 1:32 pm
by antoni
I'm currently working on dispatcher. I've already implemented few things like switching stack, address space, segment registers, restoring registers etc. Now I'm trying to implement user mode. I'm using our wiki's tutorial for this (https://wiki.osdev.org/Getting_to_Ring_3), except that I'm doing this in long mode so I have 64bit TSS, different iret stack frame etc. (I want to use the iret method).

I really don't know why LTR gives me #GP.

Here's the code:

Code: Select all

mov ax, (5 * 8) | 3
ltr ax
Saved rip (in #GP handler) points to "ltr ax" instruction.

Here's my TSS:

Code: Select all

tss = {
    limit_low = 0x1000, base_low = 0x5000, base_middle = 0x11, 
    access = 0xe9, granularity = 0x0, base_high = 0x0}
access = 0xE9 = 11101001 =
accessed: 1
read_write: 0
conforming_expand_down: 0
code: 1
code_data_segment: 0
DPL: 3 = 11
present: 1

Here you have my whole GDT:

Code: Select all

{null = {limit_low = 0xffff, base_low = 0x0, base_middle = 0x0, 
    access = 0x0, granularity = 0x1, base_high = 0x0}, code0 = {
    limit_low = 0x0, base_low = 0x0, base_middle = 0x0, access = 0x9a, 
    granularity = 0xaf, base_high = 0x0}, data0 = {limit_low = 0x0, 
    base_low = 0x0, base_middle = 0x0, access = 0x93, granularity = 0x8f, 
    base_high = 0x0}, code3 = {limit_low = 0x0, base_low = 0x0, 
    base_middle = 0x0, access = 0xfa, granularity = 0xaf, base_high = 0x0}, 
  data3 = {limit_low = 0x0, base_low = 0x0, base_middle = 0x0, 
    access = 0xf2, granularity = 0x8f, base_high = 0x0}, tss = {
    limit_low = 0x1000, base_low = 0x5000, base_middle = 0x11, 
    access = 0xe9, granularity = 0x0, base_high = 0x0}, pointer = {
    limit = 0x2f, base = 0x100100130}}

Re: LTR causes #GP

Posted: Wed Jun 02, 2021 2:01 pm
by nullplan
In 64-bit long mode, the TSS descriptor is a long descriptor (spans two entries), where the upper entry contains only the high 32 bits of the TSS base address. This is one reason why I just declare the GDT as array of 64-bit integers. Here's my code:

Code: Select all

enum {
    NULL_DESC,
    KCODE_DESC,
    KDATA_DESC,
    UCODE_DESC,
    UDATA_DESC,
    TSS_DESC,   /* TSS is long */
    TSSU_DESC,
    MAX_GDT
};
static void init_gdt(uint64_t *gdt, const struct tss* tss)
{
    uint64_t tssp = (uint64_t)tss;
    gdt[KCODE_DESC] = 0x00af9a000000ffff;
    gdt[KDATA_DESC] = 0x00af92000000ffff;
    gdt[UCODE_DESC] = 0x00affa000000ffff;
    gdt[UDATA_DESC] = 0x00aff2000000ffff;
    gdt[TSS_DESC] = (tssp << 16) & 0xffffff0000 | (tssp << 32) & 0xff00000000000000 | sizeof (struct tss) - 1 | (uint64_t)0x89 << 40;
    gdt[TSSU_DESC] = tssp >> 32;
}
The same is true for other system descriptors, like LDTs, but I don't use any of those.

Re: LTR causes #GP

Posted: Wed Jun 02, 2021 2:15 pm
by antoni
Wow! It worked. Thanks a lot!