Page 1 of 1

BabyStep6 -- Round 2 -- Flawless Victory

Posted: Tue Oct 12, 2004 3:37 am
by Pype.Clicker
Okay, this is a discussion about a statement in BabyStep6 in the FAQ.
CrazyBuddah also said it was possible to set up the GDT after switching to pmode, but i fail to see how it could be done since LGDT implies a memory reference.
-- PypeClicker

Nevertheless it must be possible, since the Multiboot specs (the bootloader - kernel handshake protocol complied to by GRUB) specifies that GDTR may be invalid and that the OS has to set up its own GDT...
-- MartinBaute

Of course, GDT can be changed after pmode is enabled, but CrazyBuddah's statement was it could be done after pmode is enabled for the first time ...
-- PypeClicker

No, no... read again: The GDTR may be invalid. That means you have to set up your very own GDT, and then load its address to GDTR. You can't edit a GDT if the GDTR is specified to be potentially invalid. Unless I'm mistaken, of course.
-- MartinBaute

Re:BabyStep6 -- Round 2 -- Fight!!

Posted: Tue Oct 12, 2004 3:47 am
by Pype.Clicker
The initial text from CrazyBuddah said
However, before or after switching to pmode, you have to use the LGDT instruction to load another special register (gdtr) with the location of a table of data structures called descriptors that tell the process how to access memory.
I do agree that you can issue LGDT *after* you switched to pmode, discarding the existing GDT and using a new one. I also agree that once you loaded all the segment descriptors with proper values and you know that no register reload will be attempted (e.g. because no interrupt is allowed and no IDT is defined), you could scratch the GDT content or reload GDTR with garbage.

Now, a complete pmode switch requires loading of segment registers and cs register, right ? to perform this, you need a valid GDT to be set up, right ? BUT lgdt instruction requires a valid data segment register to interprete the address ...

So imho, you couldn't manage to enter pmode without a LGDT instruction *before* mov cr0,eax ...

Re:BabyStep6 -- Round 2 -- Fight!!

Posted: Tue Oct 12, 2004 4:08 am
by distantvoices
Hmm ...

I *know* that you need a valid GDT loaded into GDTR via lgdt *ere* you activate pmode and do the occasional jump to flush the registers/buffers: jmp [cs]:[youraddress].

What I don't know: is it possible to load gdt into GDTR straight after the magic sequence: mov eax,cr0;or eax,1;mov cr0,eax; -- or similar, don't know by heart. say like this:

Code: Select all

  cli
  mov eax,cr0
  or eax,1
  mov cr0,eax
  lgdt [my_GDTR]
  lidt  [my_IDTR]
  jmp my_cs:my_code
You *can* live without IDT as long as you leave interrupts off and not dare to produce exceptions.

What you do after enabling of pmode with the GDTR and IDTR - is your own choice. You can also replace what is provided by some pre-kernel stage with your own tables.

stay safe

Re:BabyStep6 -- Round 2 -- Fight!!

Posted: Tue Oct 12, 2004 7:32 am
by aladdin
I think it is possible to do a lgdt/lidt instruction just after switching to pmode since these instructions do not affect the base, limit or attributes of any segment register.
all what the lgdt instruction do is to put its operand in the gdtr.

Re:BabyStep6 -- Round 2 -- Fight!!

Posted: Tue Oct 12, 2004 9:36 am
by df
when i switch i just have a code/data gdt and no idt, switch into pmode. then recreate a new gdt + idt and discard the old one.

i havnt verified but had thought, that
if the GDT is invalid after the switch, any instruction would cause a fault, which would lead to a triple fault reset?

i dont think its possible to have an invalid GDT once in pmode without triggering a fault... but now i think about it, the current CS/DS gdt might be cached in internal registers? so maybe you could invalidate them and not fault. (would make sense why you need to jump after a switch to flush it...)....

since any instruction would be tested to see if your inisde the limit, read/write bits etc etc etc...

Re:BabyStep6 -- Round 2 -- Fight!!

