[FREELDR] Fix an inconsistency bug when switching the CPU state back to 16-bit real...
authorHermès Bélusca-Maïto <hermes.belusca-maito@reactos.org>
Wed, 2 Oct 2019 01:20:10 +0000 (03:20 +0200)
committerHermès Bélusca-Maïto <hermes.belusca-maito@reactos.org>
Mon, 7 Oct 2019 23:30:15 +0000 (01:30 +0200)
Basically it appeared that without this fix, the CPU was somehow residing
in an inconsistent state, that made it crash when a full 16-bit real-mode
to 32-bit protected mode transition occurred. (Encountered when trying
to load Linux, see problem description below.)
In that situation, Bochs reports that the CPU is in "compatibility mode".

The fix is based from information from Fig.1-6 "Operating Modes of the
AMD64 Architecture" (page 12) and Chapter 14 (pages 429-446) of the
"AMD64 Architecture Programmer’s Manual Volume 2: System Programming"
https://www.amd.com/system/files/TechDocs/24593.pdf

*** THE PROBLEM ***

When booting Linux using x86 FreeLdr everything goes well.

When trying to do the same using x64 FreeLdr, the Linux code (both the
boot sector, the setup sector and the main kernel contents) is all
correctly loaded and relocated in memory as in the x86 case. We then pass
control to the decompressing code that appears to succeed. However, once
it has finished and "Parsing ELF file..." step has been run, the next
step "Booting the kernel..." crashes with a CPU Triple-Fault.

This problem **DOES NOT HAPPEN** when booting from GRUB.

Log excerpt from Bochs:

<snip>

00089459736i[BIOS  ] Booting from 07c0:0000
00089782775i[SER   ] com1: FIFO enabled
00095994535i[BIOS  ] int13_harddisk: function 41, unmapped device for ELDL=81
00095998517i[BIOS  ] int13_harddisk: function 08, unmapped device for ELDL=81
00397139785i[BIOS  ] KBD: unsupported int 16h function 03
00397143625i[BIOS  ] *** int 15h function AX=e980, BX=0000 not yet supported!
00523008104e[CPU0  ] interrupt(long mode): vector must be within IDT table limits, IDT.limit = 0x0
00523008104e[CPU0  ] interrupt(long mode): vector must be within IDT table limits, IDT.limit = 0x0
00523008104i[CPU0  ] CPU is in compatibility mode (active)
00523008104i[CPU0  ] CS.mode = 32 bit
00523008104i[CPU0  ] SS.mode = 32 bit
00523008104i[CPU0  ] EFER   = 0x00000500
00523008104i[CPU0  ] | RAX=00000000e0000011  RBX=0000000000000000
00523008104i[CPU0  ] | RCX=0000000000000000  RDX=0000000000000000
00523008104i[CPU0  ] | RSP=00000000004f8000  RBP=000000000082e003
00523008104i[CPU0  ] | RSI=0000000000099800  RDI=00000000c0611000
00523008104i[CPU0  ] |  R8=0000000000109000   R9=0000000000009020
00523008104i[CPU0  ] | R10=00000000000007e3  R11=000000000000e958
00523008104i[CPU0  ] | R12=0000000000000000  R13=0000000000000000
00523008104i[CPU0  ] | R14=0000000000000000  R15=0000000000000000
00523008104i[CPU0  ] | IOPL=0 id vip vif ac vm RF nt of df if tf SF zf af PF cf
00523008104i[CPU0  ] | SEG sltr(index|ti|rpl)     base    limit G D
00523008104i[CPU0  ] |  CS:0010( 0002| 0|  0) 00000000 ffffffff 1 1
00523008104i[CPU0  ] |  DS:0018( 0003| 0|  0) 00000000 ffffffff 1 1
00523008104i[CPU0  ] |  SS:0018( 0003| 0|  0) 00000000 ffffffff 1 1
00523008104i[CPU0  ] |  ES:0018( 0003| 0|  0) 00000000 ffffffff 1 1
00523008104i[CPU0  ] |  FS:0018( 0003| 0|  0) 00000000 ffffffff 1 1
00523008104i[CPU0  ] |  GS:0018( 0003| 0|  0) 00000000 ffffffff 1 1
00523008104i[CPU0  ] |  MSR_FS_BASE:0000000000000000
00523008104i[CPU0  ] |  MSR_GS_BASE:0000000000000000
00523008104i[CPU0  ] | RIP=0000000000409327 (0000000000409327)
00523008104i[CPU0  ] | CR0=0xe0000011 CR2=0x0000000000409327
00523008104i[CPU0  ] | CR3=0x005b5000 CR4=0x000000a0
00523008104i[CPU0  ] 0x0000000000409327: (instruction unavailable) page not present
00523008104p[CPU0  ] >>PANIC<< exception(): 3rd (13) exception with no resolution

<snip>

boot/freeldr/freeldr/arch/realmode/amd64.S

index 0a97afd..ea1494f 100644 (file)
@@ -160,6 +160,12 @@ CheckFor64BitSupport:
     .ascii "PAE or PGE not set.", CR, LF, NUL
 
 .CheckForLongMode:
+    /* Check whether extended functions are supported */
+    mov eax, HEX(80000000)
+    cpuid
+    cmp eax, HEX(80000000)  // Any function > 0x80000000 ?
+    jbe .NoLongMode         // If not, no long mode.
+    /* Check whether the CPU supports Long Mode */
     xor edx, edx
     mov eax, HEX(80000001)
     cpuid
@@ -167,6 +173,7 @@ CheckFor64BitSupport:
     test edx, edx
     jnz .Success
 
+.NoLongMode:
     mov si, offset .Msg_NoLongMode
     call writestr
     popad
@@ -240,6 +247,18 @@ BuildPageTables:
 
 /* This is the entry point from long mode */
 RealModeEntryPoint:
+
+    /* Disable long mode */
+    mov ecx, MSR_EFER
+    rdmsr
+    and eax, HEX(0FFFFFEFF) // ~0100
+    wrmsr
+
+    /* Mask PAE and PGE out */
+    mov eax, cr4
+    and eax, HEX(0FFFFFF5F) // ~00A0
+    mov cr4, eax
+
     /* Disable Protected Mode */
     mov eax, cr0
     and eax, HEX(0FFFFFFFE) // ~0x00000001
@@ -295,6 +314,8 @@ ExitToLongMode:
     mov word ptr ds:[stack16], sp
 
     /* Set PAE and PGE: 10100000b */
+    // mov eax, cr4
+    // or eax, HEX(00A0)
     mov eax, HEX(00A0)
     mov cr4, eax