pcjs/pubs/docs/x86/ops/LOADALL/386load.asm

1000 lines
37 KiB
NASM

Page 60,132
;---------------------------------------------------------------
; BEGIN LISTING 2
;---------------------------------------------------------------
;
; 386LOAD.ASM
;
; Copyright (c) 1991, 1995-Present Robert Collins
;
; You have my permission to copy and distribute this software for
; non-commercial purposes. Any commercial use of this software or
; source code is allowed, so long as the appropriate copyright
; attributions (to me) are intact, *AND* my email address is properly
; displayed.
;
; Basically, give me credit, where credit is due, and show my email
; address.
;
;-----------------------------------------------------------------------------
;
; Robert R. Collins email: rcollins@x86.org
;
;-----------------------------------------------------------------------------
;
; This program demonstrates various aspects of CPU
; behavior that become apparent when using LOADALL.
;
; Test 1: Checks that LOADALL loads all the general-
; purpose registers; loads the segment registers
; with values that are inconsistant to their
; respective descriptor cache registers.
;
; Test 2: Access extended memory in real mode.
;
; Test 3: Tests that the Present bit in a descriptor
; table can be loaded using LOADALL without
; generating exception 11. But when the segment
; is accessed, exception 13 is generated.
; NOTE: This test should be done in protected
; mode, but can be done in real mode. 1) In real
; mode, no error code is pushed on the stack
; (possibly due to a bug in the CPU). 2) Also,
; when this test is executed in real-mode, the
; '386 fails to set the Present bit when the
; segment is subsequently loaded. This latter
; condition is clearly a bug in the '386.
;
; Test 4: Test 32-bit protected mode.
;
; Test 5: Test 32-bit real mode -- a mode that is an
; illegal mode for the CPU.
;
; Test 6: Test that the Granularity bit in the descriptor
; cache register has no affect on the segment
; limit field.
;
; Test 7: Test execution breakpoints.
;
; Test 8: Test data breakpoints.
;
; This program was written for Microsoft MASM 5.1.
; This program contains compiler directives and branching
; techniques that might not be available on previous
; versions of the Macro Assembler, nor in competitive
; products.
;
;---------------------------------------------------------------
;---------------------------------------------------------------
; Compiler directives
;---------------------------------------------------------------
Title LOADALL_386
.radix 16
.8086
;---------------------------------------------------------------
; Interrupt vector segment
;---------------------------------------------------------------
ABS0 segment at 0
org 6*4
INT_6 dd ?
ABS0 ends
;---------------------------------------------------------------
; Structure definitions
;---------------------------------------------------------------
Desc_cache STRUC
db 0
_Type db ?
_CS32 db 0
db 0
_Addr dd ?
_Limit dd ?
Desc_cache ENDS
Loadall_struc STRUC
_Cr0 dd 0
_Eflags dd 2
_Eip dd 0
_Edi dd 66666666h
_Esi dd 77777777h
_Ebp dd 55555555h
_Esp dd 88888888h
_Ebx dd 22222222h
_Edx dd 44444444h
_Ecx dd 33333333h
_Eax dd 11111111h
_Dr6 dd 0
_Dr7 dd 0
_Tr dd 0
_Ldt dd 0
_Gs dd 5555h
_Fs dd 4444h
_Ds dd 2222h
_Ss dd 6666h
_Cs dd 1111h
_Es dd 3333h
TSS_Desc dd 00008900h,00070000h,00000800h
IDT_Desc dd 00000000h,00000000h,000003ffh
Gdt_Desc dd 00000000h,00000000h,00000000h
Ldt_Desc dd 00008200h,00090000h,00000088h
GS_Desc dd 00009300h,00050000h,0000ffffh
FS_Desc dd 00009300h,00040000h,0000ffffh
DS_Desc dd 00009300h,00020000h,0000ffffh
SS_Desc dd 00009300h,00060000h,0000ffffh
CS_Desc dd 00009b00h,00000000h,0000ffffh
ES_Desc dd 00009300h,00030000h,00fffffch
Loadall_Struc ENDS
Descriptor STRUC
Seg_limit dw ? ; Segment limit
Base_A15_A00 dw ? ; A00..A15 of base address
Base_A23_A16 db ? ; A16..A23 of base address
Access_rights db ? ; Segment access rights
Limit_A19_A16 db ? ; Granularity, Op-size,
; Limit A16..A19
Base_A31_A24 db ? ; A24..A31 of base address
Descriptor ENDS
INT_Desc STRUC
IGate_Offset dw ? ; Offset of handler
CSEG_Sel dw ? ; Code segment selector
db 0
db 86h ; 286 interrupt gate=16bit
; CS:IP, FLAGS
Resvd dw 0 ; Reserved=0
INT_Desc ENDS
;---------------------------------------------------------------
; Macro definitions in MACROS.386:
; INIT_DESCRIPTOR: Given a segment, offset, and descriptor
; name, calculate the 24-bit physical
; address, and store it in the descriptor.
; FARJMP: Far JUMP since MASM doesn't assemble it.
; LONGJMP: 32-bit FAR JUMP similar to above.
; IO_DELAY: Put your favorite I/O delay macro here.
; LOADALL: Copy the LOADALL memory image down to
; 0:800 and execute a '286 LOADALL.
; PRINT_STRING: Given a variable name, use the DOS
; print string command to send it to the
; screen.
;---------------------------------------------------------------
Include MACROS.386
_DATA SEGMENT PARA PUBLIC 'DATA'
;---------------------------------------------------------------
; Equates & local variables
;---------------------------------------------------------------
; Protected mode access rights
;---------------------------------------------------------------
CS_access equ 10011011b
DS_access equ 10010011b
;---------------------------------------------------------------
; Text equates
;---------------------------------------------------------------
CRLF equ <0dh,0ah>
CRLF$ equ <CRLF,'$'>
INT6 equ [bp-4]
;---------------------------------------------------------------
; Loadall table(s)
;---------------------------------------------------------------
Loadall_tbl Loadall_struc <>
Machine_State Loadall_struc <>
;---------------------------------------------------------------
; Global Descriptor Table
;---------------------------------------------------------------
GDT_386 Descriptor <Gdt3_len-1,,,DS_access>
CSEG3 Descriptor <0ffffh,0,0,CS_access>
DSEG3 Descriptor <0ffffh,0,20h,DS_access>
Gdt3_len equ $-Gdt_386
;---------------------------------------------------------------
; Interrupt Descriptor Table
;---------------------------------------------------------------
IDT_386 INT_Desc <Offset INT13,CSEG3-GDT_386> ; INT00
INT_Desc <Offset INT01,CSEG3-GDT_386> ; INT01
INT_Desc <Offset INT13,CSEG3-GDT_386> ; INT02
INT_Desc <Offset INT13,CSEG3-GDT_386> ; INT03
INT_Desc <Offset INT13,CSEG3-GDT_386> ; INT04
INT_Desc <Offset INT13,CSEG3-GDT_386> ; INT05
INT_Desc <Offset INT13,CSEG3-GDT_386> ; INT06
INT_Desc <Offset INT13,CSEG3-GDT_386> ; INT07
INT_Desc <Offset INT13,CSEG3-GDT_386> ; INT08
INT_Desc <Offset INT13,CSEG3-GDT_386> ; INT09
INT_Desc <Offset INT13,CSEG3-GDT_386> ; INT0a
INT_Desc <Offset INT13,CSEG3-GDT_386> ; INT0b
INT_Desc <Offset INT13,CSEG3-GDT_386> ; INT0c
INT_Desc <Offset INT13,CSEG3-GDT_386> ; INT0d
IDT3_Len equ $-IDT_386
;---------------------------------------------------------------
; Misc. local variables
;---------------------------------------------------------------
Mem_buffer db 400h dup (0) ; Store 2M mem.
Buffer db 40h dup (0)
Buffer2 dw 10h dup (0)
RM_IDT3_Ptr dw (256d*4)-1 ; Real-mode IDT
dd 0 ; pointer
dw 0
;---------------------------------------------------------------
; String Messages
;---------------------------------------------------------------
Passed db " PASSED.",CRLF$
Failed db "--> FAILED <--",CRLF$
Not_386 db "Not 80386 class computer.",CRLF$
Rmvd db "LOADALL removed from 80386 mask.",CRLF$
RFail db "Registers weren't loaded correctly."
LF db CRLF$
;---------------------------------------------------------------
; I'm doing this wierd string definition technique to limit the
; page width to 64 characters.
;---------------------------------------------------------------
Test_1 label word
db "Test 1: Testing 386 LOADALL instruction: ",24
Test_2 label word
db "Test 2: Testing extended memory in real mode: ",24
Test_3 label word
db "Test 3: Testing Present BIT in descriptor: ",24
Test_4 label word
db "Test 4: Testing 32-bit protected mode: ",24
Test_5 label word
db "Test 5: Testing 32-bit real mode: ",24
Test_6 label word
db "Test 6: Testing Granularity BIT: ",24
Test_7 label word
db "Test 7: Testing Execution breakpoints: ",24
Test_8 label word
db "Test 8: Testing byte, write, data breakpoints: ",24
_DATA ENDS
_TEXT SEGMENT PARA PUBLIC 'CODE'
ASSUME CS:_TEXT, ds:_DATA, ES:_DATA, SS:STACK
.386P
;---------------------------------------------------------------
; A little CS-relative data for the stack pointer. This is
; to avoid using other kludge techniques, caused by using
; LOADALL, that make using the data segment undesirable.
;---------------------------------------------------------------
Stack_ptr dd 0
dw 0
;---------------------------------------------------------------
LOADALL_386 proc far
;---------------------------------------------------------------
PUSH DS ; Setup the stack to
XOR AX,AX ; return to DOS
PUSH AX
MOV AX,_Data
MOV DS,AX
MOV ES,AX
;---------------------------------------------------------------
; Check CPU type
;---------------------------------------------------------------
Call CPU_Type ; 386, 486?
cmp ax,3 ; 386?
je short @F ; yep
Print_String LF
Print_String Not_386
retf ; go split
@@: enter 4,0 ; create stack frame
mov word ptr INT6,offset INT6_handler
mov INT6+2,cs
call set_INT6_vector ; set our INT6 handler
cli
Call Save_State
Print_String LF
Print_String Test_1
Init_descriptor <seg Gdt_386>,<offset Gdt_386>,Gdt_386
Init_descriptor cs, 0,CSEG3
;---------------------------------------------------------------
;
; TEST1: 16-bit Real mode
; Test general purpose registers
; Test Segment registers
; Test Descriptor cache base address
;
; (1) Setup LOADALL structures, and pointers
; (2) Execute LOADALL
; (3) Verify results of the test
;
;---------------------------------------------------------------
mov eax,cs ; Prepare a 32-bit
shl eax,4 ; physical address that
; is put in the LOADALL
; CS descriptor cache.
mov Loadall_tbl.CS_Desc._Addr,eax
mov eax,cr0 ; Initialize CR0 in the
and eax,0fffffff0h ; LOADALL data table
mov Loadall_tbl._CR0,eax ; DR6, DR7, EIP
mov eax,dr6
mov ebx,dr7
mov Loadall_tbl._DR6,eax
mov Loadall_tbl._DR7,ebx
mov Loadall_tbl._EIP,offset Verify_State
mov dword ptr cs:stack_ptr,esp ; save SS:ESP
mov word ptr cs:stack_ptr[4],ss
mov edi,offset loadall_tbl
LOADALL
nop ; If LOADALL is removed
Print_String failed ; from the CPU mask,
Print_String Rmvd ; then fall through
; to here.
Loadall_RET:
call Restore_state
call set_int6_vector
leave
retf
;---------------------------------------------------------------
Verify_State: ; Verify that LOADALL worked
;---------------------------------------------------------------
; This is where we land for the first test of '386 LOADALL.
; The purpose of this test is to verify that all the general
; purpose registers get loaded correctly. Specifically, we are
; testing to verify that all segment registers contain values
; that don't correspond to the memory addresses they appear to
; be pointing to. In other words, we are checking that the
; that the segment registers have one value, while their
; associated hidden descriptor cache registers have different
; values.
;---------------------------------------------------------------
cmp eax,11111111h ; Test EAX
jne @F
cmp ebx,22222222h ; Test EBX
jne @F
cmp ecx,33333333h ; Test ECX
jne @F
cmp edx,44444444h ; Test EDX
jne @F
cmp ebp,55555555h ; Test EBP
jne @F
cmp edi,66666666h ; Test EDI
jne @F
cmp esi,77777777h ; Test ESI
jne @F
cmp esp,88888888h ; Test ESP
jne short @F
mov ax,cs ; Test CS
cmp ax,1111h
jne short @F
mov ax,ds ; Test DS
cmp ax,2222h
jne short @F
mov ax,es ; Test ES
cmp ax,3333h
jne short @F
mov ax,fs ; Test FS
cmp ax,4444h
jne short @F
mov ax,gs ; Test GS
cmp ax,5555h
jne short @F
mov ax,ss ; Test SS
cmp ax,6666h
jne short @F
cmp dword ptr ds:[0],02020202h ; Test DS Desc Cache
jne short @F
cmp dword ptr es:[0],03030303h ; Test ES Desc Cache
jne short @F
cmp dword ptr fs:[0],04040404h ; Test FS Desc Cache
jne short @F
cmp dword ptr gs:[0],05050505h ; Test GS Desc Cache
jne short @F
cmp dword ptr ss:[0],06060606h ; Test SS Desc Cache
jne short @F
mov ax,_Data ; Reset segment regs.
mov ds,ax
mov es,ax
lss esp,fword ptr cs:stack_ptr ; Reset SS:ESP
FARJMP <Loadall_test>,<seg _Text> ; Continue
;---------------------------------------------------------------
; Loadall failed the REGISTERs test.
;---------------------------------------------------------------
@@: mov ax,_Data
mov ds,ax
mov es,ax
lss esp,fword ptr cs:stack_ptr
FARJMP <@F>,<seg _Text>
@@: Print_String failed
Print_String RFail
jmp loadall_ret
;---------------------------------------------------------------
; LOADALL passed
;---------------------------------------------------------------
Loadall_test:
Print_String passed
;---------------------------------------------------------------
;
; TEST2: Access extended memory while in real mode (@ 2M).
;
; (1) Fill in LOADALL structure with "reasonable" values
; (2) Save contents of extended memory
; (3) Write data pattern in extended memory
; (4) Set LOADALL registers used by this test
; (5) LOADALL
; (6) Verify results
; (7) Restore original data in extended memory
;
;---------------------------------------------------------------
; In this test, I'll access extended memory while in real mode.
; I'll assume the computer has at least 2M of memory. By
; assuming 2M, I don't need to enable A20 on the CPU bus, since
; memory @ 2M doesn't assert A20.
;---------------------------------------------------------------
; Fill LOADALL structure with more reasonable values.
;---------------------------------------------------------------
mov Loadall_tbl._EAX,0
mov Loadall_tbl._EBX,0
mov Loadall_tbl._ECX,0
mov Loadall_tbl._EDX,0
mov Loadall_tbl._EBP,0
mov Loadall_tbl._ESP,esp
mov Loadall_tbl._CS,cs
mov Loadall_tbl._DS,ds
mov Loadall_tbl._ES,es
mov Loadall_tbl._FS,8000h
mov Loadall_tbl._GS,8000h
mov Loadall_tbl._SS,ss
;---------------------------------------------------------------
; Load segment descriptor cache registers:
; DS=ES = _DATA segment
; FS=GS = 80000h (8000:0)
; SS = STACK segment
;---------------------------------------------------------------
mov ax,ds
movzx eax,ax
shl eax,4
mov bx,ss
movzx ebx,bx
shl ebx,4
mov Loadall_tbl.DS_Desc._addr,eax
mov Loadall_tbl.ES_Desc._addr,eax
mov Loadall_tbl.FS_Desc._addr,80000h;
mov Loadall_tbl.GS_Desc._addr,80000h
mov Loadall_tbl.SS_Desc._addr,ebx
;---------------------------------------------------------------
; Slip into protected mode to save the contents of memory @ 2M,
; and write a test pattern into that memory.
;---------------------------------------------------------------
Print_String Test_2
lgdt fword ptr Gdt_386 ; Load the GDT
mov ebx,cr0 ; enter protected mode
or bl,1
mov cr0,ebx
FARJMP <@F>,<CSEG3-Gdt_386>
@@: mov dx,DSEG3-Gdt_386 ; Get segment selector
mov ds,dx ; for DS @ 2M
mov cx,400h / 4
mov si,0
mov di,offset Mem_buffer
rep movsd ; Save memory
mov eax,5aa5aa5ah ; Test pattern
mov cx,400h / 4
mov di,0
mov es,dx ; ES = 2M
rep stosd ; Store test pattern
and bl,not 1 ; exit protected mdoe
mov cr0,ebx
FARJMP <@F>,<_TEXT>
;---------------------------------------------------------------
; Set LOADALL-image registers used by this test
;---------------------------------------------------------------
@@: mov ax,_data
mov ds,ax
mov es,ax
mov Loadall_tbl._EAX,5aa5aa5ah ; Test pattern
mov Loadall_tbl._ECX,400h / 4 ; # of DWORDS
mov Loadall_tbl._ESI,offset Mem_buffer
mov Loadall_tbl._EDI,0h ; ES:0
mov Loadall_tbl._EIP,offset @F ; EIP
mov Loadall_tbl.ES_Desc._Addr,200000h ; 2M
mov edi,offset loadall_tbl
LOADALL
;---------------------------------------------------------------
; Check memory for a match of the pattern
;---------------------------------------------------------------
@@: repz scasd ; data match?
mov bx,_Data ; ES still points to 2M,
mov ds,bx ; so I can reset DS and
mov cx,400h / 4 ; restore the original
mov di,0 ; contents @ 2M without
rep movsd ; changing ES.
mov es,bx ; Set ES to real mode
lss esp,fword ptr cs:stack_ptr ; Restore SS:ESP
FARJMP <@F>,<_TEXT> ; Must jump to R/W CS
@@: jz short @Test2_Pass
PRINT_STRING failed
jmp short @Test3
@Test2_Pass:
PRINT_STRING passed
;---------------------------------------------------------------
;
; TEST3: Test that the Present bit gets loaded w/out exception,
; but when a segment is accessed INT13 get generated.
;
; (1) Load GDT & IDT pointers into LOADALL table
; (2) Set PM bit, CS selector, EIP offset, clear P bit
; (3) LOADALL
; (4) Verify Results
;
;-----------------------------------------------------------------------------
@Test3: Print_String Test_3
;---------------------------------------------------------------
; Initialize the GDT & IDT descriptor cache
;---------------------------------------------------------------
mov eax,Loadall_tbl.DS_Desc._Addr
mov ebx,eax
mov Loadall_tbl.ES_Desc._Addr,eax
add eax,offset Gdt_386
add ebx,offset Idt_386
mov Loadall_tbl.GDT_Desc._Addr,eax
mov Loadall_tbl.GDT_Desc._Limit,Gdt3_len-1
mov Loadall_tbl.Idt_Desc._Addr,ebx
mov Loadall_tbl.Idt_Desc._Limit,Idt3_len-1
or Loadall_tbl._CR0,1 ; set PM bit
mov Loadall_tbl._ESI,0
mov Loadall_tbl._CS,CSEG3-Gdt_386 ; CS selector
mov Loadall_tbl._EIP,offset @Test_Present
and Loadall_tbl.GS_Desc._Type,7fh ; clear Present
mov edi,offset loadall_tbl ; bit in GS
LOADALL
@Test_Present:
mov si,gs:[si][2] ; choose this because
; it's a 4-byte instr.
Lidt fword ptr RM_IDT3_Ptr ; Restore real-mode IDT
mov eax,cr0 ; Exit protected mode.
and al,not 1
mov cr0,eax
FARJMP <@F>,<_TEXT> ; Return to R/W, real-
; mode code segment
@@: test si,1 ; pass test?
jnz short @F ; yep
Print_String failed
jmp short @Test4
@@: Print_String passed
;---------------------------------------------------------------
;
; TEST4: Test 32-bit Protected Mode operation
;
; (1) Set LOADALL operand size, PM bits, and EIP offset
; (2) LOADALL
; (3) Verify Results
;
;---------------------------------------------------------------
@Test4: Print_String Test_4
;---------------------------------------------------------------
; Perform 32-bit protected mode test
;---------------------------------------------------------------
or Loadall_tbl.CS_Desc._CS32,40h ; CS32 bit
mov Loadall_tbl._EDI,offset Loadall_tbl
mov Loadall_tbl._EIP,offset PM32
LOADALL
;---------------------------------------------------------------
; This test uses a simple technique to determine if we are in
; 32-bit mode or 16-bit mode:
;
; In 16-bit mode, this code will be executed as:
; mov si,1234h
; nop
; nop
;
; In 32-bit mode, this code will be executed as:
; mov esi,90901234h ; The two NOP's are absorbed
; ; into the 32-bit operand.
;
;---------------------------------------------------------------
;---------------------------------------------------------------
; In 16-bit mode, this code will be executed as:
; mov di,1234h
; nop
; nop
;
; In 32-bit mode, this code will be executed as:
; mov edi,90901234h
;---------------------------------------------------------------
PM32: mov si,1234h
nop
nop
;---------------------------------------------------------------
; Now, we need to exit 32-bit mode gracefully. In order to do
; that, we need code that will generate predictable results in
; both 32-bit, and 16-bit mode. If we are in 32-bit mode, then
; the following compiled code will behave as documented under
; the '32-bit mode' column. If we failed to enter 32-bit mode,
; then the following compiled code will behave as documented
; under the '16-bit mode' column. In order to exit gracefully,
; we need to know which mode we are in! Hence the following
; code to detect it!:
;
; 16-bit mode 32-bit mode
; push ax push eax
; mov ax,si mov eax,esi
; shr ax,10h shr eax,10h ; AX=9090 if in 32-bit
; ; AX=0 if in 16-bit
;
; cmp al,90h cmp al,90h ; PASS=32-bit, FAIL=16-bit
; pushf pushf ; save results for later
; shl ax,10h shl eax,10h ; EAX=90900000 if in 32-bit
; mov si,ax mov esi,eax ; ESI=90900000 if in 32-bit
; ; ESI=66660000 if in 16-bit
; popf popf ; restore ZF
; pop ax pop eax
;
; So, the result of this code is to exit 32-bit mode if we got
; there, and to keep going in 16-bit mode if we didn't.
;---------------------------------------------------------------
push ax ; See the above
mov ax,si ; explanation on how
shr ax,10h ; this code works
cmp al,90h
pushf
shl ax,10h
mov si,ax
popf
pop ax
jne short @No_PM32bitCS
or si,1 ; set SI if 32-bit mode
;---------------------------------------------------------------
; 32-bit JUMP instruction, to transfer control back to a 16-bit
; segment
;---------------------------------------------------------------
LONGJMP <@F>,<CSEG3-GDT_386> ; Construct 32-bit far
; JMP
@No_PM32bitCS: ; FAR JUMP if stuck in
FARJMP <@F>,<CSEG3-GDT_386> ; 16-bit mode.
@@: Lidt fword ptr RM_IDT3_Ptr ; Restore real-mode IDT
mov eax,cr0 ; Exit protected mode.
and al,not 1
mov cr0,eax
FARJMP <@F>,<_TEXT> ; Jump to real-mode
; code segment
@@: mov ax,_Data ; Restore real-mode
mov ds,ax ; segment registers
mov es,ax
test si,1 ; 32-bit mode passed?
jnz short @F ; yep
Print_String failed
jmp short @Test5
@@: Print_String passed
;---------------------------------------------------------------
;
; TEST5: Test 32-bit real-mode operation
;
; (1) Clear operand size, PM bits, and set EIP offset
; (2) LOADALL
; (3) Verify Results
; (4) Get the h&*@! out of 32-bit real-mode
;
;---------------------------------------------------------------
; Test 32-bit real-mode operation. Make sure we can put the
; processor in an illegal state: 32-bit real-mode.
;---------------------------------------------------------------
@Test5: Print_String Test_5
and Loadall_tbl._CR0,not 1 ; disable PM bit
and Loadall_tbl.CS_Desc._Type,not 8 ; make CS R/W
; Data segment
mov Loadall_tbl._EIP,offset RM32 ; Load EIP
LOADALL
;---------------------------------------------------------------
; This test uses the exact same technique documented in TEST4.
; See TEST4 for an explanation of these next two code sections.
;---------------------------------------------------------------
RM32: mov si,1234h
nop
nop
;---------------------------------------------------------------
; See TEST4 for an explanation of this code section.
;---------------------------------------------------------------
push ax ; See the above
mov ax,si ; explanation on how
shr ax,10h ; this code works
cmp al,90h
pushf
shl ax,10h
mov si,ax
popf
pop ax
;---------------------------------------------------------------
; Now getting out of 32-bit real mode is a bit more complicated
; than you may think. According to Intel, the internal
; descriptor cache registers get re-loaded with default values
; each time the segment is loaded. But the operand size bit in
; the CS descriptor cache doesn't get cleared in a long jump.
; So, to program around this, I need to either execute LOADALL
; again, or go to protected mode, jump to a 16-bit code segment,
; then go back to real mode. I think I will do the latter.
;---------------------------------------------------------------
jne short @RM32_Fail ; Do something else
mov eax,cr0 ; Enter protected mode
or al,1
mov cr0,eax
LONGJMP <@F>,<CSEG3-GDT_386> ; To 16-bit CS
@@: Lidt fword ptr RM_IDT3_Ptr ; Reload real-mode IDT
and al,not 1 ; Exit protected mode
mov cr0,eax
FARJMP <@RM32_Pass>,<seg _text>; Back to real-mode CS
@RM32_Fail:
Print_String failed
jmp short @Test6
@RM32_Pass:
mov ax,_Data ; Reload real-mode
mov ds,ax ; segment registers
mov es,ax
Print_String passed
;---------------------------------------------------------------
;
; TEST6: Test Granularity bit. Test that the granularity bit
; in the descriptor cache has no effect on the limit
; field of the descriptor cache. This is a three-part
; test. First, by setting G=1 in a segment descriptor
; cache whose limit=64k should cause an exception.
; Second, ES_LIMIT=16M-4. This LIMIT can't ever be
; generated under program control. So we simply need
; to access memory @ 16M-8 to verify this test. If
; an exception gets generated, then the test failed.
; Third, try to access memory @ 16M-4. If an exception
; ISN'T generated, then the test failed.
;
; (1) Set Present, Granularity bits, and EIP offset
; (2) LOADALL
; (3) Verify results
;
;---------------------------------------------------------------
; Test Granularity bit: Test that G=1 has no effect in the
; descriptor cache register. In the GS descriptor cache, I set
; the limit=64k. If G=1 has any effect on this field, then the
; following memory access will pass. We want it to fail, and
; generate an exception-13.
;---------------------------------------------------------------
@Test6: Print_String Test_6
or Loadall_tbl._CR0,1 ; set PM bit
or Loadall_tbl.GS_Desc._Type,80h ; set P=1
or Loadall_tbl.GS_Desc._CS32,80h ; set G=1
and Loadall_tbl.CS_Desc._CS32,0bfh ; clear CS32 bit
mov Loadall_tbl._EIP,offset @Test_Gran
LOADALL
@Test_Gran:
mov esi,10000h ; should generate exc.
mov si,gs:[esi]
shl si,1 ; save results
mov esi,0fffff8h ; 16M-8
mov si,es:[esi] ; shouldn't generate exc
shl si,1
mov esi,0fffffch ; 16M-4
mov si,es:[esi] ; should generate exc.
add esi,4
Lidt fword ptr RM_IDT3_Ptr ; Restore real-mode IDT
mov eax,cr0
and al,not 1
mov cr0,eax
FARJMP <@F>,<_TEXT>
@@: and si,7
cmp si,5 ; pass test?
jnz short @F ; yep
Print_String failed
jmp short @Test7
@@: Print_String passed
;---------------------------------------------------------------
; The following two expamples show how to use the debug
; registers to generate execution and data break points. They
; are included in this program because they use LOADALL to set
; DR7. Forgive the fact that they are poorly documented.
;---------------------------------------------------------------
;
; TEST7: Test execution breakpoints through debug registers.
;
; (1) Clear granularity bit, set ES_LIMIT=64k, set EIP offset
; (2) Set breakpoint qualifiers
; (3) LOADALL
; (4) Generate execution breakpoints
; (5) Verify results
;
;---------------------------------------------------------------
@Test7: Print_String Test_7
and Loadall_tbl.GS_Desc._CS32,7fh ; set G=0
mov Loadall_tbl.ES_Desc._Limit,0ffffh; limit=64k
mov Loadall_tbl._EIP,offset Test_DR0
;---------------------------------------------------------------
; Set a series of execution break points in the debug registers.
;---------------------------------------------------------------
mov bx,cs
movzx ebx,bx
shl ebx,4
lea eax,[ebx][dword ptr Test_Dr0]
mov dr0,eax
lea eax,[ebx][dword ptr Test_DR1]
mov dr1,eax
lea eax,[ebx][dword ptr Test_DR2]
mov dr2,eax
lea eax,[ebx][dword ptr Test_DR3]
mov dr3,eax
mov Loadall_tbl._DR7,0aah ; enable all code
; breakpoints
LOADALL
Test_DR0:
nop
Test_DR1:
nop
Test_DR2:
nop
Test_DR3:
nop
Lidt fword ptr RM_IDT3_Ptr ; Restore real-mode IDT
mov eax,cr0 ; exit protected mode
and al,not 1
mov cr0,eax
FARJMP <@F>,<seg _text>
@@: and si,0fh
cmp si,0fh ; pass all breakpoints?
je short @F ; yep
Print_String failed
jmp @Test8
@@: Print_String passed
;---------------------------------------------------------------
;
; TEST8: Test byte-size data write breakpoints
;
; (1) Set EIP offset
; (2) Set breakpoint qualifiers
; (3) LOADALL
; (4) Generate data breakpoints
; (5) Verify results
;
;---------------------------------------------------------------
@Test8: Print_String Test_8
mov Loadall_tbl._EAX,55h
mov Loadall_tbl._ECX,40h
mov Loadall_tbl._EDI,offset Buffer
mov Loadall_tbl._EIP,offset @Test_DRBW
mov bx,ds
movzx ebx,bx
shl ebx,4
lea eax,[ebx][Dword ptr Buffer][3]
mov dr0,eax
add eax,10h
mov dr1,eax
add eax,10h
mov dr2,eax
add eax,10h
mov dr3,eax
mov Loadall_Tbl._DR7,111102aah
LOADALL
@Test_DRBW:
rep stosb
Lidt fword ptr RM_IDT3_Ptr
mov ebx,cr0
and bl,not 1
mov cr0,ebx
FARJMP <@F>,<seg _text>
@@: and si,0fh
cmp si,0fh ; pass all breakpoints?
je short @T7P ; yep
Print_String failed
jmp @Test9
@T7P: cmp dx,4 ; INT01 4 times?
jne @B
Print_String passed
@Test9:
jmp Loadall_ret
LOADALL_386 endp
;---------------------------------------------------------------
; Minimal exception 13 handler that points past a 4-byte opcode,
; and sets the lowest bit in DI before returning.
;---------------------------------------------------------------
INT13 label word
push bp
mov bp,sp
add word ptr [bp][4],4
or si,1
pop bp
add sp,2
iret
;---------------------------------------------------------------
; Include all the protected mode functions:
; INT01: Breakpoint interrupt for debug register
; breakpoints.
; SAVE_STATE: Save machine state before LOADALL
; RESTORE_STATE: Restore machine state after LOADALL
;---------------------------------------------------------------
Include LOADFNS.386
;---------------------------------------------------------------
; Include the CPU_TYPE procedure & LOADALL test
;---------------------------------------------------------------
Include CPU_TYPE.ASM
_text ends
stack segment para stack 'stack'
db 400h dup (0)
stack ends
end LOADALL_386
;---------------------------------------------------------------
; END LISTING 2
;---------------------------------------------------------------