Page 60,132 Title Descriptor Cache Anomalies ;----------------------------------------------------------------------------- ; ; DESC32.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 ; ;----------------------------------------------------------------------------- ; DESCRIPT.ASM -- Prove that anomalies exist in the real mode definition of ; the descriptor cache registers. Intel states that all of ; these registers get default, fixed values when loaded in ; real mode. This is not the case, and this source code ; will attempt to prove that. ; ; Unfortunately, most of these cases are impossible to prove ; under software control -- and can only be proven using an ; Intel ICE. ; ;----------------------------------------------------------------------------- ; ; +-----------------------------------------------+ ; | Descriptor Cache contents: Real-Mode | ; | | ; | Operand size(*)-------------------------+ | ; | Granularity(*)------------------------+ | | ; | Accessed----------------------------+ | | | ; | Read/Write------------------------+ | | | | ; | Expansion direction-------------+ | | | | | ; | Executable--------------------+ | | | | | | ; | Code/Data Segment?----------+ | | | | | | | ; | Privilege level----------++ | | | | | | | | ; | Present----------------+ || | | | | | | | | ; | | || | | | | | | | | ; | Base Addr Limit | || | | | | | | | | ; | ---+------------+---------+-+--+-+-+-+-+-+-+-+| ; | CS |16xSeg. Reg.| Honored |Y| H|Y|H|H|Y|Y|-|H|| ; | ---+------------+---------+-+--+-+-+-+-+-+-+-+| ; | SS |16xSeg. Reg.| Honored |H| H|Y|H|H|H|Y|-|-|| ; | ---+------------+---------+-+--+-+-+-+-+-+-+-+| ; | DS |16xSeg. Reg.| Honored |H| H|Y|H|H|H|Y|-|-|| ; | ---+------------+---------+-+--+-+-+-+-+-+-+-+| ; | ES |16xSeg. Reg.| Honored |H| H|Y|H|H|H|Y|-|-|| ; | ===+============+=========+=+==+=+=+=+=+=+=+=+| ; | FS |16xSeg. Reg.| Honored |H| H|Y|H|H|H|Y|-|-|| ; | ---+------------+---------+-+--+-+-+-+-+-+-+-+| ; | GS |16xSeg. Reg.| Honored |H| H|Y|H|H|H|Y|-|-|| ; | ---+------------+---------+-+--+-+-+-+-+-+-+-+| ; | | ; | Y=Yes (1) | ; | N=No (0) | ; | U=Up (0) | ; | B=Byte (0) (*)=386 and 486 only | ; | S=16-bit (0) (*)=386 and 486 only | ; | H=Honored. Whenever the segment gets loaded, | ; | the contents of this field don't | ; | change. | ; | -=Not applicable | ; | | ; +-----------------------------------------------+ ; ; ;----------------------------------------------------------------------------- ; Compiler directives ;----------------------------------------------------------------------------- .Radix 16 .model small .386P .xlist ;----------------------------------------------------------------------------- ; Public Declarations ;----------------------------------------------------------------------------- Public Test32a1, Trap13_32, @FC32 ;----------------------------------------------------------------------------- ; External Declarations ;----------------------------------------------------------------------------- Extrn Loadall_tbl: DWord Extrn SSEG_Ptr: DWord Extrn CSEG64: Abs Extrn CSEG32_Ptr: FWord Extrn DSEG64: Abs Extrn SSEG64: Abs Extrn CS_Size: Abs Extrn Test32_Done: Dword ;----------------------------------------------------------------------------- ; External segment definition ;----------------------------------------------------------------------------- ABS0 SEGMENT AT 0 org 0ch * 4 RMInt0C label dword org 0dh * 4 RMInt0D label dword ABS0 ENDS BIOSDATA SEGMENT AT 40h org 67h Shut_Restart label dword BIOSDATA ENDS Include Struc.inc ; Include structures Include Macros.inc ; Include macros Include equates.inc ; Include local equates & I/O ports .list .data _TEXT SEGMENT USE32 PUBLIC 'CODE' ASSUME CS:_TEXT ;----------------------------------------------------------------------------- ; 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. ;----------------------------------------------------------------------------- ; Code starts here ;----------------------------------------------------------------------------- Test_CSEG32_Attr proc near ;----------------------------------------------------------------------------- ; Input: AL = Access Attributes ; AH = Big Bit (bit6) 0=Small segment; 40=Big segment ; BL = Expectant bit pattern of result ; ECX = Upper segment limit ; ESI = Address to test within segment bounds ; EDI = Address to test beyond end of segment ; EXPANSION_BIAS = An initial offset to EIP used when testing the ; expansion direction bits. ; Output: AX = Test status. Each bit represents a single test. ; 1=Pass, 0=Fail. ; Registers Modified: Ha, are you kidding, this uses LOADALL! ;----------------------------------------------------------------------------- ; This test must be flexible enough to handle an expand-down code segment ; (if such a thing can be proven to exist), and to test whether the "PRESENT" ; bit has any effect in the CS segment. ; ; Testing expand-down code segments present an interesting problem, as a ; CALL, RET, or JMP instruction may ultimately clear this bit. Therefore, ; this test must be able to detect whether the bit has been changed by ; the instruction. ;----------------------------------------------------------------------------- ; The LOADALL data image is used in the following manner for this test: ; (Register names are used for reference only. All register names refer to ; the register which will get the value stored in the LOADALL image once ; LOADALL is executed.) ; EAX[b15..b08] (AH) = Expected results from each test iteration ; [b07..b00] (AL) = Cumulative test results from each test sub-section ; (Read/write within and outside segment bounds) ; EBX = Address to test within segment bounds ; EDX = Address to test beyond segment bounds ; ESI[b31..b16] = BIASed return address in case of processor ; shutdown. This address is used to continue the ; test as if an exception had occured. ; [b07..b00] = Cumulative test results from each test section ; (test a, b, c, etc.). ; EDI = Counter used for test iteration (test a, b, c, etc.). ;----------------------------------------------------------------------------- Go_Test32: mov edi,offset Loadall_tbl LOADALL ;----------------------------------------------------------------------------- ; Determine whether the expansion direction bit has been cleared. If it has, ; then we can detect this by comparing the current EIP to the CS BIAS used ; to offset EIP for expansion direction tests. If EIP is EVER less than the ; expansion direction BIAS, then the expansion direction bit has been cleared. ; If this happens, we need to guarantee that we mark the test as having ; failed. ;----------------------------------------------------------------------------- Test32a1: mov ecx,Expansion_BIAS ; check to see of ED got reset call @F @@: pop bx movzx ebx,bx cmp ebx,ecx ; ED get reset? ja short @F mov al,byte ptr Loadall_tbl._EAX[1] ; get expected results not al ; change them mov byte ptr Loadall_tbl._EAX,al ; force ERROR conditions jmp short Test32_Results ;----------------------------------------------------------------------------- ; For expand UP segments, these next two tests don't produce any errors for ; a normal data segment. But with LOADALL it is possible to individually ; set access attributes in a manner inconsistent with segment register ; loads, and contrary to Intel documentation. The next two tests read within ; segment bounds and produce the following results (as per access attributes): ; ; EXE ED W Read Write ; 0 0 0 = YES NO ; 0 1 0 = NO NO ; 0 1 1 = NO NO ; 1 0 0 = NO NO ; 1 0 1 = YES NO ; 1 1 0 = NO NO ; 1 1 1 = YES NO ;----------------------------------------------------------------------------- @@: mov ebx,Loadall_tbl._EBX ; address to read from mov esi,offset @F ; return address for ISR add esi,word ptr Expansion_BIAS ; add offset for expansion dir. mov word ptr Loadall_tbl._ESI[2],si mov cx,cs:[ebx] ; @@: rcl byte ptr Loadall_tbl._EAX,1 ; accumulate error status mov esi,offset @F ; return address for ISR add esi,word ptr Expansion_BIAS ; add offset for expansion dir. mov word ptr Loadall_tbl._ESI[2],si mov cs:[ebx],cx ; try to generate GP from DS @@: rcl byte ptr Loadall_tbl._EAX,1 ; accumulate error status ;----------------------------------------------------------------------------- ; For expand UP segments, these next two tests should both produce errors for ; a normal data segment. The next two tests read outside the segment bounds ; and produce the following results (as per access attributes): ; ; EXE ED W Read Write ; 0 0 0 = NO NO ; 0 1 0 = YES NO ; 0 1 1 = YES YES ; 1 0 0 = NO NO ; 1 0 1 = NO NO ; 1 1 0 = NO NO ; 1 1 1 = NO NO ;----------------------------------------------------------------------------- Test2a2: mov Loadall_tbl._EIP,offset Test32a1 mov ebx,Loadall_tbl._EDX ; address to read from mov esi,offset @F ; return address for ISR add esi,word ptr Expansion_BIAS ; add offset for expansion dir. mov word ptr Loadall_tbl._ESI[2],si mov cx,cs:[ebx] ; try to generate GP from DS @@: rcl byte ptr Loadall_tbl._EAX,1 ; accumulate error status mov esi,offset @F ; return address for ISR add esi,word ptr Expansion_BIAS ; add offset for expansion dir. mov word ptr Loadall_tbl._ESI[2],si mov cs:[ebx],cx ; try to generate GP from FS Test32a2_Done: @@: rcl byte ptr Loadall_tbl._EAX,1 ; accumulate error status ;----------------------------------------------------------------------------- ; Check for intermediary status. If the results are correct, then set ; a bit in the LOADALL (ESI) data image. ;----------------------------------------------------------------------------- Test32_Results: mov al,byte ptr Loadall_tbl._EAX ; get current ERROR status cmp al,byte ptr Loadall_tbl._EAX[1] ; expected results? stc ; set results flag je short @F clc ; set result=fail flag @@: rcl byte ptr Loadall_tbl._ESI,1 dec Loadall_tbl._EDI ; check for current iteration jz Test32e_Done cmp Loadall_tbl._EDI,4 ; use CALL LABEL ? je @Test32c_CALLF_LBL ; yes cmp Loadall_tbl._EDI,3 ; use CALL MEM48 ? ; je Test32e_Done je @Test32d_CALLF_M48 cmp Loadall_tbl._EDI,2 ; use RET FAR ? je @Test32e_RETF ; yes ;----------------------------------------------------------------------------- ; For each of these tests, we need to determine if the last test passed. If ; it did NOT, then we need to reload the segment access attributes with ; LOADALL. ;----------------------------------------------------------------------------- ; USE JMP FAR LABEL32 to reload CS access attributes. ;----------------------------------------------------------------------------- test byte ptr Loadall_tbl._ESI,1 ; did last test pass? jnz short Test32b mov Loadall_tbl._EIP,offset Test32b mov eax,Expansion_BIAS ; get BIAS for expans. dir. add Loadall_tbl._EIP,eax ; add to expected EIP jmp Go_Test32 Test32b: mov byte ptr Loadall_tbl._EAX,0 ; clear error flag farjmp32 Test32a1, ;----------------------------------------------------------------------------- ; Use CALL FAR LABEL48 to reload CS access attributes. ;----------------------------------------------------------------------------- @Test32c_CALLF_LBL: test byte ptr Loadall_tbl._ESI,1 ; did last test pass? jnz short Test32c mov Loadall_tbl._EIP,offset Test32c mov eax,Expansion_BIAS ; get BIAS for expans. dir. add Loadall_tbl._EIP,eax ; add to expected EIP jmp Go_Test32 Test32c: mov byte ptr Loadall_tbl._EAX,0 ; clear error flag cmp Loadall_tbl.CS_Cache._Type,94h ; expansion direction? je short @FC32 ; yes cmp Loadall_tbl.CS_Cache._Type,96h ; expansion direction? je short @FC32 ; yes Farcall32 , @FC32: Farcall32 , ;----------------------------------------------------------------------------- ; Use CALL FAR MEM48 to reload CS access attributes. ;----------------------------------------------------------------------------- @Test32d_CALLF_M48: test byte ptr Loadall_tbl._ESI,1 ; did last test pass? jnz short Test32d mov Loadall_tbl._EIP,offset Test32c mov eax,Expansion_BIAS ; get BIAS for expans. dir. add Loadall_tbl._EIP,eax ; add to expected EIP jmp Go_Test32 Test32d: mov byte ptr Loadall_tbl._EAX,0 ; clear error flag mov eax,offset TEst32a1 mov dword ptr CSEG32_Ptr,eax mov eax,Expansion_BIAS add dword ptr CSEG32_Ptr,eax mov dword ptr CSEG32_Ptr[4],cs call CSEG32_Ptr ;----------------------------------------------------------------------------- ; Use RET FAR to reload CS access attributes. ;----------------------------------------------------------------------------- @Test32e_RETF: test byte ptr Loadall_tbl._ESI,1 ; did last test pass? jnz short Test32e mov Loadall_tbl._EIP,offset Test32e mov eax,Expansion_BIAS ; get BIAS for expans. dir. add Loadall_tbl._EIP,eax ; add to expected EIP jmp Go_Test32 Test32e: mov byte ptr Loadall_tbl._EAX,0 ; clear error flag push seg _text mov eax,offset Test32a1 ; destination EIP add eax,word ptr Expansion_BIAS ; add BIAS for exp. dir. tests push eax ; save destination EIP on stk retf ;----------------------------------------------------------------------------- ; If the processor is shutdown either by accident or intentionally (as when ; testing the PRESENT bit), execution resumes here, and will then re-continue ; at the destination address contained in the LOADALL (ESI) data image. ;----------------------------------------------------------------------------- Test32_Shut1: lss esp,fword ptr cs:SSEG_PTr mov dx,seg _Data ; Reload ALL segment registers after mov ds,dx ; RESET. mov es,dx mov fs,dx mov gs,dx mov ebp,Loadall_tbl._EBP ; get original EBP or Loadall_tbl._Eflags,1 ; set carry flag movzx eax,word ptr Loadall_tbl._ESI[2] ; get return address mov Loadall_tbl._EIP,eax ; set new destination address cmp Loadall_tbl.CS_Cache._Type,13h ; testing present bit? je short @F ; nope jmp Go_Test32 @@: mov Loadall_tbl.CS_Cache._Type,93h jmp Go_Test32 ;----------------------------------------------------------------------------- ; Done with CS test. Reload segment registers with real-mode compatible ; values. ;----------------------------------------------------------------------------- Test32e_Done: Enter_PM farjmp32 Test32_Done,CSEG64 Test_CSEG32_Attr endp ;----------------------------------------------------------------------------- Trap13_32: ; INT 0D (GP) fault trap ;----------------------------------------------------------------------------- ; Input: SI = Pointer to return address ; Output: CY ; Register(s) modified: EFLAGS ;----------------------------------------------------------------------------- enter 0,0 ; get pointer to CS:IP mov [bp][4],si leave stc db 66h retf 2 _TEXT ENDS END