[Solved] Exception after flushing GDT

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
Piezoelectric
Posts: 2
Joined: Wed Jun 13, 2018 12:29 am

[Solved] Exception after flushing GDT

Post by Piezoelectric »

Hey folks,
I'm trying to get my GDT setup following the tutorial on the Wiki. Unfortunately, as soon as I try to flush my GDT, the system (i.e. qemu) appears to jump to a random address, and if I continue execution under the debugger it will restart. I assume this means some exception is being thrown, but I don't know which one; since the GDT is a prerequisite for the IDT (as I understand it), I don't have the latter set up.

I'm using the following function to initialize the GDT. It's basically copy-pasted from the tutorial:

Code: Select all

global _set_gdt
_set_gdt:
   mov   eax, [esp + 4]
   mov   [gdtr + 2], eax
   mov   ax, [esp + 8]
   mov   [gdtr], ax
   lgdt  [gdtr]

reloadSegments:
   ; Reload CS register containing code selector:
   jmp   0x08:reload_CS ; 0x08 points at the new code selector
reload_CS:
   ; Reload data segment registers:
   mov   ax, 0x10 ; 0x10 points at the new data selector
   mov   ds, ax
   mov   es, ax
   mov   fs, ax
   mov   gs, ax
   mov   ss, ax
   ret
In my C code, I have the following GDT definitions:

Code: Select all

struct gdt_descriptor
{
    uint32_t base;
    uint32_t limit;
    uint32_t type;
};
typedef struct gdt_descriptor gdt_descriptor_t;

static const uint32_t tss = 0xffffff97;
static const uint32_t tss_length = 0x68;

static const gdt_descriptor_t gdt_definitions[] =
{
    {.base = 0, .limit = 0, .type = 0}, // Selector 0x0, null descriptor Cannot be used.
    {.base = 0, .limit = 0xffffffff, .type = 0x9A}, // Selector 0x08, code
    {.base = 0, .limit = 0xffffffff, .type = 0x10}, // Selector 0x10, data
    {.base = tss, .limit =  tss + tss_length, .type = 0x89} // Selector 0x18, TSS
};

static uint8_t gdt[GDT_ENTRY_SIZE * (sizeof(gdt_definitions) / sizeof(gdt_descriptor_t))];
I chose the TSS value arbitrarily because I don't know what it should be, but it's not being used right now anyway, AFAIK. These values are encoded into the appropriate format using the encodeGdtEntry function from the tutorial. I copied it verbatim and just changed the name to match my convention. I call it as follows:

Code: Select all

void gdt_initialize()
{
    const size_t gdt_entry_count = sizeof(gdt_definitions) / sizeof(gdt_descriptor_t);
    memset(gdt, 0, sizeof(gdt));

    for(size_t i = 0; i < gdt_entry_count; ++i)
    {
        encode_gdt_entry(gdt + (i * GDT_ENTRY_SIZE), gdt_definitions[i]);
    }
    
    _set_gdt(gdt, sizeof(gdt));
}
Here is what the GDT looks like before calling _set_gdt:

Code: Select all

(gdb) x/32xb gdt
0x107020 <gdt>:        0x00    0x00    0x00    0x00    0x00    0x00    0x40    0x00
0x107028 <gdt+8>:      0xff    0xff    0x00    0x00    0x00    0x9a    0xcf    0x00
0x107030 <gdt+16>:     0xff    0xff    0x00    0x00    0x00    0x10    0xcf    0x00
0x107038 <gdt+24>:     0xff    0xff    0x97    0xff    0xff    0x89    0xcf    0xff


Stepping through in the debugger, everything looks correct up until the `mov ds, ax` instruction in `reload_CS`. Once that executes, the exception/crash occurs. One thing I'd like to do is check the value of the GDT register once I set it, but gdb can't do that and I don't know how to do it in qemu (if it's even possible). Any help is much appreciated!

PS: I noticed that the GDT tutorial uses Intel/NASM syntax whereas the getting started tutorials use AT&T/GAS. It would be nice to make these consistent. :)
Last edited by Piezoelectric on Wed Jul 04, 2018 11:25 pm, edited 2 times in total.
nullplan
Member
Member
Posts: 1801
Joined: Wed Aug 30, 2017 8:24 am

Re: Exception after flushing GDT

Post by nullplan »

Your gdt+16 has no P bit. That one looks really b0rken. I think you need type 0x92 for that one, which should set the P bit and the S bit, and set the type to "writable data segment". And the limit for the TSS segment is just the length of the TSS minus 1. Oh, and clear the G bit on the TSS segment, since the length isn't measured in pages, right? The segment limit always the largest allowed byte (or page) offset.

As for assembly style: Them's fighting words! :wink: That's like asking for the best editor, best OS to work on, best processor-architecture...
Carpe diem!
Piezoelectric
Posts: 2
Joined: Wed Jun 13, 2018 12:29 am

Re: Exception after flushing GDT

Post by Piezoelectric »

Ugh, yup, that was it. I put the selector in where the type should have gone. Thanks! And thanks also for the advice about the TSS. That will come in handy once I actually start using it. :)

I don't want to start a religious war over assembly syntax, so I edited out my comment about my personal preference. I don't particularly care which style the wiki uses, but it would be nice to pick one and stick with it, so that users don't have to convert between them. I'm just not sure if I should make that change myself, because I don't want to step on anyone's toes.
User avatar
iansjack
Member
Member
Posts: 4706
Joined: Sat Mar 31, 2012 3:07 am
Location: Chichester, UK

Re: [Solved] Exception after flushing GDT

Post by iansjack »

The Wiki is just that - a Wiki - which means that different contributors will use different styles in all sorts of places. It's a source of information rather than a single, coherent tutorial.

It's not a bad idea to encounter different styles and become familiar with them.
Post Reply