;----------------------------------------------------------------------------- ; ; TESTSSEG.INC ; ; 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 ; ;----------------------------------------------------------------------------- ;----------------------------------------------------------------------------- Test_SSEG_Attr proc near ;----------------------------------------------------------------------------- ; This subroutine is very similar to Test_DSEG_Attr with one major ; exception. This routine expects that somewhere within its ; execution, the CPU will get RESET. If, for example, we are able to ; put SS into a Read-Only segment, then writing to that segment will ; ultimately RESET the CPU -- as writing to SS will cause exception-12, ; which will in turn cause a DOUBLE FAULT (caused by writing the return ; address from exception-12), which will then cause a triple fault ; SHUTDOWN cycle. ;----------------------------------------------------------------------------- ; Input: AL = Access Attributes ; AH = Big Bit (bit6) 0=Small segment; 40=Big segment ; BL = Expectant bit pattern of result ; ECX = Upper segment limit ; EDX = Value for ESP within tests ; ESI = Address to test within segment bounds ; EDI = Address to test beyond end of segment ; 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 is rather complicated to perform because of the implications ; of making the stack read-only or otherwise inaccessible. If the SS ; access attributes are honored in real mode, then any exception caused ; by writing to the segment will ultimately cause a triple-fault ; (RESET CPU). So this test must be rather creative in handling this ; error. ; ; Here's how it works: ; * Assuming writing to SS will cause RESET, we set CMOS & BIOS DATA ; segment ready to accept a CPU RESET. ; * Use LOADALL to set access rights as R/O ; * Change SS via MOV MEM16, MOV REG16, or LSS instructions. ; * Read from SS to verify no exception ; * Attempt writing to SS ; - If no exception, then we fall through, clear carry, and JuMP ; around code used upon RESET recovery ; - If an exception occured, then the CPU will RESET, and recover ; * All status and counters must not use the standard ones' since those ; counters are stack based. Better is to use the LOADALL data image ; since those values are relative to DS, which is a R/W segment. ;----------------------------------------------------------------------------- ; 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] = 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.). ;----------------------------------------------------------------------------- mov dword ptr SSEG_Ptr,esp ; save ESP mov Loadall_tbl.SS_Cache._Type,al mov Loadall_tbl.SS_Cache._CS32,ah mov Loadall_tbl.SS_Cache._Limit,ecx mov Loadall_tbl.FS_Cache._Addr,400h mov Loadall_tbl._FS,40h xor eax,eax ; clear accumulator for results mov ah,bl ; save expected results in AH mov Loadall_tbl._EAX,eax ; save expected results in AH mov Loadall_tbl._EBX,esi ; address to read within segment bounds mov Loadall_tbl._EDX,edi ; address to read outside segment bounds mov Loadall_tbl._EDI,4 ; test iteration counter mov Loadall_tbl._ESP,edx mov Loadall_tbl._EIP,offset Test19a1 call Enable_Gate20 ; enable A20 to CPU bus Go_Test19: mov edi,offset Loadall_tbl LOADALL assume FS:BIOSDATA ;----------------------------------------------------------------------------- ; The first attempt at testing SS as R/O is done via LOADALL. Since all ; subsequent tests are done after changing SS via MOV or LSS instructions, ; we must NOT come back to this same point every time. Therefore, we will ; change EIP in the LOADALL image to have a different destination upon all ; subsequent tests. ;----------------------------------------------------------------------------- Test19a1: CMOS_Write2 CMOS_Shutdown,User_Shutdown ;----------------------------------------------------------------------------- ; 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 word ptr Shut_Restart,offset Test19_Shut1 mov word ptr Shut_Restart[2],cs mov ebx,Loadall_tbl._EBX ; address to read from mov si,offset @F ; return address for ISR mov word ptr Loadall_tbl._ESI[2],si mov cx,ss:[ebx] ; @@: rcl byte ptr Loadall_tbl._EAX,1 ; accumulate error status CMOS_Write2 CMOS_Shutdown,User_Shutdown mov si,offset @F ; return address for ISR mov word ptr Loadall_tbl._ESI[2],si mov ss:[ebx],cx ; try to generate GP from DS @@: rcl byte ptr Loadall_tbl._EAX,1 ; accumulate error status CMOS_Write2 CMOS_Shutdown,User_Shutdown ;----------------------------------------------------------------------------- ; 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 ;----------------------------------------------------------------------------- Test19a2: mov Loadall_tbl._EIP,offset Test19a1 mov ebx,Loadall_tbl._EDX ; address to read from mov si,offset @F ; return address for ISR mov word ptr Loadall_tbl._ESI[2],si mov cx,ss:[ebx] ; try to generate GP from DS @@: rcl byte ptr Loadall_tbl._EAX,1 ; accumulate error status CMOS_Write2 CMOS_Shutdown,User_Shutdown mov si,offset @F ; return address for ISR mov word ptr Loadall_tbl._ESI[2],si mov ss:[ebx],cx ; try to generate GP from FS @@: rcl byte ptr Loadall_tbl._EAX,1 ; accumulate error status CMOS_Write2 CMOS_Shutdown,User_Shutdown ;----------------------------------------------------------------------------- ; Check for intermediary status. If the results are correct, then set ; a bit in the test_status variable. ;----------------------------------------------------------------------------- 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 Test19d_Done cmp Loadall_tbl._EDI,3 ; use MOV SEG,MEM16? je @Test19c_mem16 ; yes cmp Loadall_tbl._EDI,2 ; use MOV SEG,REG16? je @Test19d_reg16 ; yes ;----------------------------------------------------------------------------- ; Use L{SEGREG} REG to load segment. If the test_status didn't pass the last ; test, then our access attributes are hosed. If this is the case, we need to ; reload those attributes. The only way sure-fire way to do that is to ; use LOADALL. ;----------------------------------------------------------------------------- test byte ptr Loadall_tbl._ESI,1 ; did last test pass? jnz short Test19b mov Loadall_tbl._EIP,offset Test19b jmp Go_Test19 Test19b: lss esp,fword ptr SSEG_Ptr ; use LSEG form of segment load mov esp,Loadall_tbl._ESP ; restore ESP from loadall image mov byte ptr Loadall_tbl._EAX,0 ; clear error flag jmp Test19a1 ; go redo test ;----------------------------------------------------------------------------- ; Use MOV SEG,MEM16. However, if the test_status didn't pass the last test, ; then our access attributes are hosed. If this is the case, we need to ; reload those attributes. The only way sure-fire way to do that is to ; use LOADALL. ;----------------------------------------------------------------------------- @Test19c_mem16: test byte ptr Loadall_tbl._ESI,1 ; did last test pass? jnz short Test19c mov Loadall_tbl._EIP,offset Test19c jmp Go_Test19 Test19c:mov ss,SSEG mov byte ptr Loadall_tbl._EAX,0 ; clear error flag jmp Test19a1 ; go redo test ;----------------------------------------------------------------------------- ; Use MOV SEG,REG16. However, if the test_status didn't pass the last test, ; then our access attributes are hosed. If this is the case, we need to ; reload those attributes. The only way sure-fire way to do that is to ; use LOADALL. ;----------------------------------------------------------------------------- @Test19d_reg16: test byte ptr Loadall_tbl._ESI,1 ; did last test pass? jnz short Test19d mov Loadall_tbl._EIP,offset Test19d jmp Go_Test19 Test19d:mov dx,seg stack mov ss,dx mov byte ptr Loadall_tbl._EAX,0 ; clear error flag jmp Test19a1 ; go redo test Test19_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 jmp Go_Test19 ;----------------------------------------------------------------------------- ; Reload segment registers with real-mode compatible values. ;----------------------------------------------------------------------------- Test19d_Done: Enter_PM mov dx,SS_64k-Gdt_386 mov ss,dx Exit_PM lss esp,fword ptr SSEG_Ptr ; restore stack segment ;----------------------------------------------------------------------------- ; Restore ;----------------------------------------------------------------------------- call Shut_A20 ; disable A20 from CPU bus CMOS_Write2 CMOS_Shutdown,0 ; Enable NMI mov al,byte ptr Loadall_tbl._ESI ; get test results mov Loadall_tbl._EAX,0 ; clear registers used in this test mov Loadall_tbl._EBX,0 ; clear registers used in this test mov Loadall_tbl._EDX,0 ; clear registers used in this test mov Loadall_tbl._ESI,0 ; clear registers used in this test mov Loadall_tbl._EDI,0 ; clear registers used in this test mov Loadall_tbl._EFlags,2 ; clear flags used in this test mov Loadall_tbl.SS_Cache._Type,93h mov Loadall_tbl.SS_Cache._CS32,0 mov Loadall_tbl.SS_Cache._Limit,0ffffh ret Test_SSEG_Attr endp