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
16-bit protected mode?
16-bit protected mode?
This was supposed to be a cool signature...
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.
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)
and some where in a C file the GDT
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.
normally in the bootsector people use unreal mode which is in a sense switching to 16bit protected mode and back.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
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.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?
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.
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
You can largely ignore the GDT in long mode. The IDT needs to be refreshed though.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.
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.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.
Re: 16-bit protected mode?
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.
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
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.
It is confusing - it should say "protected mode with 16-bit segments".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.
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.
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
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...
- Colonel Kernel
- Member
- Posts: 1437
- Joined: Tue Oct 17, 2006 6:06 pm
- Location: Vancouver, BC, Canada
- Contact:
Re: 16-bit protected mode?
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.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).
Just a bit of history for the young whippersnappers out there...
Top three reasons why my OS project died:
- Too much overtime at work
- Got married
- My brain got stuck in an infinite loop while trying to design the memory manager