Page 1 of 1

16-bit protected mode?

Posted: Wed Oct 03, 2007 11:38 pm
by Zacariaz
a few lines of comments from the amd manuals sample code on enabling long mode:

; Establish a temporary 32-bit GDT and IDT.
; 16-bit code, real mode
; Initialize the GDTR and IDTR to point to the temporary
; 32-bit GDT and IDT, respectively.
; Execute a far jump to turn protected mode on.

so far so good.

; At this point we are in 16-bit protected mode, but long
; mode is still disabled.

What? 16-bit protected mode? Does it even exist? And if it does, why would 16-bit protected mode need a 32-bit gdt and idt?
Im confused here.

edit:
It seems there is no 32 bit code section at all, only 16 and 64 bit. However the the varius 32 bit registers is used. Also where we are still in 16 bit real mode.
What am i missing here?


Anyway, this is posted in generel ramblings as it is one of those questions that have probably been asked far too often. Anyhow, if you are up to the challence i would like an explanation.

edit:
Also pondered the:
; Establish a temporary 32-bit GDT and IDT.
what does temporary really mean? i mean it is not just gonna disapear from memory when no longer needed is it?

edit:
A link to the code might be nice ;)
14.8 / page 403

Posted: Thu Oct 04, 2007 12:50 am
by pcmattman
16-bit PMode = protected mode without the A20 iiuc. At least, that's what's logical to me at the moment.

Don't know anything about the rest though.

Posted: Thu Oct 04, 2007 1:53 am
by os64dev
16bit protected mode is just the same as 32 bit pmode meaning that you have the whole 4GiB range, paging and suchs but that you selected 16 bit code and/or data pages. It is part of you descriptor.

from the amd manual.
Code-Segment Default-Operand Size (D) Bit. Bit 22 of byte +4. In code-segment descriptors, the D bit selects the default operand size and address sizes. In legacy mode, when D=0 the default operand size and address size is 16 bits and when D=1 the default operand size and address size is 32 bits.
Data-Segment Default Operand Size (D/B) Bit. Bit 22 of byte +4. For expand-down data segments (E=1), setting D=1 sets the upper bound of the segment at 0_FFFF_FFFFh. Clearing D=0 sets the upper bound of the segment at 0_FFFFh
normally in the bootsector people use unreal mode which is in a sense switching to 16bit protected mode and back.
Also pondered the:
; Establish a temporary 32-bit GDT and IDT.
what does temporary really mean? i mean it is not just gonna disapear from memory when no longer needed is it?
Temporary is used here because once you've switched to long mode you need to do a new lgdt and lidt because of the 64bit address range and the change of descriptor entries. Once you've done that the earlier mentioned (32bit) gdt and idt are obsolete and the memory can be reused.

Brendan once provided a method for switchin to long mode directly. I have researched it a bit and according to the documents it should work. I also use that method and it is way easier.

the following code shows how it is done. (i removed some of the code but it should point you in the right direction)

Code: Select all

.global     main
.extern     bootGDT;
.extern     crt_entry;

.equ        real16   , 0x0000
.equ        data16   , 0x0008
.equ        code64   , 0x0010

