Understanding the structure and loading of 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
ElectricWEEB
Posts: 2
Joined: Sat Feb 15, 2020 3:49 pm

Understanding the structure and loading of gdt

Post by ElectricWEEB »

Okay so I'm currently in the progress of setting up gdt in protected mode. I'm booted and loaded into protected mode by grub2 so I'm in flat memory mode currently.
I'm reading this article at the forum https://wiki.osdev.org/GDT_Tutorial but I'm confused about the loading and structure of the gdt data from the memory address provided to the example function written in assembly. The example function is as follows and I added comments here to make sure I understand the assembly correctly:

Code: Select all

gdtr DW 0 ; For limit storage
     DD 0 ; For base storage
 
setGdt:
   MOV   EAX, [esp + 4] ;Load the passed variable GDT (32bits) when called from c with setGdt(GDT, sizeof(GDT))
   MOV   [gdtr + 2], EAX ;Copy the dword to the third memory address counted from gdtr label
   MOV   AX, [ESP + 8] ;Copy the size of the passed GDT parameter (4?) to the low word of the eax register
   MOV   [gdtr], AX ;Copy the size to the gdtr data structure
   LGDT  [gdtr] ;Now load the gdt from the provided data
   RET
What I could not figure out (after two days of looking around in different places) is that what kind of data the GDT parameter passed to the assembly should contain.
I think that I figured out few options that what the param should be, could you please tell me if some of these options is right or am I totally lost with this:
Option1: the gdt is a 32 bit data that is passed to the assembly by the c compiler using stack. The data contains a single gdt descriptor and as such the size is always 4?
Option2: the gdt parameter is a pointer to a list of gdt descriptor elements and the second parameter describes the number of those entries?

This must be something very simple but I just could not find a proper explanation for the LGDT instruction that would be easily understandable. I have been coding some assembly for the x86 before but those were in userspace programs so this is the first time I'm dealing with ring 0 instructions.

Also I'm confused when reading the example code for creating gdt entries. The example C code provided on the same page creates gdt entries that are 64 bits long but according to my understanding of the example assembly code that loads the gdt the passed gdt parameter should be 32 bits long?

I must be missing something very obvious here, could you please clarify this for me.
Octocontrabass
Member
Member
Posts: 5575
Joined: Mon Mar 25, 2013 7:01 pm

Re: Understanding the structure and loading of gdt

Post by Octocontrabass »

ElectricWEEB wrote:Option2: the gdt parameter is a pointer to a list of gdt descriptor elements and the second parameter describes the number of those entries?
This one.
ElectricWEEB wrote:This must be something very simple but I just could not find a proper explanation for the LGDT instruction that would be easily understandable.
Here is a complete description of what the LGDT instruction does. Which part is confusing for you?
nullplan
Member
Member
Posts: 1792
Joined: Wed Aug 30, 2017 8:24 am

Re: Understanding the structure and loading of gdt

Post by nullplan »

You need to lgdt a memory area consisting of six bytes, of which the first two are the size of the GDT minus one, and the remaining four are a pointer to the GDT. If you get a size of four, then that is way too small. For a normal OS, you usually end up needing
  1. The null descriptor
  2. A kernel code descriptor
  3. A kernel data descriptor
  4. A user code descriptor
  5. A user data descriptor
  6. A TSS descriptor
Yes, it is possible to go beyond that, but for starters you likely won't need to. So that is six descriptors, each is eight bytes, that makes forty-eight bytes in total, so the limit should be set to forty-seven.

Incidentally, in thirty-two-bit mode, the GDTR is six bytes long and the null descriptor is eight bytes long, so I would store the GDTR inside the null descriptor. Its contents are immaterial, anyway, since the CPU does not even read the descriptor when the selector has been set to zero. Sadly, in sixty-four-bit mode the GDTR is ten bytes long, so it no longer fits into the null descriptor. You can also allocate the GDTR on stack, if you want to.
Carpe diem!
PeterX
Member
Member
Posts: 590
Joined: Fri Nov 22, 2019 5:46 am

Re: Understanding the structure and loading of gdt

Post by PeterX »

Could it be that you confuse GDT and GDTR?

The "struct" you put into the GDTR is very simple:
[16bit] Size of the GDT -1
[32bit] protected mode address of the GDT (not the real mode address!) EDIT: flat memory base address, not segment:offset

The GDT itself consists of "blocks" of 8 bytes each. See the OSDev.org wiki about protected mode (or read Wikipedia about GDT or use a search engine.) The GDTR is a register which is similar to a "pointer" to the GDT.

Hope that helps. If not, specify what's puzzling you.

Happy hacking
Peter
ElectricWEEB
Posts: 2
Joined: Sat Feb 15, 2020 3:49 pm

Re: Understanding the structure and loading of gdt

Post by ElectricWEEB »

PeterX wrote: Could it be that you confuse GDT and GDTR?
This was the problem but now I figured out how to properly load the gdt, thanks to you for clarifying this.

Yes, I understood the structure of the protected mode gdt entries but I was unsure about what the instruction needed at the memory address it gets.


Another thing I'm little confused about is that when switching processor mode from 32 bit protected mode to the long mode, then what are the effects of the previously loaded gdt in the protected mode when entering the 64 bit code segment and long mode? It seems to be required to set up some kind of gdt in the protected mode before entering the long mode in order to load and execute the 64bit code. Is it fine to just set the gdt to have all memory mapped as ring0 priviledges before entering the long mode? I would be setting up a new gdt anyways in the long mode.

Also is it possible to load a gdt with overlapping code and data areas (such that all memory would be mapped as ring0 code and data at the same time) or do I always have to separate code and data areas in the memory when it comes to gdt?

HH.
PeterX
Member
Member
Posts: 590
Joined: Fri Nov 22, 2019 5:46 am

Re: Understanding the structure and loading of gdt

Post by PeterX »

ElectricWEEB wrote: Is it fine to just set the gdt to have all memory mapped as ring0 priviledges before entering the long mode?
Yes.
ElectricWEEB wrote: Also is it possible to load a gdt with overlapping code and data areas (such that all memory would be mapped as ring0 code and data at the same time) or do I always have to separate code and data areas in the memory when it comes to gdt?
Yes it is possible and no you don't have to separate this.

Note that both (all in "ring" 0 and code and data unseparated) is not state-of-the-art because of security concerns. But afaik many projects here do it that way. (I haven't looked at any long mode assembler code from this community yet, but in pmode many developers do it that way.) But there is nothing wrong with doing so and change it later (perhaps...).

Regarding your other questions: Here's a document about going to long mode:
https://wiki.osdev.org/Setting_Up_Long_Mode
Especially section "Entering Long Mode".

Happy hacking
Peter
Post Reply