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
};