Fixed LGDT and LIDT, fixed read-only memory, and fixed mode transition tests

This commit is contained in:
Jeff Parsons 2015-05-06 16:24:28 -07:00 committed by jeffpar
commit cfa4e0207f
5 changed files with 158 additions and 104 deletions

View file

@ -1,6 +1,15 @@
;
; This file is designed to run as a ROM replacement, but it has a .COM extension because it's
; also designed to run as a COM file under DOS (hence the "org 0x100").
; This file is designed to run both as a test ROM and as a DOS COM file (hence the "org 0x100"),
; which is why it has a ".com" extension instead of the more typical ".rom" extension.
;
; When used as a ROM, it should be installed at physical address 983296 (0xf0100) and aliased at
; physical address 4294902016 (0xffff0100). The jump at jmpStart should align with the CPU reset
; address (%0xfffffff0), which will transfer control to 0xf000:0x0100.
;
; The code which attempts to update myGDT and addrGDT will have no effect when installed as a ROM,
; 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.
;
cpu 386
org 0x100
@ -8,38 +17,35 @@
bits 16
DPL0 equ 0
DPL1 equ 1
DPL2 equ 2
DPL3 equ 3
ACC_TYPE_SEG equ 0x1000
ACC_PRESENT equ 0x8000
ACC_TYPE_CODE equ 0x0800
ACC_TYPE_READABLE equ 0x0200
ACC_TYPE_WRITABLE equ 0x0200
ACC_TYPE_CODE_READABLE equ 0x1a00
ACC_TYPE_DATA_WRITABLE equ 0x1200
ACC_TYPE_SEG equ 0x1000
ACC_PRESENT equ 0x8000
ACC_TYPE_CODE equ 0x0800
ACC_TYPE_READABLE equ 0x0200
ACC_TYPE_WRITABLE equ 0x0200
ACC_TYPE_CODE_READABLE equ (0x1a00 | ACC_PRESENT)
ACC_TYPE_DATA_WRITABLE equ (0x1200 | ACC_PRESENT)
EXT_NONE equ 0x0000
EXT_BIG equ 0x0040
EXT_NONE equ 0x0000
EXT_BIG equ 0x0040
;
; We build some data structures in the first page (0x0000-0x0fff) of RAM:
; If we built our data structures in RAM, we might use the first page of RAM (0x0000-0x0fff) like so:
;
; 0x0000-0x03ff Real-mode IDT (256*4)
; 0x0400-0x0bff Prot-mode IDT (256*8)
; 0x0c00-0x0cff GDT (enough room for 32 selectors)
; 0x0d00-0x0d07 IDTR
; 0x0d08-0x0d0f GDTR
; 0x0d10-0x0fff reserved
; 0x0c00-0x0cff RAM_GDT (for 32 GDT selectors)
; 0x0d00-0x0d07 RAM_IDTR
; 0x0d08-0x0d0f RAM_GDTR
; 0x0d10-0x0d13 RAM_RETF (Real-mode return address)
; 0x0d14-0x0fff reserved
;
; And in the second page (0x1000-0x1fff), we build a page directory, followed by a single page table that
; will allow us to map up to 4Mb (although we'll only create entries for the first 1Mb).
; And in the second page (0x1000-0x1fff), we might build a page directory, followed by a single page table that
; allows us to map up to 4Mb (although we'd likely only create PTEs for the first 1Mb).
;
RAM_GDT equ 0x0c00
RAM_IDTR equ 0x0d00
RAM_GDTR equ 0x0d08
;RAM_GDT equ 0x0c00
;RAM_IDTR equ 0x0d00
;RAM_GDTR equ 0x0d08
;RAM_RETF equ 0x0d10
CSEG_REAL equ 0xf000
CSEG_PROT equ 0x0008
@ -48,7 +54,7 @@ DSEG_PROT equ 0x0010
CR0_MSW_PE equ 0x0001
;
; set initializes a register to the specified value (eg, "set eax,0")
; The "set" macro initializes a register to the specified value (eg, "set eax,0")
;
%macro set 2
%ifnum %2
@ -63,49 +69,45 @@ CR0_MSW_PE equ 0x0001
%endmacro
;
; defDesc defines a descriptor, given a base (%1), limit (%2), type (%3), dpl (%4), and ext (%5)
;
%macro defDesc 1-5 0,0,0,0,0
dw (%2 & 0x0000ffff)
dw (%1 & 0x0000ffff)
dw ((%1 & 0x00ff0000) >> 16) | %3 | (%4 << 13)
dw ((%2 & 0x000f0000) >> 16) | %5 | ((%1 & 0xff000000) >> 16)
%endmacro
;
; setDesc creates a descriptor, given a base (%1), limit (%2), type (%3), ext (%4), and selector (%5)
; The "defDesc" macro defines a descriptor, given a name (%1), base (%2), limit (%3), type (%4), and ext (%5)
;
%assign selDesc 0
%macro setDesc 1-5 0,0,0,0,none
set ebx,%1
set ecx,%2
set dx,%3
set ax,%4
call storeDesc
%assign %5 selDesc
%macro defDesc 1-5 none,0,0,0,0
%assign %1 selDesc
dw (%3 & 0x0000ffff)
dw (%2 & 0x0000ffff)
%if selDesc = 0
dw ((%2 & 0x00ff0000) >> 16) | %4 | (0 << 13)
%else
dw ((%2 & 0x00ff0000) >> 16) | %4 | (0 << 13) | ACC_PRESENT
%endif
dw ((%3 & 0x000f0000) >> 16) | %5 | ((%2 & 0xff000000) >> 16)
%assign selDesc selDesc+8
%endmacro
start: cli ; disable all interrupts
mov al,0xff ; and ensure that no hardware interrupts can sneak in
out 0x21,al ; if interrupts become enabled later
out 0xa1,al
sub ax,ax
mov ds,ax
mov es,ax
mov ss,ax
mov sp,0x1000
;
; The "setDesc" macro creates a descriptor, given a name (%1), base (%2), limit (%3), type (%4), and ext (%5)
;
%macro setDesc 1-5 none,0,0,0,0
%assign %1 selDesc
set ebx,%2
set ecx,%3
set dx,%4
set ax,%5
call storeDesc
%assign selDesc selDesc+8
%endmacro
start: nop
mov eax,0x44332211
mov ebx,eax
mov ecx,0x88776655
mul ecx
div ecx
cmp eax,ebx
je near initRAM ; apparently we have to tell NASM "near" because this is a forward reference
je near initGDT ; apparently we have to tell NASM "near" because this is a forward reference
times 32768 nop ; lots of NOPs to force a 16-bit conditional jump
;
; storeDesc(EBX=base, ECX=limit, DX=type, AX=ext, DI=address of descriptor)
;
@ -119,6 +121,7 @@ storeDesc:
mov ax,dx
shr ebx,16
mov al,bl
or ax,ACC_PRESENT
stosw
pop ax
shr ecx,16
@ -128,56 +131,78 @@ storeDesc:
stosw
ret
;
; The following ROM-based data structures are obsolete, because we build these data structures in RAM now.
;
addrGDT:dw romGDTEnd - romGDT - 1 ; 16-bit limit of romGDT
dw romGDT, 0xffff ; 32-bit base address of romGDT (works as long as we're aliased at 0xffff0000)
addrGDT:dw myGDTEnd - myGDT - 1 ; 16-bit limit of myGDT
dw myGDT, 0xffff ; 32-bit base address of myGDT (works as long as we're aliased at 0xffff0000)
romGDT: defDesc 0 ; the first descriptor in any descriptor table is always a dud (it corresponds to the null descriptor)
defDesc 0x000f0000,0x0000ffff,ACC_TYPE_CODE_READABLE
defDesc 0x00000000,0x000fffff,ACC_TYPE_DATA_WRITABLE
romGDTEnd:
;
; End of ROM-based data structures
; 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)
defDesc CSEG_PROT,0x000f0000,0x0000ffff,ACC_TYPE_CODE_READABLE,EXT_NONE
defDesc DSEG_PROT,0x00000000,0x000fffff,ACC_TYPE_DATA_WRITABLE,EXT_NONE
myGDTEnd:
initRAM:
initGDT:
%ifdef RAM_GDT
set edi,RAM_GDT
mov [RAM_GDTR+2],edi
setDesc 0,0,0,0,NULL
mov eax,cs
setDesc NULL
xor eax,eax
mov ax,cs
shl eax,4
setDesc eax,0x0000ffff,ACC_TYPE_CODE_READABLE,EXT_BIG,CSEG_PROT
setDesc 0x0,0x000fffff,ACC_TYPE_DATA_WRITABLE,EXT_BIG,DSEG_PROT
setDesc CSEG_PROT,eax,0x0000ffff,ACC_TYPE_CODE_READABLE,EXT_NONE
setDesc DSEG_PROT,0x0,0x000fffff,ACC_TYPE_DATA_WRITABLE,EXT_NONE
sub edi,RAM_GDT
dec edi
mov [RAM_GDTR],di
mov word [RAM_RETF],toReal
mov word [RAM_RETF+2],cs
%else
;
; This code will have no effect if we're in ROM (in that case, myGDT et al should already be set correctly)
;
xor eax,eax
mov ax,cs
shl eax,4 ; EAX = base address of the current CS
mov edx,eax ; save it in EDX
mov [cs:myGDT+CSEG_PROT+2],ax ; update the base portions of the descriptor for CSEG_PROT
shr eax,16
mov [cs:myGDT+CSEG_PROT+4],al
mov [cs:myGDT+CSEG_PROT+7],ah
mov eax,edx ; recover the base address of the current CS
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:jmpReal+3],cs ; update the segment of the far jmp that returns us to real-mode
mov [cs:jmpStart+3],cs ; ditto for the far jmp that returns us to the start of the image
%endif
goProt: o32 lgdt [RAM_GDTR]
goProt: o32 lgdt [cs:addrGDT]
mov eax,cr0
or eax,CR0_MSW_PE
mov cr0,eax
jmp CSEG_PROT:inProt
inProt:
bits 32
nop
jmpProt:
jmp CSEG_PROT:toProt
toProt: ; bits 32 ; only if we define the CSEG_PROT descriptor with EXT_BIG
mov ax,DSEG_PROT
mov ds,ax
mov es,ax
mov ss,ax
;
; Do some protected-mode tests now...
;
goReal: mov eax,cr0
and eax,~CR0_MSW_PE
mov cr0,eax
bits 16
jmp CSEG_REAL:inReal
inReal:
or ax,1
jnz start ; apparently we do NOT have to say "near" here since this is a backward reference
nop
jmpReal:
jmp CSEG_REAL:toReal
toReal: ; bits 16 ; only if we define the CSEG_PROT descriptor with EXT_BIG
mov ax,cs
cmp ax,CSEG_REAL ; is CS equal to 0xf000?
je near jmpStart ; yes
int 0x20 ; no, so assume we're running under DOS and exit
;
; Fill the remaining space with NOPs until we get to target offset 0xFFF0.
@ -185,9 +210,10 @@ inReal:
;
times 0xfff0-0x100-($-$$) nop
jmpStart:
jmp CSEG_REAL:start
db 0x20
db '04/04/15'
db 0xFC ; 0000FFFE FC (Model ID byte)
db 0x00 ; 0000FFFF 00 (location of checksum byte)
db 0xFC ; 0000FFFE FC (Model ID byte)
db 0x00 ; 0000FFFF 00 (location of checksum byte)