Posted: Tue Oct 12, 2004 10:03 am
by Brendan
Hi,

All segment registers have a visible part and a hidden part. The visible part contains the GDT/LDT entry number (or the paragraph in real mode), while the hidden part contains the base address and limit.

In real mode changing the visible part only changes the base address (and not the limit). In protected mode changing the visible part changes everything.

When protected mode is enabled (or disabled) none of the visible and hidden parts of any segment register are modified in any way.

Therefore something like this would work perfectly:

Code: Select all

  cli
  mov eax,cr0
  or eax,1
  mov cr0,eax
  lgdt [my_GDTR]
  lidt  [my_IDTR]
  jmp my_cs:my_code

BITS 32

my_code:
  mov ax,my_data_seg
  mov ds,ax
  mov es,ax
  mov fs,ax
  mov gs,ax
  mov ss,ax

  mov edi,my_GDT_address
  mov ecx,my_GDT_size
  cld
  rep stosb

  ..more code running with trashed GDT..

When the "lgdt [my_GDTR]" is executed the old (real mode) values for the hidden part of DS is still in effect, so the GDTR is loaded without problems. Same for the IDTR. The GDT will be valid before the CPU uses it to load CS for the jump.

After the GDT is trashed everything will still work fine until a segment is changed. Assuming the kernel uses near calls until it sets up another GDT (and it doesn't enable interrupts until there's a valid IDT and GDT), then everything works.


Cheers,

Brendan

Re:BabyStep6 -- Round 2 -- Fight!!

Posted: Wed Oct 13, 2004 12:06 am
by Dreamsmith
Brendan has it right. It should also be noted that the processor doesn't give a rats arse what the visible portion of a segment register contains -- in the process of actually executing instructions that load or save to memory, it ONLY looks at the hidden portions. Furthermore, only instructions that change the visible portion of a segment register cause a lookup in the GDT or LDT (in order to get the values to load into the hidden portion).

Therefore, any instruction that does not change the value of a segment register will be completely unaffected by the invalidity of the GDTR or the visible portions of the segment registers. Since LGDT doesn't change the contents of a segment register, is requires neither a valid GDT nor a valid visible DS value to work, since it won't make use of either of these things.

Re:BabyStep6 -- Round 2 -- Fight!!

Posted: Wed Oct 13, 2004 3:35 am
by Pype.Clicker
Hmm ... That makes sense.

So the GDT is only required as soon as one whish to get out of the 64KB segment used at the moment of pmode entrance or when enabling interrupts (since IDT will contain code selectors to be used)

That's virtually the opposite of all the tutorials i read when i was toying with pmode, but it makes sense...

Re:BabyStep6 -- Round 2 -- Fight!!

Posted: Mon Jul 25, 2005 6:37 pm
by David Hayman
You can set the PMODE bit in CR0 before loading the GDT. It doesn't matter what order they're done in, as long as they're both done before you do your long jump.

Remember that you can set the new DS, SS etc. values after you have done the jump? The far jump only sets CS, so all the other segments remain 16-bit until you explicitely change them.

This means that until you set DS etc., saying

lgdt [GDTR]

uses DS implicitely, and DS is still a 16-bit segment.

Here is some demo code:


[BITS 16]

; This sets PMODE bit in CR0
mov eax, cr0
or al, 1
mov cr0, eax

; This loads GDT. Remember, DS is still 16-bit data segment here
lgdt [GDTR]

; This long jumps to 32-bit segment
jmp dword [8:SomeLabel]

[BITS 32]

; Here, we are 32-bit protected mode
; CS = 32-bit code segment
; DS, ES, FS, GS, SS = 16-bit data segment
; We must set new segment values here
SomeLabel:
mov ax, 16
mov ds, ax
mov es, ax
mov fs, ax
mov gs, ax
mov ss, ax
mov esp, 0x0000F000


So you can see that when we do LGDT, we are still referring to 16-bit data segment, so it works fine.