Once I initialize and load my GDT into the GDTR using lgdt
, how can I update the GDT later?
Am I correct if I use the sgdt
command to get the base address and then update or add entries followed by reloading it again with the lgdt
? Is there some other way to do it?
Or am I missing something and the GDT is never "meant" to be updated once initialized and loaded?
Asked
Active
Viewed 339 times
2

Peter Cordes
- 328,167
- 45
- 605
- 847

darkknight97
- 43
- 2
-
You can update the GDT anytime you want if you're RING0 or lower. – zx485 Jul 25 '20 at 19:22
-
1AFAIK you only need to LGDT if you want to change the location or size of the GDT. If you just want to modify an existing entry, you change it directly in memory and it takes effect when you reload the selector. – Nate Eldredge Jul 25 '20 at 19:28
-
@NateEldredge But how to obtain the location for changing it in memory? Using `sgdt`? Also are modifications usually done as I haven't seen anything of the sort anywhere yet? – darkknight97 Jul 25 '20 at 19:46
-
2You can SGDT if necessary, but in most cases, it was your software that set up the GDT in the first place so you ought to remember where you put it. I think most modern OSes have no need to ever modify the GDT because they try to use the x86 segmentation feature as little as possible; they set up flat segments with zero base and maximum limit and use them for everything. – Nate Eldredge Jul 25 '20 at 19:48
-
@zx485 My question was on how we can update it. Also I believe there is nothing lower than ring 0. – darkknight97 Jul 25 '20 at 19:49
-
@NateEldredge thanks. That answers my question. – darkknight97 Jul 25 '20 at 19:49
-
@darkknight97: VT-x hardware virtualization introduces a new level of privilege higher than guest ring 0. It's sometimes called "ring -1". Although of course the official CPL is still just the low 2 bits of the CS selector, still in the 0..3 range. – Peter Cordes Jul 26 '20 at 00:45
-
You don’t even have to be in ring 0 to write to the GDT; you only need to have write privilege to the memory. (Of course any sensible OS won’t give unprivileged code access to the memory containing the GDT.) – prl Jul 26 '20 at 07:31
1 Answers
5
I understand that this question is old however I want to highlight some things that the comments didn't mention, that is what the CPU caches and what does not.
- Some terminology:
- The GDT's entries are called descriptors, for this answer we are interested only for segment descriptors
- Segment registers (
cs
,ds
,es
,fs
,gs
,ss
,ldtr
,tr
) get assigned with segment selectors which specify a segment descriptors. A segment selector is always 16 bits and consists of:- 13 bits: descriptor entry index
- 1 bit: 0 for GDT 1 for LDT
- 2 bits: Requested Privilege Level (RPL)
- The CPU does not cache the GDT, loading a value to
gdtr
does nothing more than loading a value togdtr
(which consist of the GDT's base address and its size) - The CPU caches the GDT descriptors that each segment register uses, when that segment is loaded with a new segment selector:
- Each segment register has 2 parts:
- The "Visible Part", containing the segment selector
- The "Hidden Part" contained a cached copy of segment descriptor
- When you execute
mov es, ax
, withax
having the value 0x0010 for example:- The "Visible Part" gets the value 0x0010
- The "Hidden Part" receives a copy of the information stored in the 3rd entry of the GDT (because 0x0010 means 3rd entry)
- When using the segment register, only the "Hidden Part" dictates its characteristics (base, limit, access information).
- The GDT segment descriptor that was used to load the segment register may be modified or not be present at all
- The
gdtr
may point to a different location, even an invalid one, and may have any size, again, even an invalid one - the CPU does not care.
- The "Visible Part" does absolute nothing expect holding the segment selector used to load the segment register, that value can be then retrieved by a
mov ax, es
. (ax
becomes 0x0010, even thought that GDT segment descriptor may contain anything or may not even exist at the present time) - So, in order to load a new segment descriptor to a segment register, you need to load another (or even the same) selector, that will update-reload the "Hidden Part" - cache
- Each segment register has 2 parts:
- That being said, there are a lot of reasons to keep the used entries in the GDT fixed, a simple one is that when moving from kernel-mode to user-mode (and vice versa) you change segments; those descriptors must thus be present in GDT. (The CPU also automatically loads new segments in other circumstances such as far calls, interrupts, task gates, call gates, ...). In sort, only remove a descriptor if your OS does not use it anymore. It is also harder to mess up something
- Finally, to answer your question, if you want to add a segment descriptor (or any other descriptor), you add it in your current GDT, you just place the appropriate values into a free descriptor entry and then you load the new selector to a segment (You may change the size of the GDT if needed at this point). If you want to update a segment descriptor, you update it in the GDT and then you reload all the segments that use it, if you don't do that, the segments will just use the old cached values.
Source: Intel® 64 and IA-32 Architectures Software Developer’s Manual Volume 3: System Programming Guide CHAPTER 3: PROTECTED-MODE MEMORY MANAGEMENT
I suggest you to also take a look at wiki.osdev.org, specifically:
- https://wiki.osdev.org/GDT
- https://wiki.osdev.org/GDT_Tutorial
- https://wiki.osdev.org/Descriptors
- https://wiki.osdev.org/Descriptor_Cache
Please ask for any clarifications

Coconut9
- 123
- 2
- 7