.code16
.section    .boot, "x"
main:
    ljmp            $real16, $boot_init;    //- set correct cs::ip [0000:7C05].

  boot_init:
    cli;                                    //- interrupts are disabled.
    movw            %cs, %ax;
    movw            %ax, %ds;               //- ds is made identical to cs.
    movw            %ax, %ss;               //- ss is made identical to cs.
    movw            $0x1000, %sp;           //- sp points to [0000:1000].
    movw            %sp, %bp;               //- setup initial stack frame.
    subw            $0x0010, %sp;           //- reserve stack for local data.
    sti;                                    //- interrupts are enabled.

  memory_init:
    cld;                                    //- increment data pointer.
    xorw            %ax, %ax;               //- fill word is zero.
    movw            $0x0502, %di;           //- base of COBOS memory area.
    movw            $memSize, %cx;          //- size of bootloader memory in 16-bit words.
    rep             stosw;                  //- clear the memory area [0x0800-0x7C00].
    movw            $0x1000, %di;           //- PML4E base.
    movw            $0x2017, (%di);         //- 1st entry PML4E(unchached-supervisor-rw).
    movw            $0x3017, 0x1000(%di);   //- 1st entry PDPE(unchached-supervisor-rw).
    movw            $0x0093, 0x2000(%di);   //- 1st entry PDP(unchached-supervisor-rw) .
  
    movw            $0x1112, %ax;
    xorb            %bl, %bl;
    int             $0x10;                  //- text 80x50
    
  test_cpuid_support:
    pushfl;                                 //- save eflags.
    popl            %eax;                   //- store eflags in eax.
    movl            %eax, %ebx;             //- save in ebx for later testing.
    xorl            $0x00200000, %eax;      //- toggle bit 21.
    pushl           %eax;                   //- push to stack.
    popfl;                                  //- save changed eax to eflags.
    pushfl;                                 //- push eflags.
    popl            %eax;                   //- store elfags in eax.
    cmpl            %eax, %ebx;             //- see if bit 21 has changed.
    je              lockup_system;          //- equal flag means 'no cpuid' support.

  test_longmode_support:
    movl            $0x80000000, %esi;
    movl            %esi, %eax;             //- extended-function code 8000000h.
    cpuid;                                  //- get the largest extended-function.
    cmpl            %esi, %eax;             //- any function above 8000000h?
    jbe             lockup_system;          //- if not, no long mode available.
    inc             %si;
    movl            %esi, %eax;             //- extended-function code 8000001h.
    cpuid;                                  //- edx contains the extended features.
    bt              $0x1D, %edx;            //- test if long mode is supported.
    jnc             lockup_system;          //- if not, no long mode available.

  boot_unrealmode:
    pushw           %bp;
    movw            %sp, %bp;
    movw            $0x005F, -6(%bp);       //- pseudo-descriptor limit.
    movl            $bootGlobalDescriptorTable, -4(%bp);      //- pseudo-descriptor address.
    lgdt            -6(%bp);                //- load the bootloader GDT.
    popw            %bp;
    push            %ds;
    push            %es;
    movl            %cr0, %eax;             //- get the content of CR0.
    orb             $0x01, %al;             //- CR0.PE=1 (protected-mode).
    movl            %eax, %cr0;             //- update the CR0 register.
    movw            $data16, %bx;           //- 4 GiB limit 16 bits data segment.
    mov             %bx, %ds;
    mov             %bx, %es;
    decb            %al;
    movl            %eax, %cr0;             //- update the CR0 register.
    popw            %es;
    popw            %ds;

    call            a20_enable;

  boot_longmode_64bit:
    cli;                                    //- interrupts are disabled.
    movl            $0x1010, %eax;          //- PML4E = 0x1000(unchached). 
    movl            %eax, %cr3;             //- set PML4E.
    movl            %cr4, %eax;             //- get the content of CR4.
    orb             $0x20, %al;             //- enable CR4.PAE.
    movl            %eax, %cr4;             //- update the CR4 register.
    movl            $0xC0000080, %ecx;      //- get the MSR.EFER.
    rdmsr;                                  //- read MSR.
    orw             $0x0901, %ax;           //- enable longmode EFER(NXE LME SCE).
    wrmsr;                                  //- write MSR.
    movl            %cr0, %eax;             //- get the content of CR0.
    orl             $0x80000001, %eax;      //- CR0.PE=1 and CR0.PG=1 (paged protected-mode).
    movl            %eax, %cr0;             //- update the CR0 register.
    data32 ljmp     $code64, $long_mode;    //- set correct cs::ip with cs = code64.

  lockup_system:
    jmp             .;

