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 INT6 equ [bp-4] ;--------------------------------------------------------------- ; Loadall table(s) ;--------------------------------------------------------------- Loadall_tbl Loadall_struc <> Machine_State Loadall_struc <> ;--------------------------------------------------------------- ; Global Descriptor Table ;--------------------------------------------------------------- GDT_386 Descriptor 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 ; INT00 INT_Desc ; INT01 INT_Desc ; INT02 INT_Desc ; INT03 INT_Desc ; INT04 INT_Desc ; INT05 INT_Desc ; INT06 INT_Desc ; INT07 INT_Desc ; INT08 INT_Desc ; INT09 INT_Desc ; INT0a INT_Desc ; INT0b INT_Desc ; INT0c INT_Desc ; 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 ,,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 , ; Continue ;--------------------------------------------------------------- ; Loadall failed the REGISTERs test. ;--------------------------------------------------------------- @@: mov ax,_Data mov ds,ax mov es,ax lss esp,fword ptr cs:stack_ptr FARJMP <@F>, @@: 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>, @@: 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>, ; Construct 32-bit far ; JMP @No_PM32bitCS: ; FAR JUMP if stuck in FARJMP <@F>, ; 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>, ; 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>,; 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>, @@: 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>, @@: 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 ;---------------------------------------------------------------