LTR causes #GP

Question about which tools to use, bugs, the best way to implement a function, etc should go here. Don't forget to see if your question is answered in the wiki first! When in doubt post here.
Post Reply
antoni
Member
Member
Posts: 61
Joined: Sun May 24, 2020 9:11 am
Location: /dev/null

LTR causes #GP

Post 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}}
nullplan
Member
Member
Posts: 1790
Joined: Wed Aug 30, 2017 8:24 am

Re: LTR causes #GP

Post 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.
Carpe diem!
antoni
Member
Member
Posts: 61
Joined: Sun May 24, 2020 9:11 am
Location: /dev/null

Re: LTR causes #GP

Post by antoni »

Wow! It worked. Thanks a lot!
Post Reply