16-bit protected mode?

All off topic discussions go here. Everything from the funny thing your cat did to your favorite tv shows. Non-programming computer questions are ok too.
Post Reply
User avatar
Zacariaz
Member
Member
Posts: 1069
Joined: Tue May 22, 2007 2:36 pm
Contact:

16-bit protected mode?

Post 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
This was supposed to be a cool signature...
pcmattman
Member
Member
Posts: 2566
Joined: Sun Jan 14, 2007 9:15 pm
Libera.chat IRC: miselin
Location: Sydney, Australia (I come from a land down under!)
Contact:

Post 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.
User avatar
os64dev
Member
Member
Posts: 553
Joined: Sat Jan 27, 2007 3:21 pm
Location: Best, Netherlands

Post 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
};
Author of COBOS
User avatar
Candy
Member
Member
Posts: 3882
Joined: Tue Oct 17, 2006 11:33 pm
Location: Eindhoven

Post 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.
User avatar
Brendan
Member
Member
Posts: 8561
Joined: Sat Jan 15, 2005 12:00 am
Location: At his keyboard!
Contact:

Re: 16-bit protected mode?

Post 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
For all things; perfection is, and will always remain, impossible to achieve in practice. However; by striving for perfection we create things that are as perfect as practically possible. Let the pursuit of perfection be our guide.
User avatar
Zacariaz
Member
Member
Posts: 1069
Joined: Tue May 22, 2007 2:36 pm
Contact:

Post 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
This was supposed to be a cool signature...
User avatar
Colonel Kernel
Member
Member
Posts: 1437
Joined: Tue Oct 17, 2006 6:06 pm
Location: Vancouver, BC, Canada
Contact:

Re: 16-bit protected mode?

Post 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
Top three reasons why my OS project died:
  1. Too much overtime at work
  2. Got married
  3. My brain got stuck in an infinite loop while trying to design the memory manager
Don't let this happen to you!
Post Reply