page 03Ch,084h comment ~ ****************************************************************************** PVI.ASM Copyright (c) 1995 Maciej W. Rozycki 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. ****************************************************************************** Maciej W. Rozycki ul. A. Grzymaly-Siedleckiego 23/34 85-868 Bydgoszcz POLAND e-mail: prr09rm@loqi.elka.pg.gda.pl ****************************************************************************** You may define some symbols in assembler's command line, customizing the generated code. Use the following syntax: name /D[=val] PVI.ASM where: - "name" is the name of your assembler (MASM and TASM are supported, - "/D" is a switch specifier, - "" is a required symbol name, - "val" is an optional symbol value. The following symbols are recognized and determine the final code that will be generated: SetPVI -- If defined, it will enable the use of PVI setting the PVI bit in the CR4 register; otherwise an i386-compatible execution will occur. Note that the PVI bit of the CR4 register will be reset if the PVI feature is supported by the CPU. -- The value, if given, is ignored. SetTF -- If defined, it will force the TF bit in the EFLAGS register to be set when entering the test task; otherwise it will be reset. -- The value, if given, is ignored. SetIF -- If defined, it will force the IF bit in the EFLAGS register to be set when entering the test task; otherwise it will be reset. -- The value, if given, is ignored. SetVIF -- If defined, it will force the VIF bit in the EFLAGS register to be set when entering the test task; otherwise it will be reset. -- The value, if given, is ignored. SetVIP -- If defined, it will force the VIP bit in the EFLAGS register to be set when entering the test task; otherwise it will be reset. -- The value, if given, is ignored. -- Note that the #GP serving routine will reset the VIP flag if such an exception is generated and the VIF flag is set. IOPL -- If defined, it will override the default test task's IOPL value; otherwise IOPL will be set to 00b. -- Two least significant bits of the value will be copied into the IOPL field of task's EFLAGS register. CPL -- If defined, it will override the default test task's CPL value; otherwise CPL will be set to 11b. This value will affect all test task's DPL and RPL values. -- Two least significant bits of the value will be copied into the appropriate DPL and RPL fields. SetTest -- If defined, it will override the default test task's operation; otherwise no operation will be tested. The possible values of SetTest: SetNop -- no operation, SetCli -- CLI, SetSti -- STI, SetPushfd -- PUSHFD, SetPopfd -- POPFD, SetIretd -- IRETD. FlagVal -- If defined, it will override the default value that is popped to EFLAGS during the IRETD and POPFD tests (see above); otherwise a value of 0 is popped. -- The value is ignored for all tests but the POPFD one. Note: the code is intentionally constructed in such a way that no matter which conditionals are defined, the executable consists of the same opcodes which may only receive different operands. ****************************************************************************** ~ ;***************************************************************************** ; the following section will allow us assemble the code both with MASM ; and with TASM ifdef ??version ; this symbol is defined for TASM TASM equ true ; use TASM notation else MASM equ true ; use MASM notation endif ;***************************************************************************** .xall ; do not list non-code parts of macros .sfcond ; hide false conditionals ifdef TASM locals ; let us use local symbols jumps ; allow long conditional branches ; emulation for the 8086 mode endif ;***************************************************************************** ; conditional expressions ;***************************************************************************** .386p ; use the 386 syntax in declarations ; check if the PVI mechanism has to be used; this will also prevent this ; program from running on an i386 and other processors that do not support ; PVI (default = do not use PVI) ifdef SetPVI UsePVI equ 1 ; set PVI on else UsePVI equ 0 ; do not set PVI endif ;----------------------------------------------------------------------------- ; define the initial value of the test task's TF (default = 0) ifdef SetTF UseTF equ Ftf ; set TF UseTaskTrap equ 1 ; set the T field of TSS else UseTF equ 0 ; reset TF UseTaskTrap equ 0 endif ;----------------------------------------------------------------------------- ; define the initial value of the test task's IF (default = 0) ifdef SetIF UseIF equ Fif ; set IF else UseIF equ 0 ; reset IF endif ;----------------------------------------------------------------------------- ; define the initial value of the test task's VIF (default = 0) ifdef SetVIF UseVIF equ Fvif ; set VIF else UseVIF equ 0 ; reset VIF endif ;----------------------------------------------------------------------------- ; define the initial value of the test task's VIP (default = 0) ifdef SetVIP UseVIP equ Fvip ; set VIP else UseVIP equ 0 ; reset VIP endif ;----------------------------------------------------------------------------- ; define the value of the test task's IOPL (default = 0) ifdef IOPL UseIOPL equ (IOPL and 11b) shl 00ch ; set requested IOPL else UseIOPL equ 0 ; set default IOPL endif ;----------------------------------------------------------------------------- ; define the value of the test task's CPL; this will affect all task's ; descriptors and selectors (default = 3) ifdef CPL UseCPL equ CPL and 11b ; set requested CPL else UseCPL equ 11b ; set default CPL endif ;----------------------------------------------------------------------------- ; predefine some symbols SetNop equ 0 SetCli equ 1 SetSti equ 2 SetPushfd equ 3 SetPopfd equ 4 SetIretd equ 5 ifdef SetTest SetTestTemp = SetTest if SetTestTemp le SetIretd ; just in case of incorrect value UseTest equ SetTestTemp ; set the requested test else UseTest equ 0 ; set no operation endif else UseTest equ 0 ; set no operation endif ;----------------------------------------------------------------------------- ; define the value used by IRETD and POPFD ifdef FlagVal UseFlagVal equ (FlagVal or Fuf or Fnt) and not Fvm ; set the requested value (but ; prevent entering the virtual mode ; and mark the task as nested) else UseFlagVal equ Fuf or Fnt ; set NT to allow exiting from the ; test task upon IRETD execution endif ;***************************************************************************** ; helpful declarations ;***************************************************************************** .386p ; use the 386 syntax in declarations .xlist ; disable listing for declarations ;============================================================================= ; constants ;============================================================================= LevelXStackLth equ 080h ; user-level stack length (in dwords) Level0StackLth equ 080h ; system-level stack length ;----------------------------------------------------------------------------- argsiz equ db 066h ; an argument size override prefix adrsiz equ db 067h ; an address size override prefix ;----------------------------------------------------------------------------- MSDOSCall equ 021h ; MS-DOS call ;----------------------------------------------------------------------------- LF equ 00ah ; Line Feed control code CR equ 00dh ; Carriage Return control code EOF equ CR,LF,'$' ; a terminate string ;----------------------------------------------------------------------------- ; descriptor table indicators for use when defining segments GDTR equ 0 LDTR equ 1 IDTR equ 0 ;----------------------------------------------------------------------------- ; these are the fields of descriptor attributes ; use one name from each group to describe attrs (OR them) ; group 1 ByteGr equ 0000h PageGr equ 0800h ;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ; group 2 Word16 equ 0000h Word32 equ 0400h ;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ; group 3 Absent equ 0000h Presnt equ 0080h ;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ; group 4 DPL0 equ 0000h DPL1 equ 0020h DPL2 equ 0040h DPL3 equ 0060h ;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ; group 5 System equ 0000h Memory equ 0010h ;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ; group 6 ; use these values defining a descriptor of a memory segment DataRO equ 0000h DataRW equ 0002h DatDRO equ 0004h DatDRW equ 0006h CodeEO equ 0008h CodeER equ 000ah CodCEO equ 000ch CodCER equ 000eh ; NotAcc equ 0000h Accssd equ 0001h ; end of memory segment constants ; use these values defining a descriptor of a system segment or a gate Use286 equ 0000h Use386 equ 0008h ; Undef0 equ 0000h AvlTSS equ 0001h LDTabl equ 0002h BusTSS equ 0003h CallGt equ 0004h TaskGt equ 0005h IntrGt equ 0006h TrapGt equ 0007h ; end of system segment constants ;----------------------------------------------------------------------------- ; flags names - you may use them in TSSes or testing an (E)FLAGS image Fcf equ 001H ; carry flag Fuf equ 002H ; always set this flag in TSSes Fpf equ 004H ; parity flag Faf equ 010H ; auxilary carry flag Fzf equ 040H ; zero flag Fsf equ 080H ; sign flag Ftf equ 00100H ; trap flag Fif equ 00200H ; interrupt flag Fdf equ 00400H ; direction flag Fof equ 00800H ; overflow flag Fiopl1 equ 01000H ; iopl = 1 Fiopl2 equ 02000H ; iopl = 2 Fiopl3 equ 03000H ; iopl = 3 Fnt equ 04000H ; nested task Frf equ 000010000H ; resume flag Fvm equ 000020000H ; virtual mode Fac equ 000040000H ; alignment flag Fvif equ 000080000H ; virtual interrupts flag Fvip equ 000100000H ; virtual interrupt pending Fid equ 000200000H ; identification ;----------------------------------------------------------------------------- MasterPICIMR equ 021h ; the I/O address of the master PIC ; interrupt mask register ;============================================================================= ; structures ;============================================================================= ; descriptor layout Descr struc SegmentSize dw ? ; bits 0-15 of a segment size ; or the offset in a gate SegmentBase16 dw ? ; bits 0-15 of an address ; or the selector in a gate SegmentBase24 db ? ; bits 16-23 of an address ; or the number of words in a gate AttribLow db ? ; bits 0-7 of attributes AttribSizHi db ? ; bits 16-19 of a segment size ; and 8-11 of attributes SegmentBase32 db ? ; bits 24-31 of an address ; Note: fields AttribSizHi and SegmentBase32 ; combine into bits 16-31 of offset in a gate Descr ends ;----------------------------------------------------------------------------- ; a 386-type TSS structure TSS386 struc T3tr dd ? ; TSS back link (previous TR selector) T3esp0 dd ? ; ESP value for CPL = 0 T3ss0 dd ? ; SS selector for CPL = 0 T3esp1 dd ? ; ESP value for CPL = 1 T3ss1 dd ? ; SS selector for CPL = 1 T3esp2 dd ? ; ESP value for CPL = 2 T3ss2 dd ? ; SS selector for CPL = 2 T3cr3 dd ? ; page directory base (CR3) T3eip dd ? ; instruction pointer T3efl dd ? ; EFLAGS T3eax dd ? ; general purpose registers T3ecx dd ? T3edx dd ? T3ebx dd ? T3esp dd ? T3ebp dd ? T3esi dd ? T3edi dd ? T3es dd ? ; segment selectors T3cs dd ? T3ss dd ? T3ds dd ? T3fs dd ? T3gs dd ? T3ldtr dd ? ; LDT selector T3trap dw ? ; task switch trap flag value (bit 0) T3iopb dw ? ; I/O permission bitmap start ; Note: for virtual mode tasks T3iopb field may also point to the end ; of interrupt redirection bitmap (one byte beyond the map) TSS386 ends ; standard size of a 386-type TSS formatted part TSS386Length equ size TSS386-1 comment ~ ******************************************************************************* declaration of a TSS: name TSS386 ******************************************************************************* ~ ;----------------------------------------------------------------------------- ; an exception stack frame (a trap or an interrupt gate) ; the common part ExcStackFrame struc Excedi dd ? ; general purpose registers Excesi dd ? Excebp dd ? Excesp dd ? Excebx dd ? Excedx dd ? Excecx dd ? Exceax dd ? Excgs dd ? ; selectors Excfs dd ? Excds dd ? Exces dd ? ;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ; error code presence dependent part Exceerr equ Excneip ; an error code Excneip dd ? ; a return pointer Exceeip equ Excncs Excncs dd ? ; a return selector Excecs equ Excnefl Excnefl dd ? ; the eflags image Exceefl equ Excneesp Excneesp dd ? ; an external stack pointer Exceeesp equ Excness Excness dd ? ; an external stack selector Exceess dd ? ExcStackFrame ends ;============================================================================= ; macrodefinitions ;============================================================================= ; the CPUID instruction (I do not have a Pentium assembler) cpuid macro db 00fh,0a2h endm ;----------------------------------------------------------------------------- ; the AAM imm8 instruction (MASM does not know it) aami macro imm8 ifdef MASM db 0d4h,imm8 else aam imm8 endif endm ;----------------------------------------------------------------------------- ; protected mode far call (16-bit segment -> 16-bit segment) ; ; callpw sel,ofs -> use the "sel" selector and the "ofs" offset ; callpw sel -> use the "sel" selector and the null offset callpw macro sel,ofs local relofs ifnb relofs equ ofs ; now "$" references will work OK else relofs equ 0 ; default offset (for task switches ; or gates) endif db 09ah ; put the opcode dw relofs dw sel endm ;----------------------------------------------------------------------------- ; protected mode far jump (16-bit segment -> 16-bit segment) ; ; jmppw sel,ofs -> use the "sel" selector and the "ofs" offset ; jmppw sel -> use the "sel" selector and the null offset jmppw macro sel,ofs local relofs ifnb relofs equ ofs ; now "$" references will work OK else relofs equ 0 ; default offset (for task switches) endif db 0eah ; put the opcode dw relofs dw sel endm ;----------------------------------------------------------------------------- ; make a segment descriptor ; Selector - returns the segment selector ; TableStart - start of the current descriptor table ; TableType - table type: 0 - GDT, 1 - LDT, ; non-significant for IDT ; Base - 32-bit base segment address ; SegmentSize - 20-bit segment size ; Attrib - segment attributes (12 bits) makesg macro Selector,TableStart,TableType,Base,SegmentSize,Attrib Selector = $-TableStart+(TableType and 1) shl 2 descr endm ;----------------------------------------------------------------------------- ; make a gate descriptor ; Selector - returns the gate selector ; TableStart - start of the current descriptor table ; TableType - table type: 0 - GDT, 1 - LDT, ; non-significant for IDT ; PtrSelect - a code segment or TSS selector ; PtrOffset - a 32-bit offset (non-significant for a task gate) ; Attrib - gate attributes (8 bits) ; Params - number of words (dwords) for a call gate makegt macro Selector,TableStart,TableType,PtrSelect,PtrOffset,Attrib,Params Selector = $-TableStart+(TableType and 1) shl 2 descr endm ;----------------------------------------------------------------------------- comment ~ ******************************************************************************* declaration of a segment descriptor: makesg selector,start,table,base,size,attrs ******************************************************************************* declaration of a gate descriptor: a call gate: makegt selector,start,table,entry selector,entry offset,attrs,params a task gate: makegt selector,start,table,TSS selector,0,attrs,0 interrupt and trap gates: makegt selector,start,table,entry selector,entry offset,attrs,0 ******************************************************************************* ~ .list ; enable listing for the program ;***************************************************************************** ; the program ;***************************************************************************** .8086 ; assume the worst case at the start ; -- the following segment will be ; referenced in the *.exe file header cseg segment para 'CODE' ; the main program code segment cstr equ $ assume cs:cseg,ss:sseg Prog proc near ; the execution starts here mov ax,dseg ; we need to set up data segments mov ds,ax assume ds:dseg mov ax,pseg mov es,ax assume es:pseg mov dx,offset OldCPUErr ; assume the CPU is too old push sp ; now check for 80286 -- not an ; Intel-recommended, but more ; reliable method pop ax cmp ax,sp ; equal if CPU >= 80286 jne ProgEnd .286p ; we can safely assume an 80286 CPU mov dx,offset PModeErr ; assume the protected mode is active smsw ax ; check for the PM test al,001h jnz ProgEnd mov dx,offset OldCPUErr ; assume the CPU is too old pushf ; check for a 32-bit processor pushf pop ax or ah,040h ; trying to set the NT bit push ax popf pushf pop ax popf test ah,040h ; 80286 will reset it jz ProgEnd .386P ; we can safely assume an i386 CPU mov di,offset NoPVIMess ; assume no PVI support pushfd ; first check the CPUID support pushfd pop eax btc eax,015h ; complement the ID bit setc dl push eax popfd pushfd pop eax popfd shl eax,00bh ; copy the new ID value to CF sbb dl,000h ; zero means the ID bit is fixed pop eax jz NoPVISupport ; OK, CPUID is present xor eax,eax ; get the CPUID level and vendor ID cpuid test eax,eax jz NoPVISupport ; level 1 not supported cmp ecx,'letn' ; check if an Intel processor jne NoPVISupport cmp edx,'Ieni' jne NoPVISupport cmp ebx,'uneG' jne NoPVISupport xor eax,eax ; get the feature flags inc eax cpuid test dl,002h ; check the VME flag jz NoPVISupport mov di,offset PVIMess ; the CPU supports PVI mov [PVIPresent],001h ; set the PVI status flag NoPVISupport: mov dx,di mov ah,009h ; display the PVI support status int MSDOSCall mov al,UsePVI ; test if PVI has to be enabled test al,al jz DoNotUsePVI mov ah,009h ; display PVI status mov dx,offset PVIEnabled int MSDOSCall mov dx,offset NoPVIErr ; check if PVI supported cmp [PVIPresent],000h jz ProgEnd db 00fh,020h,0e0h ; mov eax,cr4 -- handcoded mov OldCR4Val,eax or al,002h ; enable the PVI feature db 00fh,022h,0e0h ; mov cr4,eax -- handcoded jmp PVISetUp ; PVI now enabled DoNotUsePVI: mov ah,009h ; display PVI status mov dx,offset PVIDisabled int MSDOSCall cmp [PVIPresent],000h ; check if PVI supported jz PVISetUp db 00fh,020h,0e0h ; mov eax,cr4 -- handcoded mov [OldCR4Val],eax and al,0fdh ; disable the PVI feature db 00fh,022h,0e0h ; mov cr4,eax -- handcoded PVISetUp: mov ah,009h ; display the CPL mov dx,offset CPLInfoMess int MSDOSCall mov ax,00200h+CPL ; decode the CPL digit call DecHBt mov dl,al int MSDOSCall mov ah,009h mov dx,offset EOFMess int MSDOSCall mov dx,offset TestTypeMess ; display the test kind int MSDOSCall mov dx,[TestTypesTable+2*UseTest] int MSDOSCall mov dx,offset TestEndMess int MSDOSCall cli ; now set up the PM environment in al,MasterPICIMR ; get the master PIC interrupt mask mov [MasterPICMask],al mov al,0ffh ; mask all hardware interrupts ; (ISA-compatible architecture only ; -- may not work for APIC-based ; systems) out MasterPICIMR,al ; adjust segment bases in descriptors xor eax,eax mov ax,cseg ; main code segment shl eax,004h add dword ptr [GDTStart+(Code16 and 0fff8h)+2],eax add dword ptr [GDTStart+(RMCode and 0fff8h)+2],eax xor eax,eax mov ax,dseg ; main data segment shl eax,004h add dword ptr [GDTStart+(Data16 and 0fff8h)+2],eax add dword ptr [GDTStart+(RMData and 0fff8h)+2],eax xor eax,eax mov ax,sseg ; main stack segment shl eax,004h add dword ptr [GDTStart+(Stack16 and 0fff8h)+2],eax xor eax,eax mov ax,pseg ; PM structures segment shl eax,004h add dword ptr [PModeIDTR+2],eax add dword ptr [PModeGDTR+2],eax add dword ptr [GDTStart+(PMode16 and 0fff8h)+2],eax add dword ptr [GDTStart+(LDTTask and 0fff8h)+2],eax add dword ptr [GDTStart+(TSSStrSeg and 0fff8h)+2],eax add dword ptr [GDTStart+(TSSTskSeg and 0fff8h)+2],eax xor eax,eax mov ax,cpl0sg ; exception's code segment shl eax,004h add dword ptr [GDTStart+(Code0 and 0fff8h)+2],eax xor eax,eax mov ax,spl0sg ; PL 0 task's stack segment shl eax,004h add dword ptr [LDTStart+(Stack0 and 0fff8h)+2],eax xor eax,eax mov ax,cplxsg ; task's code segment shl eax,004h add dword ptr [LDTStart+(Codex and 0fff8h)+2],eax xor eax,eax mov ax,splxsg ; task's stack segment shl eax,004h add dword ptr [LDTStart+(Stackx and 0fff8h)+2],eax sgdt [RModeGDTR] ; store descriptor table registers sidt [RModeIDTR] lgdt fword ptr [PModeGDTR] ; load descriptor table registers lidt fword ptr [PModeIDTR] mov eax,cr0 ; enter the PM (no paging necessary) inc ax mov cr0,eax ;----------------------------------------------------------------------------- ; protected mode start ;----------------------------------------------------------------------------- jmp Flush1 ; flush the queue on older processors Flush1: jmppw code16,Next1 ; write a PM selector to CS Next1: mov ax,TSSStrSeg ; set up the TR ltr ax xor ax,ax ; set up the LDTR lldt ax mov ax,Stack16 ; set up PM segments mov ss,ax mov ax,Data16 mov ds,ax mov ax,PMode16 mov es,ax xor ax,ax ; put null selectors to unused ; segments mov fs,ax mov gs,ax mov eax,[TSSTask.T3efl] ; get the initial EFLAGS value mov [Startefl],eax callpw TSSTskSeg ; call the test task (a task switch ; via a direct TSS reference) mov eax,[TSSTask.T3efl] ; get the resulting EFLAGS value mov [Exitefl],eax jmppw RMCode,Next2 ; restore 64 kB code segment length Next2: mov ax,RMData ; restore 64 kB data segment lengths mov es,ax assume es:dseg mov ss,ax assume ss:dseg mov ds,ax assume ds:dseg mov fs,ax assume fs:dseg mov gs,ax assume gs:dseg mov eax,cr0 ; enter the RM dec ax mov cr0,eax ;----------------------------------------------------------------------------- ; protected mode end ;----------------------------------------------------------------------------- jmp Flush2 ; flush the queue on older processors Flush2: jmppw cseg,RestoreCSAttrs ; needed to make CS segment writable RestoreCSAttrs: clts ; needed to enable normal FPU ; functionality mov ax,sseg ; restore segment registers mov ss,ax assume ss:sseg mov ax,dseg mov ds,ax assume ds:dseg mov es,ax assume es:dseg argsiz ; restore descriptor table registers lidt [RModeIDTR] argsiz lgdt [RModeGDTR] mov al,[MasterPICMask] ; restore interrupts state out MasterPICIMR,al sti cmp [PVIPresent],000h ; check if CR4 has to be restored jz NoCR4Restore mov eax,[OldCR4Val] ; restore the previous CR4 value db 00fh,022h,0e0h ; mov cr4,eax -- handcoded NoCR4Restore: mov eax,[Startefl] ; decode initial EFLAGS mov di,offset StarteflBuf call OutDWd mov eax,[Exitefl] ; and resulting EFLAGS mov di,offset ExiteflBuf call OutDWd mov ah,009h mov dx,offset StartMess ; display initial EFLAGS int MSDOSCall mov dx,offset ExitMess ; and resulting EFLAGS int MSDOSCall cmp [EflLoaded],000h je NoeflLoad mov eax,UseFlagVal ; decode loaded EFLAGS mov di,offset LoadeflBuf call OutDWd mov ah,009h mov dx,offset LoadMess ; and display int MSDOSCall NoeflLoad: cmp [EflStored],000h je NoeflStore mov eax,[Storeefl] ; decode stored EFLAGS mov di,offset StoreeflBuf call OutDWd mov ah,009h mov dx,offset StoreMess ; and display int MSDOSCall NoeflStore: cmp [DbgSignaled],000h ; test if a debug exception notice je NoDbgDetected ; should be displayed mov ah,009h mov dx,offset DbgSignalMess ; display the debug exception notice int MSDOSCall NoDbgDetected: cmp [GPFSignaled],000h ; test if a #GP report should be je NoGPFDetected ; displayed mov ax,[GPFcs] ; decode #GP's CS mov di,offset GPFcsBuf call OutWrd mov eax,[GPFeip] ; EIP, mov di,offset GPFeipBuf call OutDWd mov eax,[GPFerr] ; and error code mov di,offset GPFerrBuf call OutWrd mov ah,009h mov dx,offset GPFSignalMess ; display the #GP report int MSDOSCall NoGPFDetected: mov dx,offset SuccMess ; finish successfully mov [ErrorCond],000h assume fs:nothing,gs:nothing .8086 ; should execute on any processor ProgEnd: mov ah,009h ; print a status string int MSDOSCall mov dx,offset CRightMess ; display the author information int MSDOSCall mov ah,04ch ; exit now mov al,[ErrorCond] ; set the error code int MSDOSCall Prog endp ;----------------------------------------------------------------------------- ; convert a double word to a hexadecimal ascii form and put into the buffer ; ; parameters: ; EAX - a double word ; ES:DI - a buffer location ; results: ; DI - incremented by 8 .386 OutDWd proc near rol eax,010h call OutWrd rol eax,010h call OutWrd ret OutDWd endp ;----------------------------------------------------------------------------- ; convert a word to a hexadecimal ascii form and put into the buffer ; ; parameters: ; AX - a word ; ES:DI - a buffer location ; results: ; DI - incremented by 4 .8086 OutWrd proc near cld mov dx,ax mov al,ah aami 010h xchg ah,al call DecHBt stosb mov al,ah call DecHBt stosb xchg ax,dx aami 010h xchg ah,al call DecHBt stosb mov al,ah call DecHBt stosb ret OutWrd endp ;----------------------------------------------------------------------------- ; convert a four bit value to an ASCII encoded hexadecimal digit ; ; parameters: ; AL - a value to convert ; results: ; AL - a hexadecimal digit .8086 DecHBt proc near and al,00fh ; mask only four bits add al,090h ; figure it out yourself ;-) daa adc al,040h daa ret DecHBt endp assume nothing cend equ $-1 clth = cend-cstr ; segment length cseg ends ;============================================================================= .386p ; use the 386 syntax for the following ; 32-bit protected mode segment cplxsg segment para use32 'CODE' ; the code of the test task cxstr equ $ assume cs:cplxsg,ds:dseg,ss:splxsg Task proc near jmp [AnyTestTable+UseTest*4] ; choose the desired test TaskExit equ this far nop ; to allow a single-step exception ; if enabled iretd TaskEntry equ this far jmp Task ; jump to the start of the task Task endp ;----------------------------------------------------------------------------- ; a testing routine for no operation (default) NopTest: jmp TaskExit ;----------------------------------------------------------------------------- ; a testing routine for CLI CliTest: cli jmp TaskExit ;----------------------------------------------------------------------------- ; a testing routine for STI StiTest: sti jmp TaskExit ;----------------------------------------------------------------------------- ; a testing routine for PUSHFD PushfdTest: pushfd pop eax mov [Storeefl],eax ; store the EFLAGS in a variable mov [EflStored],001h ; set the store flag jmp TaskExit ;----------------------------------------------------------------------------- ; a testing routine for POPFD PopfdTest: push UseFlagVal mov [EflLoaded],001h ; set the load flag popfd jmp TaskExit ;----------------------------------------------------------------------------- ; a testing routine for IRETD IretdTest: push UseFlagVal push cs push offset TaskExit pushfd and byte ptr [esp+1],not (high Fnt) ; reset the the NT flag to prevent ; task switching on IRETD execution popfd mov [EflLoaded],001h ; set the load flag iretd assume nothing AnyTestTable dd offset NopTest dd offset CliTest dd offset StiTest dd offset PushfdTest dd offset PopfdTest dd offset IretdTest cxend equ $-1 cxlth equ cxend-cxstr ; segment length cplxsg ends ;============================================================================= .386p ; use the 386 syntax for the following ; 32-bit protected mode segment cpl0sg segment para use32 'CODE' ; the exceptions routines segment c0str equ $ assume cs:cpl0sg ExcDbg proc near ; the debug exception serving routine push es ; push all segment and general purpose push ds ; registers -- we do not need a push fs ; lightning speed in an exception push gs ; serving routine but a good register pushad ; access may be convenient mov eax,Data16 ; get access to the data segment mov ds,ax assume ds:dseg mov [DbgSignaled],001h ; set the debug exception flag or byte ptr [esp.ExcStackFrame.Excnefl+2],Frf shr 010h ; prevent reentering the handler ; if an instruction fetch breakpoint ; was set popad ; restore registers pop gs pop fs pop ds assume ds:nothing pop es iretd ExcDbg endp assume nothing assume cs:cpl0sg ExcGPF proc near ; the #GP serving routine push es ; push all segment and general purpose push ds ; registers -- we do not need a push fs ; lightning speed in an exception push gs ; serving routine but a good register pushad ; access may be convenient mov eax,Data16 ; get access to the data segment mov ds,ax assume ds:dseg mov eax,[esp.ExcStackFrame.Excecs] ; get the data and store them mov [GPFecs],eax mov eax,[esp.ExcStackFrame.Exceeip] mov [GPFeip],eax mov eax,[esp.ExcStackFrame.Exceerr] mov [GPFerr],eax mov [GPFSignaled],001h ; set the GPF flag test byte ptr [esp.ExcStackFrame.Exceefl+2],Fvif shr 010h ; test the VIF flag jz NovipReset and byte ptr [esp.ExcStackFrame.Exceefl+2],not (Fvip shr 010h) ; prevent a loop if VIF and VIP are ; set NovipReset: mov [esp.ExcStackFrame.Exceeip],offset TaskExit ; modify the return pointer to exit ; the task popad ; restore registers pop gs pop fs pop ds assume ds:nothing pop es add esp,000000004h ; get rid of the error code iretd ExcGPF endp assume nothing c0end equ $-1 c0lth equ c0end-c0str ; segment length cpl0sg ends ;============================================================================= .386p ; use the 386 syntax in the data ; segment pseg segment para use16 'DATA' ; the PM system segments pstr equ $ ; define global segments GDTStart equ $ ; start of the GDT makesg Dummy,GDTStart,GDTR,0,0,0 makesg TSSStrSeg,GDTStart,GDTR,(TSSStart-pstr),TSS386Length,%ByteGr or Presnt or DPL0 or System or Use386 or AvlTSS makesg TSSTskSeg,GDTStart,GDTR,(TSSTask-pstr),TSS386Length,%ByteGr or Presnt or DPL0 or System or Use386 or AvlTSS makesg LDTTask,GDTStart,GDTR,(LDTStart-pstr),LDTLength,%ByteGr or Presnt or DPL0 or System or LDTabl makesg Code16,GDTStart,GDTR,0,clth,%ByteGr or Word16 or Presnt or DPL0 or Memory or CodeEO or Accssd makesg Data16,GDTStart,GDTR,0,dlth,%ByteGr or Word16 or Presnt or DPL3 or Memory or DataRW or Accssd makesg Stack16,GDTStart,GDTR,0,slth,%ByteGr or Word16 or Presnt or DPL0 or Memory or DataRW or Accssd makesg PMode16,GDTStart,GDTR,0,plth,%ByteGr or Word16 or Presnt or DPL0 or Memory or DataRW or Accssd makesg Code0,GDTStart,GDTR,0,c0lth,%ByteGr or Word32 or Presnt or DPL0 or Memory or CodeEO or Accssd makesg RMCode,GDTStart,GDTR,0,0ffffh,%ByteGr or Word16 or Presnt or DPL0 or Memory or CodeER or Accssd makesg RMData,GDTStart,GDTR,0,0ffffh,%ByteGr or Word16 or Presnt or DPL0 or Memory or DataRW or Accssd GDTLength = $-GDTStart-1 ;----------------------------------------------------------------------------- ; define gates for all exceptions IDTStart equ $ ; start of the IDT makegt Gate00,IDTStart,IDTR,0,0,Absent,0 makegt Gate01,IDTStart,IDTR,Code0,(ExcDbg-c0str),%Presnt or DPL0 or Use386 or IntrGt,0 makegt Gate02,IDTStart,IDTR,0,0,Absent,0 makegt Gate03,IDTStart,IDTR,0,0,Absent,0 makegt Gate04,IDTStart,IDTR,0,0,Absent,0 makegt Gate05,IDTStart,IDTR,0,0,Absent,0 makegt Gate06,IDTStart,IDTR,0,0,Absent,0 makegt Gate07,IDTStart,IDTR,0,0,Absent,0 makegt Gate08,IDTStart,IDTR,0,0,Absent,0 makegt Gate09,IDTStart,IDTR,0,0,Absent,0 makegt Gate0a,IDTStart,IDTR,0,0,Absent,0 makegt Gate0b,IDTStart,IDTR,0,0,Absent,0 makegt Gate0c,IDTStart,IDTR,0,0,Absent,0 makegt Gate0d,IDTStart,IDTR,Code0,(ExcGPF-c0str),%Presnt or DPL0 or Use386 or IntrGt,0 makegt Gate0e,IDTStart,IDTR,0,0,Absent,0 makegt Gate0f,IDTStart,IDTR,0,0,Absent,0 makegt Gate10,IDTStart,IDTR,0,0,Absent,0 makegt Gate11,IDTStart,IDTR,0,0,Absent,0 makegt Gate12,IDTStart,IDTR,0,0,Absent,0 makegt Gate13,IDTStart,IDTR,0,0,Absent,0 makegt Gate14,IDTStart,IDTR,0,0,Absent,0 makegt Gate15,IDTStart,IDTR,0,0,Absent,0 makegt Gate16,IDTStart,IDTR,0,0,Absent,0 makegt Gate17,IDTStart,IDTR,0,0,Absent,0 makegt Gate18,IDTStart,IDTR,0,0,Absent,0 makegt Gate19,IDTStart,IDTR,0,0,Absent,0 makegt Gate1a,IDTStart,IDTR,0,0,Absent,0 makegt Gate1b,IDTStart,IDTR,0,0,Absent,0 makegt Gate1c,IDTStart,IDTR,0,0,Absent,0 makegt Gate1d,IDTStart,IDTR,0,0,Absent,0 makegt Gate1e,IDTStart,IDTR,0,0,Absent,0 makegt Gate1f,IDTStart,IDTR,0,0,Absent,0 IDTLength = $-IDTStart-1 ;----------------------------------------------------------------------------- ; define local segments LDTStart equ $ ; start of the LDT makesg Stack0,LDTStart,LDTR,0,s0lth,%ByteGr or Word32 or Presnt or DPL0 or Memory or DataRW or Accssd makesg Codex,LDTStart,LDTR,0,cxlth,%ByteGr or Word32 or Presnt or (UseCPL shl 5) or Memory or CodeER or Accssd makesg Stackx,LDTStart,LDTR,0,sxlth,%ByteGr or Word32 or Presnt or (UseCPL shl 5) or Memory or DataRW or Accssd LDTLength = $-LDTStart-1 ;----------------------------------------------------------------------------- ; define task state segments ; entry task's TSS TSSStart TSS386 <,,,,,,,,,,,,,,,,,,,,,,,,Dummy,0,> ; test task's TSS TSSTask TSS386 <,s0lth+1,Stack0,,,,,,offset TaskEntry,UseVIP or UseVIF or UseIOPL or UseIF or UseTF or Fuf,,,,,sxlth+1,,,,Dummy+UseCPL,Codex+UseCPL,Stackx+UseCPL,Data16+UseCPL,Dummy+UseCPL,Dummy+UseCPL,LDTTask,UseTaskTrap,> pend equ $-1 plth equ pend-pstr ; segment length pseg ends ;============================================================================= .386p ; use the 386 syntax in the data ; segment dseg segment para use16 'DATA' ; the main program data segment dstr equ $ OldCPUErr db 'This software needs an i386 or newer processor to run.',CR,LF,EOF NoPVIErr db 'This software needs a processor that supports PVI to utilize this feature.',CR,LF,EOF PModeErr db 'This software needs to be run in the real mode.',CR,LF,EOF NoPVIMess db 'This processor does not support the PVI feature.',EOF PVIMess db 'This processor supports the PVI feature.',EOF PVIEnabled db 'PVI enabled.',EOF PVIDisabled db 'PVI disabled.',EOF CPLInfoMess db 'CPL = $' TestTypeMess db 'Using the $' TestNopMess db 'no-operation$' TestCliMess db 'CLI$' TestStiMess db 'STI$' TestPushfdMess db 'PUSHFD$' TestPopfdMess db 'POPFD$' TestIretdMess db 'IRETD$' TestEndMess db ' test.',EOF StartMess db 'The initial EFLAGS value: ' StarteflBuf db '00000000.',EOF ExitMess db 'The resulting EFLAGS value: ' ExiteflBuf db '00000000.',EOF LoadMess db 'The loaded EFLAGS value: ' LoadeflBuf db '00000000.',EOF StoreMess db 'The stored EFLAGS value: ' StoreeflBuf db '00000000.',EOF DbgSignalMess db 'A debug exception was generated.',EOF GPFSignalMess db 'A #GP exception was generated at: ' GPFcsBuf db '0000:' GPFeipBuf db '00000000, error code: ' GPFerrBuf db '0000.',EOF SuccMess db 'Execution successful.',CR,LF,EOF CRightMess db 'PVI testing utility,',CR,LF db 'copyright (c) 1995 Maciej W. Rozycki, Gdansk, POLAND,',CR,LF db 'e-mail: prr09rm@loqi.elka.pg.gda.pl.',EOF EOFMess db EOF ;----------------------------------------------------------------------------- align 004h PModeIDTR dw IDTLength ; PM IDTR pseudodescriptor dd offset IDTStart align 004h PModeGDTR dw GDTLength ; PM GDTR pseudodescriptor dd offset GDTStart ErrorCond db 001h ; exit error code PVIPresent db 000h ; PVI support status ;----------------------------------------------------------------------------- TestTypesTable dw offset TestNopMess dw offset TestCliMess dw offset TestStiMess dw offset TestPushfdMess dw offset TestPopfdMess dw offset TestIretdMess ;----------------------------------------------------------------------------- EflLoaded db 000h EflStored db 000h DbgSignaled db 000h GPFSignaled db 000h ;----------------------------------------------------------------------------- align 004h RModeIDTR df ? ; a stored value of the IDTR register align 004h RModeGDTR df ? ; a stored value of the GDTR register align 004h OldCR4Val dd ? ; a stored value of the CR4 register MasterPICMask db ? ;----------------------------------------------------------------------------- align 004h Startefl dd ? ; EFLAGS value on the test task entry Exitefl dd ? ; EFLAGS value on the test task exit Storeefl dd ? ; EFLAGS stored by PUSHFD GPFerr dd ? ; #GP fault error code (if any) GPFeip dd ? ; #GP fault EIP pointer (if any) GPFcs equ this word ; a word alias for CS GPFecs dd ? ; #GP fault CS selector (if any) ;----------------------------------------------------------------------------- dend equ $-1 dlth equ dend-dstr ; segment length dseg ends ;============================================================================= .8086 ; assume the worst case at the start ; -- the following segment will be ; referenced in the *.exe file header sseg segment para stack 'STACK' ; the main program stack segment sstr equ $ dw 00400h dup (?) ; hope, this will be enough send equ $-1 slth equ send-sstr ; segment length sseg ends ;============================================================================= .386p ; use the 386 syntax in the 32-bit ; stack segment splxsg segment para use32 stack 'STACK' ; the test task's stack sxstr equ $ dd LevelXStackLth dup (?) sxend equ $-1 sxlth equ sxend-sxstr ; segment length splxsg ends ;============================================================================= .386p ; use the 386 syntax in the 32-bit ; stack segment spl0sg segment para use32 stack 'STACK' ; the test task's stack used ; for exceptions serving at ; CPL = 0 s0str equ $ dd Level0StackLth dup (?) s0end equ $-1 s0lth equ s0end-s0str ; segment length spl0sg ends end Prog