Fix protected-mode tests to NOT load a 32-bit code segment before returning to real-mode (bad idea)
This commit is contained in:
parent
cfa4e0207f
commit
45f2d6e2d1
1 changed files with 38 additions and 5 deletions
|
|
@ -10,6 +10,24 @@
|
||||||
; which is fine, because those data structures are predefined with appropriate ROM-based addresses.
|
; which is fine, because those data structures are predefined with appropriate ROM-based addresses.
|
||||||
;
|
;
|
||||||
; See the machine definition file in /modules/pcjs/bin/romtests.json for a sample ROM configuration.
|
; See the machine definition file in /modules/pcjs/bin/romtests.json for a sample ROM configuration.
|
||||||
|
;
|
||||||
|
; PMODE_32BIT Notes
|
||||||
|
; -----------------
|
||||||
|
; PMODE_32BIT is NOT enabled by default, because based on what I've seen in VirtualBox (and notes
|
||||||
|
; at http://geezer.osdevbrasil.net/johnfine/segments.htm), if CS is loaded with a 32-bit code segment
|
||||||
|
; while in protected-mode and we then return to real-mode, even if we immediately perform a FAR JMP
|
||||||
|
; with a real-mode CS, the base and limit of CS will be updated, but other CS attributes, like EXT_BIG,
|
||||||
|
; will NOT be updated. As a result, the processor will crash as soon as it starts executing 16-bit
|
||||||
|
; real-mode code, because it's being misinterpreted as 32-bit code, and there doesn't appear to be
|
||||||
|
; anything you can do about it from real-mode.
|
||||||
|
;
|
||||||
|
; The work-around: switch to a 16-bit code segment BEFORE returning to real-mode.
|
||||||
|
;
|
||||||
|
; "Unreal mode" works by setting other segment registers (eg, DS, ES) to 32-bit segments, not CS.
|
||||||
|
; SS should not be set to a 32-bit segment either, because that causes implicit pushes to use ESP
|
||||||
|
; instead of SP, even in real-mode.
|
||||||
|
;
|
||||||
|
; TODO: Verify that a 80386 cannot successfully return to real-mode when CS contains a 32-bit code segment.
|
||||||
;
|
;
|
||||||
cpu 386
|
cpu 386
|
||||||
org 0x100
|
org 0x100
|
||||||
|
|
@ -99,7 +117,7 @@ CR0_MSW_PE equ 0x0001
|
||||||
%assign selDesc selDesc+8
|
%assign selDesc selDesc+8
|
||||||
%endmacro
|
%endmacro
|
||||||
|
|
||||||
start: nop
|
start: cli
|
||||||
mov eax,0x44332211
|
mov eax,0x44332211
|
||||||
mov ebx,eax
|
mov ebx,eax
|
||||||
mov ecx,0x88776655
|
mov ecx,0x88776655
|
||||||
|
|
@ -138,8 +156,13 @@ addrGDT:dw myGDTEnd - myGDT - 1 ; 16-bit limit of myGDT
|
||||||
; TODO: Why do I need to provide a 2nd parameter for "defDesc NULL"? Is this a NASM 0.98.x bug?
|
; TODO: Why do I need to provide a 2nd parameter for "defDesc NULL"? Is this a NASM 0.98.x bug?
|
||||||
;
|
;
|
||||||
myGDT: defDesc NULL,0 ; the first descriptor in any descriptor table is always a dud (it corresponds to the null selector)
|
myGDT: defDesc NULL,0 ; the first descriptor in any descriptor table is always a dud (it corresponds to the null selector)
|
||||||
|
%ifdef PMODE_32BIT
|
||||||
|
defDesc CSEG_PROT,0x000f0000,0x0000ffff,ACC_TYPE_CODE_READABLE,EXT_BIG
|
||||||
|
defDesc DSEG_PROT,0x00000000,0x000fffff,ACC_TYPE_DATA_WRITABLE,EXT_BIG
|
||||||
|
%else
|
||||||
defDesc CSEG_PROT,0x000f0000,0x0000ffff,ACC_TYPE_CODE_READABLE,EXT_NONE
|
defDesc CSEG_PROT,0x000f0000,0x0000ffff,ACC_TYPE_CODE_READABLE,EXT_NONE
|
||||||
defDesc DSEG_PROT,0x00000000,0x000fffff,ACC_TYPE_DATA_WRITABLE,EXT_NONE
|
defDesc DSEG_PROT,0x00000000,0x000fffff,ACC_TYPE_DATA_WRITABLE,EXT_NONE
|
||||||
|
%endif
|
||||||
myGDTEnd:
|
myGDTEnd:
|
||||||
|
|
||||||
initGDT:
|
initGDT:
|
||||||
|
|
@ -159,7 +182,7 @@ initGDT:
|
||||||
mov word [RAM_RETF+2],cs
|
mov word [RAM_RETF+2],cs
|
||||||
%else
|
%else
|
||||||
;
|
;
|
||||||
; This code will have no effect if we're in ROM (in that case, myGDT et al should already be set correctly)
|
; This code will have no effect if we're in ROM (but in that case, everything should already be initialized correctly)
|
||||||
;
|
;
|
||||||
xor eax,eax
|
xor eax,eax
|
||||||
mov ax,cs
|
mov ax,cs
|
||||||
|
|
@ -172,7 +195,11 @@ initGDT:
|
||||||
mov eax,edx ; recover the base address of the current CS
|
mov eax,edx ; recover the base address of the current CS
|
||||||
add eax,myGDT ; EAX = physical address of myGDT
|
add eax,myGDT ; EAX = physical address of myGDT
|
||||||
mov [cs:addrGDT+2],eax ; update the 32-bit base address of myGDT in addrGDT
|
mov [cs:addrGDT+2],eax ; update the 32-bit base address of myGDT in addrGDT
|
||||||
mov [cs:jmpReal+3],cs ; update the segment of the far jmp that returns us to real-mode
|
%ifdef PMODE_32BIT
|
||||||
|
mov [cs:jmpReal+5],cs ; update the segment of the far jmp that returns us to real-mode
|
||||||
|
%else
|
||||||
|
mov [cs:jmpReal+3],cs
|
||||||
|
%endif
|
||||||
mov [cs:jmpStart+3],cs ; ditto for the far jmp that returns us to the start of the image
|
mov [cs:jmpStart+3],cs ; ditto for the far jmp that returns us to the start of the image
|
||||||
%endif
|
%endif
|
||||||
|
|
||||||
|
|
@ -184,7 +211,10 @@ goProt: o32 lgdt [cs:addrGDT]
|
||||||
jmpProt:
|
jmpProt:
|
||||||
jmp CSEG_PROT:toProt
|
jmp CSEG_PROT:toProt
|
||||||
|
|
||||||
toProt: ; bits 32 ; only if we define the CSEG_PROT descriptor with EXT_BIG
|
toProt:
|
||||||
|
%ifdef PMODE_32BIT
|
||||||
|
bits 32 ; only if we define the CSEG_PROT descriptor with EXT_BIG
|
||||||
|
%endif
|
||||||
mov ax,DSEG_PROT
|
mov ax,DSEG_PROT
|
||||||
mov ds,ax
|
mov ds,ax
|
||||||
mov es,ax
|
mov es,ax
|
||||||
|
|
@ -198,7 +228,10 @@ goReal: mov eax,cr0
|
||||||
jmpReal:
|
jmpReal:
|
||||||
jmp CSEG_REAL:toReal
|
jmp CSEG_REAL:toReal
|
||||||
|
|
||||||
toReal: ; bits 16 ; only if we define the CSEG_PROT descriptor with EXT_BIG
|
toReal:
|
||||||
|
%ifdef PMODE_32BIT
|
||||||
|
bits 16 ; only if we define the CSEG_PROT descriptor with EXT_BIG
|
||||||
|
%endif
|
||||||
mov ax,cs
|
mov ax,cs
|
||||||
cmp ax,CSEG_REAL ; is CS equal to 0xf000?
|
cmp ax,CSEG_REAL ; is CS equal to 0xf000?
|
||||||
je near jmpStart ; yes
|
je near jmpStart ; yes
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue