Page 60,132 ;----------------------------------------------------------------------------- ; ; INT09.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 a particular bug in the Intel '386 and '486 ; processors. When the numeric coprocessor has a segment size limit ; error, sometimes the CPU will fail to generate exception 9 (or ; exception 13 on the '486). The following are the conditions necessary ; to demonstrate the bug: ; ; * 64k data segment ; * 16-bit address operands ; * Store an 8-byte value from the NPX to memory such that the first ; dword is within the segment limit, and the second dword is outside ; the segment limit. ; ; mov edi,0FFFCh ; memory offset meeting the above conditions ; fstp [di] ; does NOT generate exception 9 ; fstp [0FFFCh] ; does NOT generate exception 9 ; fstp [edi] ; DOES generate exception 9 ; fstp [0000FFFCh] ; DOES generate exception 9 ; ;----------------------------------------------------------------------------- ; Input: None ; Output: ERROR codes to DOS interceptable by IF ERRORLEVEL commands ; [b0] = Failed test 8 ; [b1] = Failed test 7 ; [b2] = Failed test 6 ; [b3] = Failed test 5 ; [b4] = Failed test 4 ; [b5] = Failed test 3 ; [b6] = Failed test 2 ; [b7] = Failed test 1 ; Since the ERRORLEVEL is bit-mapped, the batch file must use ; 256 IF ERRORLEVEL statements to accurately decode all possible ; combinations. ; ;----------------------------------------------------------------------------- ; Compiler directives ;----------------------------------------------------------------------------- Title INT09 .radix 16 .8086 ;----------------------------------------------------------------------------- ; Structure definitions ;----------------------------------------------------------------------------- Desc_cache STRUC db 0 _Type db ? _CS32 db 0 db 0 _Addr dd ? _Limit dd ? Desc_cache 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. ; 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] ;----------------------------------------------------------------------------- ; Global Descriptor Table ;----------------------------------------------------------------------------- GDT_386 Descriptor CSEG3 Descriptor <0ffffh,0,0,CS_access> CSEG32 Descriptor <0ffffh,0,0,CS_access,40h> DSEG3 Descriptor <0ffffh,0,0,DS_access> DSEG4k Descriptor <00fffh,0,0,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 ;----------------------------------------------------------------------------- RM_IDT3_Ptr dw (256d*4)-1 ; Real-mode IDT dd 0 ; pointer dw 0 IDT3_Ptr dw IDT3_Len-1 dd 0 dw 0 NPX_CTRLWD dw 0 ; NPX ConTRoL WorD ;----------------------------------------------------------------------------- ; String Messages ;----------------------------------------------------------------------------- YP db " YES (PASSED)",CRLF$ YF db "--> YES (FAILED) <--",CRLF$ NP db " NO (PASSED)",CRLF$ NF db "--> NO (FAILED) <--",CRLF$ Int09_Test db "NPX Exception BUG (does bug exist):",CRLF$ Test_1 db "Test 1: CS=USE32, 32-bit REG operand cause INT09? ",24 Test_2 db "Test 2: CS=USE32, 16-bit REG operand cause INT09? ",24 Test_3 db "Test 3: CS=USE32, 32-bit IMMED operand cause INT09? ",24 Test_4 db "Test 4: CS=USE32, 16-bit IMMED operand cause INT09? ",24 Test_5 db "Test 5: CS=USE16, 32-bit REG operand cause INT09? ",24 Test_6 db "Test 6: CS=USE16, 16-bit REG operand cause INT09? ",24 Test_7 db "Test 7: CS=USE16, 32-bit IMMED operand cause INT09? ",24 Test_8 db "Test 8: CS=USE16, 16-bit IMMED operand cause INT09? ",24 _DATA ENDS _TEXT SEGMENT PARA USE16 PUBLIC 'CODE' ASSUME CS:_TEXT, ds:_DATA, ES:_DATA, SS:STACK .386P .387 ;----------------------------------------------------------------------------- Test_INT09 proc far ;----------------------------------------------------------------------------- MOV AX,_Data ; set up data segment MOV DS,AX MOV ES,AX ;----------------------------------------------------------------------------- ; Initialize descriptors, print message ;----------------------------------------------------------------------------- Init_descriptor ,,Gdt_386 Init_descriptor ,,Idt3_Ptr Init_descriptor ds,0,DSEG4k Init_descriptor ds,0,DSEG3 Init_descriptor cs,0,CSEG3 Init_descriptor cs,0,CSEG32 Print_String Int09_Test ;----------------------------------------------------------------------------- ; Load GDT, IDT, and enter protected mode ;----------------------------------------------------------------------------- cli ; disable interrupts lgdt fword ptr Gdt_386 lidt fword ptr Idt3_Ptr mov cx,1 mov si,0 mov edi,0fffch Fstcw npx_ctrlwd fldz mov eax,cr0 ; get 386 control register bts eax,00h ; enable protected mode mov cr0,eax ; now we're in protected mode Farjmp <@Test1>, ;----------------------------------------------------------------------------- ; Test1-Test4. Try and generate the bug under the following circumstances: ; FSTP [EDI] ; FSTP [DI] ; FSTP [0000FFFCh] ; FSTP [0FFFCh] ;----------------------------------------------------------------------------- ; Since the following code was written using Microsoft Macro Assembler, ; (5.10A, or Beta-6.0), mixing USE32, and USE16 segments isn't possible ; (though it should be). Therefore, it is necessary to write code in ; USE16 mode, that performs the desired instructions when executed in ; USE32 mode. So the following code may look a little wierd for that ; reason. ;----------------------------------------------------------------------------- ; Each test algorithm is as follows: ; * AX=Size of the op code. The INT09 handler is general, in that it doesn't ; decode the size of the op code (no I've never written an instruction ; decoder). ; * Attempt to generate the INT09. ;----------------------------------------------------------------------------- ; Test1: FSTP [EDI] ;----------------------------------------------------------------------------- @Test1: db 66h ; USE32 crud mov ax,2 db 0ddh,1fh ; FSTP [EDI] shl si,1 ;----------------------------------------------------------------------------- ; Test2: FSTP [DI] ;----------------------------------------------------------------------------- db 66h ; USE32 crud mov ax,3 db 67h,0ddh,1dh ; FSTP [DI] shl si,1 ;----------------------------------------------------------------------------- ; Test3: FSTP [0000FFFC] ;----------------------------------------------------------------------------- db 66h ; USE32 crud mov ax,6 db 0ddh,1dh ; FSTP [0000FFFC] dd 0FFFCh shl si,1 ;----------------------------------------------------------------------------- ; Test4: FSTP [FFFC] ;----------------------------------------------------------------------------- db 66h ; USE32 crud mov ax,5 db 67h,0ddh,1eh ; FSTP [FFFC] dw 0FFFCh shl si,1 ;----------------------------------------------------------------------------- ; Get out of this USE32 code segment, and try the same tests in USE16. ;----------------------------------------------------------------------------- LONGJMP <@F>, ;----------------------------------------------------------------------------- ; Test5: FSTP [EDI] ;----------------------------------------------------------------------------- @@: mov ax,3 fstp qword ptr ds:[edi] shl esi,1 ;----------------------------------------------------------------------------- ; Test6: FSTP [DI] ;----------------------------------------------------------------------------- mov ax,2 fstp qword ptr ds:[di] shl esi,1 ;----------------------------------------------------------------------------- ; Test7: FSTP [0000FFFC] ;----------------------------------------------------------------------------- mov ax,7 db 67h,0ddh,1dh ; FSTP [0000FFFC] dd 0FFFCh shl esi,1 ;----------------------------------------------------------------------------- ; Test8: FSTP [FFFC] ;----------------------------------------------------------------------------- mov ax,4 fstp qword ptr ds:[0FFFCh] ;----------------------------------------------------------------------------- ; Restore the state of the NPX, and exit protected mode ;----------------------------------------------------------------------------- Fldcw npx_ctrlwd mov eax,cr0 btr eax,0 ; disable PE bit mov cr0,eax ; now in real-mode Farjmp <@F>, ;----------------------------------------------------------------------------- ; Reload IDTR to point to a real-mode compatible interrupt table ;----------------------------------------------------------------------------- @@: Lidt fword ptr RM_IDT3_Ptr ;----------------------------------------------------------------------------- ; Print the results of each test. ;----------------------------------------------------------------------------- Print_String Test_1 ; INT09 should get generated test si,80h ; did it get generated? jnz @T1P ; yes (good) Print_String NF jmp short @F @T1P: Print_String YP @@: Print_String Test_2 ; INT09 shouldn't get generated test si,40h ; did int09 get generated? jz @T2P ; NO (good) Print_String YF jmp short @F @T2P: Print_String NP @@: Print_String Test_3 ; INT09 should get generated test si,20h ; did it get generated? jnz @T3P ; yes (good) Print_String NF jmp short @F @T3P: Print_String YP @@: Print_String Test_4 ; INT09 shouldn't get generated test si,10h ; did int09 get generated? jz @T4P ; NO (good) Print_String YF jmp short @F @T4P: Print_String NP @@: Print_String Test_5 ; INT09 should get generated test si,08h ; did it get generated? jnz @T5P ; yes (good) Print_String NF jmp short @F @T5P: Print_String YP @@: Print_String Test_6 ; INT09 shouldn't get generated test si,04h ; did int09 get generated? jz @T6P ; NO (good) Print_String YF jmp short @F @T6P: Print_String NP @@: Print_String Test_7 ; INT09 should get generated test si,02h ; did it get generated? jnz @T7P ; yes (good) Print_String NF jmp short @F @T7P: Print_String YP @@: Print_String Test_8 ; INT09 shouldn't get generated test si,01h ; did int09 get generated? jz @T8P ; NO (good) Print_String YF jmp short @F @T8P: Print_String NP @@: lea ax,[si][4c00h] ; set DOS ERRORLEVEL code xor al,0aah ; reverse polarity of tests intended ; to fail int 21h ; return to DOS with ERROR CODE Test_INT09 endp ;----------------------------------------------------------------------------- ; Minimal exception 09 handler that points past a n-byte opcode, ; and sets the lowest bit in SI before returning. ;----------------------------------------------------------------------------- ; Input: AX = Size of op code ; Output: SI[b0] = 1 ; Register(s) modified: None ;----------------------------------------------------------------------------- INT09 label word push bp mov bp,sp add word ptr [bp][4],ax or si,1 pop bp add sp,2 iret _text ends stack segment para stack 'stack' stack_space db 400h dup (0) stack ends end Test_INT09