Page 1 of 1
Loading 64-bit GDT When Switch to Long Mode
Posted: Tue Feb 05, 2013 3:54 pm
by mesterjakel
First of all thanks (1<<20)-1 for the awesome tutorials and information present on osdev.org! Second of all I apologize in advance for probably making all the classic newbie mistakes with this post (I promise, I did try to search for an answer before posting!)
AMD64 Architecture Programmer’s Manual Volume 2 Rev. 3.22 states in section 14.6.1 "Activating Long Mode" that:
Enable paging by setting CR0.PG to 1. This causes the processor to set the EFER.LMA bit to 1.
The instruction following the MOV CR0 that enables paging must be a branch[...]
(Emphasis mine)
However
Entering Long Mode Directly and a few other samples I've looked at (a noteable exception being the sample directly following in the AMD manual) load the 64-bit GDT after enabling paging, but before branching.
Normally I would just ignore this (as I'm only playing around with os/x64 development for my own amusement) but since I can't get the sample (*) to work if follow the "proper way" (by moving the
instruction to some place after jumping to 64-bit mode) I got curious about what I was doing wrong.
My question is: am I misreading the documentation or missing something? I'm testing with qemu and virtualbox (i realize they share the same emulation core).
(*) What prompted me to ask this question was my own code only working if I loaded the 64-bit GDT before switching to long mode, not so much randomly moving around instructions in the sample.
Re: Loading 64-bit GDT When Switch to Long Mode
Posted: Tue Feb 05, 2013 4:04 pm
by bluemoon
It's totally legal to LGDT after jump to 64-bit segment, however you should aware the difference in parameter between 32 and 64-bit modes, and double check the segment attributes of the current operating mode (that execute LGDT).
mesterjakel wrote:What prompted me to ask this question was my own code only working if I loaded the 64-bit GDT before switching to long mode, not so much randomly moving around instructions in the sample.
How would you think you can enter (64-bit) long mode without loading 64-bit GDT?
The proper way is enter 32-bit protected mode (you also loaded 32-bit GDT in this step), then enable LME to enter compatibility mode, then load 64-bit GDT, and jump to 64-bit segment, check the manual for details and example. The "enter long mode directly" hack currently works, but it is not the official method thus you can't blame anyone if it does not work in the future.
Re: Loading 64-bit GDT When Switch to Long Mode
Posted: Tue Feb 05, 2013 4:13 pm
by rdos
There is no need to load a 64-bit GDT unless you plan to place GDT above 4G. The protected mode GDT will work just fine in long mode as well.
Re: Loading 64-bit GDT When Switch to Long Mode
Posted: Tue Feb 05, 2013 4:21 pm
by bluemoon
By 64-bit GDT I was referring the 16 bytes GDT designed for 64-bit mode.
Re: Loading 64-bit GDT When Switch to Long Mode
Posted: Tue Feb 05, 2013 4:32 pm
by mesterjakel
bluemoon wrote:It's totally legal to LGDT after jump to 64-bit segment, however you should aware the difference in parameter between 32 and 64-bit modes, and double check the segment attributes of the current operating mode (that execute LGDT).
mesterjakel wrote:What prompted me to ask this question was my own code only working if I loaded the 64-bit GDT before switching to long mode, not so much randomly moving around instructions in the sample.
How would you think you can enter (64-bit) long mode without loading 64-bit GDT?
The proper way is enter 32-bit protected mode (you also loaded 32-bit GDT in this step), then enable LME to enter compatibility mode, then load 64-bit GDT, and jump to 64-bit segment, check the manual for details and example. The "enter long mode directly" hack currently works, but it is not the official method thus you can't blame anyone if it does not work in the future.
D'oh, you're of course right that it could never work in the "Entering LM directly" example as there is no proper 32-bit GDT loaded.
In my own code I'm already in 32-bit protected mode (with standard flat 4G descriptors) and I guess what was tripping up my understanding was that loading a 64-bit GDT is OK since I already entered compability mode after setting EFER.LME=1 (long mode enable).
I'm not particularly interested in the direct 16->64bit transition, it just seemed (a mistake I now realize) like the a good way to test/explain my theory. And I guess the part about the instruction following the mov to cr0 having to be a branch is just not strictly enforced (either way that turned out to be a red herring w.r.g. to my question).
Thanks!
Re: Loading 64-bit GDT When Switch to Long Mode
Posted: Tue Feb 05, 2013 4:38 pm
by rdos
bluemoon wrote:By 64-bit GDT I was referring the 16 bytes GDT designed for 64-bit mode.
The only GDT descriptor that is 16 bytes in long mode is the call gate descriptor, and it is not very useful (because the target selector must be 64-bit). You can define these descriptors in a 32-bit GDT as well. The only difference between the 32 and 64 bit ldgt instructions is how many bits are loaded. Once loaded, the GDT works the same way regardless how the base was loaded.
It is the IDT that contains 16 byte descriptors, not the GDT.
Re: Loading 64-bit GDT When Switch to Long Mode
Posted: Tue Feb 05, 2013 6:52 pm
by Owen
rdos wrote:bluemoon wrote:By 64-bit GDT I was referring the 16 bytes GDT designed for 64-bit mode.
The only GDT descriptor that is 16 bytes in long mode is the call gate descriptor
TSS
LDT
Re: Loading 64-bit GDT When Switch to Long Mode
Posted: Wed Feb 06, 2013 3:57 am
by rdos
Owen wrote:rdos wrote:bluemoon wrote:By 64-bit GDT I was referring the 16 bytes GDT designed for 64-bit mode.
The only GDT descriptor that is 16 bytes in long mode is the call gate descriptor
TSS
LDT
Neither the TSS, nor the LDT need to be 16 bytes long. They can be loaded in protected mode with 8 bytes entries, and they will work just fine. It is similar to GDT. If the OS doesn't need to reload these while in long mode, they can be kept as single descriptor entries. I know it works because I reload these in protected mode in the mode-switching code. The LDT especially doesn't need to be reloaded in long mode under normal circumstances since long mode doesn't support segmentation. I avoid having a 16 byte TSS by having a per CPU core TSS for long mode tasks which is loaded before switching to long mode in the mode switch code.
Re: Loading 64-bit GDT When Switch to Long Mode
Posted: Thu Feb 07, 2013 2:43 am
by bluemoon
The manual said you need it, so although it turns out that it works with 32-bit GDT I would consider it a trick, or pure luck, or an undefined behavior, but not unnecessary.
Re: Loading 64-bit GDT When Switch to Long Mode
Posted: Thu Feb 07, 2013 4:34 am
by Owen
rdos wrote:Owen wrote:rdos wrote:The only GDT descriptor that is 16 bytes in long mode is the call gate descriptor
TSS
LDT
Neither the TSS, nor the LDT need to be 16 bytes long. They can be loaded in protected mode with 8 bytes entries, and they will work just fine. It is similar to GDT. If the OS doesn't need to reload these while in long mode, they can be kept as single descriptor entries. I know it works because I reload these in protected mode in the mode-switching code. The LDT especially doesn't need to be reloaded in long mode under normal circumstances since long mode doesn't support segmentation. I avoid having a 16 byte TSS by having a per CPU core TSS for long mode tasks which is loaded before switching to long mode in the mode switch code.
In spite of the above, when loaded in long mode they are 64-bit descriptors.
The LDT is needed by some apps, even in long mode (the most common examples are apps like
dosemu, to provide DPMI services, and
WINE, to support Win32 exception handling for the apps running inside of it)
Most OSes will place their TSSes somewhere in the higher half of the long mode address space, thereby requiring it be loaded in long mode.
Re: Loading 64-bit GDT When Switch to Long Mode
Posted: Thu Feb 07, 2013 5:00 am
by rdos
bluemoon wrote:The manual said you need it, so although it turns out that it works with 32-bit GDT I would consider it a trick, or pure luck, or an undefined behavior, but not unnecessary.
I don't consider it a trick. It is pretty logical that LGDT will setup the base of the GDT, and then either use a 24-bit, 32-bit or 64-bit operand. The GDT semantics operates using the current processor mode (real mode, V86 mode, protected mode, long mode). Interpretation of gates both in IDT and GDT is dependent on processor mode, not how the base for the GDT/IDT were loaded. If this was not so, the processor would need some invisible state to determine the state. I find it pretty unlikely that this will become broken in any future processor.
Re: Loading 64-bit GDT When Switch to Long Mode
Posted: Thu Feb 07, 2013 5:39 am
by bluemoon
I would not encourage to ignore the manual or specification just because it should work as logical consequent... especially there is no justifiable benefits in usual case. However I agree there is justifiable benefits is your case.
Re: Loading 64-bit GDT When Switch to Long Mode
Posted: Thu Feb 07, 2013 5:58 am
by rdos
The TSS load operates in the same way, which was essential in my implementation. It doesn't matter if a TSS is loaded in protected mode or long mode, it will still use the semantics of the current operation mode. This also was not obvious in the manual, but that's the way it works in both Intel and AMD. Thus, in my implementation, I load the long mode per-core TSS as a 8-byte entry in protected mode, and once the processor is switched to long mode, this TSS uses the fields of long mode, not of protected mode.