.code64
long_mode:
    movl            (bootArguments + bmemBase), %eax;
    addl            $0x00000FFF, %eax;
    andl            $0xFFFFF000, %eax;
    movl            %eax, (bootArguments + bmemBase);
    callq           crt_entry;              //- call the loader routine, should never return.
    jmp             .;                      //- continuous loop with high power consumption.

.org 0x01FE, 0xCC
    .word       0xAA55;                     //- boot signature.
and some where in a C file the GDT

Code: Select all

const int64uc       bootGlobalDescriptorTable[] = {
    0x0000000000000000ul , //- null selector
    0x00CF92000000FFFFul , //- data16 selector ring 0
    0x00AF98000000FFFFul , //- code64 selector ring 0
    0x00AF92000000FFFFul , //- data64 selector ring 0
    0x00AFF2000000FFFFul , //- data64 selector ring 3
    0x00AFF8000000FFFFul  //- code64 selector ring 3
};

Posted: Thu Oct 04, 2007 4:28 am
by Candy
os64dev wrote: Temporary is used here because once you've switched to long mode you need to do a new lgdt and lidt because of the 64bit address range and the change of descriptor entries. Once you've done that the earlier mentioned (32bit) gdt and idt are obsolete and the memory can be reused.
You can largely ignore the GDT in long mode. The IDT needs to be refreshed though.
Brendan once provided a method for switchin to long mode directly. I have researched it a bit and according to the documents it should work. I also use that method and it is way easier.
I've implemented that method in my boot loader with a hello-world-from-long-mode example which both work. Tested in bochs and on my real Intel 805 Pentium-D.

Re: 16-bit protected mode?

Posted: Thu Oct 04, 2007 7:22 am
by Brendan
Hi,

In protected mode segments are either 16-bit or 32-bit, so you can have protected mode where all segment are 16-bit. In long mode segments are either 16-bit, 32-bit or 64-bit, so you can have long mode where all segment are 16-bit or 32-bit.
Zacariaz wrote:; At this point we are in 16-bit protected mode, but long
; mode is still disabled.

What? 16-bit protected mode? Does it even exist? And if it does, why would 16-bit protected mode need a 32-bit gdt and idt?
Im confused here.
It is confusing - it should say "protected mode with 16-bit segments".

People may have forgotten, but the 80286 actually did have a "16-bit protected mode" which is incompatible with the normal (80386 or later) protected mode. It didn't support virtual8086 or 32-bit segments and had a different descriptor format, and there wasn't any way to disable it once you enabled it (unless you reset the CPU); but there was 4 protection levels, it did have GDT, LDT, IDT and hardware task switching (with a different TSS format).


Cheers,

Brendan

Posted: Thu Oct 04, 2007 10:46 am
by Zacariaz
thank you very much for the answers.

About Brendans "directly to long mode" code, where can i find the full example? I have heard about it, but never been able to find it.
Of course the code posted will most likely be suficient, but i would still like to see the "original"

thanks again

Re: 16-bit protected mode?

Posted: Thu Oct 04, 2007 2:41 pm
by Colonel Kernel
Brendan wrote:People may have forgotten, but the 80286 actually did have a "16-bit protected mode" which is incompatible with the normal (80386 or later) protected mode. It didn't support virtual8086 or 32-bit segments and had a different descriptor format, and there wasn't any way to disable it once you enabled it (unless you reset the CPU); but there was 4 protection levels, it did have GDT, LDT, IDT and hardware task switching (with a different TSS format).
This is actually the operating mode that later 16-bit versions of Windows ran in. IIRC, it's not incompatible with 32-bit protected mode, at least not from a 32-bit OS' point of view. For example, Windows 9x had lots of 16-bit code that could be called from 32-bit code via thunks. It was not 16-bit real mode code -- it didn't run in V8086 mode. Only DOS programs did that in Windows 9x.

Just a bit of history for the young whippersnappers out there... :P