summaryrefslogtreecommitdiffstats
path: root/private/ntos/ke/i386
diff options
context:
space:
mode:
authorAdam <you@example.com>2020-05-17 05:51:50 +0200
committerAdam <you@example.com>2020-05-17 05:51:50 +0200
commite611b132f9b8abe35b362e5870b74bce94a1e58e (patch)
treea5781d2ec0e085eeca33cf350cf878f2efea6fe5 /private/ntos/ke/i386
downloadNT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.tar
NT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.tar.gz
NT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.tar.bz2
NT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.tar.lz
NT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.tar.xz
NT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.tar.zst
NT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.zip
Diffstat (limited to 'private/ntos/ke/i386')
-rw-r--r--private/ntos/ke/i386/abios.h147
-rw-r--r--private/ntos/ke/i386/abiosa.asm615
-rw-r--r--private/ntos/ke/i386/abiosc.c767
-rw-r--r--private/ntos/ke/i386/allproc.c397
-rw-r--r--private/ntos/ke/i386/alr.inc87
-rw-r--r--private/ntos/ke/i386/apcuser.c169
-rw-r--r--private/ntos/ke/i386/biosa.asm273
-rw-r--r--private/ntos/ke/i386/biosc.c269
-rw-r--r--private/ntos/ke/i386/callback.c252
-rw-r--r--private/ntos/ke/i386/callout.asm432
-rw-r--r--private/ntos/ke/i386/clockint.asm881
-rw-r--r--private/ntos/ke/i386/cpu.asm1037
-rw-r--r--private/ntos/ke/i386/cpu.inc61
-rw-r--r--private/ntos/ke/i386/ctxswap.asm1923
-rw-r--r--private/ntos/ke/i386/cyrix.c350
-rw-r--r--private/ntos/ke/i386/dmpstate.c363
-rw-r--r--private/ntos/ke/i386/emv86.asm1973
-rw-r--r--private/ntos/ke/i386/emxcptn.asm657
-rw-r--r--private/ntos/ke/i386/exceptn.c1270
-rw-r--r--private/ntos/ke/i386/flush.c170
-rw-r--r--private/ntos/ke/i386/flushtb.c565
-rw-r--r--private/ntos/ke/i386/gdtsup.c174
-rw-r--r--private/ntos/ke/i386/geni386.c812
-rw-r--r--private/ntos/ke/i386/i386init.c223
-rw-r--r--private/ntos/ke/i386/i386pcr.asm200
-rw-r--r--private/ntos/ke/i386/instemul.asm2873
-rw-r--r--private/ntos/ke/i386/int.asm132
-rw-r--r--private/ntos/ke/i386/intobj.c767
-rw-r--r--private/ntos/ke/i386/intsup.asm774
-rw-r--r--private/ntos/ke/i386/iopm.c529
-rw-r--r--private/ntos/ke/i386/kernlini.c1581
-rw-r--r--private/ntos/ke/i386/ki386.h34
-rw-r--r--private/ntos/ke/i386/kimacro.inc1288
-rw-r--r--private/ntos/ke/i386/largepag.c179
-rw-r--r--private/ntos/ke/i386/ldtsup.c392
-rw-r--r--private/ntos/ke/i386/ldtsup2.asm164
-rw-r--r--private/ntos/ke/i386/mi.inc43
-rw-r--r--private/ntos/ke/i386/misc.c164
-rw-r--r--private/ntos/ke/i386/mpipia.asm435
-rw-r--r--private/ntos/ke/i386/mtrr.c1887
-rw-r--r--private/ntos/ke/i386/mtrr.h124
-rw-r--r--private/ntos/ke/i386/newsysbg.asm1150
-rw-r--r--private/ntos/ke/i386/p2w.asm69
-rw-r--r--private/ntos/ke/i386/procstat.asm323
-rw-r--r--private/ntos/ke/i386/services.nap123
-rw-r--r--private/ntos/ke/i386/services.stb131
-rw-r--r--private/ntos/ke/i386/sources47
-rw-r--r--private/ntos/ke/i386/spindbg.asm162
-rw-r--r--private/ntos/ke/i386/spininst.asm943
-rw-r--r--private/ntos/ke/i386/spinlock.asm466
-rw-r--r--private/ntos/ke/i386/table.stb102
-rw-r--r--private/ntos/ke/i386/threadbg.asm99
-rw-r--r--private/ntos/ke/i386/thredini.c634
-rw-r--r--private/ntos/ke/i386/timindex.asm171
-rw-r--r--private/ntos/ke/i386/trap.asm5486
-rw-r--r--private/ntos/ke/i386/trapc.c545
-rw-r--r--private/ntos/ke/i386/vdm.c1641
-rw-r--r--private/ntos/ke/i386/vdmint21.c228
-rw-r--r--private/ntos/ke/i386/vdmp.h73
59 files changed, 37826 insertions, 0 deletions
diff --git a/private/ntos/ke/i386/abios.h b/private/ntos/ke/i386/abios.h
new file mode 100644
index 000000000..98177131f
--- /dev/null
+++ b/private/ntos/ke/i386/abios.h
@@ -0,0 +1,147 @@
+/*++
+
+Copyright (c) 1989 Microsoft Corporation
+
+Module Name:
+
+ abios.h
+
+Abstract:
+
+ This module contains the i386 kernel ABIOS specific header file.
+
+Author:
+
+ Shie-Lin Tzong (shielint) 22-May-1991
+
+Revision History:
+
+--*/
+
+//
+// Define public portion of the ABIOS Device Block
+//
+
+typedef struct _KDEVICE_BLOCK {
+ USHORT Length;
+ UCHAR Revision;
+ UCHAR SecondDeviceId;
+ USHORT LogicalId;
+ USHORT DeviceId;
+ USHORT NumberExclusivePortPairs;
+ USHORT NumberCommonPortPairs;
+} KDEVICE_BLOCK, *PKDEVICE_BLOCK;
+
+
+typedef struct _KABIOS_POINTER {
+ USHORT Offset;
+ USHORT Selector;
+} KABIOS_POINTER, *PKABIOS_POINTER;
+
+#pragma pack(1)
+
+//
+// ABIOS Function Transfer Table definition
+//
+
+typedef struct _KFUNCTION_TRANSFER_TABLE {
+ KABIOS_POINTER CommonRoutine[3];
+ USHORT FunctionCount;
+ USHORT Reserved;
+ KABIOS_POINTER SpecificRoutine;
+} KFUNCTION_TRANSFER_TABLE, *PKFUNCTION_TRANSFER_TABLE;
+
+
+//
+// ABIOS Commom Data Area definitions
+//
+
+typedef struct _KDB_FTT_SECTION {
+ KABIOS_POINTER DeviceBlock;
+ KABIOS_POINTER FunctionTransferTable;
+} KDB_FTT_SECTION, *PKDB_FTT_SECTION;
+
+typedef struct _KCOMMON_DATA_AREA {
+ USHORT DataPointer0Offset;
+ USHORT NumberLids;
+ ULONG Reserved;
+ PKDB_FTT_SECTION DbFttPointer;
+} KCOMMON_DATA_AREA, *PKCOMMON_DATA_AREA;
+
+#pragma pack()
+
+//
+// Available GDT Entry
+//
+
+typedef struct _KFREE_GDT_ENTRY {
+ struct _KFREE_GDT_ENTRY *Flink;
+ ULONG BaseMid : 8;
+ ULONG Type : 5;
+ ULONG Dpl : 2;
+ ULONG Present : 1;
+ ULONG LimitHi : 4;
+ ULONG Sys : 1;
+ ULONG Reserved_0 : 1;
+ ULONG Default_Big : 1;
+ ULONG Granularity : 1;
+ ULONG BaseHi : 8;
+} KFREE_GDT_ENTRY, *PKFREE_GDT_ENTRY;
+
+//
+// Logical Id table entry
+//
+
+typedef struct _KLID_TABLE_ENTRY {
+ ULONG Owner;
+ ULONG OwnerCount;
+} KLID_TABLE_ENTRY, *PKLID_TABLE_ENTRY;
+
+#define LID_NO_SPECIFIC_OWNER 0xffffffff
+#define NUMBER_LID_TABLE_ENTRIES 1024
+
+//
+// Macro to extract the high byte of a short offset
+//
+
+#define HIGHBYTE(l) ((UCHAR)(((USHORT)(l)>>8) & 0xff))
+
+//
+// Macro to extract the low byte of a short offset
+//
+
+#define LOWBYTE(l) ((UCHAR)(l))
+
+//
+// The following selectors are reserved for 16 bit stack, code and
+// ABIOS Common Data Area.
+//
+
+#define KGDT_STACK16 0xf8
+#define KGDT_CODE16 0xf0
+#define KGDT_CDA16 0xe8
+#define KGDT_GDT_ALIAS 0x70
+
+//
+// Misc. definitions
+//
+
+#define RESERVED_GDT_ENTRIES 28
+
+//
+// External references
+//
+
+extern PKFREE_GDT_ENTRY KiAbiosGdtStart;
+extern PKFREE_GDT_ENTRY KiAbiosGdtEnd;
+extern PUCHAR KiEndOfCode16;
+
+extern
+VOID
+KiI386CallAbios(
+ IN KABIOS_POINTER AbiosFunction,
+ IN KABIOS_POINTER DeviceBlockPointer,
+ IN KABIOS_POINTER FunctionTransferTable,
+ IN KABIOS_POINTER RequestBlock
+ );
+
diff --git a/private/ntos/ke/i386/abiosa.asm b/private/ntos/ke/i386/abiosa.asm
new file mode 100644
index 000000000..68dca5c4f
--- /dev/null
+++ b/private/ntos/ke/i386/abiosa.asm
@@ -0,0 +1,615 @@
+ title "Abios Support Assembly Routines"
+;++
+;
+; Copyright (c) 1989 Microsoft Corporation
+;
+; Module Name:
+;
+; abiosa.asm
+;
+; Abstract:
+;
+; This module implements assembley code for ABIOS support.
+;
+; Author:
+;
+; Shie-Lin Tzong (shielint) 25-May-1991
+;
+; Environment:
+;
+; Kernel mode only.
+;
+; Revision History:
+;
+;--
+.386p
+ .xlist
+include ks386.inc
+include callconv.inc ; calling convention macros
+include i386\kimacro.inc
+ .list
+
+EXTRNP _KeRaiseIrql,2,IMPORT
+EXTRNP _KeLowerIrql,1,IMPORT
+EXTRNP _KeGetCurrentIrql,0,IMPORT
+extrn _KiStack16GdtEntry:DWORD
+
+OPERAND_OVERRIDE equ 66h
+ADDRESS_OVERRIDE equ 67h
+KGDT_CDA16 equ 0E8h
+
+;++
+;
+; STACK32_TO_STACK16
+;
+; Macro Description:
+;
+; This macro remaps current 32bit stack to 16bit stack.
+;
+; Arguments:
+;
+; None.
+;
+;--
+
+STACK32_TO_STACK16 macro
+
+ mov eax, fs:PcStackLimit ; [eax] = 16-bit stack selector base
+ mov edx, eax
+ mov ecx, _KiStack16GdtEntry
+ mov word ptr [ecx].KgdtBaseLow, ax
+ shr eax, 16
+ mov byte ptr [ecx].KgdtBaseMid, al
+ mov byte ptr [ecx].KgdtBaseHi, ah
+ mov eax, esp
+ sub eax, edx
+ cli
+ mov esp, eax
+ mov eax, KGDT_STACK16
+ mov ss, ax
+
+;
+; NOTE that we MUST leave interrupts remain off.
+; We'll turn it back on after we switch to 16 bit code.
+;
+
+endm
+
+;++
+;
+; STACK16_TO_STACK32
+;
+; Macro Description:
+;
+; This macro remaps current 32bit stack to 16bit stack.
+;
+; Arguments:
+;
+; None.
+;
+;--
+
+STACK16_TO_STACK32 macro Stack32
+
+ db OPERAND_OVERRIDE
+ mov eax, esp
+ db OPERAND_OVERRIDE
+ db ADDRESS_OVERRIDE
+ add eax, fs:PcStackLimit
+ cli
+ db OPERAND_OVERRIDE
+ mov esp, eax
+ db OPERAND_OVERRIDE
+ mov eax, KGDT_R0_DATA
+ mov ss, ax
+ sti
+endm
+
+ page ,132
+ subttl "Abios Support Code"
+_TEXT SEGMENT DWORD PUBLIC 'CODE'
+ ASSUME DS:FLAT, ES:FLAT, SS:NOTHING, FS:NOTHING, GS:NOTHING
+
+;++
+; ULONG
+; KiAbiosGetGdt (
+; VOID
+; )
+;
+; Routine Description:
+;
+; This routine returns the starting address of GDT of current processor.
+;
+; Arguments:
+;
+; None.
+;
+; Return Value:
+;
+; return Pcr->GDT
+;
+;--
+
+cPublicProc _KiAbiosGetGdt,0
+
+ mov eax, fs:PcGdt
+ stdRET _KiAbiosGetGdt
+
+stdENDP _KiAbiosGetGdt
+
+;++
+; VOID
+; KiI386CallAbios(
+; IN KABIOS_POINTER AbiosFunction,
+; IN KABIOS_POINTER DeviceBlockPointer,
+; IN KABIOS_POINTER FunctionTransferTable,
+; IN KABIOS_POINTER RequestBlock
+; )
+;
+; Routine Description:
+;
+; This function invokes ABIOS service function for device driver. This
+; routine is executing at DIAPTCH_LEVEL to prevent context swapping.
+;
+; N.B. We arrive here from the Ke386AbiosCall with a 32bit CS. That is,
+; we're executing the code with cs:eip where cs contains a selector for a
+; 32bit flat segment. We want to get to a 16bit cs. That is, cs:ip.
+; The reason is that ABIOS is running at 16 bit segment.
+; Before we can call ABIOS service we must load ss and cs segment
+; registers with selectors for 16bit segments. We start by pushing a far
+; pointer to a label in the macro and then doing a retf. This allows us
+; to fall through to the next instruction, but we're now executing
+; through cs:ip with a 16bit CS. Then, we remap our 32-bit stack to 16-bit
+; stack.
+;
+; Arguments:
+;
+; AbiosFunction - a 16:16 pointer to the abios service function.
+;
+; DeviceBlockPointer - a 16:16 pointer to Device Block.
+;
+; FunctionTransferTable - a 16:16 pointer to Function Transfer Table.
+;
+; RequestBlock - a 16:16 pointer to device driver's request block.
+;
+; Return Value:
+;
+; None.
+;--
+
+KacAbiosFunction equ [ebp + 8]
+KacDeviceBlock equ [ebp + 12]
+KacFunctionTable equ [ebp + 16]
+KacRequestBlock equ [ebp + 20]
+
+cPublicProc _KiI386CallAbios,4
+
+;
+; We're using a 32bit CS:EIP - go to a 16bit CS:IP
+; Note the base of KiAbiosCallSelector is the flat address of _KiI386AbiosCall
+; routine.
+;
+
+ push ebp
+ mov ebp, esp
+ push ebx
+
+ stdCall _KeGetCurrentIrql
+ push eax ; Local Varible
+
+ cmp al, DISPATCH_LEVEL ; Is irql > Dispatch_level?
+ jae short Kac00
+
+; Raise to Dispatch Level
+ mov eax, esp
+ stdCall _KeRaiseIrql, <DISPATCH_LEVEL,eax>
+
+Kac00:
+
+;
+; Set up parameters on stack before remapping stack.
+;
+
+ push word ptr KGDT_CDA16 ; CDA anchor selector
+ push KacRequestBlock ; Request Block
+ push KacFunctionTable ; Func transfer table
+ push KacDeviceBlock ; Device Block
+ mov ebx, KacAbiosFunction ; (ebx)-> Abios Entry
+
+;
+; Remap current stack to 16:16 stack. The base of the 16bit stack selector is
+; the base of current kernel stack.
+;
+
+ STACK32_TO_STACK16 ; Switch to 16bit stack
+ push word ptr KGDT_CODE16
+IFDEF STD_CALL
+ push word ptr (offset FLAT:Kac40 - offset FLAT:_KiI386CallAbios@16)
+ push KGDT_CODE16
+ push offset FLAT:Kac30 - offset FLAT:_KiI386CallAbios@16
+ELSE
+ push word ptr (offset FLAT:Kac40 - offset FLAT:_KiI386CallAbios)
+ push KGDT_CODE16
+ push offset FLAT:Kac30 - offset FLAT:_KiI386CallAbios
+ENDIF
+ retf
+
+Kac30:
+
+;
+; Stack switching (from 32 to 16) turns interrupt off. We must turn it
+; back on.
+;
+
+ sti
+ push bx ; Yes, BX not EBX!
+ retf
+Kac40:
+ add esp, 14 ; pop out all the parameters
+
+ STACK16_TO_STACK32 ; switch back to 32 bit stack
+
+;
+; Pull callers flat return address off stack and push the
+; flat code selector followed by the return offset, then
+; execute a far return and we'll be back in the 32-bit code space.
+;
+
+ db OPERAND_OVERRIDE
+ push KGDT_R0_CODE
+ db OPERAND_OVERRIDE
+ push offset FLAT:Kac50
+ db OPERAND_OVERRIDE
+ retf
+Kac50:
+ pop eax ; [eax] = OldIrql
+ pop ebx ; restore ebx
+ cmp al, DISPATCH_LEVEL
+ jae short Kac60
+
+ stdCall _KeLowerIrql, <eax> ; Lower irql to original level
+Kac60:
+ pop ebp
+ stdRET _KiI386CallAbios
+
+stdENDP _KiI386CallAbios
+
+
+;; ********************************************************
+;;
+;; BEGIN - power_management
+;;
+;;
+
+;++
+; VOID
+; KeI386Call16BitFunction (
+; IN OUT PCONTEXT Regs
+; )
+;
+; Routine Description:
+;
+; This function calls the 16 bit function specified in the Regs.
+;
+; Parameters:
+;
+; Regs - supplies a pointer to register context to call 16 function.
+;
+; NOTE: Caller must be at DPC_LEVEL
+;
+;--
+
+cPublicProc _KeI386Call16BitFunction,1
+
+ ; verify CurrentIrql
+ ; verify context flags
+
+ push ebp ; save nonvolatile registers
+ push ebx
+ push esi
+ push edi
+
+ mov ebx, dword ptr [esp + 20] ; (ebx)-> Context
+
+;
+; We're using a 32bit CS:EIP - go to a 16bit CS:IP
+; Note the base of KiAbiosCallSelector is the flat address of _KiI386AbiosCall
+; routine.
+;
+
+;
+; Remap current stack to 16:16 stack. The base of the 16bit stack selector is
+; the base of current kernel stack.
+;
+
+ STACK32_TO_STACK16 ; Switch to 16bit stack
+ ;
+ ; Push return address from 16 bit function call to kernel
+ ;
+
+ push word ptr KGDT_CODE16
+ push word ptr (offset FLAT:Kbf40 - offset FLAT:_KiI386CallAbios@16)
+
+ ;
+ ; Load context to call with
+ ;
+
+ push word ptr [ebx].CsEFlags
+ push word ptr [ebx].CsSegCs
+ push word ptr [ebx].CsEip
+
+ mov eax, [ebx].CsEax
+ mov ecx, [ebx].CsEcx
+ mov edx, [ebx].CsEdx
+ mov edi, [ebx].CsEdi
+ mov esi, [ebx].CsEsi
+ mov ebp, [ebx].CsEbp
+ push [ebx].CsSegGs
+ push [ebx].CsSegFs
+ push [ebx].CsSegEs
+ push [ebx].CsSegDs
+ mov ebx, [ebx].CsEbx
+ pop ds
+ pop es
+ pop fs
+ pop gs
+
+ ;
+ ; Switch to 16bit CS
+ ;
+ push KGDT_CODE16
+ push offset FLAT:Kbf30 - offset FLAT:_KiI386CallAbios@16
+ retf
+
+Kbf30:
+ ;
+ ; "call" to 16 bit function
+ ;
+ iretd
+
+Kbf40:
+ ;
+ ; Push some of the returned context which will be needed to
+ ; switch back to the 32 bit SS & CS.
+ ;
+ db OPERAND_OVERRIDE
+ push ds
+
+ db OPERAND_OVERRIDE
+ push es
+
+ db OPERAND_OVERRIDE
+ push fs
+
+ db OPERAND_OVERRIDE
+ push gs
+
+ db OPERAND_OVERRIDE
+ push eax
+
+ db OPERAND_OVERRIDE
+ pushfd
+
+ db OPERAND_OVERRIDE
+ mov eax, KGDT_R0_PCR
+ mov fs, ax
+
+ db OPERAND_OVERRIDE
+ mov eax, KGDT_R3_DATA OR RPL_MASK
+ mov ds, ax
+ mov es, ax
+
+ xor eax, eax
+
+ ;
+ ; Switch back to 32 bit stack
+ ;
+
+ STACK16_TO_STACK32
+
+;
+; Push the flat code selector followed by the return offset, then
+; execute a far return and we'll be back in the 32-bit code space.
+;
+
+
+ db OPERAND_OVERRIDE
+ push KGDT_R0_CODE
+ db OPERAND_OVERRIDE
+ push offset FLAT:Kbf50
+ db OPERAND_OVERRIDE
+ retf
+
+Kbf50:
+ ;
+ ; Return resulting context
+ ;
+
+ mov eax, dword ptr [esp+44] ; (eax) = Context Record
+ pop [eax].CsEflags
+ pop [eax].CsEax
+ pop [eax].CsSegGs
+ pop [eax].CsSegFs
+ pop [eax].CsSegEs
+ pop [eax].CsSegDs
+
+ mov [eax].CsEbx, ebx
+ mov [eax].CsEcx, ecx
+ mov [eax].CsEdx, edx
+ mov [eax].CsEdi, edi
+ mov [eax].CsEsi, esi
+ mov [eax].CsEbp, ebp
+
+;
+; Restore regs & return
+;
+
+ pop edi
+ pop esi
+ pop ebx
+ pop ebp
+ stdRET _KeI386Call16BitFunction
+
+stdENDP _KeI386Call16BitFunction
+
+;++
+; USHORT
+; KeI386Call16BitCStyleFunction (
+; IN ULONG EntryOffset,
+; IN ULONG EntrySelector,
+; IN PUCHAR Parameters,
+; IN ULONG Size
+; )
+;
+; Routine Description:
+;
+; This function calls the 16 bit function which supports C style calling convension.
+;
+; Parameters:
+;
+; EntryOffset and EntrySelector - specifies the entry point of the 16 bit function.
+;
+; Parameters - supplies a pointer to a parameter block which will be
+; passed to 16 bit function as parameters.
+;
+; Size - supplies the size of the parameter block.
+;
+; NOTE: Caller must be at DPC_LEVEL
+;
+; Returned Value:
+;
+; AX returned by 16 bit function.
+;
+;--
+
+cPublicProc _KeI386Call16BitCStyleFunction,4
+
+;
+; verify CurrentIrql
+; verify context flags
+;
+
+ push ebp ; save nonvolatile registers
+ push ebx
+ push esi
+ push edi
+
+ mov edi, esp
+ mov esi, dword ptr [esp + 28] ; (esi)->BiosParameters
+ or esi, esi
+ jz short @f
+
+ mov ecx, [esp + 32] ; (ecx) = parameter size
+ sub esp, ecx ; allocate space on TOS to copy parameters
+ mov edi, esp
+ rep movsb ; (edi)-> Top of nonvolatile reg save area
+
+@@:
+
+;
+; We're using a 32bit CS:EIP - go to a 16bit CS:IP
+; Note the base of KiAbiosCallSelector is the flat address of _KiI386AbiosCall
+; routine.
+;
+
+;
+; Remap current stack to 16:16 stack. The base of the 16bit stack selector is
+; the base of current kernel stack.
+;
+
+ STACK32_TO_STACK16 ; Switch to 16bit stack
+
+;
+; Push return address from 16 bit function call to kernel
+;
+
+ push word ptr KGDT_CODE16
+ push word ptr (offset FLAT:Kbfex40 - offset FLAT:_KiI386CallAbios@16)
+
+ push word ptr 0200h ; flags
+ push word ptr [edi + 24] ; entry selector
+ push word ptr [edi + 20] ; entry offset
+
+;
+; Switch to 16bit CS
+;
+ push KGDT_CODE16
+ push offset FLAT:Kbfex30 - offset FLAT:_KiI386CallAbios@16
+ retf
+
+Kbfex30:
+;
+; "call" to 16 bit function
+;
+ iretd
+
+Kbfex40:
+;
+; Save return value.
+;
+
+ db OPERAND_OVERRIDE
+ push eax
+
+;
+; Restore Flat mode segment registers.
+;
+
+ db OPERAND_OVERRIDE
+ mov eax, KGDT_R0_PCR
+ mov fs, ax
+
+ db OPERAND_OVERRIDE
+ mov eax, KGDT_R3_DATA OR RPL_MASK
+ mov ds, ax
+ mov es, ax
+
+ xor eax, eax
+
+;
+; Switch back to 32 bit stack
+;
+
+ STACK16_TO_STACK32
+
+;
+; Push the flat code selector followed by the return offset, then
+; execute a far return and we'll be back in the 32-bit code space.
+;
+
+
+ db OPERAND_OVERRIDE
+ push KGDT_R0_CODE
+ db OPERAND_OVERRIDE
+ push offset FLAT:Kbfex50
+ db OPERAND_OVERRIDE
+ retf
+
+Kbfex50:
+ pop eax
+
+;
+; Restore regs & return
+;
+ mov esp, edi
+ pop edi
+ pop esi
+ pop ebx
+ pop ebp
+ stdRET _KeI386Call16BitCStyleFunction
+
+stdENDP _KeI386Call16BitCStyleFunction
+
+;;
+;; END - power_management
+;;
+;; ********************************************************
+
+
+ public _KiEndOfCode16
+_KiEndOfCode16 equ $
+
+
+
+_TEXT ends
+ end
diff --git a/private/ntos/ke/i386/abiosc.c b/private/ntos/ke/i386/abiosc.c
new file mode 100644
index 000000000..7927d3b11
--- /dev/null
+++ b/private/ntos/ke/i386/abiosc.c
@@ -0,0 +1,767 @@
+/*++
+
+Copyright (c) 1989 Microsoft Corporation
+
+Module Name:
+
+ abiosc.c
+
+Abstract:
+
+ This module implements ABIOS support C routines for i386 NT.
+
+Author:
+
+ Shie-Lin Tzong (shielint) 20-May-1991
+
+Environment:
+
+ Boot loader privileged, FLAT mode.
+
+
+Revision History:
+
+--*/
+
+#include "ki.h"
+#pragma hdrstop
+#include "abios.h"
+
+extern PKCOMMON_DATA_AREA KiCommonDataArea;
+extern BOOLEAN KiAbiosPresent;
+
+extern
+ULONG
+KiAbiosGetGdt (
+ VOID
+ );
+
+//
+// The reason of having these variables defined in here is to isolate
+// ABIOS from current system.
+//
+
+//
+// KiNumberFreeSelectors defines the number of available selectors for
+// ABIOS specific drivers. This number should be the same accross all
+// the processors.
+//
+
+static USHORT KiNumberFreeSelectors = 0;
+
+//
+// KiFreeGdtListHead points to the head of free GDT list on the processor 0.
+//
+
+static PKFREE_GDT_ENTRY KiFreeGdtListHead = 0L;
+
+//
+// Logica Id Table to control the ownership of logical Id.
+//
+
+PKLID_TABLE_ENTRY KiLogicalIdTable;
+
+//
+// KiAbiosGdt[] defines the Starting address of GDT for each processor.
+//
+
+ULONG KiAbiosGdt[MAXIMUM_PROCESSORS];
+
+//
+// SpinLock for accessing GDTs
+//
+
+KSPIN_LOCK KiAbiosGdtLock;
+
+//
+// Spinlock for accessing Logical Id Table
+//
+
+KSPIN_LOCK KiAbiosLidTableLock;
+
+//
+// KiStack16GdtEntry defines the address of the gdt entry for 16 bit stack.
+//
+
+ULONG KiStack16GdtEntry;
+
+VOID
+KiInitializeAbiosGdtEntry (
+ OUT PKGDTENTRY GdtEntry,
+ IN ULONG Base,
+ IN ULONG Limit,
+ IN USHORT Type
+ )
+
+/*++
+
+Routine Description:
+
+ This function initializes a GDT entry for abios specific code. Base,
+ Limit, and Type (code, data) are set according to parameters. All other
+ fields of the entry are set to match standard system values.
+
+ N.B. The BIG and GRANULARITY are always set to 0.
+
+Arguments:
+
+ GdtEntry - GDT descriptor to be filled in.
+
+ Base - Linear address of the first byte mapped by the selector.
+
+ Limit - Size of the selector in BYTE.
+
+ Type - Code or Data. All code selectors are marked readable,
+ all data selectors are marked writeable.
+
+Return Value:
+
+ Pointer to the GDT entry.
+
+--*/
+
+{
+ GdtEntry->LimitLow = (USHORT)(Limit & 0xffff);
+ GdtEntry->BaseLow = (USHORT)(Base & 0xffff);
+ GdtEntry->HighWord.Bytes.BaseMid = (UCHAR)((Base & 0xff0000) >> 16);
+ GdtEntry->HighWord.Bits.Type = Type;
+ GdtEntry->HighWord.Bits.Dpl = 0;
+ GdtEntry->HighWord.Bits.Pres = 1;
+ GdtEntry->HighWord.Bits.LimitHi = (Limit & 0xf0000) >> 16;
+ GdtEntry->HighWord.Bits.Sys = 0;
+ GdtEntry->HighWord.Bits.Reserved_0 = 0;
+ GdtEntry->HighWord.Bits.Default_Big = 0;
+ GdtEntry->HighWord.Bits.Granularity = 0;
+ GdtEntry->HighWord.Bytes.BaseHi = (UCHAR)((Base & 0xff000000) >> 24);
+}
+
+ULONG
+KiI386SelectorBase (
+ IN USHORT Selector
+ )
+
+/*++
+
+Routine Description:
+
+ This function returns the base address of the specified GDT selector.
+
+Arguments:
+
+ Selector - Supplies the desired selector.
+
+Return Value:
+
+ SelectorBase - Return the base address of the specified selector;
+ (return -1L if invalid selector)
+
+
+--*/
+
+{
+ PKGDTENTRY GdtEntry;
+
+
+ GdtEntry = (PKGDTENTRY)(KiAbiosGetGdt() + Selector);
+ if (GdtEntry->HighWord.Bits.Pres) {
+ return ((ULONG)GdtEntry->BaseLow |
+ (ULONG)GdtEntry->HighWord.Bytes.BaseMid << 16 |
+ (ULONG)GdtEntry->HighWord.Bytes.BaseHi << 24);
+ } else {
+ return (ULONG)(-1L);
+ }
+}
+
+NTSTATUS
+KeI386GetLid(
+ IN USHORT DeviceId,
+ IN USHORT RelativeLid,
+ IN BOOLEAN SharedLid,
+ IN PDRIVER_OBJECT DriverObject,
+ OUT PUSHORT LogicalId
+ )
+
+/*++
+
+Routine Description:
+
+ This function searches Device Blocks and Common Data Area for the
+ Logical Id matching the specified Device Id.
+
+ N.B. (WARNING shielint) To speed the search, this routine ASSUMES that
+ the LIDs with the same Device ID always appear consecutively in the
+ Common Data Area. IBM ABIOS doc does not explicitly specify this.
+ But from the way ABIOS initializes Device Block and Function Transfer
+ Table, I think the assumption is true.
+
+Arguments:
+
+ DeviceId - Desired Device Id.
+
+ RelativeLid - Specifies the Nth logical Id for this device Id. A value
+ of 0 indicates the first available Lid.
+
+ SharedLid - A boolean value indicates if it is a shared or exclusively
+ owned logical Id.
+
+ DriverObject - Supplies a 32-bit flat pointer of the requesting device
+ driver's driver object. The DriverObject is used to establish
+ the ownership of the desired LID.
+
+ LogicalId - A pointer to a variable which will receive the Lid.
+
+Return Value:
+
+ STATUS_SUCCESS - If the requested LID is available.
+
+ STATUS_ABIOS_NOT_PRESENT - If there is no ABIOS support in the system.
+
+ STATUS_ABIOS_LID_NOT_EXIST - If the specified LID does not exist.
+
+ STATUS_ABIOS_LID_ALREADY_OWNED - If the caller requests an exclusively
+ owned LID.
+
+--*/
+
+{
+ PKDB_FTT_SECTION CdaPointer;
+ PKDEVICE_BLOCK DeviceBlock;
+ USHORT Lid, RelativeLidCount = 1;
+ ULONG Owner;
+ USHORT Increment;
+ KIRQL OldIrql;
+ NTSTATUS Status;
+
+ if (!KiAbiosPresent) {
+ return STATUS_ABIOS_NOT_PRESENT;
+ }
+
+ if (SharedLid) {
+ Owner = LID_NO_SPECIFIC_OWNER;
+ Increment = 1;
+ } else {
+ Owner = (ULONG)DriverObject;
+ Increment = 0;
+ }
+
+ //
+ // If the Logical Id Table hasn't been created yet, create it now.
+ //
+ if (KiLogicalIdTable==NULL) {
+ KiLogicalIdTable = ExAllocatePool(NonPagedPool,
+ NUMBER_LID_TABLE_ENTRIES *
+ sizeof(KLID_TABLE_ENTRY));
+ if (KiLogicalIdTable == NULL) {
+ return(STATUS_NO_MEMORY);
+ }
+ RtlZeroMemory(KiLogicalIdTable, NUMBER_LID_TABLE_ENTRIES*sizeof(KLID_TABLE_ENTRY));
+ }
+
+ //
+ // For each Lid defined in Common Data Area, we check if it has non
+ // empty device block and function transfer table. If yes, we proceed
+ // to check the device id. Otherwise, we skip the Lid.
+ //
+
+ CdaPointer = (PKDB_FTT_SECTION)KiCommonDataArea + 2;
+ Status = STATUS_ABIOS_LID_NOT_EXIST;
+
+ ExAcquireSpinLock(&KiAbiosLidTableLock, &OldIrql);
+
+ for (Lid = 2; Lid < KiCommonDataArea->NumberLids; Lid++) {
+ if (CdaPointer->DeviceBlock.Selector != 0 &&
+ CdaPointer->FunctionTransferTable.Selector != 0) {
+
+ DeviceBlock = (PKDEVICE_BLOCK)(KiI386SelectorBase(
+ CdaPointer->DeviceBlock.Selector)
+ + (CdaPointer->DeviceBlock.Offset));
+ if (DeviceBlock->DeviceId == DeviceId) {
+ if (RelativeLid == RelativeLidCount || RelativeLid == 0) {
+ if (KiLogicalIdTable[Lid].Owner == 0L) {
+ KiLogicalIdTable[Lid].Owner = Owner;
+ KiLogicalIdTable[Lid].OwnerCount += Increment;
+ *LogicalId = Lid;
+ Status = STATUS_SUCCESS;
+ } else if (KiLogicalIdTable[Lid].Owner == LID_NO_SPECIFIC_OWNER) {
+ if (SharedLid) {
+ *LogicalId = Lid;
+ KiLogicalIdTable[Lid].OwnerCount += Increment;
+ Status = STATUS_SUCCESS;
+ } else {
+ Status = STATUS_ABIOS_LID_ALREADY_OWNED;
+ }
+ } else if (KiLogicalIdTable[Lid].Owner == (ULONG)DriverObject) {
+ *LogicalId = Lid;
+ Status = STATUS_SUCCESS;
+ } else if (RelativeLid != 0) {
+ Status = STATUS_ABIOS_LID_ALREADY_OWNED;
+ }
+ break;
+ } else {
+ RelativeLidCount++;
+ }
+ }
+ }
+ CdaPointer++;
+ }
+
+ ExReleaseSpinLock(&KiAbiosLidTableLock, OldIrql);
+ return Status;
+}
+
+NTSTATUS
+KeI386ReleaseLid(
+ IN USHORT LogicalId,
+ IN PDRIVER_OBJECT DriverObject
+ )
+
+/*++
+
+Routine Description:
+
+ This function releases a logical Id. This routine is called at ABIOS
+ device driver destallation or termination.
+
+Arguments:
+
+ LogicalId - Logical Id to be released.
+
+ DriverObject - Supplies a 32-bit flat pointer of the requesting device
+ driver's driver object. The DriverObject is used to check
+ the ownership of the specified LID.
+
+Return Value:
+
+ STATUS_SUCCESS - If the requested LID is released.
+
+ STATUS_ABIOS_NOT_PRESENT - If there is no ABIOS support in the system.
+
+ STATUS_ABIOS_NOT_LID_OWNER - If the caller does not own the LID.
+
+--*/
+
+{
+ KIRQL OldIrql;
+ NTSTATUS Status;
+
+ if (!KiAbiosPresent) {
+ return STATUS_ABIOS_NOT_PRESENT;
+ }
+
+ ExAcquireSpinLock(&KiAbiosLidTableLock, &OldIrql);
+
+ if (KiLogicalIdTable[LogicalId].Owner == (ULONG)DriverObject) {
+ KiLogicalIdTable[LogicalId].Owner = 0L;
+ Status = STATUS_SUCCESS;
+ } else if (KiLogicalIdTable[LogicalId].Owner == LID_NO_SPECIFIC_OWNER) {
+ KiLogicalIdTable[LogicalId].OwnerCount--;
+ if (KiLogicalIdTable[LogicalId].OwnerCount == 0L) {
+ KiLogicalIdTable[LogicalId].Owner = 0L;
+ }
+ Status = STATUS_SUCCESS;
+ } else {
+ Status = STATUS_ABIOS_NOT_LID_OWNER;
+ }
+
+ ExReleaseSpinLock(&KiAbiosLidTableLock, OldIrql);
+
+ return Status;
+}
+
+NTSTATUS
+KeI386AbiosCall(
+ IN USHORT LogicalId,
+ IN PDRIVER_OBJECT DriverObject,
+ IN PUCHAR RequestBlock,
+ IN USHORT EntryPoint
+ )
+
+/*++
+
+Routine Description:
+
+ This function calls an ABIOS service routine on behave of device driver
+ using Operating System Transfer Convension.
+
+Arguments:
+
+ LogicalId - Logical Id for the call.
+
+ DriverObject - Supplies a 32-bit flat pointer of the requesting device
+ driver's driver object. The DriverObject is used to verify
+ the ownership of the desired LID.
+
+ RequestBlock - A 16:16 (selector:offset) pointer to the request block.
+
+ EntryPoint - Specifies which ABIOS entry point:
+
+ 0 - Start Routine
+ 1 - Interrupt Routine
+ 2 - Timeout Routine
+
+Return Value:
+
+ STATUS_SUCCESS - If no error.
+
+ STATUS_ABIOS_NOT_PRESENT - If there is no ABIOS support in the system.
+
+ STATUS_ABIOS_INVALID_COMMAND - if the specified entry point is not supported.
+
+ STATUS_ABIOS_INVALID_LID - If the Lid specified is invalid.
+
+ STATUS_ABIOS_NOT_LID_OWNER - If the caller does not own this Lid.
+
+ (Note that the request specific ABIOS returned code is in RequestBlock.)
+
+--*/
+
+{
+
+ KABIOS_POINTER FuncTransferTable;
+ KABIOS_POINTER DeviceBlock;
+ KABIOS_POINTER AbiosFunction;
+ PKFUNCTION_TRANSFER_TABLE FttPointer;
+
+ if (!KiAbiosPresent) {
+ return STATUS_ABIOS_NOT_PRESENT;
+ }
+
+ if (LogicalId >= KiCommonDataArea->NumberLids) {
+ return STATUS_ABIOS_INVALID_LID;
+ } else if (KiLogicalIdTable[LogicalId].Owner != (ULONG)DriverObject &&
+ KiLogicalIdTable[LogicalId].Owner != LID_NO_SPECIFIC_OWNER) {
+ return STATUS_ABIOS_NOT_LID_OWNER;
+ } else if (EntryPoint > 2) {
+ return STATUS_ABIOS_INVALID_COMMAND;
+ }
+
+ FuncTransferTable = ((PKDB_FTT_SECTION)KiCommonDataArea + LogicalId)->
+ FunctionTransferTable;
+ DeviceBlock = ((PKDB_FTT_SECTION)KiCommonDataArea + LogicalId)->DeviceBlock;
+ FttPointer = (PKFUNCTION_TRANSFER_TABLE)(KiI386SelectorBase(FuncTransferTable.Selector) +
+ (ULONG)FuncTransferTable.Offset);
+ AbiosFunction = FttPointer->CommonRoutine[EntryPoint];
+ KiI386CallAbios(AbiosFunction,
+ DeviceBlock,
+ FuncTransferTable,
+ *(PKABIOS_POINTER)&RequestBlock
+ );
+
+ return STATUS_SUCCESS;
+}
+
+NTSTATUS
+KeI386AllocateGdtSelectors(
+ OUT PUSHORT SelectorArray,
+ IN USHORT NumberOfSelectors
+ )
+
+/*++
+
+Routine Description:
+
+ This function allocates a set of GDT selectors for a device driver to use.
+ Usually this allocation is performed at device driver initialization time
+ to reserve the selectors for later use.
+
+Arguments:
+
+ SelectorArray - Supplies a pointer to an array of USHORT to be filled
+ in with the GDT selectors allocated.
+
+ NumberOfSelectors - Specifies the number of selectors to be allocated.
+
+Return Value:
+
+ STATUS_SUCCESS - If the requested selectors are allocated.
+
+ STATUS_ABIOS_SELECTOR_NOT_AVAILABLE - if systen can not allocate the number
+ of selectors requested.
+
+--*/
+
+{
+ PKFREE_GDT_ENTRY GdtEntry;
+ KIRQL OldIrql;
+
+ if (KiNumberFreeSelectors >= NumberOfSelectors) {
+ ExAcquireSpinLock(&KiAbiosGdtLock, &OldIrql);
+
+ //
+ // The Free Gdt link list is maintained on Processor 0's GDT ONLY.
+ // Because the 'selector' is an offset to the beginning of GDT and
+ // it should be the same accross all the processors.
+ //
+
+ KiNumberFreeSelectors -= NumberOfSelectors;
+ GdtEntry = KiFreeGdtListHead;
+ while (NumberOfSelectors != 0) {
+ *SelectorArray++ = (USHORT)((ULONG)GdtEntry - KiAbiosGdt[0]);
+ GdtEntry = GdtEntry->Flink;
+ NumberOfSelectors--;
+ }
+ KiFreeGdtListHead = GdtEntry;
+ ExReleaseSpinLock(&KiAbiosGdtLock, OldIrql);
+ return STATUS_SUCCESS;
+ } else {
+ return STATUS_ABIOS_SELECTOR_NOT_AVAILABLE;
+ }
+}
+
+NTSTATUS
+KeI386ReleaseGdtSelectors(
+ OUT PUSHORT SelectorArray,
+ IN USHORT NumberOfSelectors
+ )
+
+/*++
+
+Routine Description:
+
+ This function releases a set of GDT selectors for a device driver.
+ Usually this function is called at device driver termination or
+ deinstallation time.
+
+Arguments:
+
+ SelectorArray - Supplies a pointer to an array of USHORT selectors
+ to be freed.
+
+ NumberOfSelectors - Specifies the number of selectors to be released.
+
+Return Value:
+
+ STATUS_SUCCESS - If the requested LID is released.
+
+--*/
+{
+ PKFREE_GDT_ENTRY GdtEntry;
+ KIRQL OldIrql;
+ ULONG Gdt;
+
+ ExAcquireSpinLock(&KiAbiosGdtLock, &OldIrql);
+
+ //
+ // The Free Gdt link list is maintained on Processor 0's GDT ONLY.
+ // Because the 'selector' is an offset to the beginning of GDT and
+ // it should be the same accross all the processors.
+ //
+
+ KiNumberFreeSelectors += NumberOfSelectors;
+ Gdt = KiAbiosGdt[0];
+ while (NumberOfSelectors != 0) {
+ GdtEntry = (PKFREE_GDT_ENTRY)(Gdt + *SelectorArray++);
+ GdtEntry->Flink = KiFreeGdtListHead;
+ KiFreeGdtListHead = GdtEntry;
+ NumberOfSelectors--;
+ }
+ ExReleaseSpinLock(&KiAbiosGdtLock, OldIrql);
+ return STATUS_SUCCESS;
+}
+
+NTSTATUS
+KeI386FlatToGdtSelector(
+ IN ULONG SelectorBase,
+ IN USHORT Length,
+ IN USHORT Selector
+ )
+
+/*++
+
+Routine Description:
+
+ This function converts a 32-bit flat address to a GDT selector-offset
+ pair. The segment set up is always 16-bit ring 0 data segment.
+
+Arguments:
+
+ SelectorBase - Supplies 32 bit flat address to be set as the base address
+ of the desired selector.
+
+ Length - Supplies the Length of the segment. The Length is a 16 bit value
+ and zero means 64KB.
+
+ Selector - Supplies the selector to be set up.
+
+Return Value:
+
+ STATUS_SUCCESS - If the requested LID is released.
+
+ STATUS_ABIOS_NOT_PRESENT - If there is no ABIOS support in the system.
+
+ STATUS_ABIOS_INVALID_SELECTOR - If the selector supplied is invalid.
+
+
+--*/
+
+{
+ PKGDTENTRY GdtEntry, GdtEntry1;
+ KIRQL OldIrql;
+ ULONG i;
+
+ if (!KiAbiosPresent) {
+ return STATUS_ABIOS_NOT_PRESENT;
+ }
+ if (Selector < RESERVED_GDT_ENTRIES * sizeof(KGDTENTRY)) {
+ return STATUS_ABIOS_INVALID_SELECTOR;
+ } else {
+ ExAcquireSpinLock(&KiAbiosGdtLock, &OldIrql);
+ GdtEntry = (PKGDTENTRY)(KiAbiosGdt[0] + Selector);
+ GdtEntry->LimitLow = (USHORT)(Length - 1);
+ GdtEntry->BaseLow = LOWWORD(SelectorBase);
+ GdtEntry->HighWord.Bytes.BaseMid = LOWBYTE(HIGHWORD(SelectorBase));
+ GdtEntry->HighWord.Bytes.BaseHi = HIGHBYTE(HIGHWORD(SelectorBase));
+ GdtEntry->HighWord.Bits.Pres = 1;
+ GdtEntry->HighWord.Bits.Type = TYPE_DATA;
+ GdtEntry->HighWord.Bits.Dpl = DPL_SYSTEM;
+ for (i = 1; i < (ULONG)KeNumberProcessors; i++) {
+ GdtEntry1 = (PKGDTENTRY)(KiAbiosGdt[i] + Selector);
+ *GdtEntry1 = *GdtEntry;
+ }
+ ExReleaseSpinLock(&KiAbiosGdtLock, OldIrql);
+ return STATUS_SUCCESS;
+ }
+}
+
+VOID
+Ki386InitializeGdtFreeList (
+ PKFREE_GDT_ENTRY EndOfGdt
+ )
+
+/*++
+
+Routine Description:
+
+ This function initializes gdt free list by linking all the unused gdt
+ entries to a free list.
+
+Arguments:
+
+ EndOfGdt - Supplies the ending address of desired GDT.
+
+Return Value:
+
+ None.
+
+--*/
+{
+ PKFREE_GDT_ENTRY GdtEntry;
+
+ GdtEntry = EndOfGdt - 1;
+ KiFreeGdtListHead = (PKFREE_GDT_ENTRY)0;
+ while (GdtEntry != (PKFREE_GDT_ENTRY)KiAbiosGetGdt() +
+ RESERVED_GDT_ENTRIES - 1) {
+ if (GdtEntry->Present == 0) {
+ GdtEntry->Flink = KiFreeGdtListHead;
+ KiFreeGdtListHead = GdtEntry;
+ KiNumberFreeSelectors++;
+ }
+ GdtEntry--;
+ }
+}
+
+VOID
+KiInitializeAbios (
+ IN UCHAR Processor
+ )
+
+/*++
+
+Routine Description:
+
+ This function initializes gdt free list and sets up selector for
+ KiI386AbiosCall (16-bit code).
+
+Arguments:
+
+ Processor - the processor who performs the initialization.
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+
+ ULONG GdtLength;
+ PKGDTENTRY AliasGdtSelectorEntry;
+ PKFREE_GDT_ENTRY EndOfGdt;
+
+ //
+ // First check if abios is recognized by osloader.
+ //
+
+ KiCommonDataArea = KeLoaderBlock->u.I386.CommonDataArea;
+
+ //
+ // NOTE For now we want to disable ABIOS support on MP.
+ //
+
+ if (KiCommonDataArea == NULL || Processor != 0) {
+ KiAbiosPresent = FALSE;
+ } else {
+ KiAbiosPresent = TRUE;
+ }
+
+ //
+ // Initialize the spinlocks for accessing GDTs and Lid Table.
+ //
+
+ KeInitializeSpinLock( &KiAbiosGdtLock );
+ KeInitializeSpinLock( &KiAbiosLidTableLock );
+
+ //
+ // Determine the starting and ending addresses of GDT.
+ //
+
+ KiAbiosGdt[Processor] = KiAbiosGetGdt();
+
+ AliasGdtSelectorEntry = (PKGDTENTRY)(KiAbiosGetGdt() + KGDT_GDT_ALIAS);
+ GdtLength = 1 + (ULONG)(AliasGdtSelectorEntry->LimitLow) +
+ (ULONG)(AliasGdtSelectorEntry->HighWord.Bits.LimitHi << 16);
+ EndOfGdt = (PKFREE_GDT_ENTRY)(KiAbiosGetGdt() + GdtLength);
+
+ //
+ // Prepare selector for 16 bit stack segment
+ //
+
+ KiStack16GdtEntry = KiAbiosGetGdt() + KGDT_STACK16;
+
+ KiInitializeAbiosGdtEntry(
+ (PKGDTENTRY)KiStack16GdtEntry,
+ 0L,
+ 0xffff,
+ TYPE_DATA
+ );
+
+ //
+ // Establish the addressability of Common Data Area selector.
+ //
+
+ KiInitializeAbiosGdtEntry(
+ (PKGDTENTRY)(KiAbiosGetGdt() + KGDT_CDA16),
+ (ULONG)KiCommonDataArea,
+ 0xffff,
+ TYPE_DATA
+ );
+
+ //
+ // Set up 16-bit code selector for KiI386AbiosCall
+ //
+
+ KiInitializeAbiosGdtEntry(
+ (PKGDTENTRY)(KiAbiosGetGdt() + KGDT_CODE16),
+ (ULONG)&KiI386CallAbios,
+ (ULONG)&KiEndOfCode16 - (ULONG)&KiI386CallAbios - 1,
+ 0x18 // TYPE_CODE
+ );
+
+ //
+ // Link all the unused GDT entries to our GDT free list.
+ //
+
+ if (Processor == 0) {
+ Ki386InitializeGdtFreeList(EndOfGdt);
+ }
+}
diff --git a/private/ntos/ke/i386/allproc.c b/private/ntos/ke/i386/allproc.c
new file mode 100644
index 000000000..410c58b58
--- /dev/null
+++ b/private/ntos/ke/i386/allproc.c
@@ -0,0 +1,397 @@
+/*++
+
+Copyright (c) 1989 Microsoft Corporation
+
+Module Name:
+
+ allproc.c
+
+Abstract:
+
+ This module allocates and intializes kernel resources required
+ to start a new processor, and passes a complete process_state
+ structre to the hal to obtain a new processor. This is done
+ for every processor.
+
+Author:
+
+ Ken Reneris (kenr) 22-Jan-92
+
+Environment:
+
+ Kernel mode only.
+ Phase 1 of bootup
+
+Revision History:
+
+--*/
+
+
+#include "ki.h"
+
+#ifdef NT_UP
+
+VOID
+KeStartAllProcessors (
+ VOID
+ )
+{
+ // UP Build - this function is a nop
+}
+
+#else
+
+extern ULONG KeRegisteredProcessors;
+
+static VOID
+KiCloneDescriptor (
+ IN PKDESCRIPTOR pSrcDescriptorInfo,
+ IN PKDESCRIPTOR pDestDescriptorInfo
+ );
+
+static VOID
+KiCloneSelector (
+ IN ULONG SrcSelector,
+ IN PKGDTENTRY pNGDT,
+ IN PKDESCRIPTOR pDestDescriptor
+ );
+
+
+#ifdef ALLOC_PRAGMA
+#pragma alloc_text(INIT,KeStartAllProcessors)
+#pragma alloc_text(INIT,KiCloneDescriptor)
+#pragma alloc_text(INIT,KiCloneSelector)
+#endif
+
+#if !defined(NT_UP)
+
+ULONG KiBarrierWait = 0;
+
+#endif
+
+
+
+VOID
+KeStartAllProcessors (
+ VOID
+ )
+/*++
+
+Routine Description:
+
+ Called by p0 during phase 1 of bootup. This function implements
+ the x86 specific code to contact the hal for each system processor.
+
+Arguments:
+
+Return Value:
+
+ All available processors are sent to KiSystemStartup.
+
+--*/
+{
+ KPROCESSOR_STATE ProcessorState;
+ KDESCRIPTOR Descriptor;
+ KDESCRIPTOR TSSDesc, DFTSSDesc, NMITSSDesc, PCRDesc;
+ PKGDTENTRY pGDT;
+ PUCHAR pStack;
+ ULONG DFStack;
+ PUCHAR pThreadObject;
+ PULONG pTopOfStack;
+ ULONG NewProcessorNumber;
+ BOOLEAN NewProcessor;
+ PKPROCESS Process;
+ PKTHREAD Thread;
+ PKTSS pTSS;
+ PLIST_ENTRY NextEntry;
+ LONG NumberProcessors;
+
+ //
+ // If the registered number of processors is greater than the maximum
+ // number of processors supported, then only allow the maximum number
+ // of supported processors.
+ //
+
+ if (KeRegisteredProcessors > MAXIMUM_PROCESSORS) {
+ KeRegisteredProcessors = MAXIMUM_PROCESSORS;
+ }
+
+ //
+ // Set barrier that will prevent any other processor from entering the
+ // idle loop until all processors have been started.
+ //
+
+ KiBarrierWait = 1;
+
+
+ while ((ULONG)KeNumberProcessors < KeRegisteredProcessors) {
+ //
+ // Build up a processor state for new processor
+ //
+
+ RtlZeroMemory ((PVOID) &ProcessorState, sizeof ProcessorState);
+
+
+ //
+ // Give the new processor it's own GDT
+ //
+
+ _asm {
+ sgdt Descriptor.Limit
+ }
+
+ KiCloneDescriptor (&Descriptor,
+ &ProcessorState.SpecialRegisters.Gdtr);
+
+ pGDT = (PKGDTENTRY) ProcessorState.SpecialRegisters.Gdtr.Base;
+
+
+ //
+ // Give new processor it's own IDT
+ //
+
+ _asm {
+ sidt Descriptor.Limit
+ }
+ KiCloneDescriptor (&Descriptor,
+ &ProcessorState.SpecialRegisters.Idtr);
+
+
+ //
+ // Give new processor it's own TSS and PCR
+ //
+
+ KiCloneSelector (KGDT_TSS, pGDT, &TSSDesc);
+ KiCloneSelector (KGDT_R0_PCR, pGDT, &PCRDesc);
+
+ //
+ // Allocate double-fault TSS & stack, and NMI TSS
+ //
+
+ KiCloneSelector (KGDT_DF_TSS, pGDT, &DFTSSDesc);
+ DFStack = (ULONG)ExAllocatePool(NonPagedPool, DOUBLE_FAULT_STACK_SIZE);
+ pTSS = (PKTSS)DFTSSDesc.Base;
+ pTSS->Esp0 = DFStack + DOUBLE_FAULT_STACK_SIZE;
+ pTSS->NotUsed2[5] = DFStack + DOUBLE_FAULT_STACK_SIZE;
+
+ KiCloneSelector (KGDT_NMI_TSS, pGDT, &NMITSSDesc);
+ pTSS = (PKTSS)NMITSSDesc.Base;
+ pTSS->Esp0 = DFStack + DOUBLE_FAULT_STACK_SIZE;
+ pTSS->NotUsed2[5] = DFStack + DOUBLE_FAULT_STACK_SIZE;
+
+
+ //
+ // Set other SpecialRegisters in processor state
+ //
+
+ _asm {
+ mov eax, cr0
+ and eax, NOT (CR0_AM or CR0_WP)
+ mov ProcessorState.SpecialRegisters.Cr0, eax
+ mov eax, cr3
+ mov ProcessorState.SpecialRegisters.Cr3, eax
+
+ pushfd
+ pop ProcessorState.ContextFrame.EFlags
+ and ProcessorState.ContextFrame.EFlags, NOT EFLAGS_INTERRUPT_MASK
+ }
+
+ ProcessorState.SpecialRegisters.Tr = KGDT_TSS;
+ pGDT[KGDT_TSS>>3].HighWord.Bytes.Flags1 = 0x89;
+
+
+ //
+ // Allocate a kernel stack and ThreadObject for the new processor
+ //
+
+ pStack = (PUCHAR)MmCreateKernelStack (FALSE);
+ pThreadObject = (PUCHAR)ExAllocatePool (NonPagedPool, sizeof(ETHREAD));
+
+ //
+ // Zero initialize these...
+ //
+
+ RtlZeroMemory ((PVOID) PCRDesc.Base, sizeof (KPCR));
+ RtlZeroMemory ((PVOID) pThreadObject, sizeof (KTHREAD));
+
+
+ //
+ // Setup context
+ // Push varibles onto new stack
+ //
+
+ pTopOfStack = (PULONG) pStack;
+ pTopOfStack[-1] = (ULONG) KeLoaderBlock;
+ ProcessorState.ContextFrame.Esp = (ULONG) (pTopOfStack-2);
+ ProcessorState.ContextFrame.Eip = (ULONG) KiSystemStartup;
+
+ ProcessorState.ContextFrame.SegCs = KGDT_R0_CODE;
+ ProcessorState.ContextFrame.SegDs = KGDT_R3_DATA;
+ ProcessorState.ContextFrame.SegEs = KGDT_R3_DATA;
+ ProcessorState.ContextFrame.SegFs = KGDT_R0_PCR;
+ ProcessorState.ContextFrame.SegSs = KGDT_R0_DATA;
+
+
+ //
+ // Initialize new processors PCR & Prcb
+ //
+
+ NewProcessorNumber = KeNumberProcessors;
+ KiInitializePcr (
+ (ULONG) NewProcessorNumber,
+ (PKPCR) PCRDesc.Base,
+ (PKIDTENTRY) ProcessorState.SpecialRegisters.Idtr.Base,
+ (PKGDTENTRY) ProcessorState.SpecialRegisters.Gdtr.Base,
+ (PKTSS) TSSDesc.Base,
+ (PKTHREAD) pThreadObject
+ );
+
+
+ //
+ // Adjust LoaderBlock so it has the next processors state
+ //
+
+ KeLoaderBlock->KernelStack = (ULONG) pTopOfStack;
+ KeLoaderBlock->Thread = (ULONG) pThreadObject;
+ KeLoaderBlock->Prcb = (ULONG) ((PKPCR) PCRDesc.Base)->Prcb;
+
+
+ //
+ // Contact hal to start new processor
+ //
+
+ NewProcessor = HalStartNextProcessor (KeLoaderBlock, &ProcessorState);
+
+
+ if (!NewProcessor) {
+ //
+ // There wasn't another processor, so free resources and
+ // break
+ //
+
+ ExFreePool ((PVOID) ProcessorState.SpecialRegisters.Gdtr.Base);
+ ExFreePool ((PVOID) ProcessorState.SpecialRegisters.Idtr.Base);
+ ExFreePool ((PVOID) TSSDesc.Base);
+ ExFreePool ((PVOID) DFTSSDesc.Base);
+ ExFreePool ((PVOID) NMITSSDesc.Base);
+ ExFreePool ((PVOID) PCRDesc.Base);
+ ExFreePool ((PVOID) pThreadObject);
+ ExFreePool ((PVOID) DFStack);
+ MmDeleteKernelStack ((PVOID) pStack, FALSE);
+ break;
+ }
+
+
+ //
+ // Wait for processor to initialize in kernel, then loop for another
+ //
+
+ while (*((volatile ULONG *) &KeLoaderBlock->Prcb) != 0)
+ { }
+ }
+
+ //
+ // Reset and synchronize the performance counters of all processors.
+ //
+
+ NumberProcessors = KeNumberProcessors;
+ KiIpiGenericCall (
+ (PKIPI_BROADCAST_WORKER) HalCalibratePerformanceCounter,
+ (ULONG)(&NumberProcessors)
+ );
+
+ //
+ // Allow all processor that were started to enter the idle loop and
+ // begin execution.
+ //
+
+ KiBarrierWait = 0;
+}
+
+
+
+static VOID
+KiCloneSelector (
+ IN ULONG SrcSelector,
+ IN PKGDTENTRY pNGDT,
+ IN PKDESCRIPTOR pDestDescriptor
+/*++
+
+Routine Description:
+
+ Makes a copy of the current selectors data, and updated the new
+ gdt's linear address to point to the new copy.
+
+Arguments:
+ SrcSelector - Selector value to clone
+ pNGDT - New gdt table which is being built
+ DescDescriptor - descriptor structure to fill in with resulting memory
+
+Return Value:
+
+--*/
+ )
+{
+ KDESCRIPTOR Descriptor;
+ PKGDTENTRY pGDT;
+ ULONG CurrentBase;
+ ULONG NewBase;
+
+ _asm {
+ sgdt fword ptr [Descriptor.Limit] ; Get GDTs addr
+ }
+
+ pGDT = (PKGDTENTRY) Descriptor.Base;
+ pGDT += SrcSelector >> 3;
+ pNGDT += SrcSelector >> 3;
+
+ CurrentBase = pGDT->BaseLow | (pGDT->HighWord.Bits.BaseMid << 16) |
+ (pGDT->HighWord.Bits.BaseHi << 24);
+
+ Descriptor.Base = CurrentBase;
+ Descriptor.Limit = pGDT->LimitLow;
+ if (pGDT->HighWord.Bits.Granularity & GRAN_PAGE)
+ Descriptor.Limit = (Descriptor.Limit << PAGE_SHIFT) -1;
+
+ KiCloneDescriptor (&Descriptor, pDestDescriptor);
+ NewBase = pDestDescriptor->Base;
+
+ pNGDT->BaseLow = (USHORT) NewBase & 0xffff;
+ pNGDT->HighWord.Bits.BaseMid = (UCHAR) (NewBase >> 16) & 0xff;
+ pNGDT->HighWord.Bits.BaseHi = (UCHAR) (NewBase >> 24) & 0xff;
+}
+
+
+
+static VOID
+KiCloneDescriptor (
+ IN PKDESCRIPTOR pSrcDescriptor,
+ IN PKDESCRIPTOR pDestDescriptor
+ )
+/*++
+
+Routine Description:
+
+ Makes a copy of the specified descriptor, and supplies a return
+ descriptor for the new copy
+
+Arguments:
+ pSrcDescriptor - descriptor to clone
+ pDescDescriptor - the cloned descriptor
+
+Return Value:
+
+--*/
+{
+ ULONG Size;
+
+ Size = pSrcDescriptor->Limit + 1;
+ pDestDescriptor->Limit = (USHORT) Size -1;
+ pDestDescriptor->Base = (ULONG) ExAllocatePool (NonPagedPool, Size);
+
+ RtlMoveMemory ((PVOID) pDestDescriptor->Base,
+ (PVOID) pSrcDescriptor->Base, Size);
+}
+
+
+#endif // !NT_UP
diff --git a/private/ntos/ke/i386/alr.inc b/private/ntos/ke/i386/alr.inc
new file mode 100644
index 000000000..8491a3aee
--- /dev/null
+++ b/private/ntos/ke/i386/alr.inc
@@ -0,0 +1,87 @@
+;++
+;
+; Copyright (c) 1989 Microsoft Corporation
+;
+; Module Name:
+;
+; alr.inc
+;
+; Abstract:
+;
+; This inlcude file defines all the equates and macros specifically
+; used for ALR Multiprocessor system implementation.
+;
+; Author:
+;
+; Shie-Lin Tzong (shielint) 29-Oct-1990
+;
+; Environment:
+;
+; Kernel mode only.
+;
+; Revision History:
+;
+;--
+
+;
+; Virtual address map for ALR CBUS IO and address space
+; CBUS memory address space starting from 64MB to (128MB - 1)
+; CBUS IO space starting from 128MB to (192MB - 1). We are interested
+; in the first 4MB only (at least for now).
+;
+
+CBUS_ADDR_START_PHYS equ 4000000h
+
+CBUS_IO_SPACE_START equ 90000000h
+CBUS_IO_SPACE_START_PHYS equ 8000000h
+CBUS_IO_SPACE_SIZE equ 400000h
+
+;
+;CPU ID for CBUS PEs and common functions for PEs
+;
+
+ALLCPUID equ 0Fh ; ID to address all the slaves
+BASECPUID equ 0Eh ; Base CPU ID
+
+PE_CRESET equ 0 ; Clear Reset
+PE_SRESET equ 1 ; Set Reset
+PE_CONTEND equ 2 ; Contend (Place slot number on ARB0-3
+ ; lines)
+PE_SETIDA equ 3 ; Set ID value on winning processor
+PE_CSWI equ 4 ; Clear software interrupt
+PE_SSWI equ 5 ; Set software interrupt
+PE_CNMI equ 6 ; Clear NMI
+PE_SNMI equ 7 ; Set NMI
+PE_SLED equ 8 ; Set LED
+PE_CLED equ 9 ; Clear LED
+
+;
+; Miscs CBUS definitions
+;
+
+ArbitrateRegister equ 0F1h
+ArbitrateMask equ 0Fh ; Lower 4 bits of Arbitrate Register
+
+
+;
+; Macros to access CBUS I/O space
+;
+; CBUS_IO_ACCESS func, cpuid
+; func - the function which will be applied to PEs
+; cpuid - the desired PE. If not specified, the cpuid is in AL register.
+;
+
+CBUS_IO_ACCESS macro func, cpuid
+
+ifnb <cpuid>
+ mov eax, (cpuid SHL 18) + CBUS_IO_SPACE_START + (func SHL 4)
+else
+ movzx eax, al
+ shl eax, 18
+ add eax, CBUS_IO_SPACE_START + (func SHL 4)
+endif
+ or byte ptr [eax], 0ffh
+
+ endm
+
+
diff --git a/private/ntos/ke/i386/apcuser.c b/private/ntos/ke/i386/apcuser.c
new file mode 100644
index 000000000..14d327ec0
--- /dev/null
+++ b/private/ntos/ke/i386/apcuser.c
@@ -0,0 +1,169 @@
+/*++
+
+Copyright (c) 1990 Microsoft Corporation
+
+Module Name:
+
+ apcuser.c
+
+Abstract:
+
+ This module implements the machine dependent code necessary to initialize
+ a user mode APC.
+
+Author:
+
+ David N. Cutler (davec) 23-Apr-1990
+
+Environment:
+
+ Kernel mode only, IRQL APC_LEVEL.
+
+Revision History:
+
+--*/
+
+#include "ki.h"
+
+VOID
+KiInitializeUserApc (
+ IN PKEXCEPTION_FRAME ExceptionFrame,
+ IN PKTRAP_FRAME TrapFrame,
+ IN PKNORMAL_ROUTINE NormalRoutine,
+ IN PVOID NormalContext,
+ IN PVOID SystemArgument1,
+ IN PVOID SystemArgument2
+ )
+
+/*++
+
+Routine Description:
+
+ This function is called to initialize the context for a user mode APC.
+
+Arguments:
+
+ ExceptionFrame - Supplies a pointer to an exception frame.
+
+ TrapFrame - Supplies a pointer to a trap frame.
+
+ NormalRoutine - Supplies a pointer to the user mode APC routine.
+
+ NormalContext - Supplies a pointer to the user context for the APC
+ routine.
+
+ SystemArgument1 - Supplies the first system supplied value.
+
+ SystemArgument2 - Supplies the second system supplied value.
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+
+ EXCEPTION_RECORD ExceptionRecord;
+ CONTEXT ContextFrame;
+ LONG Length;
+ ULONG UserStack;
+
+
+ //
+ // APCs are not defined for V86 mode; however, it is possible a
+ // thread is trying to set it's context to V86 mode - this isn't
+ // going to work, but we don't want to crash the system so we
+ // check for the possibility before hand.
+ //
+
+ if (TrapFrame->EFlags & EFLAGS_V86_MASK) {
+ return ;
+ }
+
+ //
+ // Move machine state from trap and exception frames to the context frame.
+ //
+
+ ContextFrame.ContextFlags = CONTEXT_FULL | CONTEXT_DEBUG_REGISTERS;
+ KeContextFromKframes(TrapFrame, ExceptionFrame, &ContextFrame);
+
+ //
+ // Transfer the context information to the user stack, initialize the
+ // APC routine parameters, and modify the trap frame so execution will
+ // continue in user mode at the user mode APC dispatch routine.
+ //
+
+
+ try {
+ ASSERT((TrapFrame->SegCs & MODE_MASK) != KernelMode); // Assert usermode frame
+
+ //
+ // Compute length of context record and new aligned user stack pointer.
+ //
+
+ Length = ((sizeof(CONTEXT) + CONTEXT_ROUND) &
+ ~CONTEXT_ROUND) + sizeof(KAPC_RECORD);
+ UserStack = (ContextFrame.Esp & ~CONTEXT_ROUND) - Length;
+
+ //
+ // Probe user stack area for writeability and then transfer the
+ // context record to the user stack.
+ //
+
+ ProbeForWrite((PCHAR)UserStack, Length, CONTEXT_ALIGN);
+ RtlMoveMemory((PULONG)(UserStack + (sizeof(KAPC_RECORD))),
+ &ContextFrame, sizeof(CONTEXT));
+
+ //
+ // Force correct R3 selectors into TrapFrame.
+ //
+
+ TrapFrame->SegCs = SANITIZE_SEG(KGDT_R3_CODE, UserMode);
+ TrapFrame->HardwareSegSs = SANITIZE_SEG(KGDT_R3_DATA, UserMode);
+ TrapFrame->SegDs = SANITIZE_SEG(KGDT_R3_DATA, UserMode);
+ TrapFrame->SegEs = SANITIZE_SEG(KGDT_R3_DATA, UserMode);
+ TrapFrame->SegFs = SANITIZE_SEG(KGDT_R3_TEB, UserMode);
+ TrapFrame->SegGs = 0;
+ TrapFrame->EFlags = SANITIZE_FLAGS( ContextFrame.EFlags, UserMode );
+
+ //
+ // If thread is supposed to have IOPL, then force it on in eflags
+ //
+
+ if (KeGetCurrentThread()->Iopl) {
+ TrapFrame->EFlags |= (EFLAGS_IOPL_MASK & -1); // IOPL = 3
+ }
+
+ //
+ // Set the address of the user APC routine, the APC parameters, the
+ // new frame pointer, and the new stack pointer in the current trap
+ // frame. Set the continuation address so control will be transfered
+ // to the user APC dispatcher.
+ //
+
+ TrapFrame->HardwareEsp = UserStack;
+ TrapFrame->Eip = (ULONG)KeUserApcDispatcher;
+ TrapFrame->ErrCode = 0;
+ *((PULONG)UserStack)++ = (ULONG)NormalRoutine;
+ *((PULONG)UserStack)++ = (ULONG)NormalContext;
+ *((PULONG)UserStack)++ = (ULONG)SystemArgument1;
+ *((PULONG)UserStack)++ = (ULONG)SystemArgument2;
+ } except (KiCopyInformation(&ExceptionRecord,
+ (GetExceptionInformation())->ExceptionRecord)) {
+
+ //
+ // Set the address of the exception to the current program address
+ // and raise the exception by calling the exception dispatcher.
+ //
+
+ ExceptionRecord.ExceptionAddress = (PVOID)(TrapFrame->Eip);
+ KiDispatchException(&ExceptionRecord,
+ ExceptionFrame,
+ TrapFrame,
+ UserMode,
+ TRUE);
+ }
+ return;
+}
+
diff --git a/private/ntos/ke/i386/biosa.asm b/private/ntos/ke/i386/biosa.asm
new file mode 100644
index 000000000..39116271c
--- /dev/null
+++ b/private/ntos/ke/i386/biosa.asm
@@ -0,0 +1,273 @@
+ TITLE "Call Bios support"
+;++
+;
+; Copyright (c) 1989 Microsoft Corporation
+;
+; Module Name:
+;
+; spinlock.asm
+;
+; Abstract:
+;
+; This module implements the support routines for executing int bios
+; call in v86 mode.
+;
+; Author:
+;
+; Shie-Lint Tzong (shielint) Sept 10, 1992
+;
+; Environment:
+;
+; Kernel mode only.
+;
+; Revision History:
+;
+;--
+
+.386p
+
+include ks386.inc
+include callconv.inc ; calling convention macros
+include i386\kimacro.inc
+
+VdmStartExecution EQU 0
+V86_STACK_POINTER equ 11ffeh ; see BIOSC.C
+
+ EXTRNP _NtVdmControl,2
+ extrn _KiExceptionExit:PROC
+
+_TEXT SEGMENT DWORD PUBLIC 'CODE'
+ ASSUME DS:FLAT, ES:FLAT, SS:NOTHING, FS:NOTHING, GS:NOTHING
+
+ PAGE
+ SUBTTL "Switch to V86 mode"
+;++
+;
+; VOID
+; Ki386SetupAndExitTiV86Code (
+; VOID
+; )
+;
+; Routine Description:
+;
+; This function sets up return trap frame, switch stack and
+; calls VdmStartExecution routine to put vdm context to
+; base trap frame and causes the system to execute in v86 mode by
+; doing a KiExceptionExit.
+;
+; Arguments:
+;
+; BiosArguments - Supplies a pointer to a structure which contains
+; the arguments for v86 int function.
+;
+; Return Value:
+;
+; None.
+;
+;--
+
+cPublicProc _Ki386SetupAndExitToV86Code,1
+
+NewTEB equ [ecx+32] ; location of the parameter based on
+ ; the ecx stack pointer.
+KsaeInitialStack equ [ecx]
+OriginalThTeb equ [ecx+4]
+OriginalPcTeb equ [ecx+8]
+
+;
+; Allocate TRAP FRAME at the bottom of the stack.
+;
+
+ push ebp
+ push ebx
+ push esi
+ push edi
+ sub esp, 12 ; 12 bytes for local variable
+ mov ecx, esp ; (ecx) = saved esp
+
+ sub esp, KTRAP_FRAME_LENGTH + NPX_FRAME_LENGTH
+ ; (esp)-> new trap frame
+ mov eax, esp ; (eax)->New base trap frame
+
+;
+; Initialize newly allocated trap frame to caller's nonvolatle context.
+; Note that it is very important that the trap frame we are going to create
+; is a USER mode frame. The system expects the top trap frame for user
+; mode thread is a user mode frame. (Get/SetContext enforce the rule.)
+;
+; (eax)-> Base of trap frame.
+;
+
+ mov dword ptr [eax].TsSegCs, KGDT_R0_CODE OR RPL_MASK
+ ; an invalid cs to trap it back to kernel
+ mov dword ptr [eax].TsSegEs, 0
+ mov dword ptr [eax].TsSegDs, 0
+ mov dword ptr [eax].TsSegFs, 0
+ mov dword ptr [eax].TsSegGs, 0
+ mov dword ptr [eax].TsErrCode, 0
+ mov ebx, fs:PcSelfPcr ; (ebx)->Pcr
+ mov edx, [ebx].PcInitialStack
+ mov KsaeInitialStack, edx ; (edx)->Pcr InitialSack
+
+ mov edi, [ebx]+PcPrcbData+PbCurrentThread ; (edi)->CurrentThread
+ mov edx, [edi].ThTeb
+ mov OriginalThTeb, edx
+
+ mov edx, fs:[PcTeb]
+ mov OriginalPcTeb, edx
+
+ mov edi, offset Ki386BiosCallReturnAddress
+ mov [eax].TsEsi, ecx ; Saved esp
+ mov [eax].TsEip, edi ; set up return address
+ pushfd
+ pop edi
+ and edi, 60dd7h
+ or edi, 200h ; sanitize EFLAGS
+ mov dword ptr [eax].TsHardwareSegSs, KGDT_R3_DATA OR RPL_MASK
+ mov dword ptr [eax].TsHardwareEsp, V86_STACK_POINTER
+ mov [eax].TsEflags, edi
+ mov [eax].TsExceptionList, EXCEPTION_CHAIN_END
+ mov [eax].TsPreviousPreviousMode, 0ffffffffh ; No previous mode
+if DBG
+ mov [eax].TsDbgArgMark, 0BADB0D00h ; set trap frame mark
+endif
+
+;
+; Initialize NpxState of NPX save area to zero value
+;
+
+ add eax, KTRAP_FRAME_LENGTH
+ mov dword ptr [eax]+FpCr0NpxState, 0
+
+;
+; Disable interrupt and change the stack pointer to make the new
+; trap frame be the current thread's base trap frame.
+;
+; (eax)->Npx save area
+;
+
+ mov edi, [ebx]+PcPrcbData+PbCurrentThread ; (edi)->CurrentThread
+ cli
+
+;
+; Set up various stack pointers
+;
+; Low | |
+; |-----------| <- New esp
+; | New Base |
+; |Trap Frame |
+; |-----------| <- Tss.Esp0
+; |V86 segs |
+; |-----------| <- Pcr.InitialStack
+; |Npx Area |
+; |-----------| <- Old Esp = Thread.InitialStack
+; | |
+; High | |
+;
+
+ mov [ebx].PcInitialStack, eax
+ mov esi,[ebx]+PcTss ; (esi)->TSS
+ sub eax,TsV86Gs - TsHardwareSegSs ; bias for missing fields
+ mov [ebx].PcExceptionList, EXCEPTION_CHAIN_END
+ mov [esi]+TssEsp0,eax
+ add eax, NPX_FRAME_LENGTH + (TsV86Gs - TsHardwareSegSs)
+ mov [edi].ThInitialStack, eax
+
+;
+; Set up the pointers to the fake TEB so we can execute the int10
+; call
+;
+ mov eax, NewTeb
+ mov fs:[PcTeb], eax
+ mov [edi].ThTeb, eax
+
+ mov ebx, PCR[PcGdt]
+ mov [ebx]+(KGDT_R3_TEB+KgdtBaseLow), ax
+ shr eax, 16
+ mov [ebx]+(KGDT_R3_TEB+KgdtBaseMid), al
+ mov [ebx]+(KGDT_R3_TEB+KgdtBaseHi), ah
+
+ sti
+
+; Now call VdmControl to save return 32bit frame and put vdm context
+; to new base trap frame
+
+ stdCall _NtVdmControl, <VdmStartExecution, 0>
+
+if 0
+;
+; Now call _VdmpStartExecution to save return 32bit frame and put vdm context
+; to new base trap frame
+;
+
+ mov eax, ExecAddr
+ stdCall _VdmpStartExecution, <eax>
+endif
+
+;
+; Call KiexceptionExit to 'exit' to v86 code.
+;
+
+ mov ebp, esp ; (ebp)->Exit trap frame
+ jmp _KiExceptionExit ; go execute int 10
+
+ public Ki386BiosCallReturnAddress
+Ki386BiosCallReturnAddress:
+
+;
+; After ROM BIOS int completes, the bop instruction gets executed.
+; This results in a trap to kernel mode bop handler where the
+; 16 bit Vdm context will be saved to VdmTib->VdmCOntext, and
+; the faked 32 bit user mode context (i.e. the one we created earlier)
+; be restored. Since the faked user mode context does NOT have a valid
+; iret address, the 'iret' instruction of the EXIT_ALL will be trapped to
+; our GP fault handler which recognizes this and transfers control back to
+; here.
+;
+; when we come back here, all the segment registers are set up properly
+; and esp is restored. Interrupts are disabled.
+;
+
+;
+; restore all the pointers.
+;
+
+ mov eax, fs:PcSelfPcr ; (eax)->Pcr
+ pop edx ; (edx) = Pcr InitialStack
+ mov [eax].PcInitialStack, edx ; Restore Pcr InitialStack
+ mov ecx, [eax]+PcPrcbData+PbCurrentThread ; (ecx)->CurrentThread
+ add edx, NPX_FRAME_LENGTH
+ mov [ecx].ThInitialStack, edx ; Restore Thread.InitialStack
+
+ mov eax,[eax]+PcTss ; (eax)->TSS
+ sub edx, (TsV86Gs - TsHardwareSegSs) + NPX_FRAME_LENGTH
+ mov [eax]+TssEsp0,edx
+
+;
+; restore pointers to the original TEB
+;
+ pop edx ; (edx) = OriginalThTeb
+ mov [ecx].ThTeb, edx
+ pop edx ; (edx) = OriginalPcTeb
+ mov fs:[PcTeb], edx
+
+ mov ebx, PCR[PcGdt]
+ mov [ebx]+(KGDT_R3_TEB+KgdtBaseLow), dx
+ shr edx, 16
+ mov [ebx]+(KGDT_R3_TEB+KgdtBaseMid), dl
+ mov [ebx]+(KGDT_R3_TEB+KgdtBaseHi), dh
+
+
+ sti
+
+ pop edi
+ pop esi
+ pop ebx
+ pop ebp
+ stdRET _Ki386SetupAndExitToV86Code
+
+stdENDP _Ki386SetupAndExitToV86Code
+
+_TEXT ends
+
+ end
diff --git a/private/ntos/ke/i386/biosc.c b/private/ntos/ke/i386/biosc.c
new file mode 100644
index 000000000..51434f220
--- /dev/null
+++ b/private/ntos/ke/i386/biosc.c
@@ -0,0 +1,269 @@
+/*++
+
+Copyright (c) 1989 Microsoft Corporation
+
+Module Name:
+
+ abiosc.c
+
+Abstract:
+
+ This module implements ROM BIOS support C routines for i386 NT.
+
+Author:
+
+ Shie-Lin Tzong (shielint) 10-Sept-1992
+
+Environment:
+
+ Kernel mode.
+
+
+Revision History:
+
+--*/
+#include "ki.h"
+#pragma hdrstop
+#include "vdmntos.h"
+
+
+#ifdef ALLOC_PRAGMA
+#pragma alloc_text(PAGE,Ke386CallBios)
+#endif
+
+
+//
+// Never change these equates without checking biosa.asm
+//
+
+#define V86_CODE_ADDRESS 0x10000
+#define INT_OPCODE 0xcd
+#define V86_BOP_OPCODE 0xfec4c4
+#define V86_STACK_POINTER 0x1ffe
+#define IOPM_OFFSET FIELD_OFFSET(KTSS, IoMaps[0].IoMap)
+#define VDM_TIB_ADDRESS 0x12000
+#define INT_10_TEB 0x13000
+
+//
+// External References
+//
+
+PVOID Ki386IopmSaveArea;
+BOOLEAN BiosInitialized = FALSE;
+VOID
+Ki386SetupAndExitToV86Code (
+ PVOID ExecutionAddress
+ );
+
+
+NTSTATUS
+Ke386CallBios (
+ IN ULONG BiosCommand,
+ IN OUT PCONTEXT BiosArguments
+ )
+
+/*++
+
+Routine Description:
+
+ This function invokes specified ROM BIOS code by executing
+ "INT BiosCommand." Before executing the BIOS code, this function
+ will setup VDM context, change stack pointer ...etc. If for some reason
+ the operation fails, a status code will be returned. Otherwise, this
+ function always returns success reguardless of the result of the BIOS
+ call.
+
+ N.B. This implementation relies on the fact that the direct
+ I/O access operations between apps are serialized by win user.
+
+Arguments:
+
+ BiosCommand - specifies which ROM BIOS function to invoke.
+
+ BiosArguments - specifies a pointer to the context which will be used
+ to invoke ROM BIOS.
+
+Return Value:
+
+ NTSTATUS code to specify the failure.
+
+--*/
+
+{
+
+ NTSTATUS Status = STATUS_SUCCESS;
+ PVDM_TIB VdmTib;
+ PUCHAR BaseAddress = (PUCHAR)V86_CODE_ADDRESS;
+ PTEB UserInt10Teb = (PTEB)INT_10_TEB;
+ PKTSS Tss;
+ PKPROCESS Process;
+ PKTHREAD Thread;
+ USHORT OldIopmOffset, OldIoMapBase;
+// KIRQL OldIrql;
+//#if DBG
+// PULONG IdtAddress;
+// ULONG RegionSize;
+// ULONG OldProtect;
+//#endif
+
+ //
+ // Map in ROM BIOS area to perform the int 10 code
+ //
+
+ if (!BiosInitialized) {
+ RtlZeroMemory(UserInt10Teb, sizeof(TEB));
+ }
+
+//#if DBG
+// IdtAddress = 0;
+// RegionSize = 0x1000;
+// ZwProtectVirtualMemory ( NtCurrentProcess(),
+// &IdtAddress,
+// &RegionSize,
+// PAGE_READWRITE,
+// &OldProtect
+// );
+//#endif
+
+ try {
+
+ //
+ // Write "Int BiosCommand; bop" to reserved user space (0x1000).
+ // Later control will transfer to the user space to execute
+ // these two instructions.
+ //
+
+ *BaseAddress++ = INT_OPCODE;
+ *BaseAddress++ = (UCHAR)BiosCommand;
+ *(PULONG)BaseAddress = V86_BOP_OPCODE;
+
+ //
+ // Set up Vdm(v86) context to execute the int BiosCommand
+ // instruction by copying user supplied context to VdmContext
+ // and updating the control registers to predefined values.
+ //
+
+ //
+ // We want to use a constant number for the int10.
+ //
+ //
+ // Create a fake TEB so we can switch the thread to it while we
+ // do an int10
+ //
+
+ UserInt10Teb->Vdm = (PVOID)VDM_TIB_ADDRESS;
+ VdmTib = (PVDM_TIB)VDM_TIB_ADDRESS;
+ RtlZeroMemory(VdmTib, sizeof(VDM_TIB));
+ VdmTib->Size = sizeof(VDM_TIB);
+ *pNtVDMState = 0;
+
+ VdmTib->VdmContext = *BiosArguments;
+ VdmTib->VdmContext.SegCs = (ULONG)BaseAddress >> 4;
+ VdmTib->VdmContext.SegSs = (ULONG)BaseAddress >> 4;
+ VdmTib->VdmContext.Eip = 0;
+ VdmTib->VdmContext.Esp = 2 * PAGE_SIZE - sizeof(ULONG);
+ VdmTib->VdmContext.EFlags |= EFLAGS_V86_MASK | EFLAGS_INTERRUPT_MASK;
+ VdmTib->VdmContext.ContextFlags = CONTEXT_FULL;
+
+ } except (EXCEPTION_EXECUTE_HANDLER) {
+
+ Status = GetExceptionCode();
+ }
+
+ if (Status == STATUS_SUCCESS) {
+
+ //
+ // Since we are going to v86 mode and accessing some I/O ports, we
+ // need to make sure the IopmOffset is set correctly across context
+ // swap and the I/O bit map has all the bits cleared.
+ // N.B. This implementation assumes that there is only one full
+ // screen DOS app and the io access between full screen DOS
+ // app and the server code is serialized by win user. That
+ // means even we change the IOPM, the full screen dos app won't
+ // be able to run on this IOPM.
+ // * In another words, IF THERE IS
+ // * MORE THAN ONE FULL SCREEN DOS APPS, THIS CODE IS BROKEN.*
+ //
+ // NOTE This code works on the assumption that winuser serializes
+ // direct I/O access operations.
+ //
+
+ //
+ // Call the bios from the processor which booted the machine.
+ //
+
+ Thread = KeGetCurrentThread();
+ KeSetSystemAffinityThread(1);
+ Tss = KeGetPcr()->TSS;
+
+ //
+ // Save away the original IOPM bit map and clear all the IOPM bits
+ // to allow v86 int 10 code to access ALL the io ports.
+ //
+
+ //
+ // Make sure there are at least 2 IOPM maps.
+ //
+
+ ASSERT(KeGetPcr()->GDT[KGDT_TSS / 8].LimitLow >= (0x2000 + IOPM_OFFSET - 1));
+ RtlMoveMemory (Ki386IopmSaveArea,
+ (PVOID)&Tss->IoMaps[0].IoMap,
+ PAGE_SIZE * 2
+ );
+ RtlZeroMemory ((PVOID)&Tss->IoMaps[0].IoMap, PAGE_SIZE * 2);
+
+ Process = Thread->ApcState.Process;
+ OldIopmOffset = Process->IopmOffset;
+ OldIoMapBase = Tss->IoMapBase;
+ Process->IopmOffset = (USHORT)(IOPM_OFFSET); // Set Process IoPmOffset before
+ Tss->IoMapBase = (USHORT)(IOPM_OFFSET); // updating Tss IoMapBase
+
+ //
+ // Call ASM routine to switch stack to exit to v86 mode to
+ // run Int BiosCommand.
+ //
+
+ Ki386SetupAndExitToV86Code(UserInt10Teb);
+
+ //
+ // After we return from v86 mode, the control comes here.
+ //
+ // Restore old IOPM
+ //
+
+ RtlMoveMemory ((PVOID)&Tss->IoMaps[0].IoMap,
+ Ki386IopmSaveArea,
+ PAGE_SIZE * 2
+ );
+
+ Process->IopmOffset = OldIopmOffset;
+ Tss->IoMapBase = OldIoMapBase;
+
+ //
+ // Restore old affinity for current thread.
+ //
+
+ KeRevertToUserAffinityThread();
+
+ //
+ // Copy 16 bit vdm context back to caller.
+ //
+
+ *BiosArguments = VdmTib->VdmContext;
+ BiosArguments->ContextFlags = CONTEXT_FULL;
+
+ }
+
+//#if DBG
+// IdtAddress = 0;
+// RegionSize = 0x1000;
+// ZwProtectVirtualMemory ( NtCurrentProcess(),
+// &IdtAddress,
+// &RegionSize,
+// PAGE_NOACCESS,
+// &OldProtect
+// );
+//#endif
+
+ return(Status);
+}
diff --git a/private/ntos/ke/i386/callback.c b/private/ntos/ke/i386/callback.c
new file mode 100644
index 000000000..d796b041f
--- /dev/null
+++ b/private/ntos/ke/i386/callback.c
@@ -0,0 +1,252 @@
+/*++
+
+Copyright (c) 1994 Microsoft Corporation
+
+Module Name:
+
+ callback.c
+
+Abstract:
+
+ This module implements user mode call back services.
+
+Author:
+
+ David N. Cutler (davec) 29-Oct-1994
+
+Environment:
+
+ Kernel mode only.
+
+Revision History:
+
+--*/
+
+#include "ki.h"
+
+NTSTATUS
+KeUserModeCallback (
+ IN ULONG ApiNumber,
+ IN PVOID InputBuffer,
+ IN ULONG InputLength,
+ OUT PVOID *OutputBuffer,
+ IN PULONG OutputLength
+ )
+
+/*++
+
+Routine Description:
+
+ This function call out from kernel mode to a user mode function.
+
+Arguments:
+
+ ApiNumber - Supplies the API number.
+
+ InputBuffer - Supplies a pointer to a structure that is copied
+ to the user stack.
+
+ InputLength - Supplies the length of the input structure.
+
+ Outputbuffer - Supplies a pointer to a variable that receives
+ the address of the output buffer.
+
+ Outputlength - Supplies a pointer to a variable that receives
+ the length of the output buffer.
+
+Return Value:
+
+ If the callout cannot be executed, then an error status is
+ returned. Otherwise, the status returned by the callback function
+ is returned.
+
+--*/
+
+{
+
+ ULONG Length;
+ ULONG NewStack;
+ ULONG OldStack;
+ NTSTATUS Status;
+ PULONG UserStack;
+ PVOID ValueBuffer;
+ ULONG ValueLength;
+ PEXCEPTION_REGISTRATION_RECORD ExceptionList;
+ PTEB Teb;
+
+ ASSERT(KeGetPreviousMode() == UserMode);
+
+ //
+ // Get the user mode stack pointer and attempt to copy input buffer
+ // to the user stack.
+ //
+
+ UserStack = KiGetUserModeStackAddress();
+ OldStack = *UserStack;
+ try {
+
+ //
+ // Compute new user mode stack address, probe for writability,
+ // and copy the input buffer to the user stack.
+ //
+
+ Length = (InputLength + sizeof(CHAR) - 1) & ~(sizeof(CHAR) - 1);
+ NewStack = OldStack - Length;
+ ProbeForWrite((PCHAR)(NewStack - 16), Length + 16, sizeof(CHAR));
+ RtlCopyMemory((PVOID)NewStack, InputBuffer, Length);
+
+ //
+ // Push arguments onto user stack.
+ //
+
+ *(PULONG)(NewStack - 4) = (ULONG)InputLength;
+ *(PULONG)(NewStack - 8) = (ULONG)NewStack;
+ *(PULONG)(NewStack - 12) = ApiNumber;
+ *(PULONG)(NewStack - 16) = 0;
+ NewStack -= 16;
+
+ //
+ // Save the thread's exception list to prevent total disaster
+ // if the thread returns from a callback after registering
+ // another exception handler.
+ //
+ Teb = (PTEB)KeGetCurrentThread()->Teb;
+ ExceptionList = Teb->NtTib.ExceptionList;
+
+ //
+ // Call user mode.
+ //
+
+ *UserStack = NewStack;
+ Status = KiCallUserMode(OutputBuffer, OutputLength);
+ *UserStack = OldStack;
+
+ //
+ // Restore exception list.
+ //
+ Teb->NtTib.ExceptionList = ExceptionList;
+
+ //
+ // If an exception occurs during the probe of the user stack, then
+ // always handle the exception and return the exception code as the
+ // status value.
+ //
+
+ } except (EXCEPTION_EXECUTE_HANDLER) {
+ return GetExceptionCode();
+ }
+
+ //
+ // When returning from user mode, any drawing done to the GDI TEB
+ // batch must be flushed.
+ //
+
+ if (Teb->GdiBatchCount > 0) {
+
+ //
+ // call GDI batch flush routine
+ //
+
+ KeGdiFlushUserBatch();
+ }
+
+ return Status;
+}
+
+NTSTATUS
+NtW32Call (
+ IN ULONG ApiNumber,
+ IN PVOID InputBuffer,
+ IN ULONG InputLength,
+ OUT PVOID *OutputBuffer,
+ OUT PULONG OutputLength
+ )
+
+/*++
+
+Routine Description:
+
+ This function calls a W32 function.
+
+Arguments:
+
+ ApiNumber - Supplies the API number.
+
+ InputBuffer - Supplies a pointer to a structure that is copied to
+ the user stack.
+
+ InputLength - Supplies the length of the input structure.
+
+ Outputbuffer - Supplies a pointer to a variable that recevies the
+ output buffer address.
+
+ Outputlength - Supplies a pointer to a variable that recevies the
+ output buffer length.
+
+Return Value:
+
+ TBS.
+
+--*/
+
+{
+
+ PVOID ValueBuffer;
+ ULONG ValueLength;
+ NTSTATUS Status;
+
+ ASSERT(KeGetPreviousMode() == UserMode);
+
+ //
+ // If the current thread is not a GUI thread, then fail the service
+ // since the thread does not have a large stack.
+ //
+
+ if (KeGetCurrentThread()->Win32Thread == (PVOID)&KeServiceDescriptorTable[0]) {
+ return STATUS_NOT_IMPLEMENTED;
+ }
+
+ //
+ // Probe the output buffer address and length for writeability.
+ //
+
+ try {
+ ProbeForWriteUlong((PULONG)OutputBuffer);
+ ProbeForWriteUlong(OutputLength);
+
+ //
+ // If an exception occurs during the probe of the output buffer or
+ // length, then always handle the exception and return the exception
+ // code as the status value.
+ //
+
+ } except(EXCEPTION_EXECUTE_HANDLER) {
+ return GetExceptionCode();
+ }
+
+ //
+ // Call out to user mode specifying the input buffer and API number.
+ //
+
+ Status = KeUserModeCallback(ApiNumber,
+ InputBuffer,
+ InputLength,
+ &ValueBuffer,
+ &ValueLength);
+
+ //
+ // If the callout is successful, then the output buffer address and
+ // length.
+ //
+
+ if (NT_SUCCESS(Status)) {
+ try {
+ *OutputBuffer = ValueBuffer;
+ *OutputLength = ValueLength;
+
+ } except(EXCEPTION_EXECUTE_HANDLER) {
+ }
+ }
+
+ return Status;
+}
diff --git a/private/ntos/ke/i386/callout.asm b/private/ntos/ke/i386/callout.asm
new file mode 100644
index 000000000..69e484f17
--- /dev/null
+++ b/private/ntos/ke/i386/callout.asm
@@ -0,0 +1,432 @@
+ title "Call Out to User Mode"
+;++
+;
+; Copyright (c) 1994 Microsoft Corporation
+;
+; Module Name:
+;
+; callout.asm
+;
+; Abstract:
+;
+; This module implements the code necessary to call out from kernel
+; mode to user mode.
+;
+; Author:
+;
+; David N. Cutler (davec) 1-Nov-1994
+;
+; Environment:
+;
+; Kernel mode only.
+;
+; Revision History:
+;
+;--
+
+.386p
+ .xlist
+include ks386.inc
+include i386\kimacro.inc
+include callconv.inc
+ .list
+
+ extrn _KiServiceExit:PROC
+ extrn _KeUserCallbackDispatcher:DWORD
+
+ EXTRNP _MmGrowKernelStack,1
+
+_TEXT SEGMENT DWORD PUBLIC 'CODE'
+ ASSUME DS:FLAT, ES:FLAT, SS:FLAT, FS:NOTHING, GS:NOTHING
+
+ page ,132
+ subttl "Call User Mode Function"
+;++
+;
+; NTSTATUS
+; KiCallUserMode (
+; IN PVOID *Outputbuffer,
+; IN PULONG OutputLength
+; )
+;
+; Routine Description:
+;
+; This function calls a user mode function from kernel mode.
+;
+; N.B. This function calls out to user mode and the NtCallbackReturn
+; function returns back to the caller of this function. Therefore,
+; the stack layout must be consistent between the two routines.
+;
+; Arguments:
+;
+; OutputBuffer - Supplies a pointer to the variable that receivies
+; the address of the output buffer.
+;
+; OutputLength - Supplies a pointer to a variable that receives
+; the length of the output buffer.
+;
+; Return Value:
+;
+; The final status of the call out function is returned as the status
+; of the function.
+;
+; N.B. This function does not return to its caller. A return to the
+; caller is executed when a NtCallbackReturn system service is
+; executed.
+;
+; N.B. This function does return to its caller if a kernel stack
+; expansion is required and the attempted expansion fails.
+;
+;--
+
+;
+; To support the debugger, the callback stack frame is now defined in i386.h.
+; If the stack frame is changed, i386.h must be updated and geni386
+; rebuilt and run, then rebuild this file and ntos\kd.
+;
+; The FPO record below must also be updated to correctly represent
+; the stack frame.
+;
+
+cPublicProc _KiCallUserMode, 2
+
+.FPO (3, 2, 4, 4, 0, 0)
+
+;
+; Save nonvolatile registers.
+;
+
+ push ebp ; save nonvolatile registers
+ push ebx ;
+ push esi ;
+ push edi ;
+
+;
+; Check if sufficient room is available on the kernel stack for another
+; system call.
+;
+
+ mov ebx,PCR[PcPrcbData + PbCurrentThread] ; get current thread address
+ lea eax,[esp]-KERNEL_LARGE_STACK_COMMIT ; compute bottom address
+ cmp eax,[ebx]+ThStackLimit ; check if limit exceeded
+ jae short Kcb10 ; if ae, limit not exceeded
+ stdCall _MmGrowKernelStack,<esp> ; attempt to grow kernel stack
+ or eax, eax ; check for successful completion
+ jne Kcb20 ; if ne, attempt to grow failed
+ mov eax, [ebx].ThStackLimit ; get new stack limit
+ mov PCR[PcStackLimit], eax ; set new stack limit
+
+;
+; Get the address of the current thread and save the previous trap frame
+; and calback stack addresses in the current frame. Also save the new
+; callback stack address in the thread object.
+;
+
+Kcb10: push [ebx].ThCallbackStack ; save callback stack address
+ mov edx,[ebx].ThTrapFrame ; get current trap frame address
+ push edx ; save trap frame address
+ mov esi,[ebx].ThInitialStack ; get initial stack address
+ push esi ; save initial stack address
+ mov [ebx].ThCallbackStack,esp ; save callback stack address
+
+KcbPrologEnd: ; help for the debugger
+
+;
+; Copy the numeric save area from the previous save area to the new save
+; area and establish a new initial kernel stack.
+;
+
+ mov edi,esp ; set new initial stack address
+ sub esp,NPX_FRAME_LENGTH ; compute destination NPX save area
+ sub esi,NPX_FRAME_LENGTH ; compute source NPX save area
+ cli ; disable interrupts
+ mov ecx,[esi].FpControlWord ; copy NPX state to new frame
+ mov [esp].FpControlWord,ecx ;
+ mov ecx,[esi].FpStatusWord ;
+ mov [esp].FpStatusWord,ecx ;
+ mov ecx,[esi].FpTagWord ;
+ mov [esp].FpTagWord,ecx ;
+ mov ecx,[esi].FpCr0NpxState ;
+ mov [esp].FpCr0NpxState,ecx ;
+ mov esi,PCR[PcTss] ; get address of task switch segment
+ mov [ebx].ThInitialStack,edi ; reset initial stack address
+ mov PCR[PcInitialStack],esp ; set stack check base address
+ sub esp,TsV86Gs - TsHardwareSegSs ; bias for missing V86 fields
+ mov [esi].TssEsp0,esp ; set kernel entry stack address
+
+;
+; Construct a trap frame to facilitate the transfer into user mode via
+; the standard system call exit.
+;
+
+ sub esp,TsHardwareSegSs + 4 ; allocate trap frame
+ mov ebp,esp ; set address of trap frame
+ mov ecx,(TsHardwareSegSs - TsSegFs + 4) / 4; set repeat count
+ lea edi,[esp].TsSegFs ; set destination address
+ lea esi,[edx].TsSegFs ; set source address
+ rep movsd ; copy trap information
+
+ test byte ptr [ebx]+ThDebugActive, -1 ; Do we need to restore Debug reg?
+ jnz short Kcb18 ; Yes, go save them.
+
+Kcb15: mov eax,_KeUserCallbackDispatcher ; st address of callback dispatchr
+ mov [esp].TsEip,eax ;
+ mov eax,PCR[PcExceptionList] ; get current exception list
+ mov [esp].TsExceptionList,eax ; set previous exception list
+ mov eax,[edx].TsPreviousPreviousMode ; get previous mode
+ mov [esp].TsPreviousPreviousMode,eax ; set previous mode
+ sti ; enable interrupts
+
+ SET_DEBUG_DATA ; set system call debug data for exit
+
+ jmp _KiServiceExit ; exit through service dispatch
+
+Kcb18:
+ mov ecx,(TsDr7 - TsDr0 + 4) / 4; set repeat count
+ lea edi,[esp].TsDr0 ; set destination address
+ lea esi,[edx].TsDr0 ; set source address
+ rep movsd ; copy trap information
+ jmp short Kcb15
+
+;
+; An attempt to grow the kernel stack failed.
+;
+
+Kcb20: pop edi ; restore nonvolitile register
+ pop esi ;
+ pop ebx ;
+ pop ebp ;
+ stdRET _KiCallUserMode
+
+stdENDP _KiCallUserMode
+
+ page ,132
+ subttl "Switch Kernel Stack"
+;++
+;
+; PVOID
+; KeSwitchKernelStack (
+; IN PVOID StackBase,
+; IN PVOID StackLimit
+; )
+;
+; Routine Description:
+;
+; This function switches to the specified large kernel stack.
+;
+; N.B. This function can ONLY be called when there are no variables
+; in the stack that refer to other variables in the stack, i.e.,
+; there are no pointers into the stack.
+;
+; Arguments:
+;
+; StackBase (esp + 4) - Supplies a pointer to the base of the new kernel
+; stack.
+;
+; StackLimit (esp + 8) - Suplies a pointer to the limit of the new kernel
+; stack.
+;
+; Return Value:
+;
+; The old kernel stack is returned as the function value.
+;
+;--
+
+SsStkBs equ 4 ; new kernel stack base address
+SsStkLm equ 8 ; new kernel stack limit address
+
+cPublicProc _KeSwitchKernelStack, 2
+
+;
+; Save the address of the new stack and copy the old stack to the new
+; stack.
+;
+
+ push esi ; save string move registers
+ push edi ;
+ mov edx,PCR[PcPrcbData + PbCurrentThread] ; get current thread address
+ mov edi,[esp]+SsStkBs + 8 ; get new kernel stack base address
+ mov ecx,[edx].ThStackBase ; get current stack base address
+ sub ebp,ecx ; relocate the callers frame pointer
+ add ebp,edi ;
+ mov eax,[edx].ThTrapFrame ; relocate the current trap frame address
+ sub eax,ecx ;
+ add eax,edi ;
+ mov [edx].ThTrapFrame,eax ;
+ sub ecx,esp ; compute length of copy
+ sub edi,ecx ; set destination address of copy
+ mov esi,esp ; set source address of copy
+ push edi ; save new stack pointer address
+ rep movsb ; copy old stack to new stack
+ pop edi ; restore new stack pointer address
+
+;
+; Switch to the new kernel stack and return the address of the old kernel
+; stack.
+;
+
+ mov eax,[edx].ThStackBase ; get old kernel stack base address
+ mov ecx,[esp]+SsStkBs + 8 ; get new kernel stack base address
+ mov esi,[esp]+SsStkLm + 8 ; get new kernel stack limit address
+ cli ; disable interrupts
+ mov [edx].ThStackBase,ecx ; set new kernel stack base address
+ mov [edx].ThStackLimit,esi ; set new kernel stack limit address
+ mov byte ptr [edx].ThLargeStack, 1 ; set large stack TRUE
+ mov [edx].ThInitialStack,ecx ; set new initial stack address
+ sub ecx,NPX_FRAME_lENGTH ; compute NPX save area address
+ mov PCR[PcInitialStack],ecx ; set stack check base address
+ mov PCR[PcStackLimit],esi ; set stack check limit address
+ mov edx,PCR[PcTss] ; get address of task switch segment
+ sub ecx,TsV86Gs - TsHardwareSegSs ; bias for missing V86 fields
+ mov [edx].TssEsp0,ecx ; set kernel entry stack address
+ mov esp,edi ; set new stack pointer address
+ sti ;
+ pop edi ; restore string move registers
+ pop esi ;
+ stdRET _KeSwitchKernelStack
+
+stdENDP _KeSwitchKernelStack
+
+ page ,132
+ subttl "Get User Mode Stack Address"
+;++
+;
+; PULONG
+; KiGetUserModeStackAddress (
+; VOID
+; )
+;
+; Routine Description:
+;
+; This function returns the address of the user stack address in the
+; current trap frame.
+;
+; Arguments:
+;
+; None.
+;
+; Return Value:
+;
+; The address of the user stack address.
+;
+;--
+
+cPublicProc _KiGetUserModeStackAddress, 0
+
+ mov eax,PCR[PcPrcbData + PbCurrentThread] ; get current thread address
+ mov eax,[eax].ThTrapFrame ; get current trap frame address
+ lea eax,[eax].TsHardwareEsp ; get address of stack address
+ stdRET _KiGetUserModeStackAddress
+
+stdENDP _KiGetUserModeStackAddress
+
+ page ,132
+ subttl "Return from User Mode Callback"
+;++
+;
+; NTSTATUS
+; NtCallbackReturn (
+; IN PVOID OutputBuffer OPTIONAL,
+; IN ULONG OutputLength,
+; IN NTSTATUS Status
+; )
+;
+; Routine Description:
+;
+; This function returns from a user mode callout to the kernel
+; mode caller of the user mode callback function.
+;
+; N.B. This function returns to the function that called out to user
+; mode and the KiCallUserMode function calls out to user mode.
+; Therefore, the stack layout must be consistent between the
+; two routines.
+;
+; Arguments:
+;
+; OutputBuffer - Supplies an optional pointer to an output buffer.
+;
+; OutputLength - Supplies the length of the output buffer.
+;
+; Status - Supplies the status value returned to the caller of the
+; callback function.
+;
+; Return Value:
+;
+; If the callback return cannot be executed, then an error status is
+; returned. Otherwise, the specified callback status is returned to
+; the caller of the callback function.
+;
+; N.B. This function returns to the function that called out to user
+; mode is a callout is currently active.
+;
+;--
+
+cPublicProc _NtCallbackReturn, 3
+
+ mov eax,PCR[PcPrcbData + PbCurrentThread] ; get current thread address
+ mov ecx,[eax].ThCallbackStack ; get callback stack address
+ jecxz short CbExit ; if zero, no callback stack present
+
+;
+; Restore the current exception list from the saved exception list in the
+; current trap frame, restore the trap frame and callback stack addresses,
+; store the output buffer address and length, and set the service status.
+;
+
+ mov ebx,[eax].ThTrapFrame ; get current trap frame address
+ mov edx,[ebx].TsExceptionList ; get saved exception list address
+ mov PCR[PcExceptionList],edx ; restore exception list address
+ mov edi,[esp] + 4 ; get output buffer address
+ mov esi,[esp] + 8 ; get output buffer length
+ mov ebp,[esp] + 12 ; get callout service status
+ mov ebx,[ecx].CuOutBf ; get address to store output buffer
+ mov [ebx],edi ; store output buffer address
+ mov ebx,[ecx].CuOutLn ; get address to store output length
+ mov [ebx],esi ; store output buffer length
+ cli ; disable interrupt
+ mov esi,PCR[PcInitialStack] ; get source NPX save area address
+ mov esp,ecx ; trim stack back to callback frame
+ pop ecx ; get previous initial stack address
+ mov [eax].ThInitialStack,ecx ; restore initial stack address
+ sub ecx,NPX_FRAME_LENGTH ; compute destination NPX save area
+ mov edx,[esi].FpControlWord ; copy NPX state to previous frame
+ mov [ecx].FpControlWord,edx ;
+ mov edx,[esi].FpStatusWord ;
+ mov [ecx].FpStatusWord,edx ;
+ mov edx,[esi].FpTagWord ;
+ mov [ecx].FpTagWord,edx ;
+ mov edx,[esi].FpCr0NpxState ;
+ mov [ecx].FpCr0NpxState,edx ;
+ mov edx,PCR[PcTss] ; get address of task switch segment
+ mov PCR[PcInitialStack],ecx ; restore stack check base address
+ sub ecx,TsV86Gs - TsHardwareSegSs ; bias for missing V86 fields
+ mov [edx].TssEsp0,ecx ; restore kernel entry stack address
+ sti ; enable interrupts
+ pop [eax].ThTrapFrame ; restore current trap frame address
+ pop [eax].ThCallbackStack ; restore callback stack address
+ mov eax,ebp ; set callback service status
+
+;
+; Restore nonvolatile registers, clean call parameters from stack, and
+; return to callback caller.
+;
+
+ pop edi ; restore nonvolatile registers
+ pop esi ;
+ pop ebx ;
+ pop ebp ;
+ pop edx ; save return address
+ add esp,8 ; remove parameters from stack
+ jmp edx ; return to callback caller
+
+;
+; No callback is currently active.
+;
+
+CbExit: mov eax,STATUS_NO_CALLBACK_ACTIVE ; set service status
+ stdRET _NtCallBackReturn
+
+stdENDP _NtCallbackReturn
+
+_TEXT ends
+ end
diff --git a/private/ntos/ke/i386/clockint.asm b/private/ntos/ke/i386/clockint.asm
new file mode 100644
index 000000000..4784f4dd9
--- /dev/null
+++ b/private/ntos/ke/i386/clockint.asm
@@ -0,0 +1,881 @@
+ title "Interval Clock Interrupt"
+;++
+;
+; Copyright (c) 1989 Microsoft Corporation
+;
+; Module Name:
+;
+; clockint.asm
+;
+; Abstract:
+;
+; This module implements the code necessary to field and process the
+; interval clock interrupt.
+;
+; Author:
+;
+; Shie-Lin Tzong (shielint) 12-Jan-1990
+;
+; Environment:
+;
+; Kernel mode only.
+;
+; Revision History:
+;
+; bryanwi 20-Sep-90
+;
+; Add KiSetProfileInterval, KiStartProfileInterrupt,
+; KiStopProfileInterrupt procedures.
+; KiProfileInterrupt ISR.
+; KiProfileList, KiProfileLock are delcared here.
+;
+; shielint 10-Dec-90
+; Add performance counter support.
+; Move system clock to irq8, ie we now use RTC to generate system
+; clock. Performance count and Profile use timer 1 counter 0.
+; The interval of the irq0 interrupt can be changed by
+; KiSetProfileInterval. Performance counter does not care about the
+; interval of the interrupt as long as it knows the rollover count.
+; Note: Currently I implemented 1 performance counter for the whole
+; i386 NT. It works on UP and SystemPro.
+;
+;--
+
+.386p
+ .xlist
+KERNELONLY equ 1
+include ks386.inc
+include callconv.inc ; calling convention macros
+include i386\kimacro.inc
+include mac386.inc
+ .list
+
+ EXTRNP Kei386EoiHelper
+ EXTRNP HalRequestSoftwareInterrupt,1,IMPORT,FASTCALL
+ EXTRNP _HalEndSystemInterrupt,2,IMPORT
+ extrn _KeTimeIncrement:DWORD
+ extrn _KeMaximumIncrement:DWORD
+ extrn _KeTickCount:DWORD
+ extrn _KeTimeAdjustment:DWORD
+ extrn _KiAdjustDpcThreshold:DWORD
+ extrn _KiIdealDpcRate:DWORD
+ extrn _KiMaximumDpcQueueDepth:DWORD
+ extrn _KiTickOffset:DWORD
+ extrn _KiTimerTableListHead:DWORD
+ extrn _KiTimerExpireDpc:DWORD
+ extrn _KiTimeUpdateNotifyRoutine:DWORD
+ extrn _KiProfileListHead:DWORD
+ extrn _KiProfileLock:DWORD
+ extrn _KiProfileInterval:DWORD
+ extrn _KdDebuggerEnabled:BYTE
+ EXTRNP _DbgBreakPoint
+ EXTRNP _DbgBreakPointWithStatus,1
+ EXTRNP _KdPollBreakIn
+ EXTRNP _KiDeliverApc,3
+ extrn _KeI386MachineType:DWORD
+
+ifdef NT_UP
+ LOCK_INC equ inc
+else
+ LOCK_INC equ lock inc
+endif
+
+
+_DATA SEGMENT DWORD PUBLIC 'DATA'
+public ProfileCount
+ProfileCount DD 0
+
+_DATA ends
+
+ page ,132
+ subttl "Update System Time"
+
+_TEXT$00 SEGMENT DWORD PUBLIC 'CODE'
+ ASSUME DS:FLAT, ES:FLAT, SS:NOTHING, FS:NOTHING, GS:NOTHING
+;++
+;
+; VOID
+; KeUpdateSystemTime (
+; IN KIRQL PreviousIrql,
+; IN KTRAP_FRAME TrapFrame
+; )
+;
+; Routine Description:
+;
+; This routine is entered as the result of an interrupt generated by CLOCK2.
+; Its function is to update the system time and check to determine if a timer
+; has expired.
+;
+; N.B. This routine is executed on a single processor in a multiprocess
+; system. The remainder of the processors only execute the quantum end
+; and runtime update code.
+;
+; N.B. This routine is not called, but directly jumped to. Thus, there
+; is no return address. It returns via the INTERRUPT_EXIT macro.
+;
+; Arguments:
+;
+; PreviousIrql (esp) - supplies previous irql of system
+;
+; HardwareVector (esp+4) - supplies hardware vector for EndSystemInterrupt
+;
+; TrapFrame (esp+8) - supplies base of trap frame
+;
+; EAX is the TimeIncrement value
+;
+; EBP is a pointer to the trap frame
+;
+;
+; Environment:
+;
+; IRQL = CLOCK_LEVEL
+;
+; Return Value:
+;
+; None.
+;
+;--
+cPublicProc _KeUpdateSystemTime ,0
+
+.FPO (2, 0, 0, 0, 0, 1) ; treat params as locals since functions is JMPed too
+
+if DBG
+ cmp byte ptr PCR[PcPrcbData+PbSkipTick], 0
+ jnz kust_skiptick
+endif
+
+;
+; Update interrupt time.
+;
+; N.B. The interrupt time is updated in a very strict manner so that an
+; interlock does not have to be used in an MP system to read time.
+;
+
+ mov ecx,USER_SHARED_DATA ; set address of user shared data
+ mov edi,[ecx].UsInterruptTime+0 ; get low interrupt time
+ mov esi,[ecx].UsInterruptTime+4 ; get high interrupt time
+ add edi,eax ; add time increment
+ adc esi,0 ; propagate carry
+ mov [ecx].UsInterruptTime+8,esi ; store high 2 interrupt time
+ mov [ecx].UsInterruptTime+0,edi ; store low interrupt time
+ mov [ecx].UsInterruptTime+4,esi ; store high 1 interrupt time
+
+ sub _KiTickOffset,eax ; subtract time increment
+ mov eax,_KeTickCount+0 ; get low tick count
+ mov ebx,eax ; copy low tick count
+ jg kust10 ; if greater, not complete tick
+
+;
+; Update system time.
+;
+; N.B. The system time is updated in a very strict manner so that an
+; interlock does not have to be used in an MP system to read time.
+;
+
+ mov ebx,USER_SHARED_DATA ; set address of user shared data
+ mov ecx,[ebx].UsSystemTime+0 ; get low interrupt time
+ mov edx,[ebx].UsSystemTime+4 ; get high interrupt time
+ add ecx,_KeTimeAdjustment ; add time increment
+ adc edx,0 ; propagate carry
+ mov [ebx].UsSystemTime+8,edx ; store high 2 interrupt time
+ mov [ebx].UsSystemTime+0,ecx ; store low interrupt time
+ mov [ebx].UsSystemTime+4,edx ; store high 1 interrupt time
+ mov ebx,eax ; restore low tick count
+
+;
+; Update tick count.
+;
+; N.B. The tick count is updated in a very strict manner so that an
+; interlock does not have to be used in an MP system to read count.
+;
+
+ mov ecx,eax ; copy low tick count
+ mov edx,_KeTickCount+4 ; get high tick count
+ add ecx,1 ; increment tick count
+ adc edx,0 ; propagate carry
+ mov _KeTickCount+8,edx ; store high 2 tick count
+ mov _KeTickCount+0,ecx ; store low tick count
+ mov _KeTickCount+4,edx ; store high 1 tick count
+ mov USERDATA[UsTickCountLow], ecx
+
+if 0
+ ; debug code
+ push eax
+ mov edx, esi
+ mov eax, edi ; (eax:edx) = InterruptTime
+ mov ecx, _KeMaximumIncrement
+ div ecx
+ cmp al, bl ; same bucket?
+ je short @f
+ int 3 ; no - stop
+@@:
+ pop eax
+endif
+
+;
+; Check to determine if a timer has expired.
+; (edi:esi) = KiInterruptTime
+; (eax) = KeTickCount.LowPart
+; (ebx) = KeTickCount.LowPart
+;
+
+ and eax,TIMER_TABLE_SIZE-1 ; isolate current hand value
+ lea ecx,_KiTimerTableListHead[eax*8] ; get listhead addrees
+ mov edx,[ecx] ; get first entry address
+ cmp ecx,edx ; check if list is empry
+ je short kust5 ; if equal, list is empty
+ cmp esi,[edx].TiDueTime.TmHighTime-TiTimerListEntry ; compare high
+ jb short kust5 ; if below, timer has not expired
+ ja short kust15 ; if above, timer has expired
+ cmp edi,[edx].TiDueTime.TmLowTime-TiTimerListEntry ; compare low
+ jae short kust15 ; if above or equal, time has expired
+kust5: inc eax ; advance hand value to next entry
+ inc ebx
+
+;
+; Check to determine if a timer has expired.
+; (edi:esi) = KiInterruptTime
+; (eax) = bucket
+; (ebx) = KeTickCount.LowPart
+;
+
+kust10: and eax,TIMER_TABLE_SIZE-1 ; isolate current hand value
+ lea ecx,_KiTimerTableListHead[eax*8] ; get listhead addrees
+ mov edx,[ecx] ; get first entry address
+ cmp ecx,edx ; check if list is empry
+ je kustxx ; if equal, list is empty
+ cmp esi,[edx].TiDueTime.TmHighTime-TiTimerListEntry ; compare high
+ jb kustxx ; if below, timer has not expired
+ ja short kust15 ; if above, timer has expired
+ cmp edi,[edx].TiDueTime.TmLowTime-TiTimerListEntry ; compare low
+ jb kustxx ; if below, timer has not expired
+
+kust15:
+;
+; Timer has expired, put timer expiration DPC in the current processor's DPC
+; queue.
+;
+; (ebx) = KeTickCount.LowPart
+;
+
+ mov ecx,PCR[PcPrcb] ; get processor control block address
+ lea eax,_KiTimerExpireDpc+DpDpcListEntry ; get list entry address
+ lea edx,[ecx]+PbDpcLock ; get DPC lock address
+ cmp dword ptr [eax]+(DpLock-DpDpcListEntry), 0H ; check if inserted
+ jnz kustxx ; if nz, DPC already inserted
+
+kust20: cli
+ ACQUIRE_SPINLOCK edx, kust60
+
+ inc dword ptr [ecx].PbDpcQueueDepth ; increment DPC queue depth
+ mov dword ptr [eax]+(DpLock-DpDpcListEntry), edx ; set lock address
+ mov [eax]+(DpSystemArgument1-DpDpcListEntry),ebx ; pass tick count
+ add ecx,PbDpcListHead ; compute DPC listhead address
+ mov ebx,[ecx]+LsBlink ; get address of last entry in list
+ mov [ecx]+LsBlink, eax ; set new address of last entry
+ mov [ebx]+LsFlink, eax ; set forward link in old last entry
+ mov [eax]+LsFlink, ecx ; set forward link in new last entry
+ mov [eax]+LsBlink, ebx ; set backward link in new last entry
+
+ RELEASE_SPINLOCK edx
+ sti ; enable interrupt
+
+; request dispatch interrupt
+
+ mov ecx, DISPATCH_LEVEL
+ fstCall HalRequestSoftwareInterrupt
+
+kustxx:
+if DEVL
+ cmp _KdDebuggerEnabled, 0
+ jnz short kust45
+kust30:
+endif
+ cmp _KiTickOffset,0 ; check if full tick
+ jg short Kust40 ; if not less, not a full tick
+
+ mov eax,_KeMaximumIncrement ; get maximum time incrmeent
+ add _KiTickOffset,eax ; add maximum tine to residue
+
+;
+; call KeUpdateRunTime to do the acutal work
+;
+
+; TOS const PreviousIrql
+ push [esp]
+ call _KeUpdateRunTime@4
+
+;
+; Do interrupt exit processing
+;
+
+ INTERRUPT_EXIT
+
+kust40:
+ inc dword ptr PCR[PcPrcbData+PbInterruptCount]
+ INTERRUPT_EXIT
+
+if DEVL
+kust45:
+ stdCall _KdPollBreakIn
+ or al,al
+ jz short kust30
+ stdCall _DbgBreakPointWithStatus,<DBG_STATUS_CONTROL_C>
+ jmp short kust30
+endif
+
+if DBG
+kust_skiptick:
+ mov byte ptr PCR[PcPrcbData+PbSkipTick], 0
+ jmp short kust40
+endif
+
+;
+; Lock is currently owned; spin until free and then attempt to acquire
+; lock again.
+;
+
+ALIGN 4
+kust60: sti ; spin with interrupts enabled
+ SPIN_ON_SPINLOCK edx, kust20,,DbgMp
+
+stdENDP _KeUpdateSystemTime
+
+
+ page ,132
+ subttl "Update Thread and Process Runtime"
+;++
+;
+; Routine Description:
+;
+; This routines does the actual work to update the runtime of the current
+; thread, update the runtime of the current thread's process, and
+; decrement the current thread's quantum.
+;
+; It also updates the system global counters for user and kernel mode time.
+;
+; It increments InterruptCount so that clock ticks get counted as
+; interrupts.
+;
+; Arguments:
+;
+; esp+4 constant PreviousIrql
+;
+; ebp MUST point to the machine state frame.
+;
+; Return Value:
+;
+; None.
+;
+;--
+
+cPublicProc _KeUpdateRunTime ,1
+cPublicFpo 1, 1
+
+ mov eax, PCR[PcSelfPcr]
+if DBG
+ cmp byte ptr [eax]+PcPrcbData+PbSkipTick, 0
+ jnz kutp_skiptick
+endif
+ push ebx ; we will destroy ebx
+ inc dword ptr [eax]+PcPrcbData+PbInterruptCount
+ mov ebx, [eax]+PcPrcbData+PbCurrentThread ; (ebx)->current thread
+ mov ecx, ThApcState+AsProcess[ebx]
+ ; (ecx)->current thread's process
+
+ test dword ptr [ebp]+TsEFlags,EFLAGS_V86_MASK
+ jne Kutp20 ; if ne, user mode
+
+ test byte ptr [ebp]+TsSegCs, MODE_MASK ; test if prev mode was kernel
+ jne Kutp20 ; if ne, user mode
+
+;
+; Update the total time spent in kernel mode
+;
+
+ mov edx, 0 ; set kernel mode
+ inc dword ptr [eax].PcPrcbData.PbKernelTime
+ cmp byte ptr [esp+8], DISPATCH_LEVEL
+ jc short Kutp4 ; OldIrql<2, then kernel
+ ja short Kutp3 ; OldIrql>2, then interrupt
+
+ cmp dword ptr PCR[PcPrcbData.PbDpcRoutineActive], 0
+ jz short Kutp4 ; Executing Dpc?, no then thread time
+
+ inc dword ptr [eax].PcPrcbData.PbDpcTime
+ jmp Kutp51
+
+ALIGN 4
+Kutp3:
+ inc dword ptr [eax].PcPrcbData.PbInterruptTime
+ jmp Kutp51
+
+ALIGN 4
+Kutp4:
+
+;
+; Update the time spent in kernel mode for the current thread and the current
+; thread's process.
+;
+ inc dword ptr [ebx]+ThKernelTime
+
+ LOCK_INC dword ptr [ecx]+PrKernelTime
+
+ jmp Kutp50
+
+
+;
+; Update total time spent in user mode
+;
+
+ALIGN 4
+Kutp20:
+ mov edx, 1 ; set user mode
+ inc dword ptr [eax].PcPrcbData.PbUserTime
+;
+; Update the time spend in user mode for the current thread and the current
+; thread's process.
+;
+
+ inc dword ptr [ebx]+ThUserTime
+
+ LOCK_INC dword ptr [ecx]+PrUserTime
+
+;
+; Notify registered callout routine of update time.
+;
+; N.B. The register edx contains the processor mode.
+;
+
+ALIGN 4
+Kutp50: ;
+
+ifndef NT_UP
+
+ cmp _KiTimeUpdateNotifyRoutine, 0 ; check for callout routine
+ je short Kutp51 ; if eq, no callout routine registered
+ mov ecx, [ebx].EtCid.CidUniqueThread ; set current thread unique id
+ call [_KiTimeUpdateNotifyRoutine] ; notify callout routine
+ mov eax, PCR[PcSelfPcr] ; restore PCR address
+
+endif
+
+;
+; Update the DPC request rate which is computed as the average between
+; the previous rate and the current rate.
+;
+
+ALIGN 4
+Kutp51: mov ecx, [eax].PcPrcbData.PbDpcCount ; get current DPC count
+ mov edx, [eax].PcPrcbData.PbDpcLastCount ; get last DPC count
+ mov [eax].PcPrcbData.PbDpcLastCount, ecx ; set last DPC count
+ sub ecx, edx ; compute count during interval
+ add ecx, [eax].PcPrcbData.PbDpcRequestRate ; compute sum
+ shr ecx, 1 ; average current and last
+ mov [eax].PcPrcbData.PbDpcRequestRate, ecx ; set new DPC request rate
+
+;
+; If the current DPC queue depth is not zero, a DPC routine is not active,
+; and a DPC interrupt has not been requested, then request a dispatch
+; interrupt, decrement the maximum DPC queue depth, and reset the threshold
+; counter if appropriate.
+;
+
+ cmp dword ptr [eax].PcPrcbData.PbDpcQueueDepth, 0 ; check queue depth
+ je short Kutp53 ; if eq, DPC queue depth is zero
+ cmp dword ptr [eax].PcPrcbData.PbDpcRoutineActive, 0 ; check if DPC active
+ jne short Kutp53 ; if ne, DPC routine active
+ cmp dword ptr [eax].PcPrcbData.PbDpcInterruptRequested, 0 ; check if interrupt
+ jne short Kutp53 ; if ne, DPC routine active
+ mov ecx, DISPATCH_LEVEL ; request a dispatch interrupt
+ fstCall HalRequestSoftwareInterrupt ;
+ mov eax, PCR[PcSelfPcr] ; restore address of current PCR
+ mov ecx, [eax].PcPrcbData.PbDpcRequestRate ; get DPC request rate
+ mov edx, _KiAdjustDpcThreshold ; reset initial threshold counter
+ mov [eax].PcPrcbData.PbAdjustDpcThreshold, edx ;
+ cmp ecx, _KiIdealDpcRate ; test if current rate less than ideal
+ jge short Kutp55 ; if ge, rate greater or equal ideal
+ cmp [eax].PcPrcbData.PbMaximumDpcQueueDepth, 1 ; check if depth one
+ je short Kutp55 ; if eq, maximum depth is one
+ dec dword ptr [eax].PcPrcbData.PbMaximumDpcQueueDepth ; decrement depth
+ jmp short Kutp55 ;
+
+;
+; The DPC queue is empty or a DPC routine is active or a DPC interrupt
+; has been requested. Count down the adjustment threshold and if the
+; count reaches zero, then increment the maximum DPC queue depth, but
+; no above the initial value and reset the adjustment threshold value.
+;
+
+Kutp53: dec dword ptr [eax].PcPrcbData.PbAdjustDpcThreshold ; decrement threshold
+ jnz short Kutp55 ; if nz, threshold not zero
+ mov ecx, _KiAdjustDpcThreshold ; reset initial threshold counter
+ mov [eax].PcprcbData.PbAdjustDpcThreshold, ecx ;
+ mov ecx, _KiMaximumDpcQueueDepth ; get maximum DPC queue depth
+ cmp ecx, [eax].PcPrcbData.PbMaximumDpcQueueDepth ; check depth
+ je short Kutp55 ; if eq, aleady a maximum level
+ inc dword ptr [eax].PcPrcbData.PbMaximumDpcQueueDepth ; increment maximum depth
+
+;
+; Decrement current thread quantum and check to determine if a quantum end
+; has occurred.
+;
+
+ALIGN 4
+Kutp55: sub byte ptr [ebx]+ThQuantum, CLOCK_QUANTUM_DECREMENT ; decrement quantum
+ jg Kutp75 ; if > 0, time remaining on quantum
+
+;
+; Set quantum end flag and initiate a dispather interrupt on the current
+; processor.
+;
+
+ cmp ebx,[eax].PcPrcbData.PbIdleThread ; check if idle thread
+ jz Kutp75 ; if z, then idle thread
+ mov [eax].PcPrcbData.PbQuantumEnd, esp ; set quantum end indicator
+ mov ecx, DISPATCH_LEVEL ; request dispatch interrupt
+ fstCall HalRequestSoftwareInterrupt ;
+Kutp75: ;
+ pop ebx ;
+ stdRET _KeUpdateRunTime ;
+
+if DBG
+kutp_skiptick:
+ mov byte ptr [eax]+PcPrcbData+PbSkipTick, 0
+ stdRET _KeUpdateRunTime
+endif
+
+stdENDP _KeUpdateRunTime
+
+
+;++
+;
+; PROFILING SUPPORT
+;
+;--
+
+
+;++
+;
+; VOID
+; KeProfileInterrupt (
+; IN PKTRAP_FRAME TrapFrame,
+; )
+;
+; Routine Description:
+;
+; This procedure is the ISR for the profile sampling interrupt,
+; which for x86 machines is driven off the 8254 timer1 channel 0.
+;
+; The procedure scans the list of profile objects, looking for those
+; which match the address space and return program counter captured
+; at entry. For each object that does match, the counter in its
+; profile buffer matching the bucket the PC falls into is computed,
+; and that counter is incremented.
+;
+; N.B. This routine is executed on all processors in a multiprocess
+; system.
+;
+; Arguments:
+;
+; Return Address (esp)
+;
+; TrapFrame (esp+4) - supplies pointer to profile trap frame
+;
+; Environment:
+;
+; IRQL = KiProfileIrql
+;
+;
+; Return Value:
+;
+; None.
+;
+; WARNING: Uses ALL registers
+;
+;--
+
+cPublicProc _KeProfileInterrupt ,1
+;
+; rearrange arguments to pass a source of 0 to KeProfileInterruptWithSource
+;
+ pop eax ; return code in eax
+ pop ebx ; trap frame in ebx
+ push 0 ; push source of 0 (ProfileTime)
+ push ebx ; push trap frame
+ push eax ; push return address
+ jmp short _KeProfileInterruptWithSource@8
+stdENDP _KeProfileInterrupt
+
+;++
+;
+; VOID
+; KeProfileInterruptWithSource (
+; IN PKTRAP_FRAME TrapFrame,
+; IN KPROFILE_SOURCE ProfileSource
+; )
+;
+; Routine Description:
+;
+; This procedure is the ISR for the multiple-source profile interrupt.
+;
+; Since no x86 HAL currently implements any source other than the
+; clock interrupt, this routine is just a stub that calls KeProfileInterrupt
+;
+; Arguments:
+;
+; Return Address (esp)
+;
+; TrapFrame (esp+4) - supplies pointer to profile trap frame
+;
+; ProfileSource (esp+8) - supplies source of profile interrupt
+;
+; Environment:
+;
+; IRQL = KiProfileIrql
+;
+;
+; Return Value:
+;
+; None.
+;
+; WARNING: Uses ALL registers
+;
+;--
+cPublicProc _KeProfileInterruptWithSource,2
+
+kipieip equ <dword ptr [ebp+TsEip]>
+kipsegcs equ <word ptr [ebp+TsSegCs]>
+kipeflags equ <dword ptr [ebp+TsEFlags]>
+
+ mov ebp, dword ptr [esp+4] ; (ebp)-> trap frame
+ inc dword ptr PCR[PcPrcbData+PbInterruptCount]
+
+ifndef NT_UP
+ lea eax,_KiProfileLock
+kipi05: ACQUIRE_SPINLOCK eax,kipi96
+endif
+
+;
+; Update profile data
+;
+; NOTE:
+; System and Process update loops are duplicates, to avoid overhead
+; of call instruction in what could be very high freq. interrupt.
+; be sure to update both loops for changes.
+;
+; NOTE:
+; The process loop contains code to update segment profile objects.
+; This code is not present in the system loop, because we do not
+; allow attachment of profile objects for non-flat segments on a
+; system wide basis.
+;
+; NOTE:
+; Profiling in V86 mode is handled by converting the CS:IP value to
+; a linear address (CS<<4 + IP)
+;
+
+ inc ProfileCount ; total number of hits
+
+;
+; Update system profile entries
+;
+
+ mov ebx, kipieip
+ mov edx,offset FLAT:_KiProfileListHead
+ mov esi,[edx].LsFlink ; (esi) -> profile object
+ifndef NT_UP
+ mov edi, PCR[PcSetMember] ; (edi) = current processor
+endif
+ mov ecx, [esp+8] ; (cx) = profile source
+ cmp esi,edx
+ je kipi30 ; end of system list, go do process
+
+;
+; (ebx) = sample program counter
+; (esi) -> profile object
+;
+
+ALIGN 4
+kipi10: cmp ebx,[esi+PfRangeBase-PfProfileListEntry] ; >= base?
+ jb kipi20 ; no, skip entry
+ cmp ebx,[esi+PfRangeLimit-PfProfileListEntry] ; < limit?
+ jae kipi20 ; no, skip entry
+ cmp cx,word ptr [esi+PfSource-PfProfileListEntry] ; == source?
+ jne kipi20 ; no, skip entry
+ifndef NT_UP
+ test edi,[esi+PfAffinity-PfProfileListEntry] ; affinity match?
+ jz kipi20 ; no, skip entry
+endif
+
+;
+; RangeBase <= program counter < RangeLimit, we have a hit
+;
+
+ sub ebx,[esi+PfRangeBase-PfProfileListEntry] ; (ebx) = offset in profile range
+ mov cl,[esi+PfBucketShift-PfProfileListEntry]
+ shr ebx,cl
+ and ebx,NOT 3 ; (ebx) = offset of counter for bucket
+ mov edi,[esi+PfBuffer-PfProfileListEntry] ; (edi) -> buffer
+ inc dword ptr [edi+ebx] ; record hit
+ mov ebx, kipieip ; (ebx) = sample pc
+ mov ecx, [esp+8] ; (cx) = profile source
+ifndef NT_UP
+ mov edi, PCR[PcSetMember] ; (edi) = current processor
+endif
+
+
+;
+; Go to next entry
+;
+
+ALIGN 4
+kipi20: mov esi,[esi].LsFlink ; (esi) -> profile object
+ cmp esi,edx
+ jne kipi10 ; not end of list, repeat
+
+
+;
+; Update process profile entries
+; (ebx) = sample program counter
+;
+
+ALIGN 4
+kipi30: mov eax,PCR[PcPrcbData+PbCurrentThread] ; (eax)-> current thread
+ mov eax,ThApcState+AsProcess[eax] ; (eax)-> current process
+ lea edx,[eax]+PrProfileListHead ; (edx)-> listhead
+ mov esi,[edx].LsFlink ; (esi)-> profile object
+ cmp esi,edx
+ je kipi60 ; process list end, return
+
+;
+; Check for 16 bitness
+;
+ movzx ecx,word ptr kipsegcs
+ test kipeflags,EFLAGS_V86_MASK
+ jnz kipi100 ; convert cs:ip to linear
+
+ cmp cx,KGDT_R0_CODE
+ je short kipi40
+
+ cmp cx,KGDT_R3_CODE or RPL_MASK
+ jne kipi110
+
+;
+; (ebx) = sample program counter
+; (esi) -> profile object
+;
+
+ALIGN 4
+kipi40: cmp [esi+PfSegment-PfProfileListEntry],word ptr 0 ; flat object?
+ jne kipi50 ; no, skip entry
+ cmp ebx,[esi+PfRangeBase-PfProfileListEntry] ; >= base?
+ jb kipi50 ; no, skip entry
+ cmp ebx,[esi+PfRangeLimit-PfProfileListEntry] ; < limit?
+ jae kipi50 ; no, skip entry
+ mov ecx, [esp+8] ; (cx) = profile source
+ cmp cx,word ptr [esi+PfSource-PfProfileListEntry] ; == source?
+ jne kipi50 ; no, skip entry
+ifndef NT_UP
+ mov edi,PCR[PcSetMember] ; (edi) = set member
+ test edi,[esi+PfAffinity-PfProfileListEntry] ; affinity match?
+ jz kipi50 ; no, skip entry
+endif
+
+
+;
+; RangeBase <= program counter < RangeLimit, we have a hit
+;
+
+ sub ebx,[esi+PfRangeBase-PfProfileListEntry] ; (ebx) = offset in profile range
+ mov cl,[esi+PfBucketShift-PfProfileListEntry]
+ shr ebx,cl
+ and ebx,NOT 3 ; (ebx) = offset of counter for bucket
+ mov edi,[esi+PfBuffer-PfProfileListEntry] ; (edi) -> buffer
+ inc dword ptr [edi+ebx] ; record hit
+ mov ebx, kipieip ; (ebx) = sample pc
+ mov ecx, [esp+8] ; (cx) = profile source
+
+;
+; Go to next entry
+;
+
+ALIGN 4
+kipi50: mov esi,[esi].LsFlink ; (esi) -> profile object
+ cmp esi,edx
+ jne kipi40 ; not end of list, repeat
+
+ALIGN 4
+kipi60:
+
+ifndef NT_UP
+ lea eax,_KiProfileLock
+ RELEASE_SPINLOCK eax
+endif
+ stdRet _KeProfileInterruptWithSource
+
+ifndef NT_UP
+ALIGN 4
+kipi96: SPIN_ON_SPINLOCK eax,kipi05,,DbgMp
+endif
+
+ALIGN 4
+kipi100:
+ shl ecx,4 ; segment -> paragraph
+ add ebx,ecx ; paragraph offset -> linear
+ jmp kipi40
+
+;
+; Update segment profile objects
+;
+
+;
+; (ebx) = sample program counter
+; (esi) -> profile object
+;
+
+ALIGN 4
+kipi110:
+ cmp [esi+PfSegment-PfProfileListEntry],ecx ; This segment?
+ jne kipi120 ; no, skip entry
+ cmp ebx,[esi+PfRangeBase-PfProfileListEntry] ; >= base?
+ jb kipi120 ; no, skip entry
+ cmp ebx,[esi+PfRangeLimit-PfProfileListEntry] ; < limit?
+ jae kipi120 ; no, skip entry
+ mov ecx, [esp+8] ; (cx) = profile source
+ cmp cx,word ptr [esi+PfSource-PfProfileListEntry] ; == source?
+ jne kipi120 ; no, skip entry
+ifndef NT_UP
+ mov edi,PCR[PcSetMember] ; (edi) = set member
+ test edi,[esi+PfAffinity-PfProfileListEntry] ; affinity match?
+ jnz kipi120 ; no, skip entry
+endif
+
+;
+; RangeBase <= program counter < RangeLimit, we have a hit
+;
+
+ sub ebx,[esi+PfRangeBase-PfProfileListEntry] ; (ebx) = offset in profile range
+ mov cl,[esi+PfBucketShift-PfProfileListEntry]
+ shr ebx,cl
+ and ebx,NOT 3 ; (ebx) = offset of counter for bucket
+ mov edi,[esi+PfBuffer-PfProfileListEntry] ; (edi) -> buffer
+ inc dword ptr [edi+ebx] ; record hit
+ mov ebx, kipieip ; (ebx) = sample pc
+ mov cx,kipsegcs ; ecx = sample cs
+
+;
+; Go to next entry
+;
+
+ALIGN 4
+kipi120:
+ mov esi,[esi].LsFlink ; (esi) -> profile object
+ cmp esi,edx
+ jne kipi110 ; not end of list, repeat
+
+ jmp kipi60
+
+stdENDP _KeProfileInterruptWithSource
+_TEXT$00 ends
+ end
diff --git a/private/ntos/ke/i386/cpu.asm b/private/ntos/ke/i386/cpu.asm
new file mode 100644
index 000000000..64b031484
--- /dev/null
+++ b/private/ntos/ke/i386/cpu.asm
@@ -0,0 +1,1037 @@
+ title "Processor type and stepping detection"
+;++
+;
+; Copyright (c) 1989 Microsoft Corporation
+;
+; Module Name:
+;
+; cpu.asm
+;
+; Abstract:
+;
+; This module implements the assembley code necessary to determine
+; cpu type and stepping information.
+;
+; Author:
+;
+; Shie-Lin Tzong (shielint) 28-Oct-1991.
+; Some of the code is extracted from Cruiser (mainly,
+; the code to determine 386 stepping.)
+;
+; Environment:
+;
+; 80x86
+;
+; Revision History:
+;
+;--
+
+ .xlist
+include i386\cpu.inc
+include ks386.inc
+include callconv.inc
+ .list
+
+;
+; constant for i386 32-bit multiplication test
+;
+
+MULTIPLIER equ 00000081h
+MULTIPLICAND equ 0417a000h
+RESULT_HIGH equ 00000002h
+RESULT_LOW equ 0fe7a000h
+
+;
+; Constants for Floating Point test
+;
+
+REALLONG_LOW equ 00000000
+REALLONG_HIGH equ 3FE00000h
+PSEUDO_DENORMAL_LOW equ 00000000h
+PSEUDO_DENORMAL_MID equ 80000000h
+PSEUDO_DENORMAL_HIGH equ 0000h
+
+.386p
+
+INIT SEGMENT DWORD PUBLIC 'CODE'
+ ASSUME DS:FLAT, ES:FLAT, SS:NOTHING, FS:NOTHING, GS:NOTHING
+
+
+;++
+;
+; USHORT
+; KiSetProcessorType (
+; VOID
+; )
+;
+; Routine Description:
+;
+; This function determines type of processor (80486, 80386),
+; and it's corrisponding stepping. The results are saved in
+; the current processor's PRCB.
+;
+; Arguments:
+;
+; None.
+;
+; Return Value:
+;
+; Prcb->CpuType
+; 3, 4, 5, ... 3 = 386, 4 = 486, etc..
+;
+; Prcb->CpuStep is encoded as follows:
+; lower byte as stepping #
+; upper byte as stepping letter (0=a, 1=b, 2=c, ...)
+;
+; (ax) = x86h or 0 if unrecongnized processor.
+;
+;--
+cPublicProc _KiSetProcessorType,0
+
+ mov byte ptr fs:PcPrcbData.PbCpuID, 0
+
+ push edi
+ push esi
+ push ebx ; Save C registers
+ mov eax, cr0
+ push eax
+ pushfd ; save Cr0 & flags
+
+ pop ebx ; Get flags into eax
+ push ebx ; Save original flags
+
+ mov ecx, ebx
+ xor ecx, EFLAGS_ID ; flip ID bit
+ push ecx
+ popfd ; load it into flags
+ pushfd ; re-save flags
+ pop ecx ; get flags into eax
+ cmp ebx, ecx ; did bit stay flipped?
+ jne short cpu_has_cpuid ; Yes, go use CPUID
+
+cpuid_unsupported:
+ pop ebx ; Get flags into eax
+ push ebx ; Save original flags
+
+ mov ecx, ebx
+ xor ecx, EFLAGS_AC ; flip AC bit
+ push ecx
+ popfd ; load it into flags
+ pushfd ; re-save flags
+ pop ecx ; get flags into eax
+ cmp ebx, ecx ; did bit stay flipped?
+ je short cpu_is_386 ; No, then this is a 386
+
+cpu_is_486:
+ mov byte ptr fs:PcPrcbData.PbCpuType, 4h ; Save CPU Type
+ call Get486Stepping
+ jmp cpu_save_stepping
+
+cpu_is_386:
+ mov byte ptr fs:PcPrcbData.PbCpuType, 3h ; Save CPU Type
+ call Get386Stepping
+ jmp cpu_save_stepping
+
+cpu_has_cpuid:
+ or ebx, EFLAGS_ID
+ push ebx
+ popfd ; Make sure ID bit is set
+
+ mov ecx, fs:PcIdt ; Address of IDT
+ push dword ptr [ecx+30h] ; Save Trap06 handler incase
+ push dword ptr [ecx+34h] ; the CPUID instruction faults
+
+ mov eax, offset CpuIdTrap6Handler
+ mov word ptr [ecx+30h], ax ; Set LowWord
+ shr eax, 16
+ mov word ptr [ecx+36h], ax ; Set HighWord
+
+ mov eax, 0 ; argument to CPUID
+.586p
+ cpuid ; Uses eax, ebx, ecx, edx
+.386p
+
+ mov ecx, fs:PcIdt ; Address of IDT
+ pop dword ptr [ecx+34h] ; restore trap6 handler
+ pop dword ptr [ecx+30h]
+
+ cmp eax, 3 ; check for A step of P5
+ jg short cpu_is_p5a ; A step returned family&step here
+
+ cmp eax, 1 ; make sure level 1 is supported
+ jc short cpuid_unsupported ; no, then punt
+
+ mov eax, 1 ; get the family and stepping
+ db 0fh, 0a2h
+
+ mov ebx, eax
+
+ and eax, 0F0h ; (eax) = Model
+ shl eax, 4
+ mov al, bl
+ and eax, 0F0Fh ; (eax) = Model[15:8] | Step[7:0]
+
+ and ebx, 0700h ; (bh) = CpuType
+
+ mov byte ptr fs:PcPrcbData.PbCpuID, 1 ; Has ID support
+ mov byte ptr fs:PcPrcbData.PbCpuType, bh ; Save CPU Type
+ jmp short cpu_save_stepping
+
+cpuid_trap:
+ mov ecx, fs:PcIdt ; Address of IDT
+ pop dword ptr [ecx+34h] ; restore trap6 handler
+ pop dword ptr [ecx+30h]
+ jmp cpuid_unsupported ; Go get processor information
+
+cpu_is_p5a:
+ mov byte ptr fs:PcPrcbData.PbCpuType, 5h ; CPU Type = P5
+ xor eax, eax
+
+cpu_save_stepping:
+ mov word ptr fs:PcPrcbData.PbCpuStep, ax ; Save CPU Stepping
+ popfd ; Restore flags
+ pop eax
+ mov cr0, eax
+ pop ebx
+ pop esi
+ pop edi
+ stdRET _KiSetProcessorType
+
+stdENDP _KiSetProcessorType
+
+;++
+;
+; BOOLEAN
+; CpuIdTrap6 (
+; VOID
+; )
+;
+; Routine Description:
+;
+; Temporary int 6 handler - assumes the cause of the exception was the
+; attempted CPUID instruction.
+;
+; Arguments:
+;
+; None.
+;
+; Return Value:
+;
+; none.
+;
+;--
+
+CpuIdTrap6Handler proc
+
+ mov [esp].IretEip,offset cpuid_trap
+ iretd
+
+CpuIdTrap6Handler endp
+
+
+;++
+;
+; USHORT
+; Get386Stepping (
+; VOID
+; )
+;
+; Routine Description:
+;
+; This function determines cpu stepping for i386 CPU stepping.
+;
+; Arguments:
+;
+; None.
+;
+; Return Value:
+;
+; [ax] - Cpu stepping.
+; 0 = A, 1 = B, 2 = C, ...
+;
+;--
+
+ public Get386Stepping
+Get386Stepping proc
+
+ call MultiplyTest ; Perform mutiplication test
+ jnc short G3s00 ; if nc, muttest is ok
+ mov ax, 0
+ ret
+G3s00:
+ call Check386B0 ; Check for B0 stepping
+ jnc short G3s05 ; if nc, it's B1/later
+ mov ax, 100h ; It is B0/earlier stepping
+ ret
+
+G3s05:
+ call Check386D1 ; Check for D1 stepping
+ jc short G3s10 ; if c, it is NOT D1
+ mov ax, 301h ; It is D1/later stepping
+ ret
+
+G3s10:
+ mov ax, 101h ; assume it is B1 stepping
+ ret
+
+Get386Stepping endp
+
+;++
+;
+; USHORT
+; Get486Stepping (
+; VOID
+; )
+;
+; Routine Description:
+;
+; This function determines cpu stepping for i486 CPU type.
+;
+; Arguments:
+;
+; None.
+;
+; Return Value:
+;
+; [ax] - Cpu stepping. For example, [ax] = D0h for D0 stepping.
+;
+;--
+
+ public Get486Stepping
+Get486Stepping proc
+
+ call Check486AStepping ; Check for A stepping
+ jnc short G4s00 ; if nc, it is NOT A stepping
+
+ mov ax, 0 ; set to A stepping
+ ret
+
+G4s00: call Check486BStepping ; Check for B stepping
+ jnc short G4s10 ; if nc, it is NOT a B stepping
+
+ mov ax, 100h ; set to B stepping
+ ret
+
+;
+; Before we test for 486 C/D step, we need to make sure NPX is present.
+; Because the test uses FP instruction to do the detection.
+;
+G4s10:
+ call _KiIsNpxPresent ; Check if cpu has coprocessor support?
+ or ax, ax
+ jz short G4s15 ; it is actually 486sx
+
+ call Check486CStepping ; Check for C stepping
+ jnc short G4s20 ; if nc, it is NOT a C stepping
+G4s15:
+ mov ax, 200h ; set to C stepping
+ ret
+
+G4s20: mov ax, 300h ; Set to D stepping
+ ret
+
+Get486Stepping endp
+
+;++
+;
+; BOOLEAN
+; Check486AStepping (
+; VOID
+; )
+;
+; Routine Description:
+;
+; This routine checks for 486 A Stepping.
+;
+; It takes advantage of the fact that on the A-step of the i486
+; processor, the ET bit in CR0 could be set or cleared by software,
+; but was not used by the hardware. On B or C -step, ET bit in CR0
+; is now hardwired to a "1" to force usage of the 386 math coprocessor
+; protocol.
+;
+; Arguments:
+;
+; None.
+;
+; Return Value:
+;
+; Carry Flag clear if B or later stepping.
+; Carry Flag set if A or earlier stepping.
+;
+;--
+ public Check486AStepping
+Check486AStepping proc near
+.386p
+ mov eax, cr0 ; reset ET bit in cr0
+ and eax, NOT CR0_ET
+ mov cr0, eax
+
+ mov eax, cr0 ; get cr0 back
+ test eax, CR0_ET ; if ET bit still set?
+ jnz short cas10 ; if nz, yes, still set, it's NOT A step
+ stc
+ ret
+
+cas10: clc
+ ret
+Check486AStepping endp
+
+;++
+;
+; BOOLEAN
+; Check486BStepping (
+; VOID
+; )
+;
+; Routine Description:
+;
+; This routine checks for 486 B Stepping.
+;
+; On the i486 processor, the "mov to/from DR4/5" instructions were
+; aliased to "mov to/from DR6/7" instructions. However, the i486
+; B or earlier steps generate an Invalid opcode exception when DR4/5
+; are used with "mov to/from special register" instruction.
+;
+; Arguments:
+;
+; None.
+;
+; Return Value:
+;
+; Carry Flag clear if C or later stepping.
+; Carry Flag set if B stepping.
+;
+;--
+ public Check486BStepping
+Check486BStepping proc
+
+ push ebx
+
+ mov ebx, fs:PcIdt ; Address of IDT
+ push dword ptr [ebx+30h]
+ push dword ptr [ebx+34h] ; Save Trap06 handler
+
+ mov eax, offset Temporary486Int6
+ mov word ptr [ebx+30h], ax ; Set LowWord
+ shr eax, 16
+ mov word ptr [ebx+36h], ax ; Set HighWord
+
+c4bs50: db 0fh, 21h, 0e0h ; mov eax, DR4
+ nop
+ nop
+ nop
+ nop
+ nop
+ clc ; it is C step
+ jmp short c4bs70
+c4bs60: stc ; it's B step
+c4bs70: pop dword ptr [ebx+34h] ; restore old int 6 vector
+ pop dword ptr [ebx+30h]
+
+ pop ebx
+ ret
+
+ ret
+
+Check486BStepping endp
+
+;++
+;
+; BOOLEAN
+; Temporary486Int6 (
+; VOID
+; )
+;
+; Routine Description:
+;
+; Temporary int 6 handler - assumes the cause of the exception was the
+; attempted execution of an mov to/from DR4/5 instruction.
+;
+; Arguments:
+;
+; None.
+;
+; Return Value:
+;
+; none.
+;
+;--
+
+Temporary486Int6 proc
+
+ mov [esp].IretEIp,offset c4bs60 ; set EIP to stc instruction
+ iretd
+
+Temporary486Int6 endp
+
+;++
+;
+; BOOLEAN
+; Check486CStepping (
+; VOID
+; )
+;
+; Routine Description:
+;
+; This routine checks for 486 C Stepping.
+;
+; This routine takes advantage of the fact that FSCALE produces
+; wrong result with Denormal or Pseudo-denormal operand on 486
+; C and earlier steps.
+;
+; If the value contained in ST(1), second location in the floating
+; point stack, is between 1 and 11, and the value in ST, top of the
+; floating point stack, is either a pseudo-denormal number or a
+; denormal number with the underflow exception unmasked, the FSCALE
+; instruction produces an incorrect result.
+;
+; Arguments:
+;
+; None.
+;
+; Return Value:
+;
+; Carry Flag clear if D or later stepping.
+; Carry Flag set if C stepping.
+;
+;--
+
+FpControl equ [ebp - 2]
+RealLongSt1 equ [ebp - 10]
+PseudoDenormal equ [ebp - 20]
+FscaleResult equ [ebp - 30]
+
+ public Check486CStepping
+Check486CStepping proc
+
+ push ebp
+ mov ebp, esp
+ sub esp, 30 ; Allocate space for temp real variables
+
+ mov eax, cr0 ; Don't trap while doing math
+ and eax, NOT (CR0_ET+CR0_MP+CR0_TS+CR0_EM)
+ mov cr0, eax
+
+;
+; Initialize the local FP variables to predefined values.
+; RealLongSt1 = 1.0 * (2 ** -1) = 0.5 in normalized double precision FP form
+; PseudoDenormal = a unsupported format by IEEE.
+; Sign bit = 0
+; Exponent = 000000000000000B
+; Significand = 100000...0B
+; FscaleResult = The result of FSCALE instruction. Depending on 486 step,
+; the value will be different:
+; Under C and earlier steps, 486 returns the original value
+; in ST as the result. The correct returned value should be
+; original significand and an exponent of 0...01.
+;
+
+ mov dword ptr RealLongSt1, REALLONG_LOW
+ mov dword ptr RealLongSt1 + 4, REALLONG_HIGH
+ mov dword ptr PseudoDenormal, PSEUDO_DENORMAL_LOW
+ mov dword ptr PseudoDenormal + 4, PSEUDO_DENORMAL_MID
+ mov word ptr PseudoDenormal + 8, PSEUDO_DENORMAL_HIGH
+
+.387
+ fnstcw FpControl ; Get FP control word
+ fwait
+ or word ptr FpControl, 0FFh ; Mask all the FP exceptions
+ fldcw FpControl ; Set FP control
+
+ fld qword ptr RealLongSt1 ; 0 < ST(1) = RealLongSt1 < 1
+ fld tbyte ptr PseudoDenormal; Denormalized operand. Note, i486
+ ; won't report denormal exception
+ ; on 'FLD' instruction.
+ ; ST(0) = Extended Denormalized operand
+ fscale ; try to trigger 486Cx errata
+ fstp tbyte ptr FscaleResult ; Store ST(0) in FscaleResult
+ cmp word ptr FscaleResult + 8, PSEUDO_DENORMAL_HIGH
+ ; Is Exponent changed?
+ jz short c4ds00 ; if z, no, it is C step
+ clc
+ jmp short c4ds10
+c4ds00: stc
+c4ds10: mov esp, ebp
+ pop ebp
+ ret
+
+Check486CStepping endp
+
+;++
+;
+; BOOLEAN
+; Check386B0 (
+; VOID
+; )
+;
+; Routine Description:
+;
+; This routine checks for 386 B0 or earlier stepping.
+;
+; It takes advantage of the fact that the bit INSERT and
+; EXTRACT instructions that existed in B0 and earlier versions of the
+; 386 were removed in the B1 stepping. When executed on the B1, INSERT
+; and EXTRACT cause an int 6 (invalid opcode) exception. This routine
+; can therefore discriminate between B1/later 386s and B0/earlier 386s.
+; It is intended to be used in sequence with other checks to determine
+; processor stepping by exercising specific bugs found in specific
+; steppings of the 386.
+;
+; Arguments:
+;
+; None.
+;
+; Return Value:
+;
+; Carry Flag clear if B1 or later stepping
+; Carry Flag set if B0 or prior
+;
+;--
+
+Check386B0 proc
+
+ push ebx
+
+ mov ebx, fs:PcIdt ; Address of IDT
+ push dword ptr [ebx+30h]
+ push dword ptr [ebx+34h] ; Save Trap06 handler
+
+ mov eax, offset TemporaryInt6
+ mov word ptr [ebx+30h], ax ; Set LowWord
+ shr eax, 16
+ mov word ptr [ebx+36h], ax ; Set HighWord
+
+
+;
+; Attempt execution of Extract Bit String instruction. Execution on
+; B0 or earlier with length (CL) = 0 will return 0 into the destination
+; (CX in this case). Execution on B1 or later will fail either due to
+; taking the invalid opcode trap, or if the opcode is valid, we don't
+; expect CX will be zeroed by any new instruction supported by newer
+; steppings. The dummy int 6 handler will clears the Carry Flag and
+; returns execution to the appropriate label. If the instruction
+; actually executes, CX will *probably* remain unchanged in any new
+; stepping that uses the opcode for something else. The nops are meant
+; to handle newer steppings with an unknown instruction length.
+;
+
+ xor eax,eax
+ mov edx,eax
+ mov ecx,0ff00h ; Extract length (CL) == 0, (CX) != 0
+
+b1c50: db 0fh, 0a6h, 0cah ; xbts cx,dx,ax,cl
+ nop
+ nop
+ nop
+ nop
+ nop
+ stc ; assume B0
+ jecxz short b1c70 ; jmp if B0
+b1c60: clc
+b1c70: pop dword ptr [ebx+34h] ; restore old int 6 vector
+ pop dword ptr [ebx+30h]
+
+ pop ebx
+ ret
+
+Check386B0 endp
+
+;++
+;
+; BOOLEAN
+; TemporaryInt6 (
+; VOID
+; )
+;
+; Routine Description:
+;
+; Temporary int 6 handler - assumes the cause of the exception was the
+; attempted execution of an XTBS instruction.
+;
+; Arguments:
+;
+; None.
+;
+; Return Value:
+;
+; none.
+;
+;--
+
+TemporaryInt6 proc
+
+ mov [esp].IretEip,offset b1c60 ; set IP to clc instruction
+ iretd
+
+TemporaryInt6 endp
+
+;++
+;
+; BOOLEAN
+; Check386D1 (
+; VOID
+; )
+;
+; Routine Description:
+;
+; This routine checks for 386 D1 Stepping.
+;
+; It takes advantage of the fact that on pre-D1 386, if a REPeated
+; MOVS instruction is executed when single-stepping is enabled,
+; a single step trap is taken every TWO moves steps, but should
+; occuu each move step.
+;
+; NOTE: This routine cannot distinguish between a D0 stepping and a D1
+; stepping. If a need arises to make this distinction, this routine
+; will need modification. D0 steppings will be recognized as D1.
+;
+; Arguments:
+;
+; None.
+;
+; Return Value:
+;
+; Carry Flag clear if D1 or later stepping
+; Carry Flag set if B1 or prior
+;
+;--
+
+Check386D1 proc
+ push ebx
+
+ mov ebx, fs:PcIdt ; Address of IDT
+ push dword ptr [ebx+08h]
+ push dword ptr [ebx+0ch] ; Save Trap01 handler
+
+ mov eax, offset TemporaryInt1
+ mov word ptr [ebx+08h], ax ; Set LowWord
+ shr eax, 16
+ mov word ptr [ebx+0eh], ax ; Set HighWord
+
+;
+; Attempt execution of rep movsb instruction with the Trace Flag set.
+; Execution on B1 or earlier with length (CX) > 1 will trace over two
+; iterations before accepting the trace trap. Execution on D1 or later
+; will accept the trace trap after a single iteration. The dummy int 1
+; handler will return execution to the instruction following the movsb
+; instruction. Examination of (CX) will reveal the stepping.
+;
+
+ sub esp,4 ; make room for target of movsb
+ mov esi, offset TemporaryInt1 ; (ds:esi) -> some present data
+ mov edi,esp
+ mov ecx,2 ; 2 iterations
+ pushfd
+ or dword ptr [esp], EFLAGS_TF
+ popfd ; cause a single step trap
+ rep movsb
+
+d1c60: add esp,4 ; clean off stack
+ pop dword ptr [ebx+0ch] ; restore old int 1 vector
+ pop dword ptr [ebx+08h]
+ stc ; assume B1
+ jecxz short d1cx ; jmp if <= B1
+ clc ; else clear carry to indicate >= D1
+d1cx:
+ pop ebx
+ ret
+
+Check386D1 endp
+
+;++
+;
+; BOOLEAN
+; TemporaryInt1 (
+; VOID
+; )
+;
+; Routine Description:
+;
+; Temporary int 1 handler - assumes the cause of the exception was
+; trace trap at the above rep movs instruction.
+;
+; Arguments:
+;
+; (esp)->eip of trapped instruction
+; cs of trapped instruction
+; eflags of trapped instruction
+;
+;--
+
+TemporaryInt1 proc
+
+ and [esp].IretEFlags,not EFLAGS_TF ; clear caller's Trace Flag
+ mov [esp].IretEip,offset d1c60 ; set IP to next instruction
+ iretd
+
+TemporaryInt1 endp
+
+;++
+;
+; BOOLEAN
+; MultiplyTest (
+; VOID
+; )
+;
+; Routine Description:
+;
+; This routine checks the 386 32-bit multiply instruction.
+; The reason for this check is because some of the i386 fail to
+; perform this instruction.
+;
+; Arguments:
+;
+; None.
+;
+; Return Value:
+;
+; Carry Flag clear on success
+; Carry Flag set on failure
+;
+;--
+;
+
+MultiplyTest proc
+
+ xor cx,cx ; 64K times is a nice round number
+mlt00: push cx
+ call Multiply ; does this chip's multiply work?
+ pop cx
+ jc short mltx ; if c, No, exit
+ loop mlt00 ; if nc, YEs, loop to try again
+ clc
+mltx:
+ ret
+
+MultiplyTest endp
+
+;++
+;
+; BOOLEAN
+; Multiply (
+; VOID
+; )
+;
+; Routine Description:
+;
+; This routine performs 32-bit multiplication test which is known to
+; fail on bad 386s.
+;
+; Note, the supplied pattern values must be used for consistent results.
+;
+; Arguments:
+;
+; None.
+;
+; Return Value:
+;
+; Carry Flag clear on success.
+; Carry Flag set on failure.
+;
+;--
+
+Multiply proc
+
+ mov ecx, MULTIPLIER
+ mov eax, MULTIPLICAND
+ mul ecx
+
+ cmp edx, RESULT_HIGH ; Q: high order answer OK ?
+ stc ; assume failure
+ jnz short mlpx ; N: exit with error
+
+ cmp eax, RESULT_LOW ; Q: low order answer OK ?
+ stc ; assume failure
+ jnz short mlpx ; N: exit with error
+
+ clc ; indicate success
+mlpx:
+ ret
+
+Multiply endp
+
+;++
+;
+; BOOLEAN
+; KiIsNpxPresent(
+; VOID
+; );
+;
+; Routine Description:
+;
+; This routine determines if there is any Numeric coprocessor
+; present.
+;
+; Note that we do NOT determine its type (287, 387).
+; This code is extracted from Intel book.
+;
+; Arguments:
+;
+; None.
+;
+; Return:
+;
+; TRUE - If NPX is present. Else a value of FALSE is returned.
+; Sets CR0 NPX bits accordingly.
+;
+;--
+
+cPublicProc _KiIsNpxPresent,0
+
+ push ebp ; Save caller's bp
+ mov eax, cr0
+ and eax, NOT (CR0_ET+CR0_MP+CR0_TS+CR0_EM)
+ mov cr0, eax
+ xor edx, edx
+.287
+ fninit ; Initialize NPX
+ mov ecx, 5A5A5A5Ah ; Put non-zero value
+ push ecx ; into the memory we are going to use
+ mov ebp, esp
+ fnstsw word ptr [ebp] ; Retrieve status - must use non-wait
+ cmp byte ptr [ebp], 0 ; All bits cleared by fninit?
+ jne Inp10
+
+ or eax, CR0_ET
+ mov edx, 1
+
+ cmp fs:PcPrcbData.PbCpuType, 3h
+ jbe Inp10
+
+ or eax, CR0_NE
+
+Inp10:
+ or eax, CR0_EM+CR0_TS ; During Kernel Initialization set
+ ; the EM bit
+ mov cr0, eax
+ pop eax ; clear scratch value
+ pop ebp ; Restore caller's bp
+ mov eax, edx
+ stdRet _KiIsNpxPresent
+
+
+stdENDP _KiIsNpxPresent
+
+.586p
+
+;++
+;
+; VOID
+; CPUID (
+; ULONG InEax,
+; PULONG OutEax,
+; PULONG OutEbx,
+; PULONG OutEcx,
+; PULONG OutEdx
+; );
+;
+; Routine Description:
+;
+; Executes the CPUID instruction and returns the registers from it
+;
+; Only available at INIT time
+;
+; Arguments:
+;
+; Return Value:
+;
+;--
+cPublicProc _CPUID,5
+
+ push ebx
+ push esi
+
+ mov eax, [esp+12]
+
+ cpuid
+
+ mov esi, [esp+16] ; return EAX
+ mov [esi], eax
+
+ mov esi, [esp+20] ; return EBX
+ mov [esi], ebx
+
+ mov esi, [esp+24] ; return ECX
+ mov [esi], ecx
+
+ mov esi, [esp+28] ; return EDX
+ mov [esi], edx
+
+ pop esi
+ pop ebx
+
+ stdRET _CPUID
+
+stdENDP _CPUID
+
+;++
+;
+; LONGLONG
+; RDTSC (
+; VOID
+; );
+;
+; Routine Description:
+;
+; Arguments:
+;
+; Return Value:
+;
+;--
+cPublicProc _RDTSC
+ rdtsc
+ stdRET _RDTSC
+
+stdENDP _RDTSC
+
+INIT ENDS
+
+_TEXT SEGMENT DWORD PUBLIC 'CODE' ; Put IdleLoop in text section
+ ASSUME DS:FLAT, ES:FLAT, SS:NOTHING, FS:NOTHING, GS:NOTHING
+
+;++
+;
+; ULONGLONG
+; FASTCALL
+; RDMSR (
+; IN ULONG MsrRegister
+; );
+;
+; Routine Description:
+;
+; Arguments:
+;
+; Return Value:
+;
+;--
+cPublicFastCall RDMSR, 1
+ rdmsr
+ fstRET RDMSR
+fstENDP RDMSR
+
+
+;++
+;
+; VOID
+; WRMSR (
+; IN ULONG MsrRegister
+; IN LONGLONG MsrValue
+; );
+;
+; Routine Description:
+;
+; Arguments:
+;
+; Return Value:
+;
+;--
+cPublicProc _WRMSR, 3
+ mov ecx, [esp+4]
+ mov eax, [esp+8]
+ mov edx, [esp+12]
+ wrmsr
+ stdRET _WRMSR
+stdENDP _WRMSR
+
+_TEXT ENDS
+ END
diff --git a/private/ntos/ke/i386/cpu.inc b/private/ntos/ke/i386/cpu.inc
new file mode 100644
index 000000000..ca404c6c2
--- /dev/null
+++ b/private/ntos/ke/i386/cpu.inc
@@ -0,0 +1,61 @@
+;++
+;
+; Copyright (c) 1989 Microsoft Corporation
+;
+; Module Name:
+;
+; cpu.inc
+;
+; Abstract:
+;
+; This module contains the assembly structures and definitions
+; for INTEL 80x86 CPU specifiec information. This include file
+; is mainly used by CPU.ASM to determine CPU type and stepping
+; number.
+;
+; Author:
+;
+; Shie-Lin (shielint) 1-Oct-1991
+;
+; Revision History:
+;
+;--
+
+;
+; The following equates define the control bits of CR0 register
+;
+
+CR0_AM equ 40000h
+CR0_ET equ 00010h
+
+;
+; The following equates define the control bits of EFALGS register
+;
+
+EFLAGS_AC equ 40000h
+EFLAGS_VM equ 20000h
+EFLAGS_RF equ 10000h
+EFLAGS_NF equ 4000h
+EFLAGS_IOPL equ 3000h
+EFLAGS_IF equ 200h
+EFLAGS_TF equ 100h
+EFLAGS_ID equ 200000h
+
+;
+; Define the iret frame
+;
+
+IretFrame struc
+
+IretEip dd 0
+IretCs dd 0
+IretEFlags dd 0
+
+IretFrame ends
+
+;
+; Misc. definitions
+;
+
+ADDRESS_OVERRIDE equ 67h
+OPERAND_OVERRIDE equ 66h
diff --git a/private/ntos/ke/i386/ctxswap.asm b/private/ntos/ke/i386/ctxswap.asm
new file mode 100644
index 000000000..1213fcdc1
--- /dev/null
+++ b/private/ntos/ke/i386/ctxswap.asm
@@ -0,0 +1,1923 @@
+ title "Context Swap"
+;++
+;
+; Copyright (c) 1989 Microsoft Corporation
+;
+; Module Name:
+;
+; ctxswap.asm
+;
+; Abstract:
+;
+; This module implements the code necessary to field the dispatch
+; interrupt and to perform kernel initiated context switching.
+;
+; Author:
+;
+; Shie-Lin Tzong (shielint) 14-Jan-1990
+;
+; Environment:
+;
+; Kernel mode only.
+;
+; Revision History:
+;
+; 22-feb-90 bryanwi
+; write actual swap context procedure
+;
+;--
+
+.486p
+ .xlist
+include ks386.inc
+include i386\kimacro.inc
+include mac386.inc
+include callconv.inc
+ .list
+
+ EXTRNP HalClearSoftwareInterrupt,1,IMPORT,FASTCALL
+ EXTRNP HalRequestSoftwareInterrupt,1,IMPORT,FASTCALL
+ EXTRNP KiActivateWaiterQueue,1,,FASTCALL
+ EXTRNP KiReadyThread,1,,FASTCALL
+ EXTRNP KiWaitTest,2,,FASTCALL
+ EXTRNP KfLowerIrql,1,IMPORT,FASTCALL
+ EXTRNP KfRaiseIrql,1,IMPORT,FASTCALL
+ EXTRNP _KeGetCurrentIrql,0,IMPORT
+ EXTRNP _KeGetCurrentThread,0
+ EXTRNP _KiContinueClientWait,3
+ EXTRNP _KiDeliverApc,3
+ EXTRNP _KiQuantumEnd,0
+ EXTRNP _KeBugCheckEx,5
+ extrn KiRetireDpcList:PROC
+ extrn _KiContextSwapLock:DWORD
+ extrn _KiDispatcherLock:DWORD
+ extrn _KeFeatureBits:DWORD
+ extrn _KeThreadSwitchCounters:DWORD
+ extrn _KeTickCount:DWORD
+
+ extrn __imp_@KfLowerIrql@4:DWORD
+
+ extrn _KiWaitInListHead:DWORD
+ extrn _KiWaitOutListHead:DWORD
+ extrn _KiDispatcherReadyListHead:DWORD
+ extrn _KiIdleSummary:DWORD
+ extrn _KiReadySummary:DWORD
+ extrn _KiSwapContextNotifyRoutine:DWORD
+ extrn _KiThreadSelectNotifyRoutine:DWORD
+
+if DBG
+ extrn _KdDebuggerEnabled:BYTE
+ EXTRNP _DbgBreakPoint,0
+ extrn _DbgPrint:near
+ extrn _MsgDpcTrashedEsp:BYTE
+ extrn _MsgDpcTimeout:BYTE
+ extrn _KiDPCTimeout:DWORD
+endif
+
+_TEXT$00 SEGMENT PARA PUBLIC 'CODE'
+ ASSUME DS:FLAT, ES:FLAT, SS:NOTHING, FS:NOTHING, GS:NOTHING
+
+ page ,132
+ subttl "Unlock Dispatcher Database"
+;++
+;
+; VOID
+; KiUnlockDispatcherDatabase (
+; IN KIRQL OldIrql
+; )
+;
+; Routine Description:
+;
+; This routine is entered at IRQL DISPATCH_LEVEL with the dispatcher
+; database locked. Its function is to either unlock the dispatcher
+; database and return or initiate a context switch if another thread
+; has been selected for execution.
+;
+; Arguments:
+;
+; (TOS) Return address
+;
+; (ecx) OldIrql - Supplies the IRQL when the dispatcher database
+; lock was acquired.
+;
+; Return Value:
+;
+; None.
+;
+;--
+
+cPublicFastCall KiUnlockDispatcherDatabase, 1
+
+;
+; Check if a new thread is scheduled for execution.
+;
+
+ cmp PCR[PcPrcbData+PbNextThread], 0 ; check if next thread
+ jne short Kiu20 ; if ne, new thread scheduled
+
+;
+; Release dispatcher database lock, lower IRQL to its previous level,
+; and return.
+;
+
+Kiu00: ;
+
+ifndef NT_UP
+
+ mov _KiDispatcherLock, 0 ; release dispatcher lock
+
+endif
+
+;
+; N.B. This exit jumps directly to the lower IRQL routine which has a
+; compatible fastcall interface.
+;
+
+ jmp dword ptr [__imp_@KfLowerIrql@4] ; lower IRQL to previous level
+
+;
+; A new thread has been selected to run on the current processor, but
+; the new IRQL is not below dispatch level. If the current processor is
+; not executing a DPC, then request a dispatch interrupt on the current
+; processor before restoring IRQL.
+;
+
+Kiu10: cmp dword ptr PCR[PcPrcbData.PbDpcRoutineActive],0 ; check if DPC routine active
+ jne short Kiu00 ; if ne, DPC routine is active
+
+ifndef NT_UP
+
+ mov _KiDispatcherLock, 0 ; release dispatcher lock
+
+endif
+
+ push ecx ; save new IRQL
+ mov cl, DISPATCH_LEVEL ; request dispatch interrupt
+ fstCall HalRequestSoftwareInterrupt ;
+ pop ecx ; restore new IRQL
+
+;
+; N.B. This exit jumps directly to the lower IRQL routine which has a
+; compatible fastcall interface.
+;
+
+ jmp dword ptr [__imp_@KfLowerIrql@4] ; lower IRQL to previous level
+
+;
+; Check if the previous IRQL is less than dispatch level.
+;
+
+Kiu20: cmp cl, DISPATCH_LEVEL ; check if IRQL below dispatch level
+ jge short Kiu10 ; if ge, not below dispatch level
+
+;
+; There is a new thread scheduled for execution and the previous IRQL is
+; less than dispatch level. Context swith to the new thread immediately.
+;
+;
+; N.B. The following registers MUST be saved such that ebp is saved last.
+; This is done so the debugger can find the saved ebp for a thread
+; that is not currently in the running state.
+;
+
+.fpo (4, 0, 0, 0, 0, 0)
+ sub esp, 4*4
+ mov [esp+12], ebx ; save registers
+ mov [esp+8], esi ;
+ mov [esp+4], edi ;
+ mov [esp+0], ebp ;
+ mov ebx, PCR[PcSelfPcr] ; get address of PCR
+ mov esi, [ebx].PcPrcbData.PbNextThread ; get next thread address
+ mov edi, [ebx].PcPrcbData.PbCurrentThread ; get current thread address
+ mov dword ptr [ebx].PcPrcbData.PbNextThread, 0 ; clear next thread address
+ mov [ebx].PcPrcbData.PbCurrentThread, esi ; set current thread address
+ mov [edi].ThWaitIrql, cl ; save previous IRQL
+ mov ecx, edi ; set address of current thread
+ fstCall KiReadyThread ; reready thread for execution
+ mov cl, [edi].ThWaitIrql ; set APC interrupt bypass disable
+ call SwapContext ; swap context
+ or al, al ; check if kernel APC pending
+ mov cl, [esi].ThWaitIrql ; get original wait IRQL
+ jnz short Kiu50 ; if nz, kernel APC pending
+
+Kiu30: mov ebp, [esp+0] ; restore registers
+ mov edi, [esp+4] ;
+ mov esi, [esp+8] ;
+ mov ebx, [esp+12] ;
+ add esp, 4*4
+
+;
+; N.B. This exit jumps directly to the lower IRQL routine which has a
+; compatible fastcall interface.
+;
+
+ jmp dword ptr [__imp_@KfLowerIrql@4] ; lower IRQL to previous level
+
+Kiu50: mov cl, APC_LEVEL ; lower IRQL to APC level
+ fstCall KfLowerIrql ;
+ xor eax, eax ; set previous mode to kernel
+ stdCall _KiDeliverApc, <eax, eax, eax> ; deliver kernel mode APC
+ inc dword ptr [ebx].PcPrcbData.PbApcBypassCount ; increment count
+ xor ecx, ecx ; set original wait IRQL
+ jmp short Kiu30
+
+fstENDP KiUnlockDispatcherDatabase
+
+ page ,132
+ subttl "Swap Thread"
+;++
+;
+; VOID
+; KiSwapThread (
+; VOID
+; )
+;
+; Routine Description:
+;
+; This routine is called to select the next thread to run on the
+; current processor and to perform a context switch to the thread.
+;
+; Arguments:
+;
+; None.
+;
+; Return Value:
+;
+; Wait completion status (eax).
+;
+;--
+
+cPublicFastCall KiSwapThread, 0
+.fpo (4, 0, 0, 0, 1, 0)
+
+;
+; N.B. The following registers MUST be saved such that ebp is saved last.
+; This is done so the debugger can find the saved ebp for a thread
+; that is not currently in the running state.
+;
+
+ sub esp, 4*4
+ mov [esp+12], ebx ; save registers
+ mov [esp+8], esi ;
+ mov [esp+4], edi ;
+ mov [esp+0], ebp ;
+
+ mov ebx, PCR[PcSelfPcr] ; get address of PCR
+ mov edx, [ebx].PcPrcbData.PbNextThread ; get next thread address
+ or edx, edx ; check if next thread selected
+ jnz Swt140 ; if nz, next thread selected
+
+;
+; Find the highest nibble in the ready summary that contains a set bit
+; and left justify so the nibble is in bits <31:28>
+;
+
+ mov ecx, 16 ; set base bit number
+ mov edi, _KiReadySummary ; get ready summary
+ mov esi, edi ; copy ready summary
+ shr esi, 16 ; isolate bits <31:16> of summary
+ jnz short Swt10 ; if nz, bits <31:16> are nonzero
+ xor ecx, ecx ; set base bit number
+ mov esi, edi ; set bits <15:0> of summary
+Swt10: shr esi, 8 ; isolate bits <15:8> of low bits
+ jz short Swt20 ; if z, bits <15:8> are zero
+ add ecx, 8 ; add offset to nonzero byte
+Swt20: mov esi, edi ; isolate highest nonzero byte
+ shr esi, cl ;
+ add ecx, 3 ; adjust to high bit of nibble
+ cmp esi, 10h ; check if high nibble nonzero
+ jb short Swt30 ; if b, then high nibble is zero
+ add ecx, 4 ; compute ready queue priority
+Swt30: mov esi, ecx ; left justify ready summary nibble
+ not ecx ;
+ shl edi, cl ;
+ or edi, edi ;
+
+;
+; If the next bit is set in the ready summary, then scan the corresponding
+; dispatcher ready queue.
+;
+
+Swt40: js short Swt60 ; if s, queue contains an entry
+Swt50: sub esi, 1 ; decrement ready queue priority
+ shl edi, 1 ; position next ready summary bit
+ jnz short Swt40 ; if nz, more queues to scan
+
+;
+; All ready queues were scanned without finding a runnable thread so
+; default to the idle thread and set the appropriate bit in idle summary.
+;
+
+ifdef _COLLECT_SWITCH_DATA_
+
+ inc _KeThreadSwitchCounters + TwSwitchToIdle ; increment counter
+
+endif
+
+ifdef NT_UP
+
+ mov _KiIdleSummary, 1 ; set idle summary bit
+
+else
+
+ mov eax, [ebx].PcPrcbData.PbSetMember ; get processor set member
+ or _KiIdleSummary, eax ; set idle summary bit
+
+endif
+
+ mov edx, [ebx].PcPrcbData.PbIdleThread ; set idle thread address
+ jmp Swt140 ;
+
+;
+; If the thread can execute on the current processor, then remove it from
+; the dispatcher ready queue.
+;
+
+ align 4
+swt60: lea ebp, [esi*8] + _KiDispatcherReadyListHead ; get ready queue address
+ mov ecx, [ebp].LsFlink ; get address of first queue entry
+Swt70: mov edx, ecx ; compute address of thread object
+ sub edx, ThWaitListEntry ;
+
+ifndef NT_UP
+
+ mov eax, [edx].ThAffinity ; get thread affinity
+ test eax, [ebx].PcPrcbData.PbSetMember ; test if compatible affinity
+ jnz short Swt80 ; if nz, thread affinity compatible
+ mov ecx, [ecx].LsFlink ; get address of next entry
+ cmp ebp, ecx ; check if end of list
+ jnz short Swt70 ; if nz, not end of list
+ jmp short Swt50 ;
+
+;
+; If the thread last ran on the current processor, has been waiting for
+; longer than a quantum, or its priority is greater than low realtime
+; plus 9, then select the thread. Otherwise, an attempt is made to find
+; a more appropriate candidate.
+;
+
+ align 4
+Swt80: cmp _KiThreadSelectNotifyRoutine, 0 ; check for callout routine
+ je short Swt85 ; if eq, no callout routine registered
+ push edx ; save volatile registers
+ push ecx ;
+ mov ecx, [edx].EtCid.CidUniqueThread ; set trial thread unique id
+ call [_KiThreadSelectNotifyRoutine] ; notify callout routine
+ pop ecx ; restore volatile registers
+ pop edx ;
+ or eax, eax ; check if trial thread selectable
+ jnz Swt120 ; if nz, trial thread selectable
+ jmp Swt87 ;
+
+ align 4
+Swt85: mov al, [edx].ThNextProcessor ; get last processor number
+ cmp al, [ebx].PcPrcbData.PbNumber ; check if current processor
+ jz Swt120 ; if z, same as current processor
+ mov al, [edx].ThIdealProcessor ; get ideal processor number
+ cmp al, [ebx].PcPrcbData.PbNumber ; check if current processor
+ jz short Swt120 ; if z, same as current processor
+Swt87: cmp esi, LOW_REALTIME_PRIORITY + 9 ; check if priority in range
+ jae short Swt120 ; if ae, priority not in range
+ mov edi, _KeTickCount + 0 ; get low part of tick count
+ sub edi, [edx].ThWaitTime ; compute length of wait
+ cmp edi, READY_SKIP_QUANTUM + 1 ; check if wait time exceeded
+ jae short Swt120 ; if ae, wait time exceeded
+ mov edi, edx ; set address of thread
+
+;
+; Search forward in the ready queue until the end of the list is reached
+; or a more appropriate thread is found.
+;
+
+Swt90: mov edi, [edi].ThWaitListEntry ; get address of next entry
+ cmp ebp, edi ; check if end of list
+ jz short Swt120 ; if z, end of list
+ sub edi, ThWaitListEntry ; compute address of thread
+ mov eax, [edi].ThAffinity ; get thread affinity
+ test eax, [ebx].PcPrcbData.PbSetMember ; test if compatible infinity
+ jz short Swt100 ; if z, thread affinity not compatible
+ cmp _KiThreadSelectNotifyRoutine, 0 ; check for callout routine
+ je short Swt95 ; if eq, no callout routine registered
+ push edx ; save volatile registers
+ push ecx ;
+ mov ecx, [edi].EtCid.CidUniqueThread ; set trial thread unique id
+ call [_KiThreadSelectNotifyRoutine] ; notify callout routine
+ pop ecx ; restore volatile registers
+ pop edx ;
+ or eax, eax ; check if trial thread selectable
+ jnz short Swt110 ; if nz, trial thread selectable
+ jmp short Swt100 ;
+
+ align 4
+Swt95: mov al, [edi].ThNextProcessor ; get last processor number
+ cmp al, [ebx].PcPrcbData.PbNumber ; check if current processor
+ jz short Swt110 ; if z, same as current processor
+ mov al, [edi].ThIdealProcessor ; get ideal processor number
+ cmp al, [ebx].PcPrcbData.PbNumber ; check if current processor
+ jz short Swt110 ; if z, same as current processor
+Swt100: mov eax, _KeTickCount + 0 ; get low part of tick count
+ sub eax, [edi].ThWaitTime ; compute length of wait
+ cmp eax, READY_SKIP_QUANTUM + 1 ; check if wait time exceeded
+ jb short Swt90 ; if b, wait time not exceeded
+ jmp short Swt120 ;
+
+ align 4
+Swt110: mov edx, edi ; set address of thread
+ mov ecx, edi ; compute address of list entry
+ add ecx, ThWaitListEntry ;
+Swt120: mov al, [ebx].PcPrcbData.PbNumber ; get current processor number
+
+ifdef _COLLECT_SWITCH_DATA_
+
+ lea ebp, _KeThreadSwitchCounters + TwFindIdeal ; get counter address
+ cmp al, [edx].ThIdealProcessor ; check if same as ideal processor
+ jz short Swt130 ; if z, same as ideal processor
+ add ebp, TwFindLast - TwFindIdeal ; compute address of last counter
+ cmp al, [edx].ThNextProcessor ; check if same as last processor
+ jz short Swt130 ; if z, same as last processor
+ add ebp,TwFindAny - TwFindLast ; compute address of correct counter
+Swt130: inc dword ptr [ebp] ; increment appropriate switch counter
+
+endif
+
+ mov [edx].ThNextProcessor, al ; set next processor number
+
+endif
+
+;
+; Remove the selected thread from the ready queue.
+;
+
+ mov eax, [ecx].LsFlink ; get list entry forward link
+ mov ebp, [ecx].LsBlink ; get list entry backward link
+ mov [ebp].LsFlink, eax ; set forward link in previous entry
+ mov [eax].LsBlink, ebp ; set backward link in next entry
+ cmp eax, ebp ; check if list is empty
+ jnz short Swt140 ; if nz, list is not empty
+ mov ebp, 1 ; clear ready summary bit
+ mov ecx, esi ;
+ shl ebp, cl ;
+ xor _KiReadySummary, ebp ;
+
+;
+; Swap context to the next thread.
+;
+
+Swt140: mov esi, edx ; set address of next thread
+ mov edi, [ebx].PcPrcbData.PbCurrentThread ; set current thread address
+ mov dword ptr [ebx].PcPrcbData.PbNextThread, 0 ; clear next thread address
+ mov [ebx].PcPrcbData.PbCurrentThread, esi ; set current thread address
+ mov cl, [edi].ThWaitIrql ; set APC interrupt bypass disable
+ call SwapContext ; swap context
+ or al, al ; check if kernel APC pending
+ mov edi, [esi].ThWaitStatus ; save wait completion status
+ mov cl, [esi].ThWaitIrql ; get wait IRQL
+ jnz short Swt160 ; if nz, kernel APC pending
+
+Swt150: fstCall KfLowerIrql ; lower IRQL to previous value
+ mov eax, edi ; set wait completion status
+ mov ebp, [esp+0] ; restore registers
+ mov edi, [esp+4] ;
+ mov esi, [esp+8] ;
+ mov ebx, [esp+12] ;
+ add esp, 4*4 ;
+ fstRET KiSwapThread ;
+
+Swt160: mov cl, APC_LEVEL ; lower IRQL to APC level
+ fstCall KfLowerIrql ;
+ xor eax, eax ; set previous mode to kernel
+ stdCall _KiDeliverApc, <eax, eax, eax> ; deliver kernel mode APC
+ inc dword ptr [ebx].PcPrcbData.PbApcBypassCount ; increment count
+ xor ecx, ecx ; set original wait IRQL
+ jmp short Swt150
+
+fstENDP KiSwapThread
+
+ page ,132
+ subttl "Dispatch Interrupt"
+;++
+;
+; Routine Description:
+;
+; This routine is entered as the result of a software interrupt generated
+; at DISPATCH_LEVEL. Its function is to process the Deferred Procedure Call
+; (DPC) list, and then perform a context switch if a new thread has been
+; selected for execution on the processor.
+;
+; This routine is entered at IRQL DISPATCH_LEVEL with the dispatcher
+; database unlocked. When a return to the caller finally occurs, the
+; IRQL remains at DISPATCH_LEVEL, and the dispatcher database is still
+; unlocked.
+;
+; Arguments:
+;
+; None
+;
+; Return Value:
+;
+; None.
+;
+;--
+
+ align 16
+cPublicProc _KiDispatchInterrupt ,0
+cPublicFpo 0, 0
+
+ mov ebx, PCR[PcSelfPcr] ; get address of PCR
+kdi00: lea eax, [ebx].PcPrcbData.PbDpcListHead ; get DPC listhead address
+
+;
+; Disable interrupts and check if there is any work in the DPC list
+; of the current processor.
+;
+
+kdi10: cli ; disable interrupts
+ cmp eax, [eax].LsFlink ; check if DPC List is empty
+ je short kdi40 ; if eq, list is empty
+ push ebp ; save register
+ mov ebp, eax ; set address of DPC listhead
+ call KiRetireDpcList ; process the current DPC list
+ pop ebp ; restore register
+
+;
+; Check to determine if quantum end is requested.
+;
+; N.B. If a new thread is selected as a result of processing the quantum
+; end request, then the new thread is returned with the dispatcher
+; database locked. Otherwise, NULL is returned with the dispatcher
+; database unlocked.
+;
+
+kdi40: sti ; enable interrupts
+ cmp dword ptr [ebx].PcPrcbData.PbQuantumEnd, 0 ; quantum end requested
+ jne kdi90 ; if neq, quantum end request
+
+;
+; Check to determine if a new thread has been selected for execution on this
+; processor.
+;
+
+kdi50: cmp dword ptr [ebx].PcPrcbData.PbNextThread, 0 ; check addr of next thread object
+ je short kdi70 ; if eq, then no new thread
+
+;
+; Disable interrupts and attempt to acquire the dispatcher database lock.
+;
+
+ifndef NT_UP
+
+ lea eax, _KiDispatcherLock ; get dispatch database lock address
+ cli ; disable interrupts
+ TEST_SPINLOCK eax, <short kdi80> ; Is it busy?
+ ACQUIRE_SPINLOCK eax, <short kdi80> ; Try to acquire dispatch database lock
+
+endif
+
+;
+; Raise IRQL to synchronization level.
+;
+
+ mov ecx,SYNCH_LEVEL ; raise IRQL to synchronization level
+ fstCall KfRaiseIrql ;
+ sti ; enable interrupts
+ mov eax, [ebx].PcPrcbData.PbNextThread ; get next thread address
+
+;
+; N.B. The following registers MUST be saved such that ebp is saved last.
+; This is done so the debugger can find the saved ebp for a thread
+; that is not currently in the running state.
+;
+
+.fpo (3, 0, 0, 0, 0, 0)
+
+kdi60: sub esp, 3*4
+ mov [esp+8], esi ; save registers
+ mov [esp+4], edi ;
+ mov [esp+0], ebp ;
+ mov esi, eax ; set next thread address
+ mov edi, [ebx].PcPrcbData.PbCurrentThread ; get current thread address
+ mov dword ptr [ebx].PcPrcbData.PbNextThread, 0 ; clear next thread address
+ mov [ebx].PcPrcbData.PbCurrentThread, esi ; set current thread address
+ mov ecx, edi ; set address of current thread
+ fstCall KiReadyThread ; ready thread (ecx) for execution
+ mov cl, 1 ; set APC interrupt bypass disable
+ call SwapContext ; call context swap routine
+ mov ebp, [esp+0] ; restore registers
+ mov edi, [esp+4] ;
+ mov esi, [esp+8] ;
+ add esp, 3*4
+kdi70: stdRET _KiDispatchInterrupt ; return
+
+;
+; Enable interrupts and check DPC queue.
+;
+
+ifndef NT_UP
+
+kdi80: sti ; enable interrupts
+ jmp kdi00 ;
+
+endif
+
+;
+; Process quantum end event.
+;
+; N.B. If the quantum end code returns a NULL value, then no next thread
+; has been selected for execution. Otherwise, a next thread has been
+; selected and the dispatcher databased is locked.
+;
+
+kdi90: mov dword ptr [ebx].PcPrcbData.PbQuantumEnd, 0 ; clear quantum end indicator
+ stdCall _KiQuantumEnd ; process quantum end
+ or eax, eax ; check if new thread selected
+ jne short kdi60 ; if ne, new thread selected
+ stdRET _KiDispatchInterrupt ; return
+
+stdENDP _KiDispatchInterrupt
+
+ page ,132
+ subttl "Swap Context to Next Thread"
+;++
+;
+; Routine Description:
+;
+; This routine is called to swap context from one thread to the next.
+; It swaps context, flushes the data, instruction, and translation
+; buffer caches, restores nonvolatile integer registers, and returns
+; to its caller.
+;
+; N.B. It is assumed that the caller (only caller's are within this
+; module) saved the nonvolatile registers, ebx, esi, edi, and
+; ebp. This enables the caller to have more registers available.
+;
+; Arguments:
+;
+; cl - APC interrupt bypass disable (zero enable, nonzero disable).
+; edi - Address of previous thread.
+; esi - Address of next thread.
+; ebx - Address of PCR.
+;
+; Return value:
+;
+; al - Kernel APC pending.
+; ebx - Address of PCR.
+; esi - Address of current thread object.
+;
+;--
+
+ align 16
+ public SwapContext
+SwapContext proc
+cPublicFpo 0, 2
+
+;
+; NOTE: The ES: override on the move to ThState is part of the
+; lazy-segment load system. It assures that ES has a valid
+; selector in it, thus preventing us from propagating a bad
+; ES accross a context switch.
+;
+; Note that if segments, other than the standard flat segments,
+; with limits above 2 gig exist, neither this nor the rest of
+; lazy segment loads are reliable.
+;
+; Note that ThState must be set before the dispatcher lock is released
+; to prevent KiSetPriorityThread from seeing a stale value.
+;
+
+ mov byte ptr es:[esi]+ThState, Running ; set thread state to running
+
+;
+; Acquire the context swap lock so the address space of the old process
+; cannot be deleted and then release the dispatcher database lock.
+;
+; N.B. This lock is used to protect the address space until the context
+; switch has sufficiently progressed to the point where the address
+; space is no longer needed. This lock is also acquired by the reaper
+; thread before it finishes thread termination.
+;
+
+ifndef NT_UP
+
+ lea eax,_KiContextSwapLock ; get context swap lock address
+
+sc00: ACQUIRE_SPINLOCK eax, sc100, NoChecking ; acquire context swap lock
+
+ mov _KiDispatcherLock, 0 ; release dispatcher lock
+
+endif
+
+;
+; Save the APC disable flag and the exception listhead.
+;
+
+ or cl, cl ; set zf in flags
+ mov ecx, [ebx]+PcExceptionList ; save exception list
+ pushfd ; save flags
+ push ecx ;
+
+;
+; Notify registered callout routine of swap context.
+;
+
+ifndef NT_UP
+
+ cmp _KiSwapContextNotifyRoutine, 0 ; check for callout routine
+ je short sc03 ; if eq, no callout routine registered
+ mov edx, [esi].EtCid.CidUniqueThread ; set new thread unique id
+ mov ecx, [edi].EtCid.CidUniqueThread ; set old thread unique id
+ call [_KiSwapContextNotifyRoutine] ; notify callout routine
+sc03: ;
+
+endif
+
+;
+; Accumulate the total time spent in a thread.
+;
+
+ifdef PERF_DATA
+
+ test _KeFeatureBits, KF_RDTSC ; feature supported?
+ jz short @f ; if z, feature not present
+
+.586p
+ rdtsc ; read cycle counter
+.486p
+
+ sub eax, [ebx].PcPrcbData.PbThreadStartCount.LiLowPart ; sub off thread
+ sbb edx, [ebx].PcPrcbData.PbThreadStartCount.LiHighPart ; starting time
+ add [edi].EtPerformanceCountLow, eax ; accumlate thread run time
+ adc [edi].EtPerformanceCountHigh, edx ;
+ add [ebx].PcPrcbData.PbThreadStartCount.LiLowPart, eax ; set new thread
+ adc [ebx].PcPrcbData.PbThreadStartCount.LiHighPart, edx ; starting time
+@@: ;
+
+endif
+
+;
+; On a uniprocessor system the NPX state is swapped in a lazy manner.
+; If a thread who's state is not in the coprocessor attempts to perform
+; a coprocessor operation, the current NPX state is swapped out (if needed),
+; and the new state is swapped in durning the fault. (KiTrap07)
+;
+; On a multiprocessor system we still fault in the NPX state on demand, but
+; we save the state when the thread switches out (assuming the NPX state
+; was loaded). This is because it could be difficult to obtain the threads
+; NPX in the trap handler if it was loaded into a different processors
+; coprocessor.
+;
+ mov ebp, cr0 ; get current CR0
+ mov edx, ebp
+
+ifndef NT_UP
+ cmp byte ptr [edi]+ThNpxState, NPX_STATE_LOADED ; check if NPX state
+ je sc_save_npx_state
+endif
+
+
+sc05: mov cl, [esi]+ThDebugActive ; get debugger active state
+ mov [ebx]+PcDebugActive, cl ; set new debugger active state
+
+;
+; Switch stacks:
+;
+; 1. Save old esp in old thread object.
+; 2. Copy stack base and stack limit into TSS AND PCR
+; 3. Load esp from new thread object
+;
+; Keep interrupts off so we don't confuse the trap handler into thinking
+; we've overrun the kernel stack.
+;
+
+ cli ; disable interrupts
+ mov [edi]+ThKernelStack, esp ; save old kernel stack pointer
+ mov eax, [esi]+ThInitialStack ; get new initial stack pointer
+ lea ecx, [eax]-KERNEL_STACK_SIZE ; get new kernel stack limit
+ sub eax, NPX_FRAME_LENGTH ; space for NPX_FRAME & NPX CR0 flags
+ mov [ebx]+PcStackLimit, ecx ; set new stack limit
+ mov [ebx]+PcInitialStack, eax ; set new stack base
+
+.errnz (NPX_STATE_NOT_LOADED - CR0_TS - CR0_MP)
+.errnz (NPX_STATE_LOADED - 0)
+
+; (eax) = Initial Stack
+; (ebx) = Prcb
+; (edi) = OldThread
+; (esi) = NewThread
+; (ebp) = Current CR0
+; (edx) = Current CR0
+
+ xor ecx, ecx
+ mov cl, [esi]+ThNpxState ; New NPX state is (or is not) loaded
+
+ and edx, NOT (CR0_MP+CR0_EM+CR0_TS) ; clear thread setable NPX bits
+ or ecx, edx ; or in new threads cr0
+ or ecx, [eax]+FpCr0NpxState ; merge new thread setable state
+ cmp ebp, ecx ; check if old and new CR0 match
+ jne sc_reload_cr0 ; if ne, no change in CR0
+
+;
+; N.B. It is important that the following adjustment NOT be applied to
+; the initial stack value in the PCR. If it is, it will cause the
+; location in memory that the processor pushes the V86 mode segment
+; registers and the first 4 ULONGs in the FLOATING_SAVE_AREA to
+; occupy the same memory locations, which could result in either
+; trashed segment registers in V86 mode, or a trashed NPX state.
+;
+; Adjust ESP0 so that V86 mode threads and 32 bit threads can share
+; a trapframe structure, and the NPX save area will be accessible
+; in the same manner on all threads
+;
+; This test will check the user mode flags. On threads with no user
+; mode context, the value of esp0 does not matter (we will never run
+; in user mode without a usermode context, and if we don't run in user
+; mode the processor will never use the esp0 value.
+;
+
+ align 4
+sc06: test dword ptr [eax] - KTRAP_FRAME_LENGTH + TsEFlags, EFLAGS_V86_MASK
+ jnz short sc07 ; if nz, V86 frame, no adjustment
+ sub eax, TsV86Gs - TsHardwareSegSs ; bias for missing fields
+sc07: mov ecx, [ebx]+PcTss ;
+ mov [ecx]+TssEsp0, eax ;
+ mov esp, [esi]+ThKernelStack ; set new stack pointer
+ mov eax, [esi]+ThTeb ; get user TEB address
+ mov [ebx]+PcTeb, eax ; set user TEB address
+
+;
+; Edit the TEB descriptor to point to the TEB
+;
+
+ sti ; enable interrupts
+ mov ecx, [ebx]+PcGdt ;
+ mov [ecx]+(KGDT_R3_TEB+KgdtBaseLow), ax ;
+ shr eax, 16 ;
+ mov [ecx]+(KGDT_R3_TEB+KgdtBaseMid), al ;
+ shr eax, 8
+ mov [ecx]+(KGDT_R3_TEB+KgdtBaseHi), al
+
+;
+; NOTE: Keep KiSwapProcess (below) in sync with this code!
+;
+; If the new process is not the same as the old process, then switch the
+; address space to the new process.
+;
+
+ mov eax, [edi].ThApcState.AsProcess ; get old process address
+ cmp eax, [esi].ThApcState.AsProcess ; check if process match
+ jz short sc22 ; if z, old and new process match
+ mov edi, [esi].ThApcState.AsProcess ; get new process address
+
+;
+; Update the processor set masks.
+;
+
+ifndef NT_UP
+
+if DBG
+
+ mov cl, [esi]+ThNextProcessor ; get current processor number
+ cmp cl, [ebx]+PcPrcbData+PbNumber ; same as running processor?
+ jne sc_error2 ; if ne, processor number mismatch
+
+endif
+
+ mov ecx, [ebx]+PcSetMember ; get processor set member
+ xor [eax]+PrActiveProcessors, ecx ; clear bit in old processor set
+ xor [edi]+PrActiveProcessors, ecx ; set bit in new processor set
+
+if DBG
+ test [eax]+PrActiveProcessors, ecx ; test if bit clear in old set
+ jnz sc_error4 ; if nz, bit not clear in old set
+ test [edi]+PrActiveProcessors, ecx ; test if bit set in new set
+ jz sc_error5 ; if z, bit not set in new set
+
+endif
+endif
+
+;
+; New CR3, flush tb, sync tss, set IOPM
+; CS, SS, DS, ES all have flat (GDT) selectors in them.
+; FS has the pcr selector.
+; Therefore, GS is only selector we need to flush. We null it out,
+; it will be reloaded from a stack frame somewhere above us.
+; Note: this load of GS before CR3 works around P6 step B0 errata 11
+;
+
+ xor eax, eax ; assume null ldt
+ mov gs, ax ;
+ mov eax, [edi]+PrDirectoryTableBase ; get new directory base
+ mov ebp, [ebx]+PcTss ; get new TSS
+ mov ecx, [edi]+PrIopmOffset ; get IOPM offset
+ mov cr3, eax ; flush TLB and set new directory base
+ mov [ebp]+TssCR3, eax ; make TSS be in sync with hardware
+ mov [ebp]+TssIoMapBase, cx ;
+
+;
+; LDT switch
+;
+
+ xor eax, eax ; check if null LDT limit
+ cmp word ptr [edi]+PrLdtDescriptor, ax
+ jnz short sc_load_ldt ; if nz, LDT limit
+
+ lldt ax ; set LDT
+
+;
+; Release the context swap lock.
+;
+
+ align 4
+sc22: ;
+
+ifndef NT_UP
+
+ mov _KiContextSwapLock, 0 ; release context swap lock
+
+endif
+
+;
+; Update context switch counters.
+;
+
+ inc dword ptr [esi]+ThContextSwitches ; thread count
+ inc dword ptr [ebx]+PcPrcbData+PbContextSwitches ; processor count
+ pop ecx ; restore exception list
+ mov [ebx].PcExceptionList, ecx ;
+
+;
+; If the new thread has a kernel mode APC pending, then request an APC
+; interrupt.
+;
+
+ cmp byte ptr [esi].ThApcState.AsKernelApcPending, 0 ; APC pending?
+ jne short sc80 ; if ne, kernel APC pending
+ popfd ; restore flags
+ xor eax, eax ; clear kernel APC pending
+ ret ; return
+
+;
+; The new thread has an APC interrupt pending. If APC interrupt bypass is
+; enable, then return kernel APC pending. Otherwise, request a software
+; interrupt at APC_LEVEL and return no kernel APC pending.
+;
+
+sc80: popfd ; restore flags
+ jnz short sc90 ; if nz, APC interupt bypass disabled
+ mov al, 1 ; set kernel APC pending
+ ret ;
+
+sc90: mov cl, APC_LEVEL ; request software interrupt level
+ fstCall HalRequestSoftwareInterrupt ;
+ xor eax, eax ; clear kernel APC pending
+ ret ;
+
+;
+; Wait for context swap lock to be released.
+;
+
+ifndef NT_UP
+
+sc100: SPIN_ON_SPINLOCK eax, sc00 ;
+
+endif
+
+;
+; Set for new LDT value
+;
+
+sc_load_ldt:
+ mov ebp, [ebx]+PcGdt ;
+ mov eax, [edi+PrLdtDescriptor] ;
+ mov [ebp+KGDT_LDT], eax ;
+ mov eax, [edi+PrLdtDescriptor+4] ;
+ mov [ebp+KGDT_LDT+4], eax ;
+ mov eax, KGDT_LDT ;
+
+;
+; Set up int 21 descriptor of IDT. If the process does not have Ldt, it
+; should never make any int 21 call. If it does, an exception is generated.
+; If the process has Ldt, we need to update int21 entry of LDT for the process.
+; Note the Int21Descriptor of the process may simply indicate an invalid
+; entry. In which case, the int 21 will be trpped to kernel.
+;
+
+ mov ebp, [ebx]+PcIdt ;
+ mov ecx, [edi+PrInt21Descriptor] ;
+ mov [ebp+21h*8], ecx ;
+ mov ecx, [edi+PrInt21Descriptor+4] ;
+ mov [ebp+21h*8+4], ecx ;
+ lldt ax ; set LDT
+ jmp short sc22
+
+;
+; Cr0 has changed (ie, floating point processor present), load the new value
+;
+
+sc_reload_cr0:
+if DBG
+
+ test byte ptr [esi]+ThNpxState, NOT (CR0_TS+CR0_MP)
+ jnz sc_error ;
+ test dword ptr [eax]+FpCr0NpxState, NOT (CR0_PE+CR0_MP+CR0_EM+CR0_TS)
+ jnz sc_error3 ;
+
+endif
+ mov cr0,ecx ; set new CR0 NPX state
+ jmp sc06
+
+
+ifndef NT_UP
+
+
+; Save coprocessors current context. FpCr0NpxState is the current threads
+; CR0 state. The following bits are valid: CR0_MP, CR0_EM, CR0_TS. MVDMs
+; may set and clear MP & EM as they please and the settings will be reloaded
+; on a context switch (but they will not be saved from CR0 to Cr0NpxState).
+; The kernel sets and clears TS as required.
+;
+; (ebp) = Current CR0
+; (edx) = Current CR0
+
+sc_save_npx_state:
+ and edx, NOT (CR0_MP+CR0_EM+CR0_TS) ; we need access to the NPX state
+
+ mov ecx,[ebx]+PcInitialStack ; get NPX save save area address
+
+ cmp ebp, edx ; Does CR0 need reloaded?
+ je short sc_npx10
+
+ mov cr0, edx ; set new cr0
+ mov ebp, edx ; (ebp) = (edx) = current cr0 state
+
+sc_npx10:
+;
+; The fwait following the fnsave is to make sure that the fnsave has stored the
+; data into the save area before this coprocessor state could possibly be
+; context switched in and used on a different (co)processor. I've added the
+; clocks from when the dispatcher lock is released and don't believe it's a
+; possibility. I've also timed the impact this fwait seems to have on a 486
+; when performing lots of numeric calculations. It appears as if there is
+; nothing to wait for after the fnsave (although the 486 manual says there is)
+; and therefore the calculation time far outweighed the 3clk fwait and it
+; didn't make a noticable difference.
+;
+
+ fnsave [ecx] ; save NPX state
+ fwait ; wait until NPX state is saved
+ mov byte ptr [edi]+ThNpxState, NPX_STATE_NOT_LOADED ; set no NPX state
+
+if DBG
+ mov dword ptr [ebx]+PcPrcbData+PbNpxThread, 0 ; owner of coprocessors state
+endif
+ jmp sc05
+endif
+
+
+if DBG
+sc_error5: int 3
+sc_error4: int 3
+sc_error3: int 3
+sc_error2: int 3
+sc_error: int 3
+endif
+
+SwapContext endp
+
+ page , 132
+ subttl "Flush Data Cache"
+;++
+;
+; VOID
+; KiFlushDcache (
+; )
+;
+; VOID
+; KiFlushIcache (
+; )
+;
+; Routine Description:
+;
+; This routine does nothing on i386 and i486 systems. Why? Because
+; (a) their caches are completely transparent, (b) they don't have
+; instructions to flush their caches.
+;
+; Arguments:
+;
+; None.
+;
+; Return Value:
+;
+; None.
+;
+;--
+
+cPublicProc _KiFlushDcache ,0
+cPublicProc _KiFlushIcache ,0
+
+ stdRET _KiFlushIcache
+
+stdENDP _KiFlushIcache
+stdENDP _KiFlushDcache
+
+ page , 132
+ subttl "Flush EntireTranslation Buffer"
+;++
+;
+; VOID
+; KeFlushCurrentTb (
+; )
+;
+; Routine Description:
+;
+; This function flushes the entire translation buffer (TB) on the current
+; processor and also flushes the data cache if an entry in the translation
+; buffer has become invalid.
+;
+; Arguments:
+;
+; Return Value:
+;
+; None.
+;
+;--
+
+cPublicProc _KeFlushCurrentTb ,0
+
+if DBG
+ pushfd ; ensure all flushes occur at dispatch_level or higher...
+ pop eax
+ test eax, EFLAGS_INTERRUPT_MASK
+ jz short @f
+
+ stdCall _KeGetCurrentIrql
+ cmp al, DISPATCH_LEVEL
+ jnc short @f
+ int 3
+@@:
+endif
+
+ktb00: mov eax, cr3 ; (eax) = directroy table base
+ mov cr3, eax ; flush TLB
+ stdRET _KeFlushCurrentTb
+
+.586p
+ktb_gb: mov eax, cr4 ; *** see Ki386EnableGlobalPage ***
+ and eax, not CR4_PGE ; This FlushCurrentTb version gets copied into
+ mov cr4, eax ; ktb00 at initialization time if needed.
+ or eax, CR4_PGE
+ mov cr4, eax
+ktb_eb: stdRET _KeFlushCurrentTb
+.486p
+
+stdENDP _KeFlushCurrentTb
+
+_TEXT$00 ends
+
+INIT SEGMENT DWORD PUBLIC 'CODE'
+ ASSUME DS:FLAT, ES:FLAT, SS:NOTHING, FS:NOTHING, GS:NOTHING
+
+;++
+;
+; VOID
+; Ki386EnableGlobalPage (
+; IN volatile PLONG Number
+; )
+;
+; /*++
+;
+; Routine Description:
+;
+; This routine enables the global page PDE/PTE support in the system,
+; and stalls until complete and them sets the current processors cr4
+; register to enable global page support.
+;
+; Arguments:
+;
+; Number - Supplies a pointer to count of the number of processors in
+; the configuration.
+;
+; Return Value:
+;
+; None.
+;--
+
+cPublicProc _Ki386EnableGlobalPage,1
+ push esi
+ push edi
+ push ebx
+
+ mov edx, [esp+16] ; pointer to Number
+ pushfd
+ cli
+
+;
+; Wait for all processors
+;
+ lock dec dword ptr [edx] ; count down
+egp10: cmp dword ptr [edx], 0 ; wait for all processors to signal
+ jnz short egp10
+
+ cmp PCR[PcNumber], 0 ; processor 0?
+ jne short egp20
+
+;
+; Install proper KeFlustCurrentTb function.
+;
+
+ mov edi, ktb00
+ mov esi, ktb_gb
+ mov ecx, ktb_eb - ktb_gb + 1
+ rep movsb
+
+ mov byte ptr [ktb_eb], 0
+
+;
+; Wait for P0 to signal that proper flush tb handlers have been installed
+;
+egp20: cmp byte ptr [ktb_eb], 0
+ jnz short egp20
+
+;
+; Flush TB, and enable global page support
+; (note load of CR4 is explicity done before the load of CR3
+; to work around P6 step B0 errata 11)
+;
+.586p
+ mov eax, cr4
+ and eax, not CR4_PGE ; should not be set, but let's be safe
+ mov ecx, cr3
+ mov cr4, eax
+
+ mov cr3, ecx ; Flush TB
+
+ or eax, CR4_PGE ; enable global TBs
+ mov cr4, eax
+.486p
+ popfd
+ pop ebx
+ pop edi
+ pop esi
+
+ stdRET _Ki386EnableGlobalPage
+stdENDP _Ki386EnableGlobalPage
+
+
+;++
+;
+; VOID
+; Ki386EnableCurrentLargePage (
+; IN ULONG IdentityAddr,
+; IN ULONG IdentityCr3
+; )
+;
+; /*++
+;
+; Routine Description:
+;
+; This routine enables the large page PDE support in the processor
+;
+; Arguments:
+;
+; IdentityAddr - Supplies the linear address of the label within this
+; function where (linear == physical).
+;
+; IdentityCr3 - Supplies a pointer to temporary page directory and
+; page tables that provide both the kernel (virtual ->physical) and
+; identity (linear->physical) mappings needed for this function.
+;
+; Return Value:
+;
+; None.
+;--
+
+public _Ki386LargePageIdentityLabel
+cPublicProc _Ki386EnableCurrentLargePage,2
+ mov ecx,[esp]+4 ; (ecx)-> IdentityAddr
+ mov edx,[esp]+8 ; (edx)-> IdentityCr3
+ pushfd ; save current IF state
+ cli ; disable interrupts
+
+ mov eax, cr3 ; (eax)-> original Cr3
+ mov cr3, edx ; load Cr3 with Identity mapping
+ jmp ecx ; jump to (linear == physical)
+
+_Ki386LargePageIdentityLabel:
+ mov ecx, cr0
+ and ecx, NOT CR0_PG ; clear PG bit to disable paging
+ mov cr0, ecx ; disable paging
+ jmp $+2
+
+.586p
+ mov edx, cr4
+ or edx, CR4_PSE ; enable Page Size Extensions
+ mov cr4, edx
+
+.486p
+ mov edx, offset OriginalMapping
+ or ecx, CR0_PG ; set PG bit to enable paging
+ mov cr0, ecx ; enable paging
+ jmp edx ; Return to original mapping.
+
+OriginalMapping:
+ mov cr3, eax ; restore original Cr3
+ popfd ; restore interrupts to previous
+
+ stdRET _Ki386EnableCurrentLargePage
+stdENDP _Ki386EnableCurrentLargePage
+
+INIT ends
+
+_TEXT$00 SEGMENT PARA PUBLIC 'CODE'
+ ASSUME DS:FLAT, ES:FLAT, SS:NOTHING, FS:NOTHING, GS:NOTHING
+
+ page , 132
+ subttl "Flush Single Translation Buffer"
+;++
+;
+; VOID
+; FASTCALL
+; KiFlushSingleTb (
+; IN BOOLEAN Invalid,
+; IN PVOID Virtual
+; )
+;
+; Routine Description:
+;
+; This function flushes a single TB entry.
+;
+; It only works on a 486 or greater.
+;
+; Arguments:
+;
+; Invalid - Supplies a boolean value that specifies the reason for
+; flushing the translation buffer.
+;
+; Virtual - Supplies the virtual address of the single entry that is
+; to be flushed from the translation buffer.
+;
+; Return Value:
+;
+; None.
+;
+;--
+
+cPublicFastCall KiFlushSingleTb ,2
+
+;
+; 486 or above code
+;
+ invlpg [edx]
+ fstRET KiFlushSingleTb
+
+fstENDP KiFlushSingleTb
+
+ page , 132
+ subttl "Swap Process"
+;++
+;
+; VOID
+; KiSwapProcess (
+; IN PKPROCESS NewProcess,
+; IN PKPROCESS OldProcess
+; )
+;
+; Routine Description:
+;
+; This function swaps the address space to another process by flushing
+; the data cache, the instruction cache, the translation buffer, and
+; establishes a new directory table base.
+;
+; It also swaps in the LDT and IOPM of the new process. This is necessary
+; to avoid bogus mismatches in SwapContext.
+;
+; NOTE: keep in sync with process switch part of SwapContext
+;
+; Arguments:
+;
+; Process - Supplies a pointer to a control object of type process.
+;
+; Return Value:
+;
+; None.
+;
+;--
+
+cPublicProc _KiSwapProcess ,2
+cPublicFpo 2, 0
+
+ mov edx,[esp]+4 ; (edx)-> New process
+ mov eax,[esp]+8 ; (eax)-> Old Process
+
+;
+; Acquire the context swap lock, clear the processor set member in he old
+; process, set the processor member in the new process, and release the
+; context swap lock.
+;
+
+ifndef NT_UP
+
+ lea ecx,_KiContextSwapLock ; get context swap lock address
+
+sp10: ACQUIRE_SPINLOCK ecx, sp20, NoChecking ; acquire context swap lock
+
+ mov ecx, PCR[PcSetMember]
+ xor [eax]+PrActiveProcessors,ecx ; clear bit in old processor set
+ xor [edx]+PrActiveProcessors,ecx ; set bit in new processor set
+
+if DBG
+
+ test [eax]+PrActiveProcessors,ecx ; test if bit clear in old set
+ jnz kisp_error ; if nz, bit not clear in old set
+ test [edx]+PrActiveProcessors,ecx ; test if bit set in new set
+ jz kisp_error1 ; if z, bit not set in new set
+
+endif
+
+ mov _KiContextSwapLock, 0 ; release context swap lock
+
+endif
+
+ mov ecx,PCR[PcTss] ; (ecx)-> TSS
+
+;
+; Change address space
+;
+
+ xor eax,eax ; assume ldtr is to be NULL
+ mov gs,ax ; Clear gs. (also workarounds
+ ; P6 step B0 errata 11)
+ mov eax,[edx]+PrDirectoryTableBase
+ mov cr3,eax
+ mov [ecx]+TssCR3,eax ; be sure TSS in sync with processor
+
+;
+; Change IOPM
+;
+
+ mov ax,[edx]+PrIopmOffset
+ mov [ecx]+TssIoMapBase,ax
+
+;
+; Change LDT
+;
+
+ xor eax, eax
+ cmp word ptr [edx]+PrLdtDescriptor,ax ; limit 0?
+ jz short kisp10 ; null LDT, go load NULL ldtr
+
+;
+; Edit LDT descriptor
+;
+
+ mov ecx,PCR[PcGdt]
+ mov eax,[edx+PrLdtDescriptor]
+ mov [ecx+KGDT_LDT],eax
+ mov eax,[edx+PrLdtDescriptor+4]
+ mov [ecx+KGDT_LDT+4],eax
+
+;
+; Set up int 21 descriptor of IDT. If the process does not have Ldt, it
+; should never make any int 21 call. If it does, an exception is generated.
+; If the process has Ldt, we need to update int21 entry of LDT for the process.
+; Note the Int21Descriptor of the process may simply indicate an invalid
+; entry. In which case, the int 21 will be trpped to kernel.
+;
+
+ mov ecx, PCR[PcIdt]
+ mov eax, [edx+PrInt21Descriptor]
+ mov [ecx+21h*8], eax
+ mov eax, [edx+PrInt21Descriptor+4]
+ mov [ecx+21h*8+4], eax
+
+ mov eax,KGDT_LDT ;@@32-bit op to avoid prefix
+
+;
+; Load LDTR
+;
+
+kisp10: lldt ax
+ stdRET _KiSwapProcess
+
+;
+; Wait for context swap lock to be released.
+;
+
+ifndef NT_UP
+
+sp20: SPIN_ON_SPINLOCK ecx, sp10 ;
+
+endif
+
+if DBG
+kisp_error1: int 3
+kisp_error: int 3
+endif
+
+stdENDP _KiSwapProcess
+
+ page , 132
+ subttl "Adjust TSS ESP0 value"
+;++
+;
+; VOID
+; KiAdjustEsp0 (
+; IN PKTRAP_FRAME TrapFrame
+; )
+;
+; Routine Description:
+;
+; This routine puts the apropriate ESP0 value in the esp0 field of the
+; TSS. This allows protect mode and V86 mode to use the same stack
+; frame. The ESP0 value for protected mode is 16 bytes lower than
+; for V86 mode to compensate for the missing segement registers.
+;
+; Arguments:
+;
+; TrapFrame - Supplies a pointer to the TrapFrame
+;
+; Return Value:
+;
+; None.
+;
+;--
+cPublicProc _Ki386AdjustEsp0 ,1
+
+ stdCall _KeGetCurrentThread
+
+ mov edx,[esp + 4] ; edx -> trap frame
+ mov eax,[eax]+thInitialStack ; eax = base of stack
+ test dword ptr [edx]+TsEFlags,EFLAGS_V86_MASK ; is this a V86 frame?
+ jnz short ae10
+
+ sub eax,TsV86Gs - TsHardwareSegSS ; compensate for missing regs
+ae10: sub eax,NPX_FRAME_LENGTH
+ pushfd ; Make sure we don't move
+ cli ; processors while we do this
+ mov edx,PCR[PcTss]
+ mov [edx]+TssEsp0,eax ; set Esp0 value
+ popfd
+ stdRET _Ki386AdjustEsp0
+
+stdENDP _Ki386AdjustEsp0
+
+;++
+;
+; NTSTATUS
+; KiSwitchToThread (
+; IN PKTHREAD NextThread,
+; IN ULONG WaitReason,
+; IN ULONG WaitMode,
+; IN PKEVENT WaitObject
+; )
+;
+; Routine Description:
+;
+; This function performs an optimal switch to the specified target thread
+; if possible. No timeout is associated with the wait, thus the issuing
+; thread will wait until the wait event is signaled or an APC is deliverd.
+;
+; N.B. This routine is called with the dispatcher database locked.
+;
+; N.B. The wait IRQL is assumed to be set for the current thread and the
+; wait status is assumed to be set for the target thread.
+;
+; N.B. It is assumed that if a queue is associated with the target thread,
+; then the concurrency count has been incremented.
+;
+; N.B. Control is returned from this function with the dispatcher database
+; unlocked.
+;
+; Arguments:
+;
+; NextThread - Supplies a pointer to a dispatcher object of type thread.
+;
+; WaitReason - supplies the reason for the wait operation.
+;
+; WaitMode - Supplies the processor wait mode.
+;
+; WaitObject - Supplies a pointer to a dispatcher object of type event
+; or semaphore.
+;
+; Return Value:
+;
+; The wait completion status. A value of STATUS_SUCCESS is returned if
+; the specified object satisfied the wait. A value of STATUS_USER_APC is
+; returned if the wait was aborted to deliver a user APC to the current
+; thread.
+;
+;--
+
+NextThread equ 20 ; next thread offset
+WaitReason equ 24 ; wait reason offset
+WaitMode equ 28 ; wait mode offset
+WaitObject equ 32 ; wait object offset
+
+cPublicProc _KiSwitchToThread, 4
+.fpo (4, 4, 0, 0, 1, 0)
+
+;
+; N.B. The following registers MUST be saved such that ebp is saved last.
+; This is done so the debugger can find the saved ebp for a thread
+; that is not currently in the running state.
+;
+
+ sub esp,4*4 ; save registers
+ mov [esp + 12],ebx ;
+ mov [esp + 8],esi ;
+ mov [esp + 4],edi ;
+ mov [esp + 0],ebp ;
+
+;
+; If the target thread's kernel stack is resident, the target thread's
+; process is in the balance set, the target thread can can run on the
+; current processor, and another thread has not already been selected
+; to run on the current processor, then do a direct dispatch to the
+; target thread bypassing all the general wait logic, thread priorities
+; permiting.
+;
+
+ mov esi,[esp] + NextThread ; get target thread address
+ mov ebx,PCR[PcSelfPcr] ; get address of PCR
+ mov ebp,[esi].ThApcState.AsProcess ; get target process address
+ mov edi,[ebx].PcPrcbData.PbCurrentThread ; get current thread address
+ cmp byte ptr [esi].ThKernelStackResident,1 ; check if kernel stack resident
+ jne short LongWay ; if ne, kernel stack not resident
+ cmp byte ptr [ebp].PrState,ProcessInMemory ; check if process in memory
+ jne short LongWay ; if ne, process not in memory
+
+ifndef NT_UP
+
+ cmp dword ptr [ebx].PcPrcbData.PbNextThread,0 ; check if next thread
+ jne short LongWay ; if ne, next thread already selected
+ mov ecx,[esi].ThAffinity ; get target thread affinity
+ test [ebx].PcSetMember,ecx ; check if compatible affinity
+ jz short LongWay ; if z, affinity not compatible
+
+endif
+
+;
+; Compute the new thread priority.
+;
+
+ mov cl,[edi].ThPriority ; get client thread priority
+ mov dl,[esi].ThPriority ; get server thread priority
+ cmp cl,LOW_REALTIME_PRIORITY ; check if realtime client
+ jae short ClientRealtime ; if ae, realtime client thread
+ cmp dl,LOW_REALTIME_PRIORITY ; check if realtime server
+ jae short ServerRealtime ; if ae, realtime server thread
+ cmp byte ptr [esi].ThPriorityDecrement,0 ; check if boost actice
+ jne short BoostActive ; if ne, priority boost already active
+
+;
+; Both the client and the server are not realtime and a priority boost
+; is not currently active for the server. Under these conditions an
+; optimal switch to the server can be performed if the base priority
+; of the server is above a minimum threshold or the boosted priority
+; of the server is not less than the client priority.
+;
+
+ mov al,[esi].ThBasePriority ; get server thread base priority
+ inc al ; compute boosted priority level
+ mov [esi].ThPriority,al ; assume boosted priority is okay
+ cmp al,cl ; check if high enough boost
+ jb short BoostTooLow ; if b, boosted priority less
+ cmp al,LOW_REALTIME_PRIORITY ; check if less than realtime
+ jb short SetProcessor ; if b, boosted priority not realtime
+ dec byte ptr [esi].ThPriority ; reduce priority back to base
+ jmp short SetProcessor ;
+
+;
+; The boosted priority of the server is less than the current priority of
+; the client. If the server base priority is above the required threshold,
+; then a optimal switch to the server can be performed by temporarily
+; raising the priority of the server to that of the client.
+;
+
+BoostTooLow: ;
+ cmp byte ptr [esi].ThBasePriority,BASE_PRIORITY_THRESHOLD ; check if above threshold
+ jb short LongWay ; if b, priority below threshold
+ mov [esi].ThPriority,cl ; set server thread priority
+ sub cl,[esi].ThBasePriority ; compute priority decrement value
+ mov [esi].ThPriorityDecrement,cl ; set priority decrement count value
+ mov byte ptr [esi].ThDecrementCount,ROUND_TRIP_DECREMENT_COUNT ; set count
+ jmp short SetProcessor ;
+
+;
+; A server boost has previously been applied to the server thread. Count
+; down the decrement count to determine if another optimal server switch
+; is allowed.
+;
+
+BoostActive: ;
+ dec byte ptr [esi].ThDecrementCount ; decrement server count value
+ jz short LastSwitch ; if z, no more switches allowed
+
+;
+; Another optimal switch to the server is allowed provided that the
+; server priority is not less than the client priority.
+;
+
+ cmp dl,cl ; check if server higher priority
+ jae short SetProcessor ; if ae, server higher priority
+ jmp short LongWay ;
+
+;
+; The server has exhausted the number of times an optimal switch may
+; be performed without reducing it priority. Reduce the priority of
+; the server to its original unboosted value minus one.
+;
+
+LastSwitch: ;
+ mov byte ptr [esi].ThPriorityDecrement,0 ; clear server decrement
+ mov al,[esi].ThBasePriority ; set server thread priority to base
+ mov [esi].ThPriority,al ;
+
+;
+; Ready the target thread for execution and wait on the specified wait
+; object.
+;
+
+LongWay: ;
+ mov ecx,esi ; set address of server thread
+ fstCall KiReadyThread ; ready thread for execution
+ jmp ContinueWait ;
+
+;
+; The client is realtime. In order for an optimal switch to occur, the
+; server must also be realtime and run at a high or equal priority.
+;
+
+ClientRealtime: ;
+ cmp dl,cl ; check if server lower priority
+ jb short LongWay ; if b, server is lower priority
+
+;
+; The client is not realtime and the server is realtime. An optimal switch
+; to the server can be performed.
+;
+
+ServerRealtime: ;
+ mov al,[ebp].PrThreadQuantum ; set server thread quantum
+ mov [esi].ThQuantum,al ;
+
+;
+; Set the next processor for the server thread.
+;
+
+SetProcessor: ;
+
+ifndef NT_UP
+
+ mov al,[edi].ThNextProcessor ; set server next processor number
+ mov [esi].ThNextProcessor,al ;
+
+endif
+
+;
+; Set the address of the wait block list in the client thread, initialization
+; the event wait block, and insert the wait block in client event wait list.
+;
+
+ mov edx,edi ; compute wait block address
+ add edx,EVENT_WAIT_BLOCK_OFFSET ;
+ mov [edi].ThWaitBlockList,edx ; set address of wait block list
+ mov dword ptr [edi].ThWaitStatus,0 ; set initial wait status
+ mov ecx,[esp] + WaitObject ; get address of wait object
+ mov [edx].WbNextWaitBlock,edx ; set next wait block address
+ mov [edx].WbObject,ecx ; set address of wait object
+ mov dword ptr [edx].WbWaitKey,WaitAny shl 16; set wait key and wait type
+ add ecx,EvWaitListHead ; compute wait object listhead address
+ add edx,WbWaitListEntry ; compute wait block list entry address
+ mov eax,[ecx].LsBlink ; get backward link of listhead
+ mov [ecx].LsBlink,edx ; set backward link of listhead
+ mov [eax].LsFlink,edx ; set forward link in last entry
+ mov [edx].LsFlink,ecx ; set forward link in wait entry
+ mov [edx].LsBlink,eax ; set backward link wait entry
+
+;
+; Set the client thread wait parameters, set the thread state to Waiting,
+; and in the thread in the proper wait queue.
+;
+
+ mov byte ptr [edi].ThAlertable,0 ; set alertable FALSE
+ mov al,[esp] + WaitReason ; set wait reason
+ mov [edi].ThWaitReason,al ;
+ mov al,[esp] + WaitMode ; set wait mode
+ mov [edi].ThWaitMode,al ;
+ mov ecx,_KeTickCount + 0 ; get low part of tick count
+ mov [edi].ThWaitTime,ecx ; set thread wait time
+ mov byte ptr [edi].ThState,Waiting ; set thread state
+ lea edx,_KiWaitInListHead ; get address of wait in listhead
+ cmp al,0 ; check if wait mode is kernel
+ je short Stt10 ; if e, wait mode is kernel
+ cmp byte ptr [edi].ThEnableStackSwap,0 ; check is stack swappable
+ je short Stt10 ; if e, kernel stack swap disabled
+ cmp [edi].ThPriority,LOW_REALTIME_PRIORITY + 9 ; check if priority in range
+ jb short Stt20 ; if b, thread priority in range
+Stt10: lea edx,_KiWaitOutListHead ; get address of wait out listhead
+Stt20: mov eax,[edx].LsBlink ; get backlink of wait listhead
+ mov ecx,edi ; compute list entry address
+ add ecx,ThWaitListEntry ;
+ mov [edx].LsBlink,ecx ; set backlink of wait listhead
+ mov [eax].LsFlink,ecx ; set forward link in last entry
+ mov [ecx].LsFlink,edx ; set forward link in wait entry
+ mov [ecx].LsBlink,eax ; set backward link in wait entry
+
+;
+; If the current thread is processing a queue entry, then attempt to
+; activate another thread that is blocked on the queue object.
+;
+; N.B. The next thread address can change if the routine to activate
+; a queue waiter is called.
+;
+
+ cmp dword ptr [edi].ThQueue,0 ; check if thread processing queue
+ je short Stt30 ; if e, thread not processing queue
+ mov ecx,[edi].ThQueue ; get queue object address
+ mov [ebx].PcPrcbData.PbNextThread,esi ; set next thread address
+ fstCall KiActivateWaiterQueue ; attempt to activate waiter (ecx)
+ mov esi,[ebx].PcPrcbData.PbNextThread ; get next thread address
+ mov dword ptr [ebx].PcPrcbData.PbNextThread, 0 ; get next thread to NULL
+Stt30: mov [ebx].PcPrcbData.PbCurrentThread,esi ; set current thread object address
+ mov cl,1 ; set APC interrupt bypass disable
+ call SwapContext ; swap context to target thread
+
+;
+; Lower IRQL to its previous level.
+;
+; N.B. SwapContext releases the dispatcher database lock.
+;
+; N.B. The register s2 contains the address of the new thread on return.
+;
+
+ mov ebp,[esi].ThWaitStatus ; get wait completion status
+ mov cl,[esi].ThWaitIrql ; get original IRQL
+ fstCall KfLowerIrql ; set new IRQL
+
+;
+; If the wait was not interrupted to deliver a kernel APC, then return the
+; completion status.
+;
+
+ cmp ebp,STATUS_KERNEL_APC ; check if awakened for kernel APC
+ je short KernelApc ; if e, thread awakened for kernel APC
+ mov eax, ebp ; set wait completion status
+ mov ebp,[esp + 0] ; restore registers
+ mov edi,[esp + 4] ;
+ mov esi,[esp + 8] ;
+ mov ebx,[esp + 12] ;
+ add esp,4 * 4 ;
+
+ stdRET _KiSwitchToThread ; return
+
+;
+; Disable interrupts and attempt to acquire the dispatcher database lock.
+;
+
+KernelApc: ;
+
+ifndef NT_UP
+
+ lea ecx,_KiDispatcherLock ; get dispatcher database lock address
+Stt40: cli ; disable interrupts
+ ACQUIRE_SPINLOCK ecx,<short Stt50> ; acquire dispatcher database lock
+
+endif
+
+;
+; Raise IRQL to synchronization level and save wait IRQL.
+;
+
+ mov ecx,SYNCH_LEVEL ; raise IRQL to synchronization level
+ fstCall KfRaiseIrql ;
+ sti ; enable interrupts
+ mov [esi].ThWaitIrql,al ; set wait IRQL
+
+ContinueWait: ;
+ mov eax,[esp] + WaitObject ; get wait object address
+ mov ecx,[esp] + WaitReason ; get wait reason
+ mov edx,[esp] + WaitMode ; get wait mode
+ stdCall _KiContinueClientWait,<eax, ecx, edx> ; continue client wait
+ mov ebp,[esp + 0] ; restore registers
+ mov edi,[esp + 4] ;
+ mov esi,[esp + 8] ;
+ mov ebx,[esp + 12] ;
+ add esp,4 * 4 ;
+
+ stdRET _KiSwitchToThread ; return
+
+;
+; Spin until dispatcher database lock is available.
+;
+
+ifndef NT_UP
+
+Stt50: sti ; enable interrupts
+ SPIN_ON_SPINLOCK ecx,<short Stt40> ; wait for dispatcher database lock
+
+endif
+
+stdENDP _KiSwitchToThread
+
+_TEXT$00 ends
+ end
diff --git a/private/ntos/ke/i386/cyrix.c b/private/ntos/ke/i386/cyrix.c
new file mode 100644
index 000000000..9cc786ff6
--- /dev/null
+++ b/private/ntos/ke/i386/cyrix.c
@@ -0,0 +1,350 @@
+/*++
+
+Copyright (c) 1989 Microsoft Corporation
+
+Module Name:
+
+ cyrix.c
+
+Abstract:
+
+ Detects and initializes Cryix processors
+
+Author:
+
+ Ken Reneris (kenr) 24-Feb-1994
+
+Environment:
+
+ Kernel mode only.
+
+Revision History:
+
+--*/
+
+#include "ki.h"
+
+#define Cx486_SLC 0x0
+#define Cx486_DLC 0x1
+#define Cx486_SLC2 0x2
+#define Cx486_DLC2 0x3
+#define Cx486_SRx 0x4 // Retail Upgrade Cx486SLC
+#define Cx486_DRx 0x5 // Retail Upgrade Cx486DLC
+#define Cx486_SRx2 0x6 // Retail Upgrade 2x Cx486SLC
+#define Cx486_DRx2 0x7 // Retail Upgrade 2x Cx486DLC
+#define Cx486DX 0x1a
+#define Cx486DX2 0x1b
+#define M1 0x30
+
+#define CCR0 0xC0
+#define CCR1 0xC1
+#define CCR2 0xC2
+#define CCR3 0xC3
+
+#define DIR0 0xFE
+#define DIR1 0xFF
+
+
+// SRx & DRx flags
+#define CCR0_NC0 0x01 // No cache 64k @ 1M boundaries
+#define CCR0_NC1 0x02 // No cache 640k - 1M
+#define CCR0_A20M 0x04 // Enables A20M#
+#define CCR0_KEN 0x08 // Enables KEN#
+#define CCR0_FLUSH 0x10 // Enables FLUSH#
+
+// DX flags
+#define CCR1_NO_LOCK 0x10 // Ignore lock prefixes
+
+
+ULONG
+Ke386CyrixId (
+ VOID
+ );
+
+UCHAR
+ReadCyrixRegister (
+ IN UCHAR Register
+ );
+
+VOID
+WriteCyrixRegister (
+ IN UCHAR Register,
+ IN UCHAR Value
+ );
+
+VOID
+Ke386ConfigureCyrixProcessor (
+ VOID
+ );
+
+#ifdef ALLOC_PRAGMA
+#pragma alloc_text(PAGE,Ke386CyrixId)
+#pragma alloc_text(PAGELK,Ke386ConfigureCyrixProcessor)
+#endif
+
+
+extern UCHAR CmpCyrixID[];
+
+
+
+ULONG
+Ke386CyrixId (
+ VOID
+ )
+/*++
+
+Routine Description:
+
+ Detects and returns the Cyrix ID of the processor.
+ This function only detects Cyrix processors which have internal
+ cache support.
+
+Arguments:
+
+ Configure - If TRUE, causes this function to alter
+ the Cyrix CCR registers for the optimal NT
+ performance.
+
+ If FALSE, the processors configuration is
+ not altered.
+
+
+Return Value:
+
+ Cyrix ID of the processor
+ 0 if not a Cyrix processor
+
+--*/
+
+{
+ ULONG CyrixID;
+ UCHAR r3, c;
+ UCHAR flags;
+ PKPRCB Prcb;
+
+ CyrixID = 0;
+
+ Prcb = KeGetCurrentPrcb();
+ if (Prcb->CpuID && strcmp (Prcb->VendorString, CmpCyrixID)) {
+
+ //
+ // Not a Cyrix processor
+ //
+
+ return 0;
+ }
+
+ //
+ // Test Div instruction to see if the flags
+ // do not get altered
+ //
+
+ _asm {
+ xor eax, eax
+ sahf ; flags = ah
+
+ lahf ; ah = flags
+ mov flags, ah ; save flags
+
+ mov eax, 5
+ mov ecx, 2
+ div cl ; 5 / 2 = ?
+
+ lahf
+ sub flags, ah ; flags = orig_flags - new_flags
+ }
+
+ if (flags == 0) {
+
+ //
+ // See if the Cyrix CCR3 register bit 0x80 can be editted.
+ //
+
+ r3 = ReadCyrixRegister(CCR3); // Read CCR3
+ c = r3 ^ 0x80; // flip bit 80
+ WriteCyrixRegister(CCR3, c); // Write CCR3
+ ReadCyrixRegister(CCR0); // select new register
+ c = ReadCyrixRegister(CCR3); // Read new CCR3 value
+
+ if (ReadCyrixRegister(CCR3) != r3) {
+
+ //
+ // Read the Cyrix ID type register
+ //
+
+ CyrixID = ReadCyrixRegister(DIR0) + 1;
+ }
+
+ WriteCyrixRegister(CCR3, r3); // restore original CCR3 value
+ }
+
+ if (CyrixID > 0x7f) {
+ // invalid setting
+ CyrixID = 0;
+ }
+
+ return CyrixID;
+}
+
+static UCHAR
+ReadCyrixRegister (
+ IN UCHAR Register
+ )
+/*++
+
+Routine Description:
+
+ Reads an internal Cyrix ID register. Note the internal register
+ space is accessed via I/O addresses which are hooked internally
+ to the processor.
+
+ The caller is responsible for only calling this function on
+ a Cyrix processor.
+
+Arguments:
+
+ Register - Which Cyrix register to read
+
+Return Value:
+
+ The registers value
+
+--*/
+
+{
+ UCHAR Value;
+
+ _asm {
+ mov al, Register
+ cli
+ out 22h, al
+ in al, 23h
+ sti
+ mov Value, al
+ }
+ return Value;
+}
+
+
+static VOID
+WriteCyrixRegister (
+ IN UCHAR Register,
+ IN UCHAR Value
+ )
+/*++
+
+Routine Description:
+
+ Write an internal Cyrix ID register. Note the internal register
+ space is accessed via I/O addresses which are hooked internally
+ to the processor.
+
+ The caller is responsible for only calling this function on
+ a Cyrix processor.
+
+Arguments:
+
+ Register - Which Cyrix register to written
+ Value - Value to write into the register
+
+Return Value:
+
+ The registers value
+
+--*/
+
+{
+ _asm {
+ mov al, Register
+ mov cl, Value
+ cli
+ out 22h, al
+ mov al, cl
+ out 23h, al
+ sti
+ }
+}
+
+
+VOID
+Ke386ConfigureCyrixProcessor (
+ VOID
+ )
+{
+ UCHAR r0, r1;
+ ULONG id, rev;
+ PVOID LockHandle;
+
+
+ PAGED_CODE();
+
+ id = Ke386CyrixId();
+ if (id) {
+
+ LockHandle = MmLockPagableCodeSection (&Ke386ConfigureCyrixProcessor);
+
+ id = id - 1;
+ rev = ReadCyrixRegister(DIR1);
+
+ if ((id >= 0x20 && id <= 0x27) ||
+ ((id & 0xF0) == M1 && rev < 0x17)) {
+
+ //
+ // These steppings have a write-back cache problem.
+ // On these chips the L1 w/b cache can be disabled by
+ // setting only the NW bit.
+ //
+
+ _asm {
+ cli
+
+ mov eax, cr0
+ or eax, CR0_NW
+ mov cr0, eax
+
+ sti
+ }
+ }
+
+
+ switch (id) {
+ case Cx486_SRx:
+ case Cx486_DRx:
+ case Cx486_SRx2:
+ case Cx486_DRx2:
+
+ //
+ // These processors have an internal cache feature
+ // let's turn it on.
+ //
+
+ r0 = ReadCyrixRegister(CCR0);
+ r0 |= CCR0_NC1 | CCR0_FLUSH;
+ r0 &= ~CCR0_NC0;
+ WriteCyrixRegister(CCR0, r0);
+
+ // Clear Non-Cacheable Region 1
+ WriteCyrixRegister(0xC4, 0);
+ WriteCyrixRegister(0xC5, 0);
+ WriteCyrixRegister(0xC6, 0);
+ break;
+
+ case Cx486DX:
+ case Cx486DX2:
+ //
+ // Set NO_LOCK flag on these processors according to
+ // the number of booted processors
+ //
+
+ r1 = ReadCyrixRegister(CCR1);
+ r1 |= CCR1_NO_LOCK;
+ if (KeNumberProcessors > 1) {
+ r1 &= ~CCR1_NO_LOCK;
+ }
+ WriteCyrixRegister(CCR1, r1);
+ break;
+ }
+
+ MmUnlockPagableImageSection (LockHandle);
+ }
+}
diff --git a/private/ntos/ke/i386/dmpstate.c b/private/ntos/ke/i386/dmpstate.c
new file mode 100644
index 000000000..d0a2f51c8
--- /dev/null
+++ b/private/ntos/ke/i386/dmpstate.c
@@ -0,0 +1,363 @@
+/*++
+
+Copyright (c) 1992 Microsoft Corporation
+
+Module Name:
+
+ dmpstate.c
+
+Abstract:
+
+ This module implements the architecture specific routine that dumps
+ the machine state when a bug check occurs and no debugger is hooked
+ to the system. It is assumed that it is called from bug check.
+
+Author:
+
+ David N. Cutler (davec) 17-Jan-1992
+
+Environment:
+
+ Kernel mode.
+
+Revision History:
+
+--*/
+
+#include "ki.h"
+
+
+BOOLEAN
+KiReadStackValue(
+ IN ULONG Address,
+ OUT PULONG Value
+ );
+
+PVOID
+KiPcToFileHeader(
+ IN PVOID PcValue,
+ OUT PLDR_DATA_TABLE_ENTRY *DataTableEntry
+ );
+
+//
+// Define external data.
+//
+
+extern ULONG ExBuildVersion;
+extern LIST_ENTRY PsLoadedModuleList;
+
+VOID
+KeDumpMachineState (
+ IN PKPROCESSOR_STATE ProcessorState,
+ IN PCHAR Buffer,
+ IN PULONG BugCheckParameters,
+ IN ULONG NumberOfParameters,
+ IN PKE_BUGCHECK_UNICODE_TO_ANSI UnicodeToAnsiRoutine
+ )
+
+/*++
+
+Routine Description:
+
+ This function formats and displays the machine state at the time of the
+ to bug check.
+
+Arguments:
+
+ ProcessorState - Supplies a pointer to the processor's state
+
+ Buffer - Supplies a pointer to a buffer to be used to output machine
+ state information.
+
+ BugCheckParameters - Supplies additional bugcheck information
+
+ NumberOfParameters - sizeof BugCheckParameters array
+
+ UnicodeToAnsiRoutine - Supplies a pointer to a routine to convert Unicode strings
+ to Ansi strings without touching paged translation tables.
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ PLIST_ENTRY ModuleListHead;
+ PLIST_ENTRY Next;
+ ULONG StackAddr;
+ ULONG PossiblePc;
+ ULONG Index, NoLines;
+ ULONG DisplayWidth, DisplayHeight;
+ ULONG CursorColumn, CursorRow;
+ ULONG i, j;
+ PLDR_DATA_TABLE_ENTRY DataTableEntry;
+ PVOID ImageBase;
+ PKPRCB Prcb;
+ UCHAR AnsiBuffer[ 32 ];
+ ULONG DateStamp;
+
+
+ //
+ // Query display parameters.
+ //
+
+ HalQueryDisplayParameters(&DisplayWidth,
+ &DisplayHeight,
+ &CursorColumn,
+ &CursorRow);
+
+ //
+ // At this point the context record contains the machine state at the
+ // call to bug check.
+ //
+ // Put out the system version and the title line with the PSR and FSR.
+ //
+
+ //
+ // Check to see if any BugCheckParameters are valid code addresses.
+ // If so, print them for the user
+ //
+
+ NoLines = 8;
+ for (i=0; i < NumberOfParameters; i++) {
+ ImageBase = KiPcToFileHeader((PVOID) BugCheckParameters[i], &DataTableEntry);
+ if (ImageBase == NULL) {
+ continue;
+ }
+
+ sprintf (Buffer, "*** Address %08lx has base at %08lx - %-12.12s\n",
+ BugCheckParameters[i], ImageBase,
+ (*UnicodeToAnsiRoutine)( &DataTableEntry->BaseDllName, AnsiBuffer, sizeof( AnsiBuffer )));
+ HalDisplayString(Buffer);
+ NoLines++;
+ }
+ Prcb = KeGetCurrentPrcb();
+ if (Prcb->CpuID) {
+ sprintf(Buffer, "\n\nCPUID:%.12s %x.%x.%x",
+ Prcb->VendorString,
+ Prcb->CpuType,
+ Prcb->CpuStep >> 8,
+ Prcb->CpuStep & 0x0f
+ );
+
+ } else {
+ sprintf(Buffer, "\n\np%x-%04x", Prcb->CpuType, Prcb->CpuStep);
+ }
+ HalDisplayString (Buffer);
+
+ sprintf(Buffer, " irql:%x%s SYSVER 0x%08x\n\n",
+ KeGetCurrentIrql(),
+ KeIsExecutingDpc() ? " DPC" : " ",
+ NtBuildNumber);
+ HalDisplayString(Buffer);
+ NoLines += 3;
+
+ //
+ // Dump the loaded module list
+ //
+
+ if (KeLoaderBlock != NULL) {
+ ModuleListHead = &KeLoaderBlock->LoadOrderListHead;
+
+ } else {
+ ModuleListHead = &PsLoadedModuleList;
+ }
+
+ Next = ModuleListHead->Flink;
+
+ if (Next != NULL) {
+ for (i=0; i < DisplayWidth; i += 40) {
+ HalDisplayString ("Dll Base DateStmp - Name ");
+ }
+ HalDisplayString ("\n");
+ NoLines += 2;
+
+ while (NoLines < DisplayHeight && Next != ModuleListHead) {
+ for (i=0; i < 2 && Next != ModuleListHead; i++) {
+ DataTableEntry = CONTAINING_RECORD(Next,
+ LDR_DATA_TABLE_ENTRY,
+ InLoadOrderLinks);
+
+ Next = Next->Flink;
+ if (MmDbgReadCheck(DataTableEntry->DllBase) != NULL) {
+ PIMAGE_NT_HEADERS NtHeaders;
+
+ NtHeaders = RtlImageNtHeader(DataTableEntry->DllBase);
+ DateStamp = NtHeaders->FileHeader.TimeDateStamp;
+
+ } else {
+ DateStamp = 0;
+ }
+ sprintf (Buffer, "%08lx %08lx - %-18.18s ",
+ DataTableEntry->DllBase,
+ DateStamp,
+ (*UnicodeToAnsiRoutine)( &DataTableEntry->BaseDllName, AnsiBuffer, sizeof( AnsiBuffer )));
+
+ HalDisplayString (Buffer);
+ }
+ HalDisplayString ("\n");
+ NoLines++;
+ }
+ HalDisplayString ("\n");
+ }
+
+ //
+ // Dump some of the current stack
+ //
+
+ StackAddr = ProcessorState->ContextFrame.Esp - sizeof(ULONG);
+ j = 0;
+ while (NoLines < DisplayHeight) {
+
+ StackAddr += sizeof(ULONG);
+ if (!KiReadStackValue(StackAddr, &PossiblePc)) {
+ HalDisplayString ("\n");
+ break;
+ }
+
+ ImageBase = KiPcToFileHeader((PVOID) PossiblePc, &DataTableEntry);
+ if (ImageBase == NULL) {
+ continue;
+ }
+
+ if (j == 0) {
+ sprintf(Buffer, "Address dword dump Build [%ld] %25s - Name\n",
+ NtBuildNumber & 0xFFFFFFF,
+ " "
+ );
+
+ HalDisplayString(Buffer);
+ NoLines++;
+ j++;
+ }
+
+ sprintf(Buffer, "%08lx %08lx ", StackAddr, PossiblePc);
+ HalDisplayString (Buffer);
+
+ for (i=0; i < 5; i++) {
+ if (KiReadStackValue(StackAddr+i*sizeof(ULONG), &PossiblePc)) {
+ sprintf (Buffer, "%08lx ", PossiblePc);
+ HalDisplayString (Buffer);
+ } else {
+ HalDisplayString (" ");
+ }
+ }
+
+ sprintf (Buffer, "- %-14.14s\n",
+ (*UnicodeToAnsiRoutine)( &DataTableEntry->BaseDllName, AnsiBuffer, sizeof( AnsiBuffer )));
+ HalDisplayString(Buffer);
+ NoLines++;
+ }
+
+ return;
+}
+
+PVOID
+KiPcToFileHeader(
+ IN PVOID PcValue,
+ OUT PLDR_DATA_TABLE_ENTRY *DataTableEntry
+ )
+
+/*++
+
+Routine Description:
+
+ This function returns the base of an image that contains the
+ specified PcValue. An image contains the PcValue if the PcValue
+ is within the ImageBase, and the ImageBase plus the size of the
+ virtual image.
+
+Arguments:
+
+ PcValue - Supplies a PcValue.
+
+ DataTableEntry - Suppies a pointer to a variable that receives the
+ address of the data table entry that describes the image.
+
+Return Value:
+
+ NULL - No image was found that contains the PcValue.
+
+ NON-NULL - Returns the base address of the image that contain the
+ PcValue.
+
+--*/
+
+{
+
+ PLIST_ENTRY ModuleListHead;
+ PLDR_DATA_TABLE_ENTRY Entry;
+ PLIST_ENTRY Next;
+ ULONG Bounds;
+ PVOID ReturnBase, Base;
+
+ //
+ // If the module list has been initialized, then scan the list to
+ // locate the appropriate entry.
+ //
+
+ if (KeLoaderBlock != NULL) {
+ ModuleListHead = &KeLoaderBlock->LoadOrderListHead;
+
+ } else {
+ ModuleListHead = &PsLoadedModuleList;
+ }
+
+ ReturnBase = NULL;
+ Next = ModuleListHead->Flink;
+ if (Next != NULL) {
+ while (Next != ModuleListHead) {
+ Entry = CONTAINING_RECORD(Next,
+ LDR_DATA_TABLE_ENTRY,
+ InLoadOrderLinks);
+
+ Next = Next->Flink;
+ Base = Entry->DllBase;
+ Bounds = (ULONG)Base + Entry->SizeOfImage;
+ if ((ULONG)PcValue >= (ULONG)Base && (ULONG)PcValue < Bounds) {
+ *DataTableEntry = Entry;
+ ReturnBase = Base;
+ break;
+ }
+ }
+ }
+
+ return ReturnBase;
+}
+
+BOOLEAN
+KiReadStackValue(
+ IN ULONG Address,
+ OUT PULONG Value
+ )
+/*++
+
+Routine Description:
+
+ This function reads a dword off the current stack.
+
+Arguments:
+
+ Address - Stack address to read
+
+ Value - value of dword at the supplied stack address
+
+Return Value:
+
+ FALSE - Address was out of range
+ TRUE - dword returned
+
+--*/
+{
+ PKPCR Pcr;
+
+ Pcr = KeGetPcr();
+ if (Address > (ULONG) Pcr->NtTib.StackBase ||
+ Address < (ULONG) Pcr->NtTib.StackLimit) {
+ return FALSE;
+ }
+
+ *Value = *((PULONG) Address);
+ return TRUE;
+}
diff --git a/private/ntos/ke/i386/emv86.asm b/private/ntos/ke/i386/emv86.asm
new file mode 100644
index 000000000..e0642c180
--- /dev/null
+++ b/private/ntos/ke/i386/emv86.asm
@@ -0,0 +1,1973 @@
+ title "Vdm Instuction Emulation"
+;++
+;
+; Copyright (c) 1989 Microsoft Corporation
+;
+; Module Name:
+;
+; emv86.asm
+;
+; Abstract:
+;
+; This module contains the routines for emulating instructions and
+; faults from v86 mode.
+;
+; Author:
+;
+; sudeep bharati (sudeepb) 16-Nov-1992
+;
+; Environment:
+;
+; Kernel mode only.
+;
+; Notes:
+;
+;
+; Revision History:
+;
+;--
+.386p
+ .xlist
+include ks386.inc
+include i386\kimacro.inc
+include mac386.inc
+include i386\mi.inc
+include callconv.inc
+include ..\..\vdm\i386\vdm.inc
+include ..\..\vdm\i386\vdmtb.inc
+ .list
+
+ extrn VdmOpcode0f:proc
+ extrn _DbgPrint:proc
+ extrn _KeI386VdmIoplAllowed:dword
+ extrn _KeI386VirtualIntExtensions:dword
+ EXTRNP _Ki386VdmDispatchIo,5
+ EXTRNP _Ki386VdmDispatchStringIo,8
+ EXTRNP _KiDispatchException,5
+ EXTRNP _Ki386VdmReflectException,1
+ EXTRNP _VdmEndExecution,2
+ extrn VdmDispatchBop:near
+ EXTRNP _VdmPrinterStatus,3
+ EXTRNP _VdmPrinterWriteData, 3
+ EXTRNP _VdmDispatchInterrupts,2
+ EXTRNP _KeBugCheck,1
+ EXTRNP _VdmSkipNpxInstruction,4
+ifdef VDMDBG
+ extrn TraceOpcode:near
+endif
+
+ extrn _ExVdmOpcodeDispatchCounts:dword
+ extrn OpcodeIndex:byte
+ extrn _VdmUserCr0MapIn:byte
+
+; SUPPORT Intel CPU/Non PC/AT machine
+ extrn _VdmFixedStateLinear:dword
+ extrn _KeI386MachineType:dword
+
+ page ,132
+
+ifdef VDMDBG
+%out Debugging version
+endif
+
+; Force assume into place
+
+_PAGE SEGMENT DWORD PUBLIC 'CODE'
+ ASSUME DS:NOTHING, ES:NOTHING, SS:NOTHING, FS:NOTHING, GS:NOTHING
+_PAGE ENDS
+
+_TEXT$00 SEGMENT DWORD PUBLIC 'CODE'
+ ASSUME DS:NOTHING, ES:NOTHING, SS:NOTHING, FS:NOTHING, GS:NOTHING
+_TEXT$00 ENDS
+
+_DATA SEGMENT DWORD PUBLIC 'DATA'
+
+;
+; Instruction emulation emulates the following instructions.
+; The emulation affects the noted user mode registers.
+;
+;
+; In V86 mode, the following instruction are emulated in the kernel
+;
+; Registers (E)Flags (E)SP SS CS
+; PUSHF X X
+; POPF X X
+; INTnn X X X
+; INTO X X X
+; IRET X X X
+; CLI X
+; STI X
+;
+;
+; INSB
+; INSW
+; OUTSB
+; OUTSW
+; INBimm
+; INWimm
+; OUTBimm
+; OUTWimm
+; INB
+; INW
+; OUTB
+; OUTW
+;
+; WARNING What do we do about 32 bit io instructions??
+
+
+; OpcodeDispatchV86 - table of routines used to emulate instructions
+; in v86 mode.
+
+ public OpcodeDispatchV86
+dtBEGIN OpcodeDispatchV86,OpcodeInvalidV86
+ dtS VDM_INDEX_0F , Opcode0FV86
+ dtS VDM_INDEX_ESPrefix , OpcodeESPrefixV86
+ dtS VDM_INDEX_CSPrefix , OpcodeCSPrefixV86
+ dtS VDM_INDEX_SSPrefix , OpcodeSSPrefixV86
+ dtS VDM_INDEX_DSPrefix , OpcodeDSPrefixV86
+ dtS VDM_INDEX_FSPrefix , OpcodeFSPrefixV86
+ dtS VDM_INDEX_GSPrefix , OpcodeGSPrefixV86
+ dtS VDM_INDEX_OPER32Prefix , OpcodeOPER32PrefixV86
+ dtS VDM_INDEX_ADDR32Prefix , OpcodeADDR32PrefixV86
+ dtS VDM_INDEX_INSB , OpcodeINSBV86
+ dtS VDM_INDEX_INSW , OpcodeINSWV86
+ dtS VDM_INDEX_OUTSB , OpcodeOUTSBV86
+ dtS VDM_INDEX_OUTSW , OpcodeOUTSWV86
+ dtS VDM_INDEX_PUSHF , OpcodePUSHFV86
+ dtS VDM_INDEX_POPF , OpcodePOPFV86
+ dtS VDM_INDEX_INTnn , OpcodeINTnnV86
+ dtS VDM_INDEX_INTO , OpcodeINTOV86
+ dtS VDM_INDEX_IRET , OpcodeIRETV86
+ dts VDM_INDEX_NPX , OpcodeNPXV86
+ dtS VDM_INDEX_INBimm , OpcodeINBimmV86
+ dtS VDM_INDEX_INWimm , OpcodeINWimmV86
+ dtS VDM_INDEX_OUTBimm , OpcodeOUTBimmV86
+ dtS VDM_INDEX_OUTWimm , OpcodeOUTWimmV86
+ dtS VDM_INDEX_INB , OpcodeINBV86
+ dtS VDM_INDEX_INW , OpcodeINWV86
+ dtS VDM_INDEX_OUTB , OpcodeOUTBV86
+ dtS VDM_INDEX_OUTW , OpcodeOUTWV86
+ dtS VDM_INDEX_LOCKPrefix , OpcodeLOCKPrefixV86
+ dtS VDM_INDEX_REPNEPrefix , OpcodeREPNEPrefixV86
+ dtS VDM_INDEX_REPPrefix , OpcodeREPPrefixV86
+ dtS VDM_INDEX_CLI , OpcodeCLIV86
+ dtS VDM_INDEX_STI , OpcodeSTIV86
+ dtS VDM_INDEX_HLT , OpcodeHLTV86
+dtEND MAX_VDM_INDEX
+
+_DATA ENDS
+
+_PAGE SEGMENT DWORD USE32 PUBLIC 'CODE'
+ ASSUME DS:NOTHING, ES:NOTHING, SS:FLAT, FS:NOTHING, GS:NOTHING
+
+ page ,132
+ subttl "Overide Prefix Macro"
+;++
+;
+; Routine Description:
+;
+; This macro generates the code for handling override prefixes
+; The routine name generated is OpcodeXXXXPrefix, where XXXX is
+; the name used in the macro invocation. The code will set the
+; PREFIX_XXXX bit in the Prefix flags.
+;
+; Arguments
+; name = name of prefix
+; esi = address of reg info
+; edx = opcode
+;
+; Returns
+; user mode Eip advanced
+; eax advanced
+; edx contains next byte of opcode
+;
+; NOTE: This routine exits by dispatching through the table again.
+;--
+opPrefix macro name
+ public Opcode&name&PrefixV86
+Opcode&name&PrefixV86 proc
+
+ or ebx,PREFIX_&name
+
+
+ifdef VDMDBG
+_DATA segment
+Msg&name&Prefix db 'NTVDM: Encountered override prefix &name& %lx at '
+ db 'address %lx', 0ah, 0dh, 0
+_DATA ends
+
+ push [ebp].TsEip
+ push [ebp].TsSegCs
+ push offset FLAT:Msg&name&Prefix
+ call _DbgPrint
+ add esp,12
+
+endif
+
+ jmp OpcodeGenericPrefixV86 ; dispatch to next handler
+
+Opcode&name&PrefixV86 endp
+endm
+
+irp prefix, <ES, CS, SS, DS, FS, GS, OPER32, ADDR32, LOCK, REPNE, REP>
+
+ opPrefix prefix
+
+endm
+
+ page ,132
+ subttl "Instruction Emulation Dispatcher for V86"
+;++
+;
+; Routine Description:
+;
+; This routine dispatches to the opcode specific emulation routine,
+; based on the first byte of the opcode. Two byte opcodes, and prefixes
+; result in another level of dispatching, from the handling routine.
+;
+; Arguments:
+;
+; ebp = pointer to trap frame
+;
+; Returns:
+;
+; EAX = 0 failure
+; 1 success
+
+cPublicProc _Ki386DispatchOpcodeV86,0
+
+ifdef VDMDBG
+ push 0
+ call TraceOpcode
+endif
+
+ mov esi,[ebp].TsSegCs
+ shl esi,4
+ add esi,[ebp].TsEip
+ movzx ecx, byte ptr [esi]
+ movzx edx, OpcodeIndex[ecx] ;get opcode index
+
+ mov edi,1
+ xor ebx,ebx
+
+ ; All handler routines will get the following on entry
+ ; ebx -> prefix flags
+ ; ebp -> trap frame
+ ; cl -> byte at the faulting address
+ ; interrupts enabled and Irql at APC level
+ ; esi -> address of faulting instruction
+ ; edi -> instruction length count
+ ; All handler routines return
+ ; EAX = 0 for failure
+ ; EAX = 1 for success
+
+if DEVL
+ inc _ExVdmOpcodeDispatchCounts[edx * type _ExVdmOpcodeDispatchCounts]
+endif
+ jmp dword ptr OpcodeDispatchV86[edx * type OpcodeDispatchV86]
+
+stdENDP _Ki386DispatchOpcodeV86
+
+
+ page ,132
+ subttl "Invalid Opcode Handler"
+;++
+;
+; Routine Description:
+;
+; This routine emulates an invalid opcode. It prints the invalid
+; opcode message, and causes a GP fault to be reflected to the
+; debuger
+;
+; Arguments:
+; EBX -> prefix flags
+; EBP -> trap frame
+; CL -> byte at the faulting address
+; interrupts disabled
+; ESI -> address of faulting instruction
+; EDI -> instruction length count
+;
+; Returns:
+; EAX = 0 for failure
+; EAX = 1 for success
+;
+; All registers can be trashed except ebp/esp.
+;
+
+ public OpcodeInvalidV86
+OpcodeInvalidV86 proc
+
+ifdef VDMDBG
+_DATA segment
+MsgInvalidOpcode db 'NTVDM: An invalid opcode %lx was encountered at '
+ db 'address %x:%x',0ah, 0dh, 0
+_DATA ends
+
+ push [ebp].TsEip
+ push [ebp].TsSegCs
+ and ecx,0ffh
+ push ecx
+ push offset FLAT:MsgInvalidOpcode
+ call _DbgPrint ; display invalid opcode message
+ add esp,16
+endif
+
+ xor eax,eax ; ret fail
+ ret
+
+OpcodeInvalidV86 endp
+
+
+ page ,132
+ subttl "Generic Prefix Handler"
+;++
+;
+; Routine Description:
+;
+; This routine handles the generic portion of all of the prefixes,
+; and dispatches the next byte of the opcode.
+;
+; Arguments:
+;
+; EBX -> prefix flags
+; EBP -> trap frame
+; CL -> byte at the faulting address
+; interrupts disabled
+; ESI -> address of faulting instruction
+; EDI -> instruction length count
+;
+; Returns:
+; EAX = 0 for failure
+; EAX = 1 for success
+;
+; All registers can be trashed except ebp/esp.
+;
+
+ public OpcodeGenericPrefixV86
+OpcodeGenericPrefixV86 proc
+
+ inc esi
+ inc edi
+ movzx ecx, byte ptr [esi]
+ movzx edx, OpcodeIndex[ecx] ;get opcode index
+if DEVL
+ inc _ExVdmOpcodeDispatchCounts[edx * type _ExVdmOpcodeDispatchCounts]
+endif
+ jmp OpcodeDispatchV86[edx * type OpcodeDispatchV86]
+
+OpcodeGenericPrefixV86 endp
+
+
+ page ,132
+ subttl "Byte string in Opcode Handler"
+;++
+;
+; Routine Description:
+;
+; This routine emulates an INSB opcode. Currently, it prints
+; a message, and ignores the instruction.
+;
+; Arguments:
+; EBX -> prefix flags
+; EBP -> trap frame
+; CL -> byte at the faulting address
+; interrupts disabled
+; ESI -> address of faulting instruction
+; EDI -> instruction length count
+;
+; Returns:
+; EAX = 0 for failure
+; EAX = 1 for success
+;
+; All registers can be trashed except ebp/esp.
+;
+; WARNING size override? ds override?
+
+ public OpcodeINSBV86
+OpcodeINSBV86 proc
+
+ifdef VDMDBG
+_DATA segment
+MsgINSBOpcode db 'NTVDM: An INSB opcode %lx was encountered at '
+ db 'address %lx',0ah, 0dh, 0
+_DATA ends
+ push ebx
+ push [ebp].TsEip
+ push [ebp].TsSegCs
+ push offset FLAT:MsgINSBOpcode
+ call _DbgPrint ; display INSB opcode message
+ add esp,12
+ pop ebx
+
+endif
+ push ebp ; trap frame
+ push edi ; size of insb
+ movzx eax,word ptr [ebp].TsV86Es
+ shl eax,16
+ movzx ecx,word ptr [ebp].TsEdi
+ or eax,ecx
+ push eax ; address
+ mov eax,1
+ xor ecx, ecx
+ test ebx,PREFIX_REP ; prefixREP
+ jz oisb20
+
+ mov ecx, 1
+ movzx eax,word ptr [ebp].TsEcx
+oisb20:
+ push eax ; number of io ops
+ push TRUE ; read op
+ push ecx ; REP prefix ?
+ push 1 ; byte op
+ movzx eax,word ptr [ebp].TsEdx
+ push eax ; port number
+
+ ; Ki386VdmDispatchStringIo enables interrupts
+IFDEF STD_CALL
+ call _Ki386VdmDispatchStringIo@32 ; use retval
+ELSE
+ call _Ki386VdmDispatchStringIo ; use retval
+ add esp,24
+ENDIF
+ ret
+
+OpcodeINSBV86 endp
+
+ page ,132
+ subttl "Word String In Opcode Handler"
+;++
+;
+; Routine Description:
+;
+; This routine emulates an INSW opcode. Currently, it prints
+; a message, and ignores the instruction.
+;
+; Arguments:
+;
+; EBX -> prefix flags
+; EBP -> trap frame
+; CL -> byte at the faulting address
+; interrupts disabled
+; ESI -> address of faulting instruction
+; EDI -> instruction length count
+;
+; Returns:
+; EAX = 0 for failure
+; EAX = 1 for success
+;
+; All registers can be trashed except ebp/esp.
+;
+
+ public OpcodeINSWV86
+OpcodeINSWV86 proc
+
+ifdef VDMDBG
+_DATA segment
+MsgINSWOpcode db 'NTVDM: An INSW opcode %lx was encountered at '
+ db 'address %lx',0ah, 0dh, 0
+_DATA ends
+
+ push ebx
+ push [ebp].TsEip
+ push [ebp].TsSegCs
+ push offset FLAT:MsgINSWOpcode
+ call _DbgPrint ; display INSW opcode message
+ add esp,12
+ pop ebx
+endif
+
+ push ebp ; trap frame
+ push edi ; size of insw
+ movzx eax,word ptr [ebp].TsV86Es
+ shl eax,16
+ movzx ecx,word ptr [ebp].TsEdi
+ or eax,ecx
+ push eax ; address
+ mov eax,1
+ xor ecx, ecx
+ test ebx,PREFIX_REP ; prefixREP
+ jz oisw20
+
+ mov ecx, 1
+ movzx eax,word ptr [ebp].TsEcx
+oisw20:
+ push eax ; number of io ops
+ push TRUE ; read op
+ push ecx ; REP prefix ?
+ push 2 ; word op
+ movzx eax,word ptr [ebp].TsEdx
+ push eax ; port number
+
+ ; Ki386VdmDispatchStringIo enables interrupts
+IFDEF STD_CALL
+ call _Ki386VdmDispatchStringIo@32 ; use retval
+ELSE
+ call _Ki386VdmDispatchStringIo ; use retval
+ add esp,24
+ENDIF
+ ret
+
+OpcodeINSWV86 endp
+
+ page ,132
+ subttl "Byte String Out Opcode Handler"
+;++
+;
+; Routine Description:
+;
+; This routine emulates an OUTSB opcode. Currently, it prints
+; a message, and ignores the instruction.
+;
+; Arguments:
+;
+; EBX -> prefix flags
+; EBP -> trap frame
+; CL -> byte at the faulting address
+; interrupts disabled
+; ESI -> address of faulting instruction
+; EDI -> instruction length count
+;
+; Returns:
+; EAX = 0 for failure
+; EAX = 1 for success
+;
+; All registers can be trashed except ebp/esp.
+;
+
+ public OpcodeOUTSBV86
+OpcodeOUTSBV86 proc
+
+ifdef VDMDBG
+_DATA segment
+MsgOUTSBOpcode db 'NTVDM: An OUTSB opcode %lx was encountered at '
+ db 'address %lx',0ah, 0dh, 0
+_DATA ends
+
+ push ebx
+ push [ebp].TsEip
+ push [ebp].TsSegCs
+ push offset FLAT:MsgOUTSBOpcode
+ call _DbgPrint ; display OUTSB opcode message
+ add esp,12
+ pop ebx
+endif
+
+ push ebp ; trap frame
+ push edi ; size of outsb
+ movzx eax,word ptr [ebp].TsV86Ds
+ shl eax,16
+ movzx ecx,word ptr [ebp].TsEsi
+ or eax,ecx
+ push eax ; address
+ mov eax,1
+ xor ecx, ecx
+ test ebx,PREFIX_REP ; prefixREP
+ jz oosb20
+
+ mov ecx, 1
+ movzx eax,word ptr [ebp].TsEcx
+oosb20:
+ push eax ; number of io ops
+ push FALSE ; write op
+ push ecx ; REP prefix ?
+ push 1 ; byte op
+ movzx eax,word ptr [ebp].TsEdx
+ push eax ; port number
+
+ ; Ki386VdmDispatchStringIo enables interrupts
+IFDEF STD_CALL
+ call _Ki386VdmDispatchStringIo@32 ; use retval
+ELSE
+ call _Ki386VdmDispatchStringIo ; use retval
+ add esp,24
+ENDIF
+ ret
+
+OpcodeOUTSBV86 endp
+
+ page ,132
+ subttl "Word String Out Opcode Handler"
+;++
+;
+; Routine Description:
+;
+; This routine emulates an OUTSW opcode. Currently, it prints
+; a message, and ignores the instruction
+;
+; Arguments:
+;
+; EBX -> prefix flags
+; EBP -> trap frame
+; CL -> byte at the faulting address
+; interrupts disabled
+; ESI -> address of faulting instruction
+; EDI -> instruction length count
+;
+; Returns:
+; EAX = 0 for failure
+; EAX = 1 for success
+;
+; All registers can be trashed except ebp/esp.
+;
+
+ public OpcodeOUTSWV86
+OpcodeOUTSWV86 proc
+
+ifdef VDMDBG
+_DATA segment
+MsgOUTSWOpcode db 'NTVDM: An OUTSW opcode %lx was encountered at '
+ db 'address %lx',0ah, 0dh, 0
+_DATA ends
+ push ebx
+ push [ebp].TsEip
+ push [ebp].TsSegCs
+ push offset FLAT:MsgOUTSWOpcode
+ call _DbgPrint ; display OUTSW opcode message
+ add esp,12
+ pop ebx
+endif
+
+ push ebp ; trap frame
+ push edi ; size of outsw
+ movzx eax,word ptr [ebp].TsV86Ds
+ shl eax,16
+ movzx ecx,word ptr [ebp].TsEsi
+ or eax,ecx
+ push eax ; address
+
+ mov eax,1
+ xor ecx, ecx
+ test ebx,PREFIX_REP ; prefixREP
+ jz oosw20
+
+ mov ecx, 1
+ movzx eax,word ptr [ebp].TsEcx
+oosw20:
+ push eax ; number of io ops
+ push FALSE ; write op
+ push ecx ; REP prefix ?
+ push 2 ; word op
+ movzx eax,word ptr [ebp].TsEdx
+ push eax ; port number
+
+ ; Ki386VdmDispatchStringIo enables interrupts
+IFDEF STD_CALL
+ call _Ki386VdmDispatchStringIo@32 ; use retval
+ELSE
+ call _Ki386VdmDispatchStringIo ; use retval
+ add esp,24
+ENDIF
+ ret
+
+OpcodeOUTSWV86 endp
+
+ page ,132
+ subttl "PUSHF Opcode Handler"
+;++
+;
+; Routine Description:
+;
+; This routine emulates an PUSHF opcode. Currently, it prints
+; a message, and simulates the instruction.
+;
+; Get SS
+; shift left 4
+; get SP
+; subtract 2
+; get flags
+; put in virtual interrupt flag
+; put on stack
+; update sp
+;
+; Arguments:
+; EBX -> prefix flags
+; EBP -> trap frame
+; CL -> byte at the faulting address
+; interrupts disabled
+; ESI -> address of faulting instruction
+; EDI -> instruction length count
+;
+; Returns:
+; EAX = 0 for failure
+; EAX = 1 for success
+;
+; All registers can be trashed except ebp/esp.
+;
+ public OpcodePUSHFV86
+OpcodePUSHFV86 proc
+
+ifdef VDMDBG
+_DATA segment
+MsgPUSHFOpcode db 'NTVDM: An PUSHF opcode %lx was encountered at '
+ db 'address %lx',0ah, 0dh, 0
+_DATA ends
+ push eax
+ push [ebp].TsEip
+ push [ebp].TsSegCs
+
+ push offset FLAT:MsgPUSHFOpcode
+ call _DbgPrint ; display PUSHF opcode message
+ add esp,12
+ pop eax
+endif
+
+ mov eax,_VdmFixedStateLinear ; get pointer to VDM State
+ mov eax, dword ptr [eax] ; get virtual int flag
+ and eax,VDM_VIRTUAL_INTERRUPTS OR VDM_VIRTUAL_AC OR VDM_VIRTUAL_NT
+ mov edx,dword ptr [ebp].TsEFlags
+ and edx,NOT EFLAGS_INTERRUPT_MASK
+ or eax,edx
+ or eax,EFLAGS_IOPL_MASK
+ movzx ecx,word ptr [ebp].TsHardwareSegSS
+ shl ecx,4
+ movzx edx,word ptr [ebp].TsHardwareEsp
+ sub dx,2
+
+ test ebx,PREFIX_OPER32 ; check operand size
+ jnz puf10
+
+ mov [ecx + edx],ax
+puf05:
+ mov word ptr [ebp].TsHardwareEsp,dx ; update client esp
+ add dword ptr [ebp].TsEip,edi
+
+ mov eax,1
+ ret
+
+puf10: sub dx,2
+ mov [ecx + edx],eax
+ jmp puf05
+
+OpcodePUSHFV86 endp
+
+ page ,132
+ subttl "POPF Opcode Handler"
+;++
+;
+; Routine Description:
+;
+; This routine emulates an POPF opcode. Currently, it prints
+; a message, and returns to the monitor.
+;
+; Arguments:
+; EBX -> prefix flags
+; EBP -> trap frame
+; CL -> byte at the faulting address
+; interrupts disabled
+; ESI -> address of faulting instruction
+; EDI -> instruction length count
+;
+; Returns:
+; EAX = 0 for failure
+; EAX = 1 for success
+;
+; All registers can be trashed except ebp/esp.
+;
+
+ public OpcodePOPFV86
+OpcodePOPFV86 proc
+
+ifdef VDMDBG
+_DATA segment
+MsgPOPFOpcode db 'NTVDM: An POPF opcode %lx was encountered at '
+ db 'address %lx',0ah, 0dh, 0
+_DATA ends
+ push eax
+ push ebx
+ push [ebp].TsEip
+ push [ebp].TsSegCs
+ push offset FLAT:MsgPOPFOpcode
+ call _DbgPrint ; display POPF opcode message
+ add esp,12
+ pop ebx
+ pop eax
+endif
+ mov eax,_VdmFixedStateLinear ; get pointer to VDM State
+ mov ecx,[ebp].TsHardwareSegSS
+ shl ecx,4
+ movzx edx,word ptr [ebp].TsHardwareEsp
+ mov ecx,[ecx + edx] ; get flags from stack
+ add edx,4
+ test ebx,PREFIX_OPER32 ; check operand size
+ jnz pof10
+ and ecx,0ffffh
+ sub edx,2
+pof10:
+ mov [ebp].TsHardwareEsp,edx
+ and ecx, NOT EFLAGS_IOPL_MASK
+ mov ebx,ecx
+ and ebx, NOT EFLAGS_NT_MASK
+
+ test _KeI386VirtualIntExtensions, dword ptr V86_VIRTUAL_INT_EXTENSIONS
+ jz short pof15
+ or ebx, EFLAGS_VIF
+ test ebx, EFLAGS_INTERRUPT_MASK
+ jnz pof15
+ and ebx, NOT EFLAGS_VIF
+
+pof15:
+ or ebx, (EFLAGS_INTERRUPT_MASK OR EFLAGS_V86_MASK)
+ mov [ebp].TsEFlags,ebx
+ and ecx, (EFLAGS_INTERRUPT_MASK OR EFLAGS_ALIGN_CHECK OR EFLAGS_NT_MASK)
+ MPLOCK and [eax],NOT (EFLAGS_INTERRUPT_MASK OR EFLAGS_ALIGN_CHECK OR EFLAGS_NT_MASK)
+ MPLOCK or [eax],ecx
+ add dword ptr [ebp].TsEip,edi
+
+ mov eax,dword ptr [eax]
+ test eax,VDM_INTERRUPT_PENDING
+ jz pof25
+
+ test eax,VDM_VIRTUAL_INTERRUPTS
+ jz pof25
+
+ call VdmDispatchIntAck
+
+pof25:
+ mov eax,1 ; handled
+ ret
+OpcodePOPFV86 endp
+
+ page ,132
+ subttl "INTnn Opcode Handler"
+;++
+;
+; Routine Description:
+;
+; This routine emulates an INTnn opcode. It retrieves the handler
+; from the IVT, pushes the current cs:ip and flags on the stack,
+; and dispatches to the handler.
+;
+; Arguments:
+; EBX -> prefix flags
+; EBP -> trap frame
+; CL -> byte at the faulting address
+; interrupts disabled
+; ESI -> address of faulting instruction
+; EDI -> instruction length count
+;
+; Returns:
+; EAX = 0 for failure
+; EAX = 1 for success
+;
+; All registers can be trashed except ebp/esp.
+;
+
+ public OpcodeINTnnV86
+OpcodeINTnnV86 proc
+
+ifdef VDMDBG
+_DATA segment
+MsgINTnnOpcode db 'NTVDM: An INTnn opcode %lx was encountered at '
+ db 'address %lx',0ah, 0dh, 0
+_DATA ends
+ push eax
+ push [ebp].TsEip
+ push [ebp].TsSegCs
+
+ push offset FLAT:MsgINTnnOpcode
+ call _DbgPrint ; display INTnn opcode message
+ add esp,12
+ pop eax
+endif
+
+;
+; Int nn in v86 mode always disables interrupts
+;
+
+ mov edx,[ebp].TsEflags
+;
+; If KeI386VdmIoplAllowed is true, direct IF manipulation is allowed
+;
+ test _KeI386VdmIoplAllowed,1
+ jz oinnv10
+
+ mov eax,edx ; save original flags
+ and edx,NOT EFLAGS_INTERRUPT_MASK
+ jmp oinnv20
+
+;
+; Else, IF and some other flags bits are virtualized
+;
+oinnv10:
+ mov eax,_VdmFixedStateLinear ; get pointer to VDM State
+ mov ecx,dword ptr [eax]
+ MPLOCK and [eax],NOT VDM_VIRTUAL_INTERRUPTS
+ mov eax,ecx
+ and eax,VDM_VIRTUAL_INTERRUPTS OR VDM_VIRTUAL_AC
+ and edx,NOT EFLAGS_INTERRUPT_MASK
+ or eax,edx
+ or edx, EFLAGS_INTERRUPT_MASK
+
+oinnv20:
+ and edx,NOT (EFLAGS_NT_MASK OR EFLAGS_TF_MASK)
+ mov [ebp].TsEflags,edx
+ or eax, EFLAGS_IOPL_MASK
+ movzx ecx,word ptr [ebp].TsHardwareSegSS
+ shl ecx,4
+ movzx edx,word ptr [ebp].TsHardwareEsp ; ecx+edx is user stack
+ sub dx,2
+ mov word ptr [ecx+edx],ax ; push flags
+ mov ax,word ptr [ebp].TsSegCS
+ sub dx,2
+ mov word ptr [ecx+edx],ax ; push cs
+ movzx eax,word ptr [ebp].TsEip
+ add eax, edi
+ inc eax
+ sub dx,2
+ mov word ptr [ecx+edx],ax ; push ip
+ mov [ebp].TsHardwareEsp,dx ; update sp on trap frame
+
+ inc esi
+ movzx ecx,byte ptr [esi] ; ecx is int#
+ mov ebx,[ecx*4]
+ mov word ptr [ebp].TsEip,bx
+ shr ebx,16 ; ebx+edx is the handler add
+ mov [ebp].TsSegCs,bx ; cs:ip on trap frame is updated
+
+ mov eax,1
+ ret
+
+OpcodeINTnnV86 endp
+
+ page ,132
+ subttl "INTO Opcode Handler"
+;++
+;
+; Routine Description:
+;
+; This routine emulates an INTO opcode. Currently, it prints
+; a message, and reflects a GP fault to the debugger.
+;
+; Arguments:
+; EBX -> prefix flags
+; EBP -> trap frame
+; CL -> byte at the faulting address
+; interrupts disabled
+; ESI -> address of faulting instruction
+; EDI -> instruction length count
+;
+; Returns:
+; EAX = 0 for failure
+; EAX = 1 for success
+;
+; All registers can be trashed except ebp/esp.
+;
+
+ public OpcodeINTOV86
+OpcodeINTOV86 proc
+
+
+ifdef VDMDBG
+_DATA segment
+MsgINTOOpcode db 'NTVDM: An INTO opcode %lx was encountered at '
+ db 'address %lx',0ah, 0dh, 0
+_DATA ends
+ push [ebp].TsEip
+ push [ebp].TsSegCs
+
+ push offset FLAT:MsgINTOOpcode
+ call _DbgPrint ; display INTO opcode message
+ add esp,12
+endif
+ xor eax,eax ; ret fail
+ ret
+
+OpcodeINTOV86 endp
+
+ page ,132
+ subttl "IRET Opcode Handler"
+;++
+;
+; Routine Description:
+;
+; This routine emulates an IRET opcode. It retrieves the flags,
+; and new instruction pointer from the stack and puts them into
+; the user context.
+;
+;
+; Arguments:
+; EBX -> prefix flags
+; EBP -> trap frame
+; CL -> byte at the faulting address
+; interrupts disabled
+; ESI -> address of faulting instruction
+; EDI -> instruction length count
+;
+; Returns:
+; EAX = 0 for failure
+; EAX = 1 for success
+;
+; All registers can be trashed except ebp/esp.
+;
+;
+
+ public OpcodeIRETV86
+OpcodeIRETV86 proc
+
+
+ifdef VDMDBG
+_DATA segment
+MsgIRETOpcode db 'NTVDM: An IRET opcode %lx was encountered at '
+ db 'address %lx',0ah, 0dh, 0
+_DATA ends
+ push eax
+ push [ebp].TsEip
+ push [ebp].TsSegCs
+
+ push offset FLAT:MsgIRETOpcode
+ call _DbgPrint ; display IRET opcode message
+ add esp,12
+ pop eax
+endif
+
+ mov eax,_VdmFixedStateLinear ; get pointer to VDM State
+ movzx ecx,word ptr [ebp].TsHardwareSegSS
+ shl ecx,4
+ movzx edx,word ptr [ebp].TsHardwareEsp ; ebx+edx is user stack
+ add ecx,edx
+ test ebx,PREFIX_OPER32
+ jnz irt50 ; normally not
+
+ movzx edi,word ptr [ecx] ; get ip value
+ mov [ebp].TsEip,edi
+ movzx esi,word ptr [ecx+2] ; get cs value
+ mov [ebp].TsSegCs,esi
+ add edx,6
+ mov [ebp].TsHardwareEsp,edx ; update sp on trap frame
+ movzx ebx,word ptr [ecx+4] ; get flag value
+
+irt10:
+ and ebx, NOT (EFLAGS_IOPL_MASK OR EFLAGS_NT_MASK)
+ mov ecx,ebx
+
+ test _KeI386VirtualIntExtensions, dword ptr V86_VIRTUAL_INT_EXTENSIONS
+ jz short irt15
+ or ebx, EFLAGS_VIF
+ test ebx, EFLAGS_INTERRUPT_MASK
+ jnz irt15
+ and ebx, NOT EFLAGS_VIF
+
+irt15:
+ or ebx, (EFLAGS_V86_MASK OR EFLAGS_INTERRUPT_MASK)
+ mov [ebp].TsEFlags,ebx ; update flags n trap frame
+ and ecx, EFLAGS_INTERRUPT_MASK
+ MPLOCK and [eax],NOT VDM_VIRTUAL_INTERRUPTS
+ MPLOCK or [eax],ecx
+ mov ebx,[eax]
+
+
+ ; at this point esi is the cs and edi is the ip where v86 mode
+ ; will return. Now we will check if this returning instruction
+ ; is a bop. if so we will directly dispatch the bop from here
+ ; saving a full round trip. This will be really helpful to
+ ; com apps.
+
+ shl esi,4
+ add esi,edi
+ mov ax, word ptr [esi]
+ cmp ax, 0c4c4h
+ je irtbop
+
+ test ebx,VDM_INTERRUPT_PENDING
+ jz short irt25
+
+ test ebx,VDM_VIRTUAL_INTERRUPTS
+ jz short irt25
+
+ call VdmDispatchIntAck ; VdmDispatchIntAck enables interrupts
+
+irt25:
+ mov eax,1 ; handled
+ ret
+
+ ; ireting to a bop
+irtbop:
+ call VdmDispatchBop ; this expects ebp to be trap frame
+ jmp short irt25
+
+irt50:
+ mov edi, [ecx] ; get ip value
+ mov [ebp].TsEip,edi
+ movzx esi,word ptr [ecx+4] ; get cs value
+ mov [ebp].TsSegCs,esi
+ add edx,12
+ mov [ebp].TsHardwareEsp,edx ; update sp on trap frame
+ mov ebx, [ecx+8] ; get flag value
+ jmp irt10 ; rejoin the common path
+
+OpcodeIRETV86 endp
+
+
+ page ,132
+ subttl "In Byte Immediate Opcode Handler"
+;++
+;
+; Routine Description:
+;
+; This routine emulates an in byte immediate opcode. Currently, it
+; prints a message, and ignores the instruction.
+;
+; Arguments:
+; EBX -> prefix flags
+; EBP -> trap frame
+; CL -> byte at the faulting address
+; interrupts disabled
+; ESI -> address of faulting instruction
+; EDI -> instruction length count
+;
+; Returns:
+; EAX = 0 for failure
+; EAX = 1 for success
+;
+; All registers can be trashed except ebp/esp.
+;
+
+ public OpcodeINBimmV86
+OpcodeINBimmV86 proc
+
+ifdef VDMDBG
+_DATA segment
+MsgINBimmOpcode db 'NTVDM: An INBimm opcode %lx was encountered at '
+ db 'address %lx',0ah, 0dh, 0
+_DATA ends
+ push [ebp].TsEip
+ push [ebp].TsSegCs
+ push offset FLAT:MsgINBimmOpcode
+ call _DbgPrint ; display INBimm opcode message
+ add esp,12
+endif
+
+ inc esi
+ inc edi
+ movzx ecx,byte ptr [esi]
+
+ ; Ki386VdmDispatchIo enables interrupts
+ stdCall _Ki386VdmDispatchIo, <ecx, 1, TRUE, edi, ebp>
+ ret
+
+OpcodeINBimmV86 endp
+
+ page ,132
+ subttl "Word In Immediate Opcode Handler"
+;++
+;
+; Routine Description:
+;
+; This routine emulates an in word immediate opcode. Currently, it
+; prints a message, and ignores the instruction.
+;
+; Arguments:
+; EAX -> pointer to vdm state in DOS arena
+; EBX -> prefix flags
+; EBP -> trap frame
+; CL -> byte at the faulting address
+; interrupts disabled
+; ESI -> address of faulting instruction
+; EDI -> instruction length count
+;
+; Returns:
+; EAX = 0 for failure
+; EAX = 1 for success
+;
+; All registers can be trashed except ebp/esp.
+;
+
+ public OpcodeINWimmV86
+OpcodeINWimmV86 proc
+
+ifdef VDMDBG
+_DATA segment
+MsgINWimmOpcode db 'NTVDM: An INWimm opcode %lx was encountered at '
+ db 'address %lx',0ah, 0dh, 0
+_DATA ends
+
+ push [ebp].TsEip
+ push [ebp].TsSegCs
+ push offset FLAT:MsgINWimmOpcode
+ call _DbgPrint ; display INWimm opcode message
+ add esp,12
+endif
+
+ inc esi
+ inc edi
+ movzx ecx,byte ptr [esi]
+; edi - instruction size
+; TRUE - read op
+; 2 - word op
+; ecx - port number
+ ; Ki386VdmDispatchIo enables interrupts
+ stdCall _Ki386VdmDispatchIo, <ecx, 2, TRUE, edi, ebp>
+
+ ret
+
+OpcodeINWimmV86 endp
+
+ page ,132
+ subttl "Out Byte Immediate Opcode Handler"
+;++
+;
+; Routine Description:
+;
+; This routine emulates an invalid opcode. Currently, it prints
+; a message, and ignores the instruction.
+;
+; Arguments:
+; EAX -> pointer to vdm state in DOS arena
+; EBX -> prefix flags
+; EBP -> trap frame
+; CL -> byte at the faulting address
+; interrupts disabled
+; ESI -> address of faulting instruction
+; EDI -> instruction length count
+;
+; Returns:
+; EAX = 0 for failure
+; EAX = 1 for success
+;
+; All registers can be trashed except ebp/esp.
+;
+
+ public OpcodeOUTBimmV86
+OpcodeOUTBimmV86 proc
+
+ifdef VDMDBG
+_DATA segment
+MsgOUTBimmOpcode db 'NTVDM: An OUTBimm opcode %lx was encountered at '
+ db 'address %lx',0ah, 0dh, 0
+_DATA ends
+
+ push [ebp].TsEip
+ push [ebp].TsSegCs
+ push offset FLAT:MsgOUTBimmOpcode
+ call _DbgPrint ; display OUTBimm opcode message
+ add esp,12
+endif
+
+
+ inc edi
+ inc esi
+ movzx ecx,byte ptr [esi]
+; edi - instruction size
+; FALSE - write op
+; 1 - byte op
+; ecx - port #
+ ; Ki386VdmDispatchIo enables interrupts
+ stdCall _Ki386VdmDispatchIo, <ecx, 1, FALSE, edi, ebp>
+
+ ret
+
+OpcodeOUTBimmV86 endp
+
+ page ,132
+ subttl "Out Word Immediate Opcode Handler"
+;++
+;
+; Routine Description:
+;
+; This routine emulates an out word immediate opcode. Currently,
+; it prints a message, and ignores the instruction.
+;
+; Arguments:
+; EAX -> pointer to vdm state in DOS arena
+; EBX -> prefix flags
+; EBP -> trap frame
+; CL -> byte at the faulting address
+; interrupts disabled
+; ESI -> address of faulting instruction
+; EDI -> instruction length count
+;
+; Returns:
+; EAX = 0 for failure
+; EAX = 1 for success
+;
+; All registers can be trashed except ebp/esp.
+;
+;
+
+ public OpcodeOUTWimmV86
+OpcodeOUTWimmV86 proc
+
+
+ifdef VDMDBG
+_DATA segment
+MsgOUTWimmOpcode db 'NTVDM: An OUTWimm opcode %lx was encountered at '
+ db 'address %lx',0ah, 0dh, 0
+_DATA ends
+
+ push [ebp].TsEip
+ push [ebp].TsSegCs
+ push offset FLAT:MsgOUTWimmOpcode
+ call _DbgPrint ; display OUTWimm opcode message
+ add esp,12
+endif
+
+
+ inc esi
+ inc edi
+ movzx ecx,byte ptr [esi]
+; edi - instruction size
+; FALSE - write op
+; 2 - word op
+; ecx - port number
+ ; Ki386VdmDispatchIo enables interrupts
+ stdCall _Ki386VdmDispatchIo, <ecx, 2, FALSE, edi, ebp>
+
+ ret
+
+OpcodeOUTWimmV86 endp
+
+ page ,132
+ subttl "INB Opcode Handler"
+;++
+;
+; Routine Description:
+;
+; This routine emulates an INB opcode. Currently, it prints
+; a message, and ignores the instruction.
+;
+; Arguments:
+; EAX -> pointer to vdm state in DOS arena
+; EBX -> prefix flags
+; EBP -> trap frame
+; CL -> byte at the faulting address
+; interrupts disabled
+; ESI -> address of faulting instruction
+; EDI -> instruction length count
+;
+; Returns:
+; EAX = 0 for failure
+; EAX = 1 for success
+;
+; All registers can be trashed except ebp/esp.
+;
+
+ public OpcodeINBV86
+OpcodeINBV86 proc
+
+ifdef VDMDBG
+_DATA segment
+MsgINBOpcode db 'NTVDM: An INB opcode %lx was encountered at '
+ db 'address %lx',0ah, 0dh, 0
+_DATA ends
+
+ push [ebp].TsEip
+ push [ebp].TsSegCs
+ push offset FLAT:MsgINBOpcode
+ call _DbgPrint ; display INB opcode message
+ add esp,12
+endif
+
+ movzx ebx,word ptr [ebp].TsEdx
+
+
+; JAPAN - SUPPORT Intel CPU/Non PC/AT compatible machine
+; Get Hardware Id, PC_AT_COMPATIBLE is 0x00XX
+ test _KeI386MachineType, MACHINE_TYPE_MASK
+ jnz oib_reflect
+
+; edi - instruction size
+; TRUE - read op
+; 1 - byte op
+; ebx - port number
+
+ cmp ebx, 3bdh
+ jz oib_prt1
+ cmp ebx, 379h
+ jz oib_prt1
+ cmp ebx, 279h
+ jz oib_prt1
+
+oib_reflect:
+ ; Ki386VdmDispatchIo enables interrupts
+ stdCall _Ki386VdmDispatchIo, <ebx, 1, TRUE, edi, ebp>
+oib_com:
+ ret
+
+oib_prt1:
+ ; call printer status routine with port number, size, trap frame
+ stdCall _VdmPrinterStatus, <ebx, edi, ebp>
+ or al,al
+ jz short oib_reflect
+ jmp short oib_com
+
+OpcodeINBV86 endp
+
+ page ,132
+ subttl "INW Opcode Handler"
+;++
+;
+; Routine Description:
+;
+; This routine emulates an INW opcode. Currently, it prints
+; a message, and ignores the instruction.
+;
+; Arguments:
+; EAX -> pointer to vdm state in DOS arena
+; EBX -> prefix flags
+; EBP -> trap frame
+; CL -> byte at the faulting address
+; interrupts disabled
+; ESI -> address of faulting instruction
+; EDI -> instruction length count
+;
+; Returns:
+; EAX = 0 for failure
+; EAX = 1 for success
+;
+; All registers can be trashed except ebp/esp.
+;
+;
+
+ public OpcodeINWV86
+OpcodeINWV86 proc
+
+ifdef VDMDBG
+_DATA segment
+MsgINWOpcode db 'NTVDM: An INW opcode %lx was encountered at '
+ db 'address %lx',0ah, 0dh, 0
+_DATA ends
+
+ push [ebp].TsEip
+ push [ebp].TsSegCs
+ push offset FLAT:MsgINWOpcode
+ call _DbgPrint ; display INW opcode message
+ add esp,12
+endif
+
+ movzx ebx,word ptr [ebp].TsEdx
+
+; edi - instruction size
+; TRUE - read operation
+; 2 - word op
+; ebx - port number
+ ; Ki386VdmDispatchIo enables interrupts
+ stdCall _Ki386VdmDispatchIo, <ebx, 2, TRUE, edi, ebp>
+
+ ret
+OpcodeINWV86 endp
+
+ page ,132
+ subttl "OUTB Opcode Handler"
+;++
+;
+; Routine Description:
+;
+; This routine emulates an OUTB opcode. Currently, it prints
+; a message, and ignores the instruction.
+;
+; Arguments:
+; EAX -> pointer to vdm state in DOS arena
+; EBX -> prefix flags
+; EBP -> trap frame
+; CL -> byte at the faulting address
+; interrupts disabled
+; ESI -> address of faulting instruction
+; EDI -> instruction length count
+;
+; Returns:
+; EAX = 0 for failure
+; EAX = 1 for success
+;
+; All registers can be trashed except ebp/esp.
+;
+;
+
+ public OpcodeOUTBV86
+OpcodeOUTBV86 proc
+
+ifdef VDMDBG
+_DATA segment
+MsgOUTBOpcode db 'NTVDM: An OUTB opcode %lx was encountered at '
+ db 'address %lx',0ah, 0dh, 0
+_DATA ends
+
+ push [ebp].TsEip
+ push [ebp].TsSegCs
+ push offset FLAT:MsgOUTBOpcode
+ call _DbgPrint ; display OUTB opcode message
+ add esp,12
+endif
+
+ movzx ebx,word ptr [ebp].TsEdx
+
+ cmp ebx, 3bch
+ jz oob_prt1
+ cmp ebx, 378h
+ jz oob_prt1
+ cmp ebx, 278h
+ jz oob_prt1
+
+oob_reflect:
+
+; edi - instruction size
+; FALSE - write op
+; 1 - byte op
+; ebx - port number
+ ; Ki386VdmDispatchIo enables interrupts
+ stdCall _Ki386VdmDispatchIo, <ebx, 1, FALSE, edi, ebp>
+
+ ret
+oob_prt1:
+ ; call printer write data routine with port number, size, trap frame
+ stdCall _VdmPrinterWriteData, <ebx, edi, ebp>
+ or al,al
+ jz short oob_reflect
+ ;al already has TRUE
+ ret
+OpcodeOUTBV86 endp
+
+ page ,132
+ subttl "OUTW Opcode Handler"
+;++
+;
+; Routine Description:
+;
+; This routine emulates an OUTW opcode. Currently, it prints
+; a message, and ignores the instruction.
+;
+; Arguments:
+; EAX -> pointer to vdm state in DOS arena
+; EBX -> prefix flags
+; EBP -> trap frame
+; CL -> byte at the faulting address
+; interrupts disabled
+; ESI -> address of faulting instruction
+; EDI -> instruction length count
+;
+; Returns:
+; EAX = 0 for failure
+; EAX = 1 for success
+;
+; All registers can be trashed except ebp/esp.
+;
+;
+
+ public OpcodeOUTWV86
+OpcodeOUTWV86 proc
+
+ifdef VDMDBG
+_DATA segment
+MsgOUTWOpcode db 'NTVDM: An OUTW opcode %lx was encountered at '
+ db 'address %lx',0ah, 0dh, 0
+_DATA ends
+
+ push [ebp].TsEip
+ push [ebp].TsSegCs
+ push offset FLAT:MsgOUTWOpcode
+ call _DbgPrint ; display OUTW opcode message
+ add esp,12
+endif
+
+
+ movzx ebx,word ptr [ebp].TsEdx
+; edi - instruction size
+; FALSE - write op
+; 2 - word op
+; ebx - port #
+ ; Ki386VdmDispatchIo enables interrupts
+ stdCall _Ki386VdmDispatchIo, <ebx, 2, FALSE, edi, ebp>
+
+ ret
+
+OpcodeOUTWV86 endp
+
+
+ page ,132
+ subttl "CLI Opcode Handler"
+;++
+;
+; Routine Description:
+;
+; This routine emulates an CLI opcode. Currently, it prints
+; a message, and clears the virtual interrupt flag in the VdmTeb.
+;
+; Arguments:
+; EAX -> pointer to vdm state in DOS arena
+; EBX -> prefix flags
+; EBP -> trap frame
+; CL -> byte at the faulting address
+; interrupts disabled
+; ESI -> address of faulting instruction
+; EDI -> instruction length count
+;
+; Returns:
+; EAX = 0 for failure
+; EAX = 1 for success
+;
+; All registers can be trashed except ebp/esp.
+;
+;
+
+ public OpcodeCLIV86
+OpcodeCLIV86 proc
+
+ifdef VDMDBG
+_DATA segment
+MsgCLIOpcode db 'NTVDM: An CLI opcode %lx was encountered at '
+ db 'address %lx',0ah, 0dh, 0
+_DATA ends
+ push eax
+ push [ebp].TsEip
+ push [ebp].TsSegCs
+ push offset FLAT:MsgCLIOpcode
+ call _DbgPrint ; display CLI opcode message
+ add esp,12
+ pop eax
+endif
+
+ mov eax,_VdmFixedStateLinear ; get pointer to VDM State
+ MPLOCK and dword ptr [eax],NOT VDM_VIRTUAL_INTERRUPTS
+ add dword ptr [ebp].TsEip,edi
+
+ mov eax,1
+ ret
+
+OpcodeCLIV86 endp
+
+ page ,132
+ subttl "STI Opcode Handler"
+;++
+;
+; Routine Description:
+;
+; This routine emulates an STI opcode. Currently, it prints
+; a message, and sets the virtual interrupt flag in the VDM teb.
+;
+; Arguments:
+; EAX -> pointer to vdm state in DOS arena
+; EBX -> prefix flags
+; EBP -> trap frame
+; CL -> byte at the faulting address
+; interrupts disabled
+; ESI -> address of faulting instruction
+; EDI -> instruction length count
+;
+; Returns:
+; EAX = 0 for failure
+; EAX = 1 for success
+;
+; All registers can be trashed except ebp/esp.
+;
+;
+
+ public OpcodeSTIV86
+OpcodeSTIV86 proc
+
+ifdef VDMDBG
+_DATA segment
+MsgSTIOpcode db 'NTVDM: An STI opcode %lx was encountered at '
+ db 'address %lx',0ah, 0dh, 0
+_DATA ends
+
+ push eax
+ push [ebp].TsEip
+ push [ebp].TsSegCs
+ push offset FLAT:MsgSTIOpcode
+ call _DbgPrint ; display STI opcode message
+ add esp,12
+ pop eax
+endif
+
+ mov eax,_VdmFixedStateLinear ; get pointer to VDM State
+ test _KeI386VirtualIntExtensions, dword ptr V86_VIRTUAL_INT_EXTENSIONS
+ jz short os10
+
+ or [ebp].TsEFlags, dword ptr EFLAGS_VIF
+
+os10: MPLOCK or dword ptr [eax],EFLAGS_INTERRUPT_MASK
+os20: add dword ptr [ebp].TsEip,edi
+ mov eax,dword ptr [eax]
+ test eax,VDM_INTERRUPT_PENDING
+ jz short os30
+
+ call VdmDispatchIntAck
+os30: mov eax,1
+ ret
+
+OpcodeSTIV86 endp
+
+
+;
+; If we get here, we have executed an NPX instruction in user mode
+; with the emulator installed. If the EM bit was not set in CR0, the
+; app really wanted to execute the instruction for detection purposes.
+; In this case, we need to clear the TS bit, and restart the instruction.
+; Otherwise we need to reflect the exception
+;
+;
+; Reginfo structure
+;
+
+ public Opcode0FV86
+Opcode0FV86 proc
+
+RI equ [ebp - REGINFOSIZE]
+
+ push ebp
+ mov ebp,esp
+ sub esp,REGINFOSIZE
+ push esi
+ push edi
+
+
+ ; Initialize RegInfo
+do10: mov esi,[ebp]
+
+
+ ; initialize rest of the trap from which was'nt initialized for
+ ; v86 mode
+ mov eax, [esi].TsV86Es
+ mov [esi].TsSegEs,eax
+ mov eax, [esi].TsV86Ds
+ mov [esi].TsSegDs,eax
+ mov eax, [esi].TsV86Fs
+ mov [esi].TsSegFs,eax
+ mov eax, [esi].TsV86Gs
+ mov [esi].TsSegGs,eax
+
+ mov RI.RiTrapFrame,esi
+ mov eax,[esi].TsHardwareSegSs
+ mov RI.RiSegSs,eax
+ mov eax,[esi].TsHardwareEsp
+ mov RI.RiEsp,eax
+ mov eax,[esi].TsEFlags
+ mov RI.RiEFlags,eax
+ mov eax,[esi].TsSegCs
+ mov RI.RiSegCs,eax
+ mov eax,[esi].TsEip
+ dec edi
+ add eax,edi ; for prefixes
+ mov RI.RiEip,eax
+
+ mov RI.RiPrefixFlags,ebx
+ lea esi,RI
+
+ CsToLinearV86
+ call VdmOpcode0f ; enables interrupts
+
+ test eax,0FFFFh
+ jz do20
+
+ mov edi,RI.RiTrapFrame
+ mov eax,RI.RiEip ; advance eip
+ mov [edi].TsEip,eax
+do19: mov eax,1
+do20:
+ pop edi
+ pop esi
+ mov esp,ebp
+ pop ebp
+ ret
+
+Opcode0FV86 endp
+
+
+;++
+;
+; Routine Description: VdmDispatchIntAck
+; pushes stack arguments for VdmDispatchInterrupts
+; and invokes VdmDispatchInterrupts
+;
+; Expects VDM_INTERRUPT_PENDING, and VDM_VIRTUAL_INTERRUPTS
+;
+; Arguments:
+; EBP -> trap frame
+;
+; Returns:
+; nothing
+;
+;
+ public VdmDispatchIntAck
+VdmDispatchIntAck proc
+
+ mov eax,_VdmFixedStateLinear ; get pointer to VDM State
+ test [eax],VDM_INT_HARDWARE ; check for hardware int
+ mov eax, PCR[PcTeb]
+ mov eax,[eax].TbVdm ; get pointer to VdmTib
+ jz short dia20
+
+ ;
+ ; dispatch hardware int directly from kernel
+ ;
+ stdCall _VdmDispatchInterrupts, <ebp, eax> ; TrapFrame, VdmTib
+dia10:
+ ret
+
+
+ ;
+ ; Switch to monitor context to dispatch timer int
+ ;
+dia20:
+ mov dword ptr [eax].VtEIEvent,VdmIntAck
+ mov dword ptr [eax].VtEIInstSize,0
+ mov dword ptr [eax].VtEiIntAckInfo,0
+ stdCall _VdmEndExecution, <ebp, eax> ; TrapFrame, VdmTib
+ jmp short dia10
+
+VdmDispatchIntAck endp
+
+
+ public vdmDebugPoint
+vdmDebugPoint proc
+ ret
+vdmDebugPoint endp
+
+
+
+ page ,132
+ subttl "HLT Opcode Handler"
+;++
+;
+; Routine Description:
+;
+; This routine emulates an HLT opcode. If the halt instruction is
+; followed by the magic number (to be found in a crackerjack box),
+; we use the hlt + magic number as a prefix, and emulate the following
+; instruction. This allows code running in segmented protected mode to
+; access the virtual interrupt flag.
+;
+; Arguments:
+; EAX -> pointer to vdm state in DOS arena
+; EBX -> prefix flags
+; EBP -> trap frame
+; CL -> byte at the faulting address
+; interrupts disabled
+; ESI -> address of faulting instruction
+; EDI -> instruction length count
+;
+; Returns:
+; EAX = 0 for failure
+; EAX = 1 for success
+;
+; All registers can be trashed except ebp/esp.
+;
+
+ public OpcodeHLTV86
+OpcodeHLTV86 proc
+
+ifdef VDMDBG
+_DATA segment
+MsgHLTOpcode db 'NTVDM: An HLT opcode %lx was encountered at '
+ db 'address %lx',0ah, 0dh, 0
+_DATA ends
+ push eax
+ push [ebp].TsEip
+ push [ebp].TsSegCs
+ push offset FLAT:MsgHLTOpcode
+ call _DbgPrint ; display HLT opcode message
+ add esp,12
+ pop eax
+endif
+
+ add dword ptr [ebp].TsEip,edi
+ mov eax,1
+ ret
+
+OpcodeHLTV86 endp
+
+_PAGE ends
+
+_TEXT$00 SEGMENT DWORD PUBLIC 'CODE'
+ ASSUME DS:NOTHING, ES:NOTHING, SS:NOTHING, FS:NOTHING, GS:NOTHING
+
+ subttl "NPX Opcode Handler"
+;++
+;
+; Routine Description:
+;
+; This routine emulates all NPX opcodes, when the system
+; has the R3 emulator installed and the c86 apps takes a
+; trap07.
+;
+; Arguments:
+; EBX -> prefix flags
+; EBP -> trap frame
+; CL -> byte at the faulting address
+; ESI -> address of faulting instruction
+; EDI -> instruction length count
+;
+; Returns:
+; EAX = 0 for failure
+; EAX = 1 for success
+;
+; All registers can be trashed except ebp/esp.
+; moved from emv86.asm as it must be non-pagable
+
+ public OpcodeNPXV86
+OpcodeNPXV86 proc
+ mov edx, PCR[PcInitialStack]
+ mov edx, [edx].FpCr0NpxState
+ test edx, CR0_EM ; Does app want NPX traps?
+ jnz short onp40
+
+ ; MP bit can never be set while the EM bit is cleared, so we know
+ ; the faulting instruction is not an FWAIT
+
+onp30: and ebx, PREFIX_ADDR32
+ stdCall _VdmSkipNpxInstruction, <ebp, ebx, esi, edi>
+ or al, al ; was it handled?
+ jnz short onp60 ; no, go raise exception to app
+
+onp40: stdCall _Ki386VdmReflectException, <7> ; trap #
+
+onp60: mov eax,1
+ ret
+
+
+OpcodeNPXV86 endp
+
+
+;++ KiVdmSetUserCR0
+;
+; eax
+;
+ public KiVdmSetUserCR0
+KiVdmSetUserCR0 proc
+
+ and eax, CR0_MP OR CR0_EM ; Sanitize parameter
+ shr eax, 1
+ movzx eax, _VdmUserCr0MapIn[eax]
+
+ push esp ; Pass current Esp to handler
+ push offset scr_fault ; Set Handler address
+ push PCR[PcExceptionList] ; Set next pointer
+ mov PCR[PcExceptionList],esp ; Link us on
+
+ mov edx, PCR[PcTeb] ; Shadow user's CR0 state to
+ mov edx,[edx].TbVdm ; Teb for R3 emulator
+ mov [edx].VtVdmContext.CsFloatSave.FpCr0NpxState, eax
+
+scr10: pop PCR[PcExceptionList] ; Remove our exception handle
+ add esp, 8 ; clear stack
+
+ mov edx, PCR[PcInitialStack] ; Get fp save area
+ mov ebx, PCR[PcPrcbData + PbCurrentThread] ; (ebx) = current thread
+
+scr20: cli ; sync with context swap
+ and [edx].FpCr0NpxState, NOT (CR0_MP+CR0_EM+CR0_PE)
+ or [edx].FpCr0NpxState,eax ; set fp save area bits
+
+ mov eax,cr0
+ and eax, NOT (CR0_MP+CR0_EM+CR0_TS) ; turn off bits we will change
+ or al, [ebx].ThNpxState ; set scheduler bits
+ or eax,[edx].FpCr0NpxState ; set user's bits
+ mov cr0,eax
+ sti
+ ret
+
+scr_fault:
+;
+; WARNING: Here we directly unlink the exception handler from the
+; exception registration chain. NO unwind is performed. We can take
+; this short cut because we know that our handler is a leaf-node.
+;
+
+ mov esp, [esp+8] ; (esp)-> ExceptionList
+ jmp short scr10
+
+
+KiVdmSetUserCR0 endp
+
+_TEXT$00 ENDS
+
+ end
diff --git a/private/ntos/ke/i386/emxcptn.asm b/private/ntos/ke/i386/emxcptn.asm
new file mode 100644
index 000000000..58e2ed253
--- /dev/null
+++ b/private/ntos/ke/i386/emxcptn.asm
@@ -0,0 +1,657 @@
+ page 78,132
+;*******************************************************************************
+; Copyright (c) Microsoft Corporation 1991
+; All Rights Reserved
+;
+; ke\i386\emxcptn.asm
+;
+; Module to support getting/setting context to and from the R3
+; emulator.
+;
+;Revision History:
+;
+;
+;*******************************************************************************
+
+ .386p
+_TEXT SEGMENT DWORD PUBLIC 'CODE'
+ ASSUME DS:FLAT, ES:FLAT, SS:NOTHING, FS:NOTHING, GS:NOTHING
+
+
+;;*******************************************************************************
+;;
+;; Include some more macros and constants.
+;;
+;;*******************************************************************************
+;
+
+NT386 equ 1
+
+ include ks386.inc
+ include em387.inc ; Emulator TEB data layout
+ include callconv.inc
+
+ EXTRNP _KeGetCurrentIrql,0
+ EXTRNP _KeBugCheck,1
+ EXTRNP _ExRaiseStatus,1
+ extrn _Ki387RoundModeTable:dword
+
+
+ subttl _KiEm87StateToNpxFrame
+ page
+
+;*** _KiEm87StateToNpxFrames
+;
+; Translates the R3 emulators state to the NpxFrame
+;
+; Returns TRUE if NpxFrame sucessfully completed.
+; else FALSE
+;
+; Warning: This function can only be called at Irql 0 with interrupts
+; enabled. It is intended to be called only to deal with R3 exceptions
+; when the emulator is being used.
+;
+; Revision History:
+;
+;
+;*******************************************************************************
+
+cPublicProc _KiEm87StateToNpxFrame, 1
+ push ebp
+ mov ebp, esp
+ push ebx ; Save C runtime varibles
+ push edi
+ push esi
+
+ push esp ; Pass current Esp to handler
+ push offset stnpx_30 ; Set Handler address
+ push PCR[PcExceptionList] ; Set next pointer
+ mov PCR[PcExceptionList],esp ; Link us on
+
+if DBG
+ pushfd ; Sanity check
+ pop ecx ; make sure interrupts are enabled
+ test ecx, EFLAGS_INTERRUPT_MASK
+ jz short stnpx_err
+
+ stdCall _KeGetCurrentIrql ; Sanity check
+ cmp al, DISPATCH_LEVEL ; make sure Irql is below DPC level
+ jnc short stnpx_err
+endif
+
+ xor eax, eax ; set FALSE
+
+ mov ebx,PCR[PcPrcbData+PbCurrentThread]
+ mov ebx,[ebx]+ThApcState+AsProcess
+ test byte ptr [ebx]+PrVdmFlag,0fh ; is this a vdm process?
+ jnz short stnpx_10 ; Yes, then not supported
+
+ mov ebx, PCR[PcTeb] ; R3 Teb
+ cmp [ebx].Einstall, 0 ; Initialized?
+ je short stnpx_10 ; No, then don't return NpxFrame
+
+ test [ebx].CURErr, Summary ; Completed?
+ jz short stnpx_10 ; No, then don't return NpxFrame
+
+ mov esi, [ebp+8] ; (esi) = NpxFrame
+ call SaveState
+
+ mov eax, 1 ; Return TRUE
+stnpx_10:
+ pop PCR[PcExceptionList] ; Remove our exception handle
+ add esp, 8 ; clear stack
+ pop esi
+ pop edi
+ pop ebx
+ pop ebp
+ stdRET _KiEm87StateToNpxFrame
+
+stnpx_30:
+;
+; WARNING: Here we directly unlink the exception handler from the
+; exception registration chain. NO unwind is performed. We can take
+; this short cut because we know that our handler is a leaf-node.
+;
+
+ mov esp, [esp+8] ; (esp)-> ExceptionList
+ xor eax, eax ; Return FALSE
+ jmp short stnpx_10
+
+if DBG
+stnpx_err:
+ stdCall _KeBugCheck <IRQL_NOT_LESS_OR_EQUAL>
+endif
+_KiEm87StateToNpxFrame ENDP
+
+
+;*** SaveEnv
+;
+;
+; ARGUMENTS
+;
+; (esi) = NpxFrame
+; (ebx) = PcTeb
+;
+;
+; DESCRIPTION
+;
+
+SaveEnv:
+ xor ax,ax
+ mov [esi].reserved1,ax
+ mov [esi].reserved2,ax
+ mov [esi].reserved3,ax
+ mov [esi].reserved4,ax
+ mov [esi].reserved5,ax
+ mov ax,[ebx].ControlWord
+ mov [esi].E32_ControlWord,ax
+ call GetEMSEGStatusWord
+ mov [esi].E32_StatusWord,ax
+ call GetTagWord
+ mov [esi].E32_TagWord,ax
+ mov ax,cs
+ mov [esi].E32_CodeSeg,ax ; NOTE: Not R0 code & stack
+ mov ax,ss
+ mov [esi].E32_DataSeg,ax
+ mov eax,[ebx].PrevCodeOff
+ mov [esi].E32_CodeOff,eax
+ mov eax,[ebx].PrevDataOff
+ mov [esi].E32_DataOff,eax
+ ret
+
+
+;*** SaveState -
+;
+; ARGUMENTS
+; (esi) = where to store environment
+; (ebx) = PcTeb
+;
+; DESCRIPTION
+;
+; REGISTERS
+; Destroys ALL, but EBX
+;
+
+SaveState: ; Enter here for debugger save state
+ mov dword ptr [esi].FpCr0NpxState, CR0_EM
+
+ call SaveEnv
+ add esi,size Env80x87_32 ;Skip over environment
+ mov ebp,NumLev ;Save entire stack
+ mov edi,[ebx].CURstk
+ss_loop:
+ mov eax,[ebx+edi].ExpSgn
+ call StoreTempReal ;in emstore.asm
+ add esi,10
+
+ mov edi,[ebx].CURstk
+;;; NextStackElem edi,SaveState
+ cmp edi,INITstk
+ jae short ss_wrap
+ add edi,Reg87Len
+ss_continue:
+ mov [ebx].CURstk,edi
+ dec ebp
+ jnz short ss_loop
+ ret
+ss_wrap:
+ mov edi, BEGstk
+ jmp short ss_continue
+
+
+;*** GetTagWord - figures out what the tag word is from the numeric stack
+; and returns the value of the tag word in ax.
+;
+; ARGUMENTS
+; (ebx) = PcTeb
+;
+
+GetTagWord:
+ push esi
+ xor eax, eax
+ mov ecx, NumLev ; get tags for regs. 0, 7 - 1
+ mov esi, INITstk
+GetTagLoop:
+ mov dh, [ebx+esi].bTag ; The top 2 bits of Tag are the X87 tag bits.
+ shld ax, dx, 2
+ sub esi, Reg87Len
+ loop GetTagLoop
+ rol ax, 2 ; This moves Tag(0) into the low 2 bits
+ pop esi
+ ret
+
+
+;*** GetEMSEGStatusWord
+;
+; User status word returned in ax.
+; Uses status word in per-thread data area, otherwise
+; identical to GetStatusWord
+;
+; ARGUMENTS
+; (ebx) = PcTeb
+
+GetEMSEGStatusWord:
+ mov eax, [ebx].CURstk
+ sub eax, BEGstk
+
+ ;
+ ; Make sure the 'div' won't overflowed.
+ ;
+
+ cmp eax, Reg87Len * (NumLev + 2)
+ ja short @f
+
+ mov dl,Reg87Len
+ div dl
+ inc eax
+ and eax, 7 ; eax is now the stack number
+ shl ax, 11
+ or ax, [ebx].StatusWord ; or in the rest of the status word.
+ ret
+@@:
+ mov eax, STATUS_INTEGER_OVERFLOW
+ stdCall _ExRaiseStatus, <eax>
+ ret ; Should never come here ...
+
+;*** StoreTempReal
+;
+;
+; ARGUMENTS
+; ??
+; (ebx) = PcTeb
+;
+
+StoreTempReal:
+ mov edx,[ebx+edi].lManHi
+ mov edi,[ebx+edi].lManLo
+;mantissa in edx:edi, exponent in high eax, sign in ah bit 7, tag in al
+;memory destination is esi
+ mov ecx,eax ;get copy of sign and tag
+ shr ecx,16 ;Bring exponent down
+ cmp al,bTAG_ZERO
+ jz short StoreIEEE80 ;Skip bias if zero
+ add ecx,IexpBias-TexpBias ;Correct bias
+ cmp al,bTAG_DEN
+ jz short Denorm80
+StoreIEEE80:
+ and eax,bSign shl 8
+ or ecx,eax ;Combine sign with exponent
+ mov [esi],edi
+ mov [esi+4],edx
+ mov [esi+8],cx
+ ret
+
+Denorm80:
+;Must change it to a denormal
+ dec ecx
+ neg ecx ;Use as shift count
+ cmp cl,32 ;Long shift?
+ jae LongDenorm
+ shrd edi,edx,cl
+ shr edx,cl
+ xor ecx,ecx ;Exponent is zero
+ jmp short StoreIEEE80
+
+LongDenorm:
+;edi must be zero if we have 32 bits to shift
+ xchg edx,edi ;32-bit right shift
+ shr edi,cl ;shift count is modulo-32
+ xor ecx,ecx ;Exponent is zero
+ jmp short StoreIEEE80
+
+
+;****************************************************
+;****************************************************
+;****************************************************
+;****************************************************
+
+
+;*** _KiNpxFrameToEm87State
+;
+; Translates the NpxFrame to the R3 emulators state
+;
+; Returns TRUE if NpxFrame state sucessfully transfered.
+; else FALSE
+;
+; Warning: This function can only be called at Irql 0 with interrupts
+; enabled. It is intended to be called only to deal with R3 exceptions
+; when the emulator is being used.
+;
+; Revision History:
+;
+;
+;*******************************************************************************
+
+cPublicProc _KiNpxFrameToEm87State, 1
+ push ebp
+ mov ebp, esp
+ push ebx ; Save C runtime varibles
+ push edi
+ push esi
+
+ push esp ; Pass current Esp to handler
+ push offset npxts_30 ; Set Handler address
+ push PCR[PcExceptionList] ; Set next pointer
+ mov PCR[PcExceptionList],esp ; Link us on
+
+if DBG
+ pushfd ; Sanity check
+ pop ecx ; make sure interrupts are enabled
+ test ecx, EFLAGS_INTERRUPT_MASK
+ jz short npxts_err
+
+ stdCall _KeGetCurrentIrql ; Sanity check
+ cmp al, DISPATCH_LEVEL ; make sure Irql is below DPC level
+ jnc short npxts_err
+endif
+
+ xor eax, eax ; set FALSE
+
+ mov ebx,PCR[PcPrcbData+PbCurrentThread]
+ mov ebx,[ebx]+ThApcState+AsProcess
+ test byte ptr [ebx]+PrVdmFlag,0fh ; is this a vdm process?
+ jnz short npxts_10 ; Yes, then not supported
+
+ mov ebx, PCR[PcTeb] ; R3 Teb
+ cmp [ebx].Einstall, 0 ; Initialized?
+ je short npxts_10 ; No, then don't set NpxFrame
+
+ mov esi, [ebp+8] ; (esi) = NpxFrame
+ call StorState
+ or [ebx].CURErr, Summary ; Set completed
+
+ mov eax, 1 ; Return TRUE
+npxts_10:
+ pop PCR[PcExceptionList] ; Remove our exception handle
+ add esp, 8 ; clear stack
+ pop esi
+ pop edi
+ pop ebx
+ pop ebp
+ stdRet _KiNpxFrameToEm87State
+
+npxts_30:
+;
+; WARNING: Here we directly unlink the exception handler from the
+; exception registration chain. NO unwind is performed. We can take
+; this short cut because we know that our handler is a leaf-node.
+;
+
+ mov esp, [esp+8] ; (esp)-> ExceptionList
+ xor eax, eax ; Return FALSE
+ jmp short npxts_10
+ ret
+
+if DBG
+npxts_err:
+ stdCall _KeBugCheck <IRQL_NOT_LESS_OR_EQUAL>
+endif
+_KiNpxFrameToEm87State ENDP
+
+
+
+;*** StorState - emulate FRSTOR [address]
+;
+; ARGUMENTS
+; (esi) = where to get the environment
+; (ebx) = PcTeb
+;
+;
+; DESCRIPTION
+; This routine emulates an 80387 FRSTOR (restore state)
+
+StorState:
+;First we set up the status word so that [CURstk] is initialized.
+;The floating-point registers are stored in logical ST(0) - ST(7) order,
+;not physical register order. We don't do a full load of the environment
+;because we're not ready to use the tag word yet.
+
+ mov ax, [esi].E32_StatusWord
+ call SetEmStatusWord ;Initialize [CURstk]
+ add esi,size Env80x87_32 ;Skip over environment
+
+;Load of temp real has one difference from real math chip: it is an invalid
+;operation to load an unsupported format. By ensuring the exception is
+;masked, we will convert unsupported format to Indefinite. Note that the
+;mask and [CURerr] will be completely restored by the FLDENV at the end.
+
+ mov [ebx].CWmask,3FH ;Mask off invalid operation exception
+ mov edi,[ebx].CURstk
+ mov ebp,NumLev
+FrstorLoadLoop:
+ push esi
+ call LoadTempReal ;In emload.asm
+ pop esi
+ add esi,10 ;Point to next temp real
+;;; NextStackElem edi,Frstor
+ cmp edi,INITstk
+ jae short fr_wrap
+ add edi,Reg87Len
+fr_continue:
+ dec ebp
+ jnz short FrstorLoadLoop
+ sub esi,NumLev*10+size Env80x87_32 ;Point to start of env.
+;
+; Stor Enviroment
+; (esi) = where to get enviroment
+; (ebx) = PcTeb
+;
+
+ mov ax, [esi].E32_StatusWord
+ call SetEmStatusWord ; set up status word
+ mov ax, [esi].E32_ControlWord
+ call SetControlWord
+ mov ax, [esi].E32_TagWord
+ call UseTagWord
+
+ mov eax, [esi].E32_CodeOff
+ mov [ebx].PrevCodeOff, eax
+ mov eax, [esi].E32_DataOff
+ mov [ebx].PrevDataOff, eax
+ ret
+
+fr_wrap:
+ mov edi, BEGstk
+ jmp short fr_continue
+
+
+;*** SetEmStatusWord -
+;
+; Given user status word in ax, set into emulator.
+; Destroys ebx only.
+
+
+SetEmStatusWord:
+ and ax,7F7FH
+ mov cx,ax
+ and cx,3FH ; set up CURerr in case user
+ mov [ebx].CURerr,cl ; wants to force an exception
+ mov ecx, eax
+ and ecx, not (7 shl 11) ; remove stack field.
+ mov [ebx].StatusWord, cx
+
+ sub ah, 8 ; adjust for emulator's stack layout
+ and ah, 7 shl 3
+ mov al, ah
+ shr ah, 1
+ add al, ah ; stack field * 3 * 4
+.erre Reg87Len eq 12
+ and eax, 255 ; eax is now 12*stack number
+ add eax, BEGstk
+ mov [ebx].CURstk, eax
+ ret
+
+SetControlWord:
+ and ax,0F3FH ; Limit to valid values
+ mov [ebx].ControlWord, ax ; Store in the emulated control word
+ not al ;Flip mask bits for fast compare
+ and al,3FH ;Limit to valid mask bits
+ mov [ebx].ErrMask,al
+ and eax,(RoundControl + PrecisionControl) shl 8
+.erre RoundControl eq 1100B
+.erre PrecisionControl eq 0011B
+ shr eax,6 ;Put PC and RC in bits 2-5
+ mov ecx,_Ki387RoundModeTable
+ mov ecx,[ecx+eax] ;Get correct RoundMode vector
+ mov [ebx].RoundMode,ecx
+ mov [ebx].SavedRoundMode,ecx
+ and eax,RoundControl shl (8-6) ;Mask off precision control
+ mov ecx,_Ki387RoundModeTable
+ mov ecx,[ecx+(eax+PC64 shl (8-6))];Get correct RoundMode vector
+ mov [ebx].TransRound,ecx ;Round mode w/o precision
+ ret
+
+
+;*** UseTagWord - Set up tags using tag word from environment
+;
+; ARGUMENTS
+; ax - should contain the tag word
+;
+; Destroys ax,bx,cx,dx,di
+
+UseTagWord:
+ ror ax, 2 ; mov Tag(0) into top bits of ax
+ mov edi,INITstk
+ mov ecx, NumLev
+UseTagLoop:
+ mov dl,bTAG_EMPTY
+ cmp ah, 0c0h ;Is register to be tagged Empty?
+ jae short SetTag ;Yes, go mark it
+ mov dl,[ebx+edi].bTag ;Get current tag
+ cmp dl,bTAG_EMPTY ;Is register currently Empty?
+ je short SetTagNotEmpty ;If so, go figure out tag for it
+SetTag:
+ mov [ebx+edi].bTag,dl
+UseTagLoopCheck:
+ sub edi, Reg87Len
+ shl eax, 2
+ loop UseTagLoop
+ ret
+
+SetTagEmpty:
+ mov [ebx+edi].bTag, bTAG_EMPTY
+ jmp short UseTagLoopCheck
+
+SetTagNotEmpty:
+;Register is currently tagged empty, but new tag word says it is not empty.
+;Figure out a new tag for it. The rules are:
+;
+;1. Everything is either normalized or zero--unnormalized formats cannot
+;get in. So if the high half mantissa is zero, the number is zero.
+;
+;2. Although the exponent bias is different, NANs and Infinities are in
+;standard IEEE format - exponent is TexpMax, mantissa indicates NAN vs.
+;infinity (mantissa for infinity is 800..000H).
+;
+;3. Denormals have an exponent less than TexpMin.
+;
+;4. If the low half of the mantissa is zero, it is tagged bTAG_SNGL
+;
+;5. Everything else is bTAG_VALID
+
+ cmp [ebx+edi].lManHi, 0
+ mov dl,bTAG_ZERO ;Try zero first
+ jz short SetTag ;Is mantissa zero?
+ mov edx,[ebx+edi].ExpSgn
+ mov dl,bTAG_DEN
+ cmp edx,TexpMin shl 16 ;Is it denormal?
+ jl short SetTag
+ cmp [ebx+edi].lManLo,0 ;Is low half zero?
+.erre bTAG_VALID eq 1
+.erre bTAG_SNGL eq 0
+ setnz dl ;if low half==0 then dl=0 else dl=1
+ cmp edx,TexpMax shl 16 ;Is it NAN or Infinity?
+ jl short SetTag ;If not, it's valid
+.erre (bTAG_VALID - bTAG_SNGL) shl TAG_SHIFT eq (bTAG_NAN - bTAG_INF)
+ shl dl,TAG_SHIFT
+ add dl,bTAG_INF - bTAG_SNGL
+;If the low bits were zero we have just changed bTAG_SNGL to bTAG_INF
+;If the low bits weren't zero, we changed bTAG_VALID to bTAG_NAN
+;See if infinity is really possible: is high half 80..00H?
+ cmp [ebx+edi].lManHi,1 shl 31 ;Is it infinity?
+ jz short SetTag ;Store tag for infinity or NAN
+ mov dl,bTAG_NAN
+ jmp short SetTag
+
+
+;*** LoadTempReal
+;
+;
+;
+
+LoadTempReal:
+ mov ebx,[esi+4] ;Get high half of mantissa
+ mov cx,[esi+8] ;Get exponent and sign
+ mov esi,[esi] ;Get low half of mantissa
+ mov eax,ecx
+ and ch,7FH ;Mask off sign bit
+ shl ecx,16 ;Move exponent to high end
+ mov ch,ah ;Restore sign
+ jz short ZeroOrDenorm80
+;Check for unsupported format: unnormals (MSB not set)
+ or ebx,ebx
+ jns short Unsupported
+ sub ecx,(IexpBias-TexpBias) shl 16 ;Correct the bias
+ cmp ecx,TexpMax shl 16
+ jge short NANorInf80
+SetupTag:
+ or esi,esi ;Any bits in low half?
+.erre bTAG_VALID eq 1
+.erre bTAG_SNGL eq 0
+ setnz cl ;if low half==0 then cl=0 else cl=1
+ jmp short SaveStack
+
+NANorInf80:
+ mov cl,bTAG_NAN
+ cmp ebx,1 shl 31 ;Only 1 bit set means infinity
+ jnz short SaveStack
+ or esi,esi
+ jnz short SaveStack
+ mov cl,bTAG_INF
+ jmp short SaveStack
+
+ZeroOrDenorm80:
+;Exponent is zero. Number is either zero or denormalized
+ or ebx,ebx
+ jnz short ShortNorm80 ;Are top 32 bits zero?
+ or esi,esi ;Are low 32 bits zero too?
+ jnz LongNorm80
+ mov cl,bTAG_ZERO
+ jmp short SaveStack
+
+;This code accepts and works correctly with pseudo-denormals (MSB already set)
+LongNorm80:
+ xchg ebx,esi ;Shift up 32 bits
+ sub ecx,32 shl 16 ;Correct exponent
+ShortNorm80:
+ add ecx,(TexpBias-IexpBias+1-31) shl 16 ;Fix up bias
+ bsr edx,ebx ;Scan for MSB
+;Bit number in edx ranges from 0 to 31
+ mov cl,dl
+ not cl ;Convert bit number to shift count
+ shld ebx,esi,cl
+ shl esi,cl
+ shl edx,16 ;Move exp. adjustment to high end
+ add ecx,edx ;Adjust exponent
+ jmp short SetUpTag
+
+SaveStack:
+ mov eax, PCR[PcTeb]
+ mov [eax].CURstk,edi
+ mov [eax+edi].lManLo,esi
+ mov [eax+edi].lManHi,ebx
+ mov [eax+edi].ExpSgn,ecx
+ mov ebx, eax ; (ebx) = PcTeb
+ ret
+
+Unsupported:
+ mov ebx, PCR[PcTeb]
+ or [ebx].CURerr,Invalid ; (assume it's masked?)
+ mov [ebx+edi].lManLo,0
+ mov [ebx+edi].lManHi,0C0000000H
+ mov [ebx+edi].ExpSgn,TexpMax shl 16 + bSign shl 8 + bTAG_NAN
+ mov [ebx].CURstk,edi ;Update top of stack
+ ret
+
+_TEXT ENDS
+END
diff --git a/private/ntos/ke/i386/exceptn.c b/private/ntos/ke/i386/exceptn.c
new file mode 100644
index 000000000..7c700e591
--- /dev/null
+++ b/private/ntos/ke/i386/exceptn.c
@@ -0,0 +1,1270 @@
+/*++
+
+Copyright (c) 1989 Microsoft Corporation
+
+Module Name:
+
+ exceptn.c
+
+Abstract:
+
+ This module implement the code necessary to dispatch expections to the
+ proper mode and invoke the exception dispatcher.
+
+Author:
+
+ David N. Cutler (davec) 30-Apr-1989
+
+Environment:
+
+ Kernel mode only.
+
+Revision History:
+
+ 14-Feb-1990 shielint
+
+ Modified for NT386 interrupt manager
+
+ 6-April-1990 bryanwi
+
+ Fix non-canonical stack case for 386.
+
+--*/
+
+#include "ki.h"
+
+extern UCHAR VdmUserCr0MapIn[];
+
+VOID
+Ki386AdjustEsp0(
+ IN PKTRAP_FRAME TrapFrame
+ );
+
+BOOLEAN
+KiEm87StateToNpxFrame(
+ OUT PFLOATING_SAVE_AREA NpxFrmae
+ );
+
+BOOLEAN
+KiNpxFrameToEm87State(
+ IN PFLOATING_SAVE_AREA NpxFrmae
+ );
+
+
+ULONG
+KiEspFromTrapFrame(
+ IN PKTRAP_FRAME TrapFrame
+ )
+
+/*++
+
+Routine Description:
+
+ This routine fetches the correct esp from a trapframe, accounting
+ for whether the frame is a user or kernel mode frame, and whether
+ it has been edited.
+
+Arguments:
+
+ TrapFrame - Supplies a pointer to a trap frame from which volatile context
+ should be copied into the context record.
+
+Return Value:
+
+ Value of Esp.
+
+--*/
+
+{
+ if (((TrapFrame->SegCs & MODE_MASK) != KernelMode) ||
+ (TrapFrame->EFlags & EFLAGS_V86_MASK)) {
+
+ // User mode frame, real value of Esp is always in HardwareEsp.
+
+ return TrapFrame->HardwareEsp;
+
+ } else {
+
+ if ((TrapFrame->SegCs & FRAME_EDITED) == 0) {
+
+ // Kernel mode frame which has had esp edited,
+ // value of Esp is in TempEsp.
+
+ return TrapFrame->TempEsp;
+
+ } else {
+
+ // Kernel mode frame has has not had esp edited, compute esp.
+
+ return (ULONG)&TrapFrame->HardwareEsp;
+ }
+ }
+}
+
+VOID
+KiEspToTrapFrame(
+ IN PKTRAP_FRAME TrapFrame,
+ IN ULONG Esp
+ )
+
+/*++
+
+Routine Description:
+
+ This routine sets the specified value Esp into the trap frame,
+ accounting for whether the frame is a user or kernel mode frame,
+ and whether it has been edited before.
+
+Arguments:
+
+ TrapFrame - Supplies a pointer to a trap frame from which volatile context
+ should be copied into the context record.
+
+ Esp - New value for Esp.
+
+Return Value:
+
+ None.
+
+--*/
+{
+ ULONG OldEsp;
+
+ OldEsp = KiEspFromTrapFrame(TrapFrame);
+
+ if (((TrapFrame->SegCs & MODE_MASK) != KernelMode) ||
+ (TrapFrame->EFlags & EFLAGS_V86_MASK)) {
+
+ //
+ // User mode trap frame
+ //
+
+ TrapFrame->HardwareEsp = Esp;
+
+ } else {
+
+ //
+ // Kernel mode esp can't be lowered or iret emulation will fail
+ //
+
+ if (Esp < OldEsp)
+ KeBugCheck(SET_OF_INVALID_CONTEXT);
+
+ //
+ // Edit frame, setting edit marker as needed.
+ //
+
+ if ((TrapFrame->SegCs & FRAME_EDITED) == 0) {
+
+ // Kernel frame that has already been edited,
+ // store value in TempEsp.
+
+ TrapFrame->TempEsp = Esp;
+
+ } else {
+
+ // Kernel frame for which Esp is being edited first time.
+ // Save real SegCs, set marked in SegCs, save Esp value.
+
+ if (OldEsp != Esp) {
+ TrapFrame->TempSegCs = TrapFrame->SegCs;
+ TrapFrame->SegCs = TrapFrame->SegCs & ~FRAME_EDITED;
+ TrapFrame->TempEsp = Esp;
+ }
+ }
+ }
+}
+
+ULONG
+KiSegSsFromTrapFrame(
+ IN PKTRAP_FRAME TrapFrame
+ )
+
+/*++
+
+Routine Description:
+
+ This routine fetches the correct ss from a trapframe, accounting
+ for whether the frame is a user or kernel mode frame.
+
+Arguments:
+
+ TrapFrame - Supplies a pointer to a trap frame from which volatile context
+ should be copied into the context record.
+
+Return Value:
+
+ Value of SegSs.
+
+--*/
+
+{
+ if (TrapFrame->EFlags & EFLAGS_V86_MASK){
+ return TrapFrame->HardwareSegSs;
+ } else if ((TrapFrame->SegCs & MODE_MASK) != KernelMode) {
+
+ //
+ // It's user mode. The HardwareSegSs contains R3 data selector.
+ //
+
+ return TrapFrame->HardwareSegSs | RPL_MASK;
+ } else {
+ return KGDT_R0_DATA;
+ }
+}
+
+VOID
+KiSegSsToTrapFrame(
+ IN PKTRAP_FRAME TrapFrame,
+ IN ULONG SegSs
+ )
+
+/*++
+
+Routine Description:
+
+ It turns out that in a flat system there are only two legal values
+ for SS. Therefore, this procedure forces the appropriate one
+ of those values to be used. The legal SS value is a function of
+ which CS value is already set.
+
+Arguments:
+
+ TrapFrame - Supplies a pointer to a trap frame from which volatile context
+ should be copied into the context record.
+
+ SegSs - value of SS caller would like to set.
+
+Return Value:
+
+ Nothing.
+
+--*/
+
+{
+ SegSs &= SEGMENT_MASK; // Throw away the high order trash bits
+
+ if (TrapFrame->EFlags & EFLAGS_V86_MASK) {
+ TrapFrame->HardwareSegSs = SegSs;
+ } else if ((TrapFrame->SegCs & MODE_MASK) == UserMode) {
+
+ //
+ // If user mode, we simply put SegSs to trapfram. If the SegSs
+ // is a bogus value. The trap0d handler will be able to detect
+ // this and handle it appropriately.
+ //
+
+ TrapFrame->HardwareSegSs = SegSs | RPL_MASK;
+ }
+
+ //
+ // else {
+ // The frame is a kernel mode frame, which does not have
+ // a place to store SS. Therefore, do nothing.
+ //
+}
+
+VOID
+KeContextFromKframes (
+ IN PKTRAP_FRAME TrapFrame,
+ IN PKEXCEPTION_FRAME ExceptionFrame,
+ IN OUT PCONTEXT ContextFrame
+ )
+
+/*++
+
+Routine Description:
+
+ This routine moves the selected contents of the specified trap and exception frames
+ frames into the specified context frame according to the specified context
+ flags.
+
+Arguments:
+
+ TrapFrame - Supplies a pointer to a trap frame from which volatile context
+ should be copied into the context record.
+
+ ExceptionFrame - Supplies a pointer to an exception frame from which context
+ should be copied into the context record. This argument is ignored since
+ there is no exception frame on NT386.
+
+ ContextFrame - Supplies a pointer to the context frame that receives the
+ context copied from the trap and exception frames.
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+
+ PFLOATING_SAVE_AREA NpxFrame;
+ BOOLEAN StateSaved;
+ ULONG i;
+
+ UNREFERENCED_PARAMETER( ExceptionFrame );
+
+ //
+ // Set control information if specified.
+ //
+
+ if ((ContextFrame->ContextFlags & CONTEXT_CONTROL) == CONTEXT_CONTROL) {
+
+ //
+ // Set registers ebp, eip, cs, eflag, esp and ss.
+ //
+
+ ContextFrame->Ebp = TrapFrame->Ebp;
+ ContextFrame->Eip = TrapFrame->Eip;
+
+ if (((TrapFrame->SegCs & FRAME_EDITED) == 0) &&
+ ((TrapFrame->EFlags & EFLAGS_V86_MASK) == 0)) {
+ ContextFrame->SegCs = TrapFrame->TempSegCs & SEGMENT_MASK;
+ } else {
+ ContextFrame->SegCs = TrapFrame->SegCs & SEGMENT_MASK;
+ }
+ ContextFrame->EFlags = TrapFrame->EFlags;
+ ContextFrame->SegSs = KiSegSsFromTrapFrame(TrapFrame);
+ ContextFrame->Esp = KiEspFromTrapFrame(TrapFrame);
+ }
+
+ //
+ // Set segment register contents if specified.
+ //
+
+ if ((ContextFrame->ContextFlags & CONTEXT_SEGMENTS) == CONTEXT_SEGMENTS) {
+
+ //
+ // Set segment registers gs, fs, es, ds.
+ //
+ // These values are junk most of the time, but useful
+ // for debugging under certain conditions. Therefore,
+ // we report whatever was in the frame.
+ //
+ if (TrapFrame->EFlags & EFLAGS_V86_MASK) {
+ ContextFrame->SegGs = TrapFrame->V86Gs & SEGMENT_MASK;
+ ContextFrame->SegFs = TrapFrame->V86Fs & SEGMENT_MASK;
+ ContextFrame->SegEs = TrapFrame->V86Es & SEGMENT_MASK;
+ ContextFrame->SegDs = TrapFrame->V86Ds & SEGMENT_MASK;
+ }
+ else {
+ if (TrapFrame->SegCs == KGDT_R0_CODE) {
+ //
+ // Trap frames created from R0_CODE traps do not save
+ // the following selectors. Set them in the frame now.
+ //
+
+ TrapFrame->SegGs = 0;
+ TrapFrame->SegFs = KGDT_R0_PCR;
+ TrapFrame->SegEs = KGDT_R3_DATA | RPL_MASK;
+ TrapFrame->SegDs = KGDT_R3_DATA | RPL_MASK;
+ }
+
+ ContextFrame->SegGs = TrapFrame->SegGs & SEGMENT_MASK;
+ ContextFrame->SegFs = TrapFrame->SegFs & SEGMENT_MASK;
+ ContextFrame->SegEs = TrapFrame->SegEs & SEGMENT_MASK;
+ ContextFrame->SegDs = TrapFrame->SegDs & SEGMENT_MASK;
+ }
+
+ }
+
+ //
+ // Set integer register contents if specified.
+ //
+
+ if ((ContextFrame->ContextFlags & CONTEXT_INTEGER) == CONTEXT_INTEGER) {
+
+ //
+ // Set integer registers edi, esi, ebx, edx, ecx, eax
+ //
+
+ ContextFrame->Edi = TrapFrame->Edi;
+ ContextFrame->Esi = TrapFrame->Esi;
+ ContextFrame->Ebx = TrapFrame->Ebx;
+ ContextFrame->Ecx = TrapFrame->Ecx;
+ ContextFrame->Edx = TrapFrame->Edx;
+ ContextFrame->Eax = TrapFrame->Eax;
+ }
+
+ //
+ // Fetch floating register contents if requested, and type of target
+ // is user. (system frames have no fp state, so ignore request)
+ //
+
+ if ( ((ContextFrame->ContextFlags & CONTEXT_FLOATING_POINT) ==
+ CONTEXT_FLOATING_POINT) &&
+ ((TrapFrame->SegCs & MODE_MASK) == UserMode)) {
+
+ //
+ // This is the base TrapFrame, and the NpxFrame is on the base
+ // of the kernel stack, just above it in memory.
+ //
+
+ NpxFrame = (PFLOATING_SAVE_AREA)(TrapFrame + 1);
+
+ if (KeI386NpxPresent) {
+
+ //
+ // Force the coprocessors state to the save area and copy it
+ // to the context frame.
+ //
+
+ KiFlushNPXState ();
+ ContextFrame->FloatSave.ControlWord = NpxFrame->ControlWord;
+ ContextFrame->FloatSave.StatusWord = NpxFrame->StatusWord;
+ ContextFrame->FloatSave.TagWord = NpxFrame->TagWord;
+ ContextFrame->FloatSave.ErrorOffset = NpxFrame->ErrorOffset;
+ ContextFrame->FloatSave.ErrorSelector = NpxFrame->ErrorSelector;
+ ContextFrame->FloatSave.DataOffset = NpxFrame->DataOffset;
+ ContextFrame->FloatSave.DataSelector = NpxFrame->DataSelector;
+ ContextFrame->FloatSave.Cr0NpxState = NpxFrame->Cr0NpxState;
+ for (i = 0; i < SIZE_OF_80387_REGISTERS; i++) {
+ ContextFrame->FloatSave.RegisterArea[i] = NpxFrame->RegisterArea[i];
+ }
+ } else {
+
+ //
+ // The 80387 is being emulated by the R3 emulator.
+ // ** The only time the Npx state is ever obtained or set is
+ // ** for userlevel handling. Current Irql must be 0 or 1.
+ // Go slurp the emulator's R3 data and generate the
+ // floating point context
+ //
+
+ StateSaved = KiEm87StateToNpxFrame(&ContextFrame->FloatSave);
+ if (StateSaved) {
+ ContextFrame->FloatSave.Cr0NpxState = NpxFrame->Cr0NpxState;
+ } else {
+
+ //
+ // The floatingpoint state can not be determined.
+ // Remove the floatingpoint flag from the context frame flags.
+ //
+
+ ContextFrame->ContextFlags &= (~CONTEXT_FLOATING_POINT) | CONTEXT_i386;
+ }
+ }
+ }
+
+ //
+ // Fetch Dr register contents if requested. Values may be trash.
+ //
+
+ if ((ContextFrame->ContextFlags & CONTEXT_DEBUG_REGISTERS) ==
+ CONTEXT_DEBUG_REGISTERS) {
+
+ ContextFrame->Dr0 = TrapFrame->Dr0;
+ ContextFrame->Dr1 = TrapFrame->Dr1;
+ ContextFrame->Dr2 = TrapFrame->Dr2;
+ ContextFrame->Dr3 = TrapFrame->Dr3;
+ ContextFrame->Dr6 = TrapFrame->Dr6;
+
+ //
+ // If it's a user mode frame, and the thread doesn't have DRs set,
+ // and we just return the trash in the frame, we risk accidentally
+ // making the thread active with trash values on a set. Therefore,
+ // Dr7 must be set to 0 if we get a non-active user mode frame.
+ //
+
+ if ((((TrapFrame->SegCs & MODE_MASK) != KernelMode) ||
+ ((TrapFrame->EFlags & EFLAGS_V86_MASK) != 0)) &&
+ (KeGetCurrentThread()->DebugActive == TRUE)) {
+
+ ContextFrame->Dr7 = TrapFrame->Dr7;
+
+ } else {
+
+ ContextFrame->Dr7 = 0L;
+
+ }
+ }
+
+}
+
+VOID
+KeContextToKframes (
+ IN OUT PKTRAP_FRAME TrapFrame,
+ IN OUT PKEXCEPTION_FRAME ExceptionFrame,
+ IN PCONTEXT ContextFrame,
+ IN ULONG ContextFlags,
+ IN KPROCESSOR_MODE PreviousMode
+ )
+
+/*++
+
+Routine Description:
+
+ This routine moves the selected contents of the specified context frame into
+ the specified trap and exception frames according to the specified context
+ flags.
+
+Arguments:
+
+ TrapFrame - Supplies a pointer to a trap frame that receives the volatile
+ context from the context record.
+
+ ExceptionFrame - Supplies a pointer to an exception frame that receives
+ the nonvolatile context from the context record. This argument is
+ ignored since there is no exception frame on NT386.
+
+ ContextFrame - Supplies a pointer to a context frame that contains the
+ context that is to be copied into the trap and exception frames.
+
+ ContextFlags - Supplies the set of flags that specify which parts of the
+ context frame are to be copied into the trap and exception frames.
+
+ PreviousMode - Supplies the processor mode for which the trap and exception
+ frames are being built.
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+
+ PFLOATING_SAVE_AREA NpxFrame;
+ ULONG i;
+ BOOLEAN StateSaved;
+ BOOLEAN ModeChanged;
+#if DBG
+ PKPCR Pcr;
+ KIRQL OldIrql;
+#endif
+
+ UNREFERENCED_PARAMETER( ExceptionFrame );
+
+ //
+ // Set control information if specified.
+ //
+
+ if ((ContextFlags & CONTEXT_CONTROL) == CONTEXT_CONTROL) {
+
+ if ((ContextFrame->EFlags & EFLAGS_V86_MASK) !=
+ (TrapFrame->EFlags & EFLAGS_V86_MASK)) {
+ ModeChanged = TRUE;
+ } else {
+ ModeChanged = FALSE;
+ }
+
+
+ //
+ // Set registers eflag, ebp, eip, cs, esp and ss.
+ // Eflags is set first, so that the auxilliary routines
+ // can check the v86 bit to determine as well as cs, to
+ // determine if the frame is kernel or user mode. (v86 mode cs
+ // can have any value)
+ //
+
+ TrapFrame->EFlags = SANITIZE_FLAGS(ContextFrame->EFlags, PreviousMode);
+ TrapFrame->Ebp = ContextFrame->Ebp;
+ TrapFrame->Eip = ContextFrame->Eip;
+ if (TrapFrame->EFlags & EFLAGS_V86_MASK) {
+ TrapFrame->SegCs = ContextFrame->SegCs;
+ } else {
+ TrapFrame->SegCs = SANITIZE_SEG(ContextFrame->SegCs, PreviousMode);
+ if (PreviousMode != KernelMode && TrapFrame->SegCs < 8) {
+
+ //
+ // If user mode and the selector value is less than 8, we
+ // know it is an invalid selector. Set it to flat user
+ // mode selector. Another reason we need to check for this
+ // is that any cs value less than 8 causes our exit kernel
+ // macro to treat its exit trap fram as an edited frame.
+ //
+
+ TrapFrame->SegCs = KGDT_R3_CODE | RPL_MASK;
+ }
+ }
+ KiSegSsToTrapFrame(TrapFrame, ContextFrame->SegSs);
+ KiEspToTrapFrame(TrapFrame, ContextFrame->Esp);
+ if (ModeChanged) {
+ Ki386AdjustEsp0(TrapFrame); // realign esp0 in the tss
+ }
+ }
+
+ //
+ // Set segment register contents if specified.
+ //
+
+ if ((ContextFlags & CONTEXT_SEGMENTS) == CONTEXT_SEGMENTS) {
+
+ //
+ // Set segment registers gs, fs, es, ds.
+ //
+
+ //
+ // There's only one legal value for DS and ES, so simply set it.
+ // This allows KeContextFromKframes to report the real values in
+ // the frame. (which are junk most of the time, but sometimes useful
+ // for debugging.)
+ // Only 2 legal values for FS, let either one be set.
+ // Force GS to be 0 to deal with entry via SysCall and exit
+ // via exception.
+ //
+ // For V86 mode, the FS, GS, DS, and ES registers must be properly
+ // set from the supplied context.
+ //
+
+ if (TrapFrame->EFlags & EFLAGS_V86_MASK) {
+ TrapFrame->V86Fs = ContextFrame->SegFs;
+ TrapFrame->V86Es = ContextFrame->SegEs;
+ TrapFrame->V86Ds = ContextFrame->SegDs;
+ TrapFrame->V86Gs = ContextFrame->SegGs;
+ } else if (((TrapFrame->SegCs & MODE_MASK) == KernelMode)) {
+
+ //
+ // set up the standard selectors
+ //
+
+ TrapFrame->SegFs = SANITIZE_SEG(ContextFrame->SegFs, PreviousMode);
+ TrapFrame->SegEs = KGDT_R3_DATA | RPL_MASK;
+ TrapFrame->SegDs = KGDT_R3_DATA | RPL_MASK;
+ TrapFrame->SegGs = 0;
+ } else {
+
+ //
+ // If user mode, we simply return whatever left in context frame
+ // and let trap 0d handle it (if later we trap while popping the
+ // trap frame.) V86 mode also get handled here.
+ //
+
+ TrapFrame->SegFs = ContextFrame->SegFs;
+ TrapFrame->SegEs = ContextFrame->SegEs;
+ TrapFrame->SegDs = ContextFrame->SegDs;
+ if (TrapFrame->SegCs == (KGDT_R3_CODE | RPL_MASK)) {
+ TrapFrame->SegGs = 0;
+ } else {
+ TrapFrame->SegGs = ContextFrame->SegGs;
+ }
+ }
+ }
+ //
+ // Set integer registers contents if specified.
+ //
+
+ if ((ContextFlags & CONTEXT_INTEGER) == CONTEXT_INTEGER) {
+
+ //
+ // Set integer registers edi, esi, ebx, edx, ecx, eax.
+ //
+ // Can NOT call RtlMoveMemory here because the regs aren't
+ // contiguous in pusha frame, and we don't want to export
+ // bits of junk into context record.
+ //
+
+ TrapFrame->Edi = ContextFrame->Edi;
+ TrapFrame->Esi = ContextFrame->Esi;
+ TrapFrame->Ebx = ContextFrame->Ebx;
+ TrapFrame->Ecx = ContextFrame->Ecx;
+ TrapFrame->Edx = ContextFrame->Edx;
+ TrapFrame->Eax = ContextFrame->Eax;
+
+ }
+
+ //
+ // Set floating register contents if requested, and type of target
+ // is user. (system frames have no fp state, so ignore request)
+ //
+
+ if (((ContextFlags & CONTEXT_FLOATING_POINT) == CONTEXT_FLOATING_POINT) &&
+ ((TrapFrame->SegCs & MODE_MASK) == UserMode)) {
+
+ //
+ // This is the base TrapFrame, and the NpxFrame is on the base
+ // of the kernel stack, just above it in memory.
+ //
+
+ NpxFrame = (PFLOATING_SAVE_AREA)(TrapFrame + 1);
+
+ if (KeI386NpxPresent) {
+
+ //
+ // Set coprocessor stack, control and status registers
+ //
+
+ KiFlushNPXState ();
+ NpxFrame->ControlWord = ContextFrame->FloatSave.ControlWord;
+ NpxFrame->StatusWord = ContextFrame->FloatSave.StatusWord;
+ NpxFrame->TagWord = ContextFrame->FloatSave.TagWord;
+ NpxFrame->ErrorOffset = ContextFrame->FloatSave.ErrorOffset;
+ NpxFrame->ErrorSelector = ContextFrame->FloatSave.ErrorSelector;
+ NpxFrame->DataOffset = ContextFrame->FloatSave.DataOffset;
+ NpxFrame->DataSelector = ContextFrame->FloatSave.DataSelector;
+
+ //
+ // Make sure only valid floating state bits are moved to Cr0NpxState.
+ //
+
+ NpxFrame->Cr0NpxState &= ~(CR0_EM | CR0_MP | CR0_TS);
+
+ //
+ // Only let VDMs turn on the EM bit. The kernel can't do
+ // anything for FLAT apps
+ //
+ if (KeGetCurrentThread()->ApcState.Process->VdmFlag & 0xf) {
+ NpxFrame->Cr0NpxState |= ContextFrame->FloatSave.Cr0NpxState &
+ (CR0_EM | CR0_MP);
+ }
+
+ for (i = 0; i < SIZE_OF_80387_REGISTERS; i++) {
+ NpxFrame->RegisterArea[i] = ContextFrame->FloatSave.RegisterArea[i];
+ }
+
+ } else {
+
+ if (KeGetCurrentThread()->ApcState.Process->VdmFlag & 0xf) {
+
+ //
+ // This is a special hack to allow SetContext for VDMs to
+ // turn on/off it's CR0_EM bit.
+ //
+
+ NpxFrame->Cr0NpxState &= ~(CR0_MP | CR0_TS | CR0_EM | CR0_PE);
+ NpxFrame->Cr0NpxState |=
+ VdmUserCr0MapIn[ContextFrame->FloatSave.Cr0NpxState & (CR0_EM | CR0_MP)];
+
+ } else {
+
+ //
+ // The 80387 is being emulated by the R3 emulator.
+ // ** The only time the Npx state is ever obtained or set is
+ // ** for userlevel handling. Current Irql must be 0 or 1.
+ // And the context being set must be for the current thread.
+ // Go smash the floatingpoint context into the R3 emulator's
+ // data area.
+ //
+#if DBG
+ OldIrql = KeRaiseIrqlToSynchLevel();
+ Pcr = KeGetPcr();
+ ASSERT (Pcr->Prcb->CurrentThread->Teb == Pcr->NtTib.Self);
+ KeLowerIrql (OldIrql);
+#endif
+
+ StateSaved = KiNpxFrameToEm87State(&ContextFrame->FloatSave);
+ if (StateSaved) {
+
+ //
+ // Make sure only valid floating state bits are moved to
+ // Cr0NpxState. Since we are emulating, don't allow
+ // resetting CR0_EM.
+ //
+
+ NpxFrame->Cr0NpxState &= ~(CR0_MP | CR0_TS);
+ NpxFrame->Cr0NpxState |=
+ ContextFrame->FloatSave.Cr0NpxState & CR0_MP;
+ }
+ }
+ }
+ }
+
+ //
+ // Set debug register state if specified. If previous mode is user
+ // mode (i.e. it's a user frame we're setting) and if effect will be to
+ // cause at least one of the LE (local enable) bits in Dr7 to be
+ // set (i.e. at least one of Dr0,1,2,3 are active) then set DebugActive
+ // in the thread object to true. Otherwise set it to false.
+ //
+
+ if ((ContextFlags & CONTEXT_DEBUG_REGISTERS) == CONTEXT_DEBUG_REGISTERS) {
+
+ TrapFrame->Dr0 = SANITIZE_DRADDR(ContextFrame->Dr0, PreviousMode);
+ TrapFrame->Dr1 = SANITIZE_DRADDR(ContextFrame->Dr1, PreviousMode);
+ TrapFrame->Dr2 = SANITIZE_DRADDR(ContextFrame->Dr2, PreviousMode);
+ TrapFrame->Dr3 = SANITIZE_DRADDR(ContextFrame->Dr3, PreviousMode);
+ TrapFrame->Dr6 = SANITIZE_DR6(ContextFrame->Dr6, PreviousMode);
+ TrapFrame->Dr7 = SANITIZE_DR7(ContextFrame->Dr7, PreviousMode);
+
+ if (PreviousMode != KernelMode) {
+ KeGetPcr()->DebugActive = KeGetCurrentThread()->DebugActive =
+ (BOOLEAN)((ContextFrame->Dr7 & DR7_ACTIVE) != 0);
+ }
+ }
+
+ //
+ // If thread is supposed to have IOPL, then force it on in eflags
+ //
+ if (KeGetCurrentThread()->Iopl) {
+ TrapFrame->EFlags |= (EFLAGS_IOPL_MASK & -1); // IOPL = 3
+ }
+
+ return;
+}
+
+VOID
+KiDispatchException (
+ IN PEXCEPTION_RECORD ExceptionRecord,
+ IN PKEXCEPTION_FRAME ExceptionFrame,
+ IN PKTRAP_FRAME TrapFrame,
+ IN KPROCESSOR_MODE PreviousMode,
+ IN BOOLEAN FirstChance
+ )
+
+/*++
+
+Routine Description:
+
+ This function is called to dispatch an exception to the proper mode and
+ to cause the exception dispatcher to be called. If the previous mode is
+ kernel, then the exception dispatcher is called directly to process the
+ exception. Otherwise the exception record, exception frame, and trap
+ frame contents are copied to the user mode stack. The contents of the
+ exception frame and trap are then modified such that when control is
+ returned, execution will commense in user mode in a routine which will
+ call the exception dispatcher.
+
+Arguments:
+
+ ExceptionRecord - Supplies a pointer to an exception record.
+
+ ExceptionFrame - Supplies a pointer to an exception frame. For NT386,
+ this should be NULL.
+
+ TrapFrame - Supplies a pointer to a trap frame.
+
+ PreviousMode - Supplies the previous processor mode.
+
+ FirstChance - Supplies a boolean value that specifies whether this is
+ the first (TRUE) or second (FALSE) chance for the exception.
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ CONTEXT ContextFrame;
+ EXCEPTION_RECORD ExceptionRecord1, ExceptionRecord2;
+ LONG Length;
+ ULONG UserStack1;
+ ULONG UserStack2;
+
+ //
+ // Move machine state from trap and exception frames to a context frame,
+ // and increment the number of exceptions dispatched.
+ //
+
+ KeGetCurrentPrcb()->KeExceptionDispatchCount += 1;
+ ContextFrame.ContextFlags = CONTEXT_FULL | CONTEXT_DEBUG_REGISTERS;
+ if (PreviousMode == UserMode) {
+ //
+ // For usermode exceptions always try to dispatch the floating
+ // point state. This allows expection handlers & debuggers to
+ // examine/edit the npx context if required. Plus it allows
+ // exception handlers to use fp instructions without detroying
+ // the npx state at the time of the exception.
+ //
+ // Note: If there's no 80387, ContextTo/FromKFrames will use the
+ // emulator's current state. If the emulator can not give the
+ // current state, then the context_floating_point bit will be
+ // turned off by ContextFromKFrames.
+ //
+
+ ContextFrame.ContextFlags |= CONTEXT_FLOATING_POINT;
+ }
+
+ KeContextFromKframes(TrapFrame, ExceptionFrame, &ContextFrame);
+
+ //
+ // if it is BREAK_POINT exception, we subtract 1 from EIP and report
+ // the updated EIP to user. This is because Cruiser requires EIP
+ // points to the int 3 instruction (not the instruction following int 3).
+ // In this case, BreakPoint exception is fatal. Otherwise we will step
+ // on the int 3 over and over again, if user does not handle it
+ //
+ // if the BREAK_POINT occured in V86 mode, the debugger running in the
+ // VDM will expect CS:EIP to point after the exception (the way the
+ // processor left it. this is also true for protected mode dos
+ // app debuggers. We will need a way to detect this.
+ //
+ //
+
+// if ((ExceptionRecord->ExceptionCode == STATUS_BREAKPOINT) &&
+// !(ContextFrame.EFlags & EFLAGS_V86_MASK)) {
+
+ switch (ExceptionRecord->ExceptionCode) {
+ case STATUS_BREAKPOINT:
+ ContextFrame.Eip--;
+ break;
+ }
+
+ //
+ // Select the method of handling the exception based on the previous mode.
+ //
+
+ ASSERT ((
+ !((PreviousMode == KernelMode) &&
+ (ContextFrame.EFlags & EFLAGS_V86_MASK))
+ ));
+
+ if (PreviousMode == KernelMode) {
+
+ //
+ // Previous mode was kernel.
+ //
+ // If the kernel debugger is active, then give the kernel debugger the
+ // first chance to handle the exception. If the kernel debugger handles
+ // the exception, then continue execution. Else attempt to dispatch the
+ // exception to a frame based handler. If a frame based handler handles
+ // the exception, then continue execution.
+ //
+ // If a frame based handler does not handle the exception,
+ // give the kernel debugger a second chance, if it's present.
+ //
+ // If the exception is still unhandled, call KeBugCheck().
+ //
+
+ if (FirstChance == TRUE) {
+
+ if ((KiDebugRoutine != NULL) &&
+ (((KiDebugRoutine) (TrapFrame,
+ ExceptionFrame,
+ ExceptionRecord,
+ &ContextFrame,
+ PreviousMode,
+ FALSE)) != FALSE)) {
+
+ goto Handled1;
+ }
+
+ // Kernel debugger didn't handle exception.
+
+ if (RtlDispatchException(ExceptionRecord, &ContextFrame) == TRUE) {
+ goto Handled1;
+ }
+ }
+
+ //
+ // This is the second chance to handle the exception.
+ //
+
+ if ((KiDebugRoutine != NULL) &&
+ (((KiDebugRoutine) (TrapFrame,
+ ExceptionFrame,
+ ExceptionRecord,
+ &ContextFrame,
+ PreviousMode,
+ TRUE)) != FALSE)) {
+
+ goto Handled1;
+ }
+
+ KeBugCheckEx(
+ KMODE_EXCEPTION_NOT_HANDLED,
+ ExceptionRecord->ExceptionCode,
+ (ULONG)ExceptionRecord->ExceptionAddress,
+ ExceptionRecord->ExceptionInformation[0],
+ ExceptionRecord->ExceptionInformation[1]
+ );
+
+ } else {
+
+ //
+ // Previous mode was user.
+ //
+ // If this is the first chance and the current process has a debugger
+ // port, then send a message to the debugger port and wait for a reply.
+ // If the debugger handles the exception, then continue execution. Else
+ // transfer the exception information to the user stack, transition to
+ // user mode, and attempt to dispatch the exception to a frame based
+ // handler. If a frame based handler handles the exception, then continue
+ // execution with the continue system service. Else execute the
+ // NtRaiseException system service with FirstChance == FALSE, which
+ // will call this routine a second time to process the exception.
+ //
+ // If this is the second chance and the current process has a debugger
+ // port, then send a message to the debugger port and wait for a reply.
+ // If the debugger handles the exception, then continue execution. Else
+ // if the current process has a subsystem port, then send a message to
+ // the subsystem port and wait for a reply. If the subsystem handles the
+ // exception, then continue execution. Else terminate the thread.
+ //
+
+
+ if (FirstChance == TRUE) {
+
+ //
+ // This is the first chance to handle the exception.
+ //
+
+ if ( PsGetCurrentProcess()->DebugPort ) {
+ if ( (KiDebugRoutine != NULL) &&
+ KdIsThisAKdTrap(ExceptionRecord, &ContextFrame, UserMode) ) {
+
+ if ((((KiDebugRoutine) (TrapFrame,
+ ExceptionFrame,
+ ExceptionRecord,
+ &ContextFrame,
+ PreviousMode,
+ FALSE)) != FALSE)) {
+
+ goto Handled1;
+ }
+ }
+ } else {
+ if ((KiDebugRoutine != NULL) &&
+ (((KiDebugRoutine) (TrapFrame,
+ ExceptionFrame,
+ ExceptionRecord,
+ &ContextFrame,
+ PreviousMode,
+ FALSE)) != FALSE)) {
+
+ goto Handled1;
+ }
+ }
+
+ if (DbgkForwardException(ExceptionRecord, TRUE, FALSE)) {
+ goto Handled2;
+ }
+
+ //
+ // Transfer exception information to the user stack, transition
+ // to user mode, and attempt to dispatch the exception to a frame
+ // based handler.
+
+ repeat:
+ try {
+
+ //
+ // If the SS segment is not 32 bit flat, there is no point
+ // to dispatch exception to frame based exception handler.
+ //
+
+ if (TrapFrame->HardwareSegSs != (KGDT_R3_DATA | RPL_MASK) ||
+ TrapFrame->EFlags & EFLAGS_V86_MASK ) {
+ ExceptionRecord2.ExceptionCode = STATUS_ACCESS_VIOLATION;
+ ExceptionRecord2.ExceptionFlags = 0;
+ ExceptionRecord2.NumberParameters = 0;
+ ExRaiseException(&ExceptionRecord2);
+ }
+
+ //
+ // Compute length of context record and new aligned user stack
+ // pointer.
+ //
+
+ Length = (sizeof(CONTEXT) + CONTEXT_ROUND) & ~CONTEXT_ROUND;
+ UserStack1 = (ContextFrame.Esp & ~CONTEXT_ROUND) - Length;
+
+ //
+ // Probe user stack area for writeability and then transfer the
+ // context record to the user stack.
+ //
+
+ ProbeForWrite((PCHAR)UserStack1, Length, CONTEXT_ALIGN);
+ RtlMoveMemory((PULONG)UserStack1, &ContextFrame, sizeof(CONTEXT));
+
+ //
+ // Compute length of exception record and new aligned stack
+ // address.
+ //
+
+ Length = (sizeof(EXCEPTION_RECORD) - (EXCEPTION_MAXIMUM_PARAMETERS -
+ ExceptionRecord->NumberParameters) * sizeof(ULONG) +3) &
+ (~3);
+ UserStack2 = UserStack1 - Length;
+
+ //
+ // Probe user stack area for writeability and then transfer the
+ // context record to the user stack area.
+ // N.B. The probing length is Length+8 because there are two
+ // arguments need to be pushed to user stack later.
+ //
+
+ ProbeForWrite((PCHAR)(UserStack2 - 8), Length + 8, sizeof(ULONG));
+ RtlMoveMemory((PULONG)UserStack2, ExceptionRecord, Length);
+
+ //
+ // Push address of exception record, context record to the
+ // user stack. They are the two parameters required by
+ // _KiUserExceptionDispatch.
+ //
+
+ *(PULONG)(UserStack2 - sizeof(ULONG)) = UserStack1;
+ *(PULONG)(UserStack2 - 2*sizeof(ULONG)) = UserStack2;
+
+ //
+ // Set new stack pointer to the trap frame.
+ //
+
+ KiSegSsToTrapFrame(TrapFrame, KGDT_R3_DATA);
+ KiEspToTrapFrame(TrapFrame, (UserStack2 - sizeof(ULONG)*2));
+
+ //
+ // Force correct R3 selectors into TrapFrame.
+ //
+
+ TrapFrame->SegCs = SANITIZE_SEG(KGDT_R3_CODE, PreviousMode);
+ TrapFrame->SegDs = SANITIZE_SEG(KGDT_R3_DATA, PreviousMode);
+ TrapFrame->SegEs = SANITIZE_SEG(KGDT_R3_DATA, PreviousMode);
+ TrapFrame->SegFs = SANITIZE_SEG(KGDT_R3_TEB, PreviousMode);
+ TrapFrame->SegGs = 0;
+
+ //
+ // Set the address of the exception routine that will call the
+ // exception dispatcher and then return to the trap handler.
+ // The trap handler will restore the exception and trap frame
+ // context and continue execution in the routine that will
+ // call the exception dispatcher.
+ //
+
+ TrapFrame->Eip = (ULONG)KeUserExceptionDispatcher;
+ return;
+
+ } except (KiCopyInformation(&ExceptionRecord1,
+ (GetExceptionInformation())->ExceptionRecord)) {
+
+ //
+ // If the exception is a stack overflow, then attempt
+ // to raise the stack overflow exception. Otherwise,
+ // the user's stack is not accessible, or is misaligned,
+ // and second chance processing is performed.
+ //
+
+ if (ExceptionRecord1.ExceptionCode == STATUS_STACK_OVERFLOW) {
+ ExceptionRecord1.ExceptionAddress = ExceptionRecord->ExceptionAddress;
+ RtlMoveMemory((PVOID)ExceptionRecord,
+ &ExceptionRecord1, sizeof(EXCEPTION_RECORD));
+ goto repeat;
+ }
+ }
+ }
+
+ //
+ // This is the second chance to handle the exception.
+ //
+
+ if (DbgkForwardException(ExceptionRecord, TRUE, TRUE)) {
+ goto Handled2;
+ } else if (DbgkForwardException(ExceptionRecord, FALSE, TRUE)) {
+ goto Handled2;
+ } else {
+ ZwTerminateThread(NtCurrentThread(), ExceptionRecord->ExceptionCode);
+ KeBugCheckEx(
+ KMODE_EXCEPTION_NOT_HANDLED,
+ ExceptionRecord->ExceptionCode,
+ (ULONG)ExceptionRecord->ExceptionAddress,
+ ExceptionRecord->ExceptionInformation[0],
+ ExceptionRecord->ExceptionInformation[1]
+ );
+ }
+ }
+
+ //
+ // Move machine state from context frame to trap and exception frames and
+ // then return to continue execution with the restored state.
+ //
+
+Handled1:
+ KeContextToKframes(TrapFrame, ExceptionFrame, &ContextFrame,
+ ContextFrame.ContextFlags, PreviousMode);
+
+ //
+ // Exception was handled by the debugger or the associated subsystem
+ // and state was modified, if necessary, using the get state and set
+ // state capabilities. Therefore the context frame does not need to
+ // be transfered to the trap and exception frames.
+ //
+
+Handled2:
+ return;
+}
+
+ULONG
+KiCopyInformation (
+ IN OUT PEXCEPTION_RECORD ExceptionRecord1,
+ IN PEXCEPTION_RECORD ExceptionRecord2
+ )
+
+/*++
+
+Routine Description:
+
+ This function is called from an exception filter to copy the exception
+ information from one exception record to another when an exception occurs.
+
+Arguments:
+
+ ExceptionRecord1 - Supplies a pointer to the destination exception record.
+
+ ExceptionRecord2 - Supplies a pointer to the source exception record.
+
+Return Value:
+
+ A value of EXCEPTION_EXECUTE_HANDLER is returned as the function value.
+
+--*/
+
+{
+
+ //
+ // Copy one exception record to another and return value that causes
+ // an exception handler to be executed.
+ //
+
+ RtlMoveMemory((PVOID)ExceptionRecord1,
+ (PVOID)ExceptionRecord2,
+ sizeof(EXCEPTION_RECORD));
+
+ return EXCEPTION_EXECUTE_HANDLER;
+}
+
+
+NTSTATUS
+KeRaiseUserException(
+ IN NTSTATUS ExceptionCode
+ )
+
+/*++
+
+Routine Description:
+
+ This function causes an exception to be raised in the calling thread's user-mode
+ context. It does this by editing the trap frame the kernel was entered with to
+ point to trampoline code that raises the requested exception.
+
+Arguments:
+
+ ExceptionCode - Supplies the status value to be used as the exception
+ code for the exception that is to be raised.
+
+Return Value:
+
+ The status value that should be returned by the caller.
+
+--*/
+
+{
+ PKTHREAD Thread;
+ PKTRAP_FRAME TrapFrame;
+ PTEB Teb;
+ ULONG PreviousEip;
+
+ ASSERT(KeGetPreviousMode() == UserMode);
+
+ Thread = KeGetCurrentThread();
+ TrapFrame = Thread->TrapFrame;
+ Teb = (PTEB)Thread->Teb;
+
+ //
+ // In order to create the correct call stack, we return the previous
+ // EIP as the status code. The usermode trampoline code will push this
+ // onto the stack for use as the return address. The status code to
+ // be raised is passed in the TEB.
+ //
+
+ try {
+ Teb->ExceptionCode = ExceptionCode;
+ } except(EXCEPTION_EXECUTE_HANDLER) {
+ return(ExceptionCode);
+ }
+
+ PreviousEip = TrapFrame->Eip;
+ TrapFrame->Eip = KeRaiseUserExceptionDispatcher;
+
+ return((NTSTATUS)PreviousEip);
+}
diff --git a/private/ntos/ke/i386/flush.c b/private/ntos/ke/i386/flush.c
new file mode 100644
index 000000000..1d7f41d9b
--- /dev/null
+++ b/private/ntos/ke/i386/flush.c
@@ -0,0 +1,170 @@
+/*++
+
+Copyright (c) 1990 Microsoft Corporation
+
+Module Name:
+
+ flush.c
+
+Abstract:
+
+ This module implements i386 machine dependent kernel functions to flush
+ the data and instruction caches and to stall processor execution.
+
+Author:
+
+ David N. Cutler (davec) 26-Apr-1990
+
+Environment:
+
+ Kernel mode only.
+
+Revision History:
+
+--*/
+
+#include "ki.h"
+
+
+// i386 and i486 have transparent caches, so these routines are nooped
+// out in macros in i386.h.
+
+#if 0
+
+VOID
+KeSweepDcache (
+ IN BOOLEAN AllProcessors
+ )
+
+/*++
+
+Routine Description:
+
+ This function flushes the data cache on all processors that are currently
+ running threads which are children of the current process or flushes the
+ data cache on all processors in the host configuration.
+
+Arguments:
+
+ AllProcessors - Supplies a boolean value that determines which data
+ caches are flushed.
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+
+ HalSweepDcache();
+ return;
+}
+
+VOID
+KeSweepIcache (
+ IN BOOLEAN AllProcessors
+ )
+
+/*++
+
+Routine Description:
+
+ This function flushes the instruction cache on all processors that are
+ currently running threads which are children of the current process or
+ flushes the instruction cache on all processors in the host configuration.
+
+Arguments:
+
+ AllProcessors - Supplies a boolean value that determines which instruction
+ caches are flushed.
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+
+ HalSweepIcache();
+
+#if defined(R4000)
+
+ HalSweepDcache();
+
+#endif
+
+ return;
+}
+
+VOID
+KeSweepIcacheRange (
+ IN BOOLEAN AllProcessors,
+ IN PVOID BaseAddress,
+ IN ULONG Length
+ )
+
+/*++
+
+Routine Description:
+
+ This function flushes the an range of virtual addresses from the primary
+ instruction cache on all processors that are currently running threads
+ which are children of the current process or flushes the range of virtual
+ addresses from the primary instruction cache on all processors in the host
+ configuration.
+
+Arguments:
+
+ AllProcessors - Supplies a boolean value that determines which instruction
+ caches are flushed.
+
+ BaseAddress - Supplies a pointer to the base of the range that is flushed.
+
+ Length - Supplies the length of the range that is flushed if the base
+ address is specified.
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+
+ ULONG Offset;
+
+ //
+ // If the length of the range is greater than the size of the primary
+ // instruction cache, then set the length of the flush to the size of
+ // the primary instruction cache and set the ase address of zero.
+ //
+ // N.B. It is assumed that the size of the primary instruction and
+ // data caches are the same.
+ //
+
+ if (Length > PCR->FirstLevelIcacheSize) {
+ BaseAddress = (PVOID)0;
+ Length = PCR->FirstLevelIcacheSize;
+ }
+
+ //
+ // Flush the specified range of virtual addresses from the primary
+ // instruction cache.
+ //
+
+ Offset = (ULONG)BaseAddress & PCR->DcacheAlignment;
+ Length = (Offset + Length + PCR->DcacheAlignment) & ~PCR->DcacheAlignment;
+ BaseAddress = (PVOID)((ULONG)BaseAddress & ~PCR->DcacheAlignment);
+ HalSweepIcacheRange(BaseAddress, Length);
+
+#if defined(R4000)
+
+ HalSweepDcacheRange(BaseAddress, Length);
+
+#endif
+
+ return;
+}
+#endif
diff --git a/private/ntos/ke/i386/flushtb.c b/private/ntos/ke/i386/flushtb.c
new file mode 100644
index 000000000..77e8a5511
--- /dev/null
+++ b/private/ntos/ke/i386/flushtb.c
@@ -0,0 +1,565 @@
+/*++
+
+Copyright (c) 1989 Microsoft Corporation
+Copyright (c) 1990 Microsoft Corporation
+
+Module Name:
+
+ tbflush.c
+
+Abstract:
+
+ This module implements machine dependent functions to flush
+ the translation buffers in an Intel x86 system.
+
+ N.B. This module contains only MP versions of the TB flush routines.
+ The UP versions are macros in ke.h
+ KeFlushEntireTb remains a routine for the UP system since it is
+ exported from the kernel for backwards compatibility.
+
+Author:
+
+ David N. Cutler (davec) 13-May-1989
+
+Environment:
+
+ Kernel mode only.
+
+Revision History:
+
+ Shie-Lin Tzong (shielint) 30-Aug-1990
+ Implement MP version of KeFlushSingleTb and KeFlushEntireTb.
+
+--*/
+
+#include "ki.h"
+
+VOID
+KiFlushTargetEntireTb (
+ IN PKIPI_CONTEXT SignalDone,
+ IN PVOID Invalid,
+ IN PVOID Parameter2,
+ IN PVOID Parameter3
+ );
+
+VOID
+KiFlushTargetMultipleTb (
+ IN PKIPI_CONTEXT SignalDone,
+ IN PVOID Parameter1,
+ IN PVOID Parameter2,
+ IN PVOID Parameter3
+ );
+
+
+VOID
+KiFlushTargetSingleTb (
+ IN PKIPI_CONTEXT SignalDone,
+ IN PVOID Parameter1,
+ IN PVOID Parameter2,
+ IN PVOID Parameter3
+ );
+
+#if defined(NT_UP)
+#undef KeFlushEntireTb
+#endif
+
+
+VOID
+KeFlushEntireTb (
+ IN BOOLEAN Invalid,
+ IN BOOLEAN AllProcessors
+ )
+
+/*++
+
+Routine Description:
+
+ This function flushes the entire translation buffer (TB) on all processors
+ that are currently running threads which are child of the current process
+ or flushes the entire translation buffer on all processors in the host
+ configuration.
+
+Arguments:
+
+ Invalid - Supplies a boolean value that specifies the reason for flushing
+ the translation buffer.
+
+ AllProcessors - Supplies a boolean value that determines which translation
+ buffers are to be flushed.
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+
+ KIRQL OldIrql;
+ PKPRCB Prcb;
+ PKPROCESS Process;
+ KAFFINITY TargetProcessors;
+
+ //
+ // Compute the target set of processors, disable context switching,
+ // and send the flush entire parameters to the target processors,
+ // if any, for execution.
+ //
+
+#if defined(NT_UP)
+
+ OldIrql = KeRaiseIrqlToSynchLevel();
+
+#else
+
+ if (AllProcessors != FALSE) {
+ OldIrql = KeRaiseIrqlToSynchLevel();
+ Prcb = KeGetCurrentPrcb();
+ TargetProcessors = KeActiveProcessors;
+
+ } else {
+ KiLockContextSwap(&OldIrql);
+ Prcb = KeGetCurrentPrcb();
+ Process = Prcb->CurrentThread->ApcState.Process;
+ TargetProcessors = Process->ActiveProcessors;
+ }
+
+ TargetProcessors &= ~Prcb->SetMember;
+ if (TargetProcessors != 0) {
+ KiIpiSendPacket(TargetProcessors,
+ KiFlushTargetEntireTb,
+ NULL,
+ NULL,
+ NULL);
+
+ IPI_INSTRUMENT_COUNT (Prcb->Number, FlushEntireTb);
+ }
+
+#endif
+
+ //
+ // Flush TB on current processor.
+ //
+
+ KeFlushCurrentTb();
+
+ //
+ // Wait until all target processors have finished and complete packet.
+ //
+
+#if defined(NT_UP)
+
+ KeLowerIrql(OldIrql);
+
+#else
+
+ if (TargetProcessors != 0) {
+ KiIpiStallOnPacketTargets();
+ }
+
+ if (AllProcessors != FALSE) {
+ KeLowerIrql(OldIrql);
+
+ } else {
+ KiUnlockContextSwap(OldIrql);
+ }
+
+#endif
+
+ return;
+}
+
+#if !defined(NT_UP)
+
+
+VOID
+KiFlushTargetEntireTb (
+ IN PKIPI_CONTEXT SignalDone,
+ IN PVOID Parameter1,
+ IN PVOID Parameter2,
+ IN PVOID Parameter3
+ )
+
+/*++
+
+Routine Description:
+
+ This is the target function for flushing the entire TB.
+
+Arguments:
+
+ SignalDone - Supplies a pointer to a variable that is cleared when the
+ requested operation has been performed.
+
+ Parameter1 - Parameter3 - Not used.
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+
+ //
+ // Flush the entire TB on the current processor.
+ //
+
+ KiIpiSignalPacketDone(SignalDone);
+ KeFlushCurrentTb();
+ return;
+}
+
+VOID
+KeFlushMultipleTb (
+ IN ULONG Number,
+ IN PVOID *Virtual,
+ IN BOOLEAN Invalid,
+ IN BOOLEAN AllProcessors,
+ IN PHARDWARE_PTE *PtePointer OPTIONAL,
+ IN HARDWARE_PTE PteValue
+ )
+
+/*++
+
+Routine Description:
+
+ This function flushes multiple entries from the translation buffer
+ on all processors that are currently running threads which are
+ children of the current process or flushes a multiple entries from
+ the translation buffer on all processors in the host configuration.
+
+Arguments:
+
+ Number - Supplies the number of TB entries to flush.
+
+ Virtual - Supplies a pointer to an array of virtual addresses that
+ are within the pages whose translation buffer entries are to be
+ flushed.
+
+ Invalid - Supplies a boolean value that specifies the reason for
+ flushing the translation buffer.
+
+ AllProcessors - Supplies a boolean value that determines which
+ translation buffers are to be flushed.
+
+ PtePointer - Supplies an optional pointer to an array of pointers to
+ page table entries that receive the specified page table entry
+ value.
+
+ PteValue - Supplies the the new page table entry value.
+
+Return Value:
+
+ The previous contents of the specified page table entry is returned
+ as the function value.
+
+--*/
+
+{
+
+ ULONG Index;
+ KIRQL OldIrql;
+ PKPRCB Prcb;
+ PKPROCESS Process;
+ KAFFINITY TargetProcessors;
+
+ ASSERT(KeGetCurrentIrql() >= DISPATCH_LEVEL);
+
+ //
+ // Compute target set of processors.
+ //
+
+ Prcb = KeGetCurrentPrcb();
+ if (AllProcessors != FALSE) {
+ OldIrql = KeRaiseIrqlToSynchLevel();
+ TargetProcessors = KeActiveProcessors;
+
+ } else {
+ KiLockContextSwap(&OldIrql);
+ Process = Prcb->CurrentThread->ApcState.Process;
+ TargetProcessors = Process->ActiveProcessors;
+ }
+
+ TargetProcessors &= ~Prcb->SetMember;
+
+ //
+ // If a page table entry address array is specified, then set the
+ // specified page table entries to the specific value.
+ //
+
+ if (ARGUMENT_PRESENT(PtePointer)) {
+ for (Index = 0; Index < Number; Index += 1) {
+ *PtePointer[Index] = PteValue;
+ }
+ }
+
+ //
+ // If any target processors are specified, then send a flush multiple
+ // packet to the target set of processors.
+ //
+
+ if (TargetProcessors != 0) {
+ KiIpiSendPacket(TargetProcessors,
+ KiFlushTargetMultipleTb,
+ (PVOID)Invalid,
+ (PVOID)Number,
+ (PVOID)Virtual);
+
+ IPI_INSTRUMENT_COUNT (Prcb->Number, FlushMultipleTb);
+ }
+
+ //
+ // Flush the specified entries from the TB on the current processor.
+ //
+
+ for (Index = 0; Index < Number; Index += 1) {
+ KiFlushSingleTb(Invalid, Virtual[Index]);
+ }
+
+ //
+ // Wait until all target processors have finished and complete packet.
+ //
+
+ if (TargetProcessors != 0) {
+ KiIpiStallOnPacketTargets();
+ }
+
+ //
+ // Release the context swap lock.
+ //
+
+ if (AllProcessors != FALSE) {
+ KeLowerIrql(OldIrql);
+
+ } else {
+ KiUnlockContextSwap(OldIrql);
+ }
+
+ return;
+}
+
+VOID
+KiFlushTargetMultipleTb (
+ IN PKIPI_CONTEXT SignalDone,
+ IN PVOID Invalid,
+ IN PVOID Number,
+ IN PVOID Virtual
+ )
+
+/*++
+
+Routine Description:
+
+ This is the target function for flushing multiple TB entries.
+
+Arguments:
+
+ SignalDone - Supplies a pointer to a variable that is cleared when the
+ requested operation has been performed.
+
+ Invalid - Supplies a bollean value that determines whether the virtual
+ address is invalid.
+
+ Number - Supplies the number of TB entries to flush.
+
+ Virtual - Supplies a pointer to an array of virtual addresses that
+ are within the pages whose translation buffer entries are to be
+ flushed.
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+
+ ULONG Index;
+ PVOID VirtualAddress[FLUSH_MULTIPLE_MAXIMUM];
+
+ //
+ // Capture the virtual addresses that are to be flushed from the TB
+ // on the current processor and signal pack done.
+ //
+
+ for (Index = 0; Index < (ULONG) Number; Index += 1) {
+ VirtualAddress[Index] = ((PVOID *)(Virtual))[Index];
+ }
+
+ KiIpiSignalPacketDone(SignalDone);
+
+ //
+ // Flush the specified virtual address for the TB on the current
+ // processor.
+ //
+
+ for (Index = 0; Index < (ULONG) Number; Index += 1) {
+ KiFlushSingleTb((BOOLEAN)Invalid, VirtualAddress [Index]);
+ }
+}
+
+HARDWARE_PTE
+KeFlushSingleTb (
+ IN PVOID Virtual,
+ IN BOOLEAN Invalid,
+ IN BOOLEAN AllProcessors,
+ IN PHARDWARE_PTE PtePointer,
+ IN HARDWARE_PTE PteValue
+ )
+
+/*++
+
+Routine Description:
+
+ This function flushes a single entry from translation buffer (TB) on all
+ processors that are currently running threads which are child of the current
+ process or flushes the entire translation buffer on all processors in the
+ host configuration.
+
+Arguments:
+
+ Virtual - Supplies a virtual address that is within the page whose
+ translation buffer entry is to be flushed.
+
+ Invalid - Supplies a boolean value that specifies the reason for flushing
+ the translation buffer.
+
+ AllProcessors - Supplies a boolean value that determines which translation
+ buffers are to be flushed.
+
+ PtePointer - Address of Pte to update with new value.
+
+ PteValue - New value to put in the Pte. Will simply be assigned to
+ *PtePointer, in a fashion correct for the hardware.
+
+Return Value:
+
+ Returns the contents of the PtePointer before the new value
+ is stored.
+
+--*/
+
+{
+
+ KIRQL OldIrql;
+ PKPRCB Prcb;
+ PKPROCESS Process;
+ HARDWARE_PTE OldPteValue;
+ KAFFINITY TargetProcessors;
+
+ ASSERT(KeGetCurrentIrql() >= DISPATCH_LEVEL);
+
+ //
+ // Compute target set of processors.
+ //
+
+ Prcb = KeGetCurrentPrcb();
+ if (AllProcessors != FALSE) {
+ OldIrql = KeRaiseIrqlToSynchLevel();
+ TargetProcessors = KeActiveProcessors;
+
+ } else {
+ KiLockContextSwap(&OldIrql);
+ Process = Prcb->CurrentThread->ApcState.Process;
+ TargetProcessors = Process->ActiveProcessors;
+ }
+
+ TargetProcessors &= ~Prcb->SetMember;
+
+ //
+ // Capture the previous contents of the page table entry and set the
+ // page table entry to the new value.
+ //
+
+ OldPteValue = *PtePointer;
+ *PtePointer = PteValue;
+
+ //
+ // If any target processors are specified, then send a flush single
+ // packet to the target set of processors.
+ //
+
+ if (TargetProcessors != 0) {
+ KiIpiSendPacket(TargetProcessors,
+ KiFlushTargetSingleTb,
+ (PVOID)Invalid,
+ (PVOID)Virtual,
+ NULL);
+
+ IPI_INSTRUMENT_COUNT(Prcb->Number, FlushSingleTb);
+ }
+
+
+ //
+ // Flush the specified entry from the TB on the current processor.
+ //
+
+ KiFlushSingleTb(Invalid, Virtual);
+
+ //
+ // Wait until all target processors have finished and complete packet.
+ //
+
+ if (TargetProcessors != 0) {
+ KiIpiStallOnPacketTargets();
+ }
+
+ //
+ // Release the context swap lock.
+ //
+
+ if (AllProcessors != FALSE) {
+ KeLowerIrql(OldIrql);
+
+ } else {
+ KiUnlockContextSwap(OldIrql);
+ }
+
+ return(OldPteValue);
+}
+
+VOID
+KiFlushTargetSingleTb (
+ IN PKIPI_CONTEXT SignalDone,
+ IN PVOID Invalid,
+ IN PVOID VirtualAddress,
+ IN PVOID Parameter3
+ )
+
+/*++
+
+Routine Description:
+
+ This is the target function for flushing a single TB entry.
+
+Arguments:
+
+ SignalDone Supplies a pointer to a variable that is cleared when the
+ requested operation has been performed.
+
+ Invalid - Supplies a bollean value that determines whether the virtual
+ address is invalid.
+
+ Virtual - Supplies a virtual address that is within the page whose
+ translation buffer entry is to be flushed.
+
+ Parameter3 - Not used.
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+
+ //
+ // Flush a single entry from the TB on the current processor.
+ //
+
+ KiIpiSignalPacketDone(SignalDone);
+ KiFlushSingleTb((BOOLEAN)Invalid, (PVOID)VirtualAddress);
+}
+
+#endif
diff --git a/private/ntos/ke/i386/gdtsup.c b/private/ntos/ke/i386/gdtsup.c
new file mode 100644
index 000000000..f4d6d12a1
--- /dev/null
+++ b/private/ntos/ke/i386/gdtsup.c
@@ -0,0 +1,174 @@
+/*++
+
+Copyright (c) 1991 Microsoft Corporation
+
+Module Name:
+
+ gdtsup.c
+
+Abstract:
+
+ This module implements interfaces that support manipulation of i386 Gdts.
+ These entry points only exist on i386 machines.
+
+Author:
+
+ Dave Hastings (daveh) 28 May 1991
+
+Environment:
+
+ Kernel mode only.
+
+Revision History:
+
+--*/
+
+#include "ki.h"
+
+#ifdef ALLOC_PRAGMA
+#pragma alloc_text(PAGE,KeI386SetGdtSelector)
+#endif
+
+VOID
+Ke386GetGdtEntryThread(
+ IN PKTHREAD Thread,
+ IN ULONG Offset,
+ IN PKGDTENTRY Descriptor
+ )
+/*++
+
+Routine Description:
+
+ This routine returns the contents of an entry in the Gdt. If the
+ entry is thread specific, the entry for the specified thread is
+ created and returned (KGDT_LDT, and KGDT_R3_TEB). If the selector
+ is processor dependent, the entry for the current processor is
+ returned (KGDT_R0_PCR).
+
+Arguments:
+
+ Thread -- Supplies a pointer to the thread to return the entry for.
+
+ Offset -- Supplies the offset in the Gdt. This value must be 0
+ mod 8.
+
+ Descriptor -- Returns the contents of the Gdt descriptor
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ PKGDTENTRY Gdt;
+ PKPROCESS Process;
+
+ //
+ // If the entry is out of range, don't return anything
+ //
+
+ if (Offset >= KGDT_NUMBER * sizeof(KGDTENTRY)) {
+ return ;
+ }
+
+ if (Offset == KGDT_LDT) {
+
+ //
+ // Materialize Ldt selector
+ //
+
+ Process = Thread->ApcState.Process;
+ RtlMoveMemory( Descriptor,
+ &(Process->LdtDescriptor),
+ sizeof(KGDTENTRY)
+ );
+
+ } else {
+
+ //
+ // Copy Selector from Ldt
+ //
+ // N.B. We will change the base later, if it is KGDT_R3_TEB
+ //
+
+
+ Gdt = KiPcr()->GDT;
+
+ RtlMoveMemory(Descriptor, (PCHAR)Gdt + Offset, sizeof(KGDTENTRY));
+
+ //
+ // if it is the TEB selector, fix the base
+ //
+
+ if (Offset == KGDT_R3_TEB) {
+ Descriptor->BaseLow = (USHORT)((ULONG)(Thread->Teb) & 0xFFFF);
+ Descriptor->HighWord.Bytes.BaseMid =
+ (UCHAR) ( ( (ULONG)(Thread->Teb) & 0xFF0000L) >> 16);
+ Descriptor->HighWord.Bytes.BaseHi =
+ (CHAR) ( ( (ULONG)(Thread->Teb) & 0xFF000000L) >> 24);
+ }
+ }
+
+ return ;
+}
+
+NTSTATUS
+KeI386SetGdtSelector (
+ ULONG Selector,
+ PKGDTENTRY GdtValue
+ )
+/*++
+
+Routine Description:
+
+ Sets a GDTs returned via KeI386AllocateGdtSelectors to the supplied
+ GdtValue.
+
+Arguments:
+
+ Selector - Which GDT to set
+ GdtValue - GDT value to set into GDT
+
+Return Value:
+
+ status code
+
+--*/
+{
+ KAFFINITY TargetSet;
+ PKPRCB Prcb;
+ PKPCR Pcr;
+ PKGDTENTRY GdtEntry;
+ ULONG GdtIndex, BitNumber;
+
+ PAGED_CODE ();
+
+ //
+ // Verify GDT entry passed, and it's above the kernel GDT values
+ //
+
+ GdtIndex = Selector >> 3;
+ if ((Selector & 0x7) != 0 || GdtIndex < KGDT_NUMBER) {
+ return STATUS_UNSUCCESSFUL;
+ }
+
+ //
+ // Set gdt entry in each processors GDT
+ //
+
+ TargetSet = KeActiveProcessors;
+ while (TargetSet != 0) {
+ BitNumber = KiFindFirstSetRightMember(TargetSet);
+ ClearMember(BitNumber, TargetSet);
+
+ Prcb = KiProcessorBlock[BitNumber];
+ Pcr = CONTAINING_RECORD (Prcb, KPCR, PrcbData);
+ GdtEntry = Pcr->GDT + GdtIndex;
+
+ // set it
+ *GdtEntry = *GdtValue;
+ }
+
+ return STATUS_SUCCESS;
+}
diff --git a/private/ntos/ke/i386/geni386.c b/private/ntos/ke/i386/geni386.c
new file mode 100644
index 000000000..cfefb3611
--- /dev/null
+++ b/private/ntos/ke/i386/geni386.c
@@ -0,0 +1,812 @@
+/*++
+
+Copyright (c) 1989 Microsoft Corporation
+
+Module Name:
+
+ genoff.c
+
+Abstract:
+
+ This module implements a program which generates structure offset
+ definitions for kernel structures that are accessed in assembly code.
+
+Author:
+
+ Bryan M. Willman (bryanwi) 16-Oct-90
+
+Revision History:
+
+--*/
+
+#include "ki.h"
+#pragma hdrstop
+
+#include "nturtl.h"
+#include "vdmntos.h"
+#include "abios.h"
+
+//
+// Define architecture specific generation macros.
+//
+
+#define genAlt(Name, Type, Member) \
+ p2(#Name, OFFSET(Type, Member))
+
+#define genCom(Comment) \
+ p1("\n"); \
+ p1(";\n"); \
+ p1("; " Comment "\n"); \
+ p1(";\n"); \
+ p1("\n")
+
+#define genDef(Prefix, Type, Member) \
+ p2(#Prefix #Member, OFFSET(Type, Member))
+
+#define genVal(Name, Value) \
+ p2(#Name, Value)
+
+#define genSpc() p1("\n");
+
+//
+// Define member offset computation macro.
+//
+
+#define OFFSET(type, field) ((LONG)(&((type *)0)->field))
+
+FILE *OutKs386;
+FILE *OutHal386;
+
+ULONG OutputEnabled;
+
+#define KS386 0x01
+#define HAL386 0x02
+
+#define KERNEL KS386
+#define HAL HAL386
+
+//
+// p1 prints a single string.
+//
+
+VOID p1(PUCHAR outstring);
+
+//
+// p2 prints the first argument as a string, followed by " equ " and
+// the hexadecimal value of "Value".
+//
+
+VOID p2(PUCHAR a, LONG b);
+
+//
+// p2a first argument is the format string. second argument is passed
+// to the printf function
+//
+
+VOID p2a(PUCHAR a, LONG b);
+
+//
+// EnableInc(a) - Enables output to goto specified include file
+//
+
+#define EnableInc(a) OutputEnabled |= a;
+
+//
+// DisableInc(a) - Disables output to goto specified include file
+//
+
+#define DisableInc(a) OutputEnabled &= ~a;
+
+int
+_CRTAPI1
+main(
+ int argc,
+ char *argv[]
+ )
+{
+ char *outName;
+
+ printf ("Sizeof DeviceObject %d\n", sizeof (DEVICE_OBJECT));
+ printf ("Sizeof DeviceObject Ext %d\n", sizeof (DEVOBJ_EXTENSION));
+
+ outName = argc >= 2 ? argv[1] : "\\nt\\public\\sdk\\inc\\ks386.inc";
+ OutKs386 = fopen(outName, "w" );
+ if (OutKs386 == NULL) {
+ fprintf(stderr, "GENi386: Could not create output file '%s'.\n", outName);
+ fprintf( stderr, "sizeof( EPROCESS ) == %04x\n", sizeof( EPROCESS ) );
+ fprintf( stderr, "Win32Process %08x\n",OFFSET(EPROCESS, Win32Process));
+ exit (1);
+ }
+
+ fprintf( stderr, "GENi386: Writing %s header file.\n", outName );
+
+ outName = argc >= 3 ? argv[2] : "\\nt\\private\\ntos\\inc\\hal386.inc";
+ OutHal386 = fopen( outName, "w" );
+ if (OutHal386 == NULL) {
+ fprintf(stderr, "GENi386: Could not create output file '%s'.\n", outName);
+ fprintf(stderr, "GENi386: Execution continuing. Hal results ignored '%s'.\n", outName);
+ }
+
+ fprintf( stderr, "GENi386: Writing %s header file.\n", outName );
+
+ fprintf( stderr, "sizeof( TEB ) == %04x %s\n", sizeof( TEB ), sizeof( TEB ) >= PAGE_SIZE ? "Warning, TEB too Large" : "" );
+ fprintf( stderr, "sizeof( PEB ) == %04x %s\n", sizeof( PEB ), sizeof( PEB ) >= PAGE_SIZE ? "Warning, PEB too Large" : "" );
+ fprintf( stderr, "sizeof( KTHREAD ) == %04x\n", sizeof( KTHREAD ) );
+ fprintf( stderr, "sizeof( ETHREAD ) == %04x\n", sizeof( ETHREAD ) );
+ fprintf( stderr, "sizeof( KPROCESS ) == %04x\n", sizeof( KPROCESS ) );
+ fprintf( stderr, "sizeof( EPROCESS ) == %04x\n", sizeof( EPROCESS ) );
+ fprintf( stderr, "sizeof( KEVENT ) == %04x\n", sizeof( KEVENT ) );
+ fprintf( stderr, "sizeof( KSEMAPHORE ) == %04x\n", sizeof( KSEMAPHORE ) );
+
+ EnableInc (KS386);
+
+ //
+ // Include architecture independent definitions.
+ //
+
+#include "..\genxx.inc"
+
+ //
+ // Generate architecture dependent definitions.
+ //
+
+ p1("\n");
+ p1("; \n");
+ p1("; Apc Record Structure Offset Definitions\n");
+ p1("; \n");
+ p1("\n");
+ p2("ArNormalRoutine", OFFSET(KAPC_RECORD, NormalRoutine));
+ p2("ArNormalContext", OFFSET(KAPC_RECORD, NormalContext));
+ p2("ArSystemArgument1", OFFSET(KAPC_RECORD, SystemArgument1));
+ p2("ArSystemArgument2", OFFSET(KAPC_RECORD, SystemArgument2));
+ p2("ApcRecordLength", sizeof(KAPC_RECORD));
+ p1("\n");
+
+ EnableInc(HAL386);
+ p1("\n");
+ p1("; \n");
+ p1("; Processor Control Registers Structure Offset Definitions\n");
+ p1("; \n");
+ p1("\n");
+ p2("KI_BEGIN_KERNEL_RESERVED", KI_BEGIN_KERNEL_RESERVED);
+ p1("ifdef NT_UP\n");
+ p2a(" P0PCRADDRESS equ 0%lXH\n", KIP0PCRADDRESS);
+ p2a(" PCR equ ds:[0%lXH]\n", KIP0PCRADDRESS);
+ p1("else\n");
+ p1(" PCR equ fs:\n");
+ p1("endif\n\n");
+ p2("PcExceptionList", OFFSET(KPCR, NtTib.ExceptionList));
+ p2("PcInitialStack", OFFSET(KPCR, NtTib.StackBase));
+ p2("PcStackLimit", OFFSET(KPCR, NtTib.StackLimit));
+ p2("PcSelfPcr", OFFSET(KPCR, SelfPcr));
+ p2("PcPrcb", OFFSET(KPCR, Prcb));
+ p2("PcTeb", OFFSET(KPCR, NtTib.Self));
+ p2("PcIrql", OFFSET(KPCR, Irql));
+ p2("PcIRR", OFFSET(KPCR, IRR));
+ p2("PcIrrActive", OFFSET(KPCR, IrrActive));
+ p2("PcIDR", OFFSET(KPCR, IDR));
+ p2("PcIdt", OFFSET(KPCR, IDT));
+ p2("PcGdt", OFFSET(KPCR, GDT));
+ p2("PcTss", OFFSET(KPCR, TSS));
+ p2("PcDebugActive", OFFSET(KPCR, DebugActive));
+ p2("PcNumber", OFFSET(KPCR, Number));
+ p2("PcVdmAlert", OFFSET(KPCR, VdmAlert));
+ p2("PcSetMember", OFFSET(KPCR, SetMember));
+ p2("PcStallScaleFactor", OFFSET(KPCR, StallScaleFactor));
+ p2("PcHal", OFFSET(KPCR, HalReserved));
+ p2("PcKernel", OFFSET(KPCR, KernelReserved));
+ DisableInc (HAL386);
+ p2("PcPrcbData", OFFSET(KPCR, PrcbData));
+ p2("ProcessorControlRegisterLength", sizeof(KPCR));
+ p2("TebPeb", OFFSET(TEB, ProcessEnvironmentBlock));
+ p2("PebBeingDebugged", OFFSET(PEB, BeingDebugged));
+ p2("PebKernelCallbackTable", OFFSET(PEB, KernelCallbackTable));
+
+ EnableInc (HAL386);
+ p1("\n");
+ p1(";\n");
+ p1("; Defines for user shared data\n");
+ p1(";\n");
+ p2("USER_SHARED_DATA", KI_USER_SHARED_DATA);
+ p2("MM_SHARED_USER_DATA_VA", MM_SHARED_USER_DATA_VA);
+ p2a("USERDATA equ ds:[0%lXH]\n", KI_USER_SHARED_DATA);
+ p2("UsTickCountLow", OFFSET(KUSER_SHARED_DATA, TickCountLow));
+ p2("UsTickCountMultiplier", OFFSET(KUSER_SHARED_DATA, TickCountMultiplier));
+ p2("UsInterruptTime", OFFSET(KUSER_SHARED_DATA, InterruptTime));
+ p2("UsSystemTime", OFFSET(KUSER_SHARED_DATA, SystemTime));
+
+ p1("\n");
+ p1(";\n");
+ p1("; Tss Structure Offset Definitions\n");
+ p1(";\n\n");
+ p2("TssEsp0", OFFSET(KTSS, Esp0));
+ p2("TssCR3", OFFSET(KTSS, CR3));
+ p2("TssIoMapBase", OFFSET(KTSS, IoMapBase));
+ p2("TssIoMaps", OFFSET(KTSS, IoMaps));
+ p2("TssLength", sizeof(KTSS));
+ p1("\n");
+ DisableInc (HAL386);
+
+ EnableInc (HAL386);
+ p1(";\n");
+ p1("; Gdt Descriptor Offset Definitions\n");
+ p1(";\n\n");
+ p2("KGDT_R3_DATA", KGDT_R3_DATA);
+ p2("KGDT_R3_CODE", KGDT_R3_CODE);
+ p2("KGDT_R0_CODE", KGDT_R0_CODE);
+ p2("KGDT_R0_DATA", KGDT_R0_DATA);
+ p2("KGDT_R0_PCR", KGDT_R0_PCR);
+ p2("KGDT_STACK16", KGDT_STACK16);
+ p2("KGDT_CODE16", KGDT_CODE16);
+ p2("KGDT_TSS", KGDT_TSS);
+ DisableInc (HAL386);
+ p2("KGDT_R3_TEB", KGDT_R3_TEB);
+ p2("KGDT_DF_TSS", KGDT_DF_TSS);
+ p2("KGDT_NMI_TSS", KGDT_NMI_TSS);
+ p2("KGDT_LDT", KGDT_LDT);
+ p1("\n");
+
+ EnableInc (HAL386);
+ p1(";\n");
+ p1("; GdtEntry Offset Definitions\n");
+ p1(";\n\n");
+ p2("KgdtBaseLow", OFFSET(KGDTENTRY, BaseLow));
+ p2("KgdtBaseMid", OFFSET(KGDTENTRY, HighWord.Bytes.BaseMid));
+ p2("KgdtBaseHi", OFFSET(KGDTENTRY, HighWord.Bytes.BaseHi));
+ p2("KgdtLimitHi", OFFSET(KGDTENTRY, HighWord.Bytes.Flags2));
+ p2("KgdtLimitLow", OFFSET(KGDTENTRY, LimitLow));
+ p1("\n");
+
+ //
+ // Processor block structure definitions.
+ //
+
+ genCom("Processor Block Structure Offset Definitions");
+
+ genDef(Pb, KPRCB, CurrentThread);
+ genDef(Pb, KPRCB, NextThread);
+ genDef(Pb, KPRCB, IdleThread);
+ genDef(Pb, KPRCB, Number);
+ genDef(Pb, KPRCB, SetMember);
+ genDef(Pb, KPRCB, CpuID);
+ genDef(Pb, KPRCB, CpuType);
+ genDef(Pb, KPRCB, CpuStep);
+ genDef(Pb, KPRCB, HalReserved);
+ genDef(Pb, KPRCB, ProcessorState);
+
+ DisableInc (HAL386);
+
+ genDef(Pb, KPRCB, NpxThread);
+ genDef(Pb, KPRCB, InterruptCount);
+ genDef(Pb, KPRCB, KernelTime);
+ genDef(Pb, KPRCB, UserTime);
+ genDef(Pb, KPRCB, DpcTime);
+ genDef(Pb, KPRCB, InterruptTime);
+ genDef(Pb, KPRCB, ApcBypassCount);
+ genDef(Pb, KPRCB, DpcBypassCount);
+ genDef(Pb, KPRCB, AdjustDpcThreshold);
+ genDef(Pb, KPRCB, ThreadStartCount);
+ genAlt(PbAlignmentFixupCount, KPRCB, KeAlignmentFixupCount);
+ genAlt(PbContextSwitches, KPRCB, KeContextSwitches);
+ genAlt(PbDcacheFlushCount, KPRCB, KeDcacheFlushCount);
+ genAlt(PbExceptionDispatchCount, KPRCB, KeExceptionDispatchCount);
+ genAlt(PbFirstLevelTbFills, KPRCB, KeFirstLevelTbFills);
+ genAlt(PbFloatingEmulationCount, KPRCB, KeFloatingEmulationCount);
+ genAlt(PbIcacheFlushCount, KPRCB, KeIcacheFlushCount);
+ genAlt(PbSecondLevelTbFills, KPRCB, KeSecondLevelTbFills);
+ genAlt(PbSystemCalls, KPRCB, KeSystemCalls);
+ genDef(Pb, KPRCB, CurrentPacket);
+ genDef(Pb, KPRCB, TargetSet);
+ genDef(Pb, KPRCB, WorkerRoutine);
+ genDef(Pb, KPRCB, IpiFrozen);
+ genDef(Pb, KPRCB, RequestSummary);
+ genDef(Pb, KPRCB, SignalDone);
+ genDef(Pb, KPRCB, IpiFrame);
+ genDef(Pb, KPRCB, DpcInterruptRequested);
+ genDef(Pb, KPRCB, MaximumDpcQueueDepth);
+ genDef(Pb, KPRCB, MinimumDpcRate);
+ genDef(Pb, KPRCB, DpcListHead);
+ genDef(Pb, KPRCB, DpcQueueDepth);
+ genDef(Pb, KPRCB, DpcRoutineActive);
+ genDef(Pb, KPRCB, DpcCount);
+ genDef(Pb, KPRCB, DpcLastCount);
+ genDef(Pb, KPRCB, DpcRequestRate);
+ genDef(Pb, KPRCB, DpcLock);
+ genDef(Pb, KPRCB, SkipTick);
+ genDef(Pb, KPRCB, QuantumEnd);
+ genVal(ProcessorBlockLength, ((sizeof(KPRCB) + 15) & ~15));
+
+ //
+ // Interprocessor command definitions.
+ //
+
+ genCom("Immediate Interprocessor Command Definitions");
+
+ genVal(IPI_APC, IPI_APC);
+ genVal(IPI_DPC, IPI_DPC);
+ genVal(IPI_FREEZE, IPI_FREEZE);
+ genVal(IPI_PACKET_READY, IPI_PACKET_READY);
+
+ p1("; \n");
+ p1("; Thread Environment Block Structure Offset Definitions\n");
+ p1("; \n");
+ p1("\n");
+
+ p2("TbExceptionList", OFFSET(TEB, NtTib.ExceptionList));
+ p2("TbStackBase", OFFSET(TEB, NtTib.StackBase));
+ p2("TbStackLimit", OFFSET(TEB, NtTib.StackLimit));
+ p2("TbEnvironmentPointer", OFFSET(TEB, EnvironmentPointer));
+ p2("TbVersion", OFFSET(TEB, NtTib.Version));
+ p2("TbFiberData", OFFSET(TEB, NtTib.FiberData));
+ p2("TbArbitraryUserPointer", OFFSET(TEB, NtTib.ArbitraryUserPointer));
+ p2("TbClientId", OFFSET(TEB, ClientId));
+ p2("TbThreadLocalStoragePointer", OFFSET(TEB,
+ ThreadLocalStoragePointer));
+ p2("TbCountOfOwnedCriticalSections", OFFSET(TEB, CountOfOwnedCriticalSections));
+ p2("TbSystemReserved1", OFFSET(TEB, SystemReserved1));
+ p2("TbSystemReserved2", OFFSET(TEB, SystemReserved2));
+ p2("TbVdm", OFFSET(TEB, Vdm));
+ p2("TbCsrClientThread", OFFSET(TEB, CsrClientThread));
+ p2("TbGdiClientPID", OFFSET(TEB, GdiClientPID));
+ p2("TbGdiClientTID", OFFSET(TEB, GdiClientTID));
+ p2("TbGdiThreadLocalInfo", OFFSET(TEB, GdiThreadLocalInfo));
+ p2("TbglDispatchTable", OFFSET(TEB, glDispatchTable));
+ p2("TbglSectionInfo", OFFSET(TEB, glSectionInfo));
+ p2("TbglSection", OFFSET(TEB, glSection));
+ p2("TbglTable", OFFSET(TEB, glTable));
+ p2("TbglCurrentRC", OFFSET(TEB, glCurrentRC));
+ p2("TbglContext", OFFSET(TEB, glContext));
+ p2("TbWin32ClientInfo", OFFSET(TEB, Win32ClientInfo));
+ p2("TbWOW32Reserved", OFFSET(TEB, WOW32Reserved));
+ p2("TbWin32ThreadInfo", OFFSET(TEB, Win32ThreadInfo));
+ p2("TbSpare1", OFFSET(TEB, Spare1));
+ p2("TbExceptionCode", OFFSET(TEB, ExceptionCode));
+ p2("TbDeallocationStack", OFFSET(TEB, DeallocationStack));
+ p2("TbGdiBatchCount", OFFSET(TEB, GdiBatchCount));
+
+ EnableInc (HAL386);
+ p1(";\n");
+ p1(";\n");
+ p1("; Time Fields (TIME_FIELDS) Structure Offset Definitions\n");
+ p1(";\n\n");
+ p2("TfSecond", OFFSET(TIME_FIELDS, Second));
+ p2("TfMinute", OFFSET(TIME_FIELDS, Minute));
+ p2("TfHour", OFFSET(TIME_FIELDS, Hour));
+ p2("TfWeekday", OFFSET(TIME_FIELDS, Weekday));
+ p2("TfDay", OFFSET(TIME_FIELDS, Day));
+ p2("TfMonth", OFFSET(TIME_FIELDS, Month));
+ p2("TfYear", OFFSET(TIME_FIELDS, Year));
+ p2("TfMilliseconds", OFFSET(TIME_FIELDS, Milliseconds));
+ p1("\n");
+ DisableInc (HAL386);
+
+ EnableInc (HAL386);
+ p1("; \n");
+ p1("; constants for system irql and IDT vector conversion\n");
+ p1("; \n");
+ p1("\n");
+ p2("MAXIMUM_IDTVECTOR", MAXIMUM_IDTVECTOR);
+ p2("MAXIMUM_PRIMARY_VECTOR", MAXIMUM_PRIMARY_VECTOR);
+ p2("PRIMARY_VECTOR_BASE", PRIMARY_VECTOR_BASE);
+ p2("RPL_MASK", RPL_MASK);
+ p2("MODE_MASK", MODE_MASK);
+ p1("\n");
+ p1("; \n");
+ p1("; Flags in the CR0 register\n");
+ p1("; \n");
+ p1("\n");
+ p2("CR0_PG", CR0_PG);
+ p2("CR0_ET", CR0_ET);
+ p2("CR0_TS", CR0_TS);
+ p2("CR0_EM", CR0_EM);
+ p2("CR0_MP", CR0_MP);
+ p2("CR0_PE", CR0_PE);
+ p2("CR0_CD", CR0_CD);
+ p2("CR0_NW", CR0_NW);
+ p2("CR0_AM", CR0_AM);
+ p2("CR0_WP", CR0_WP);
+ p2("CR0_NE", CR0_NE);
+ p1("\n");
+ p1("; \n");
+ p1("; Flags in the CR4 register\n");
+ p1("; \n");
+ p1("\n");
+ p2("CR4_VME", CR4_VME);
+ p2("CR4_PVI", CR4_PVI);
+ p2("CR4_TSD", CR4_TSD);
+ p2("CR4_DE", CR4_DE);
+ p2("CR4_PSE", CR4_PSE);
+ p2("CR4_PAE", CR4_PAE);
+ p2("CR4_MCE", CR4_MCE);
+ p2("CR4_PGE", CR4_PGE);
+ p1("; \n");
+ p1("; Miscellaneous Definitions\n");
+ p1("; \n");
+ p1("\n");
+ p2("MAXIMUM_PROCESSORS", MAXIMUM_PROCESSORS);
+ p2("INITIAL_STALL_COUNT", INITIAL_STALL_COUNT);
+ p2("IRQL_NOT_GREATER_OR_EQUAL", IRQL_NOT_GREATER_OR_EQUAL);
+ p2("IRQL_NOT_LESS_OR_EQUAL", IRQL_NOT_LESS_OR_EQUAL);
+ DisableInc (HAL386);
+ p2("BASE_PRIORITY_THRESHOLD", BASE_PRIORITY_THRESHOLD);
+ p2("EVENT_PAIR_INCREMENT", EVENT_PAIR_INCREMENT);
+ p2("LOW_REALTIME_PRIORITY", LOW_REALTIME_PRIORITY);
+ p2("BlackHole", 0xffffa000);
+ p2("KERNEL_LARGE_STACK_COMMIT", KERNEL_LARGE_STACK_COMMIT);
+ p2("KERNEL_STACK_SIZE", KERNEL_STACK_SIZE);
+ p2("DOUBLE_FAULT_STACK_SIZE", DOUBLE_FAULT_STACK_SIZE);
+ p2("EFLAG_SELECT", EFLAG_SELECT);
+ p2("BREAKPOINT_BREAK ", BREAKPOINT_BREAK);
+ p2("IPI_FREEZE", IPI_FREEZE);
+ p2("CLOCK_QUANTUM_DECREMENT", CLOCK_QUANTUM_DECREMENT);
+ p2("READY_SKIP_QUANTUM", READY_SKIP_QUANTUM);
+ p2("THREAD_QUANTUM", THREAD_QUANTUM);
+ p2("WAIT_QUANTUM_DECREMENT", WAIT_QUANTUM_DECREMENT);
+ p2("ROUND_TRIP_DECREMENT_COUNT", ROUND_TRIP_DECREMENT_COUNT);
+
+ //
+ // Print trap frame offsets relative to sp.
+ //
+
+ EnableInc (HAL386);
+ p1("\n");
+ p1("; \n");
+ p1("; Trap Frame Offset Definitions and Length\n");
+ p1("; \n");
+ p1("\n");
+
+ p2("TsExceptionList", OFFSET(KTRAP_FRAME, ExceptionList));
+ p2("TsPreviousPreviousMode", OFFSET(KTRAP_FRAME, PreviousPreviousMode));
+ p2("TsSegGs", OFFSET(KTRAP_FRAME, SegGs));
+ p2("TsSegFs", OFFSET(KTRAP_FRAME, SegFs));
+ p2("TsSegEs", OFFSET(KTRAP_FRAME, SegEs));
+ p2("TsSegDs", OFFSET(KTRAP_FRAME, SegDs));
+ p2("TsEdi", OFFSET(KTRAP_FRAME, Edi));
+ p2("TsEsi", OFFSET(KTRAP_FRAME, Esi));
+ p2("TsEbp", OFFSET(KTRAP_FRAME, Ebp));
+ p2("TsEbx", OFFSET(KTRAP_FRAME, Ebx));
+ p2("TsEdx", OFFSET(KTRAP_FRAME, Edx));
+ p2("TsEcx", OFFSET(KTRAP_FRAME, Ecx));
+ p2("TsEax", OFFSET(KTRAP_FRAME, Eax));
+ p2("TsErrCode", OFFSET(KTRAP_FRAME, ErrCode));
+ p2("TsEip", OFFSET(KTRAP_FRAME, Eip));
+ p2("TsSegCs", OFFSET(KTRAP_FRAME, SegCs));
+ p2("TsEflags", OFFSET(KTRAP_FRAME, EFlags));
+ p2("TsHardwareEsp", OFFSET(KTRAP_FRAME, HardwareEsp));
+ p2("TsHardwareSegSs", OFFSET(KTRAP_FRAME, HardwareSegSs));
+ p2("TsTempSegCs", OFFSET(KTRAP_FRAME, TempSegCs));
+ p2("TsTempEsp", OFFSET(KTRAP_FRAME, TempEsp));
+ p2("TsDbgEbp", OFFSET(KTRAP_FRAME, DbgEbp));
+ p2("TsDbgEip", OFFSET(KTRAP_FRAME, DbgEip));
+ p2("TsDbgArgMark", OFFSET(KTRAP_FRAME, DbgArgMark));
+ p2("TsDbgArgPointer", OFFSET(KTRAP_FRAME, DbgArgPointer));
+ p2("TsDr0", OFFSET(KTRAP_FRAME, Dr0));
+ p2("TsDr1", OFFSET(KTRAP_FRAME, Dr1));
+ p2("TsDr2", OFFSET(KTRAP_FRAME, Dr2));
+ p2("TsDr3", OFFSET(KTRAP_FRAME, Dr3));
+ p2("TsDr6", OFFSET(KTRAP_FRAME, Dr6));
+ p2("TsDr7", OFFSET(KTRAP_FRAME, Dr7));
+ p2("TsV86Es", OFFSET(KTRAP_FRAME, V86Es));
+ p2("TsV86Ds", OFFSET(KTRAP_FRAME, V86Ds));
+ p2("TsV86Fs", OFFSET(KTRAP_FRAME, V86Fs));
+ p2("TsV86Gs", OFFSET(KTRAP_FRAME, V86Gs));
+ p2("KTRAP_FRAME_LENGTH", KTRAP_FRAME_LENGTH);
+ p2("KTRAP_FRAME_ALIGN", KTRAP_FRAME_ALIGN);
+ p2("FRAME_EDITED", FRAME_EDITED);
+ p2("EFLAGS_ALIGN_CHECK", EFLAGS_ALIGN_CHECK);
+ p2("EFLAGS_V86_MASK", EFLAGS_V86_MASK);
+ p2("EFLAGS_INTERRUPT_MASK", EFLAGS_INTERRUPT_MASK);
+ p2("EFLAGS_VIF", EFLAGS_VIF);
+ p2("EFLAGS_VIP", EFLAGS_VIP);
+ p2("EFLAGS_USER_SANITIZE", EFLAGS_USER_SANITIZE);
+ p1("\n");
+
+
+ p1(";\n");
+ p1("; Context Frame Offset and Flag Definitions\n");
+ p1(";\n");
+ p1("\n");
+ p2("CONTEXT_FULL", CONTEXT_FULL);
+ p2("CONTEXT_DEBUG_REGISTERS", CONTEXT_DEBUG_REGISTERS);
+ p2("CONTEXT_CONTROL", CONTEXT_CONTROL);
+ p2("CONTEXT_FLOATING_POINT", CONTEXT_FLOATING_POINT);
+ p2("CONTEXT_INTEGER", CONTEXT_INTEGER);
+ p2("CONTEXT_SEGMENTS", CONTEXT_SEGMENTS);
+ p1("\n");
+
+ //
+ // Print context frame offsets relative to sp.
+ //
+
+ p2("CsContextFlags", OFFSET(CONTEXT, ContextFlags));
+ p2("CsFloatSave", OFFSET(CONTEXT, FloatSave));
+ p2("CsSegGs", OFFSET(CONTEXT, SegGs));
+ p2("CsSegFs", OFFSET(CONTEXT, SegFs));
+ p2("CsSegEs", OFFSET(CONTEXT, SegEs));
+ p2("CsSegDs", OFFSET(CONTEXT, SegDs));
+ p2("CsEdi", OFFSET(CONTEXT, Edi));
+ p2("CsEsi", OFFSET(CONTEXT, Esi));
+ p2("CsEbp", OFFSET(CONTEXT, Ebp));
+ p2("CsEbx", OFFSET(CONTEXT, Ebx));
+ p2("CsEdx", OFFSET(CONTEXT, Edx));
+ p2("CsEcx", OFFSET(CONTEXT, Ecx));
+ p2("CsEax", OFFSET(CONTEXT, Eax));
+ p2("CsEip", OFFSET(CONTEXT, Eip));
+ p2("CsSegCs", OFFSET(CONTEXT, SegCs));
+ p2("CsEflags", OFFSET(CONTEXT, EFlags));
+ p2("CsEsp", OFFSET(CONTEXT, Esp));
+ p2("CsSegSs", OFFSET(CONTEXT, SegSs));
+ p2("CsDr0", OFFSET(CONTEXT, Dr0));
+ p2("CsDr1", OFFSET(CONTEXT, Dr1));
+ p2("CsDr2", OFFSET(CONTEXT, Dr2));
+ p2("CsDr3", OFFSET(CONTEXT, Dr3));
+ p2("CsDr6", OFFSET(CONTEXT, Dr6));
+ p2("CsDr7", OFFSET(CONTEXT, Dr7));
+ p2("ContextFrameLength", (sizeof(CONTEXT) + 15) & (~15));
+ p2("DR6_LEGAL", DR6_LEGAL);
+ p2("DR7_LEGAL", DR7_LEGAL);
+ p2("DR7_ACTIVE", DR7_ACTIVE);
+
+ //
+ // Print Registration Record Offsets relative to base
+ //
+
+ p2("ErrHandler",
+ OFFSET(EXCEPTION_REGISTRATION_RECORD, Handler));
+ p2("ErrNext",
+ OFFSET(EXCEPTION_REGISTRATION_RECORD, Next));
+ p1("\n");
+
+ //
+ // Print floating point field offsets relative to Context.FloatSave
+ //
+
+ p1(";\n");
+ p1("; Floating save area field offset definitions\n");
+ p1(";\n");
+ p2("FpControlWord ", OFFSET(FLOATING_SAVE_AREA, ControlWord));
+ p2("FpStatusWord ", OFFSET(FLOATING_SAVE_AREA, StatusWord));
+ p2("FpTagWord ", OFFSET(FLOATING_SAVE_AREA, TagWord));
+ p2("FpErrorOffset ", OFFSET(FLOATING_SAVE_AREA, ErrorOffset));
+ p2("FpErrorSelector", OFFSET(FLOATING_SAVE_AREA, ErrorSelector));
+ p2("FpDataOffset ", OFFSET(FLOATING_SAVE_AREA, DataOffset));
+ p2("FpDataSelector ", OFFSET(FLOATING_SAVE_AREA, DataSelector));
+ p2("FpRegisterArea ", OFFSET(FLOATING_SAVE_AREA, RegisterArea));
+ p2("FpCr0NpxState ", OFFSET(FLOATING_SAVE_AREA, Cr0NpxState));
+
+ p1("\n");
+ p2("NPX_FRAME_LENGTH", sizeof(FLOATING_SAVE_AREA));
+
+ //
+ // Processor State Frame offsets relative to base
+ //
+
+ p1(";\n");
+ p1("; Processor State Frame Offset Definitions\n");
+ p1(";\n");
+ p1("\n");
+ p2("PsContextFrame",
+ OFFSET(KPROCESSOR_STATE, ContextFrame));
+ p2("PsSpecialRegisters",
+ OFFSET(KPROCESSOR_STATE, SpecialRegisters));
+ p2("SrCr0", OFFSET(KSPECIAL_REGISTERS, Cr0));
+ p2("SrCr2", OFFSET(KSPECIAL_REGISTERS, Cr2));
+ p2("SrCr3", OFFSET(KSPECIAL_REGISTERS, Cr3));
+ p2("SrCr4", OFFSET(KSPECIAL_REGISTERS, Cr4));
+ p2("SrKernelDr0", OFFSET(KSPECIAL_REGISTERS, KernelDr0));
+ p2("SrKernelDr1", OFFSET(KSPECIAL_REGISTERS, KernelDr1));
+ p2("SrKernelDr2", OFFSET(KSPECIAL_REGISTERS, KernelDr2));
+ p2("SrKernelDr3", OFFSET(KSPECIAL_REGISTERS, KernelDr3));
+ p2("SrKernelDr6", OFFSET(KSPECIAL_REGISTERS, KernelDr6));
+ p2("SrKernelDr7", OFFSET(KSPECIAL_REGISTERS, KernelDr7));
+ p2("SrGdtr", OFFSET(KSPECIAL_REGISTERS, Gdtr.Limit));
+
+ p2("SrIdtr", OFFSET(KSPECIAL_REGISTERS, Idtr.Limit));
+ p2("SrTr", OFFSET(KSPECIAL_REGISTERS, Tr));
+ p2("SrLdtr", OFFSET(KSPECIAL_REGISTERS, Ldtr));
+ p2("ProcessorStateLength", ((sizeof(KPROCESSOR_STATE) + 15) & ~15));
+ DisableInc (HAL386);
+
+ //
+ // E Process fields relative to base
+ //
+
+ p1(";\n");
+ p1("; EPROCESS\n");
+ p1(";\n");
+ p1("\n");
+ p2("EpDebugPort",
+ OFFSET(EPROCESS, DebugPort));
+
+ //
+ // E Resource fields relative to base
+ //
+
+ p1("\n");
+ p1(";\n");
+ p1("; NTDDK Resource\n");
+ p1(";\n");
+ p1("\n");
+ p2("RsOwnerThreads", OFFSET(NTDDK_ERESOURCE, OwnerThreads));
+ p2("RsOwnerCounts", OFFSET(NTDDK_ERESOURCE, OwnerCounts));
+ p2("RsTableSize", OFFSET(NTDDK_ERESOURCE, TableSize));
+ p2("RsActiveCount", OFFSET(NTDDK_ERESOURCE, ActiveCount));
+ p2("RsFlag", OFFSET(NTDDK_ERESOURCE, Flag));
+ p2("RsInitialOwnerThreads", OFFSET(NTDDK_ERESOURCE, InitialOwnerThreads));
+ p2("RsOwnedExclusive", ResourceOwnedExclusive);
+
+ //
+ // Define machine type (temporarily)
+ //
+
+ EnableInc (HAL386);
+ p1(";\n");
+ p1("; Machine type definitions (Temporarily)\n");
+ p1(";\n");
+ p1("\n");
+ p2("MACHINE_TYPE_ISA", MACHINE_TYPE_ISA);
+ p2("MACHINE_TYPE_EISA", MACHINE_TYPE_EISA);
+ p2("MACHINE_TYPE_MCA", MACHINE_TYPE_MCA);
+
+ DisableInc (HAL386);
+ p1(";\n");
+ p1("; KeFeatureBits defines\n");
+ p1(";\n");
+ p1("\n");
+ p2("KF_V86_VIS", KF_V86_VIS);
+ p2("KF_RDTSC", KF_RDTSC);
+ p2("KF_CR4", KF_CR4);
+ p2("KF_GLOBAL_PAGE", KF_GLOBAL_PAGE);
+ p2("KF_LARGE_PAGE", KF_LARGE_PAGE);
+ p2("KF_CMPXCHG8B", KF_CMPXCHG8B);
+
+ EnableInc (HAL386);
+ p1(";\n");
+ p1("; LoaderParameterBlock offsets relative to base\n");
+ p1(";\n");
+ p1("\n");
+ p2("LpbLoadOrderListHead",OFFSET(LOADER_PARAMETER_BLOCK,LoadOrderListHead));
+ p2("LpbMemoryDescriptorListHead",OFFSET(LOADER_PARAMETER_BLOCK,MemoryDescriptorListHead));
+ p2("LpbKernelStack",OFFSET(LOADER_PARAMETER_BLOCK,KernelStack));
+ p2("LpbPrcb",OFFSET(LOADER_PARAMETER_BLOCK,Prcb));
+ p2("LpbProcess",OFFSET(LOADER_PARAMETER_BLOCK,Process));
+ p2("LpbThread",OFFSET(LOADER_PARAMETER_BLOCK,Thread));
+ p2("LpbI386",OFFSET(LOADER_PARAMETER_BLOCK,u.I386));
+ p2("LpbRegistryLength",OFFSET(LOADER_PARAMETER_BLOCK,RegistryLength));
+ p2("LpbRegistryBase",OFFSET(LOADER_PARAMETER_BLOCK,RegistryBase));
+ p2("LpbConfigurationRoot",OFFSET(LOADER_PARAMETER_BLOCK,ConfigurationRoot));
+ p2("LpbArcBootDeviceName",OFFSET(LOADER_PARAMETER_BLOCK,ArcBootDeviceName));
+ p2("LpbArcHalDeviceName",OFFSET(LOADER_PARAMETER_BLOCK,ArcHalDeviceName));
+ DisableInc (HAL386);
+
+ p2("PAGE_SIZE",PAGE_SIZE);
+
+ //
+ // Define the VDM instruction emulation count indexes
+ //
+
+ p1("\n");
+ p1(";\n");
+ p1("; VDM equates.\n");
+ p1(";\n");
+ p1("\n");
+ p2("VDM_INDEX_Invalid", VDM_INDEX_Invalid);
+ p2("VDM_INDEX_0F", VDM_INDEX_0F);
+ p2("VDM_INDEX_ESPrefix", VDM_INDEX_ESPrefix);
+ p2("VDM_INDEX_CSPrefix", VDM_INDEX_CSPrefix);
+ p2("VDM_INDEX_SSPrefix", VDM_INDEX_SSPrefix);
+ p2("VDM_INDEX_DSPrefix", VDM_INDEX_DSPrefix);
+ p2("VDM_INDEX_FSPrefix", VDM_INDEX_FSPrefix);
+ p2("VDM_INDEX_GSPrefix", VDM_INDEX_GSPrefix);
+ p2("VDM_INDEX_OPER32Prefix", VDM_INDEX_OPER32Prefix);
+ p2("VDM_INDEX_ADDR32Prefix", VDM_INDEX_ADDR32Prefix);
+ p2("VDM_INDEX_INSB", VDM_INDEX_INSB);
+ p2("VDM_INDEX_INSW", VDM_INDEX_INSW);
+ p2("VDM_INDEX_OUTSB", VDM_INDEX_OUTSB);
+ p2("VDM_INDEX_OUTSW", VDM_INDEX_OUTSW);
+ p2("VDM_INDEX_PUSHF", VDM_INDEX_PUSHF);
+ p2("VDM_INDEX_POPF", VDM_INDEX_POPF);
+ p2("VDM_INDEX_INTnn", VDM_INDEX_INTnn);
+ p2("VDM_INDEX_INTO", VDM_INDEX_INTO);
+ p2("VDM_INDEX_IRET", VDM_INDEX_IRET);
+ p2("VDM_INDEX_NPX", VDM_INDEX_NPX);
+ p2("VDM_INDEX_INBimm", VDM_INDEX_INBimm);
+ p2("VDM_INDEX_INWimm", VDM_INDEX_INWimm);
+ p2("VDM_INDEX_OUTBimm", VDM_INDEX_OUTBimm);
+ p2("VDM_INDEX_OUTWimm", VDM_INDEX_OUTWimm);
+ p2("VDM_INDEX_INB", VDM_INDEX_INB);
+ p2("VDM_INDEX_INW", VDM_INDEX_INW);
+ p2("VDM_INDEX_OUTB", VDM_INDEX_OUTB);
+ p2("VDM_INDEX_OUTW", VDM_INDEX_OUTW);
+ p2("VDM_INDEX_LOCKPrefix", VDM_INDEX_LOCKPrefix);
+ p2("VDM_INDEX_REPNEPrefix", VDM_INDEX_REPNEPrefix);
+ p2("VDM_INDEX_REPPrefix", VDM_INDEX_REPPrefix);
+ p2("VDM_INDEX_CLI", VDM_INDEX_CLI);
+ p2("VDM_INDEX_STI", VDM_INDEX_STI);
+ p2("VDM_INDEX_HLT", VDM_INDEX_HLT);
+ p2("MAX_VDM_INDEX", MAX_VDM_INDEX);
+
+ //
+ // Vdm feature bits
+ //
+
+ p1("\n");
+ p1(";\n");
+ p1("; VDM feature bits.\n");
+ p1(";\n");
+ p1("\n");
+ p2("V86_VIRTUAL_INT_EXTENSIONS",V86_VIRTUAL_INT_EXTENSIONS);
+ p2("PM_VIRTUAL_INT_EXTENSIONS",PM_VIRTUAL_INT_EXTENSIONS);
+
+ //
+ // Selector type
+ //
+ p1("\n");
+ p1(";\n");
+ p1("; Selector types.\n");
+ p1(";\n");
+ p1("\n");
+ p2("SEL_TYPE_NP",SEL_TYPE_NP);
+
+ //
+ // Usermode callout frame
+ //
+ DisableInc (HAL386);
+ genCom("Usermode callout frame definitions");
+ p2("CuInStk", OFFSET(KCALLOUT_FRAME, InStk));
+ p2("CuTrFr", OFFSET(KCALLOUT_FRAME, TrFr));
+ p2("CuCbStk", OFFSET(KCALLOUT_FRAME, CbStk));
+ p2("CuEdi", OFFSET(KCALLOUT_FRAME, Edi));
+ p2("CuEsi", OFFSET(KCALLOUT_FRAME, Esi));
+ p2("CuEbx", OFFSET(KCALLOUT_FRAME, Ebx));
+ p2("CuEbp", OFFSET(KCALLOUT_FRAME, Ebp));
+ p2("CuRet", OFFSET(KCALLOUT_FRAME, Ret));
+ p2("CuOutBf", OFFSET(KCALLOUT_FRAME, OutBf));
+ p2("CuOutLn", OFFSET(KCALLOUT_FRAME, OutLn));
+ EnableInc (HAL386);
+
+ return 0;
+}
+
+
+VOID
+p1 (PUCHAR a)
+{
+ if (OutputEnabled & KS386) {
+ fprintf(OutKs386,a);
+ }
+
+ if (OutputEnabled & HAL386) {
+ if ( OutHal386 ) {
+ fprintf(OutHal386,a);
+ }
+ }
+}
+
+VOID
+p2 (PUCHAR a, LONG b)
+{
+ if (OutputEnabled & KS386) {
+ fprintf(OutKs386, "%s equ 0%lXH\n", a, b);
+ }
+
+ if (OutputEnabled & HAL386) {
+ if ( OutHal386 ) {
+ fprintf(OutHal386, "%s equ 0%lXH\n", a, b);
+ }
+ }
+}
+
+VOID
+p2a (PUCHAR b, LONG c)
+{
+ if (OutputEnabled & KS386) {
+ fprintf(OutKs386, b, c);
+ }
+
+ if (OutputEnabled & HAL386) {
+ if ( OutHal386 ) {
+ fprintf(OutHal386, b, c);
+ }
+ }
+}
diff --git a/private/ntos/ke/i386/i386init.c b/private/ntos/ke/i386/i386init.c
new file mode 100644
index 000000000..69bcce12b
--- /dev/null
+++ b/private/ntos/ke/i386/i386init.c
@@ -0,0 +1,223 @@
+/*++
+
+Copyright (c) 1989 Microsoft Corporation
+
+Module Name:
+
+ i386init.c
+
+Abstract:
+
+ This module contains code to manipulate i386 hardware structures used
+ only by the kernel.
+
+Author:
+
+ Bryan Willman 22 Feb 90
+
+Revision History:
+
+--*/
+
+#include "ki.h"
+
+VOID
+KiInitializeMachineType (
+ VOID
+ );
+
+
+#ifdef ALLOC_PRAGMA
+#pragma alloc_text(INIT,KiInitializeGDT)
+#pragma alloc_text(INIT,KiInitializeGdtEntry)
+#pragma alloc_text(INIT,KiInitializeMachineType)
+#endif
+
+
+KIRQL KiProfileIrql = PROFILE_LEVEL;
+ULONG KeI386MachineType = 0;
+BOOLEAN KeI386NpxPresent;
+ULONG KeI386ForceNpxEmulation;
+ULONG KeI386CpuType;
+ULONG KeI386CpuStep;
+PVOID Ki387RoundModeTable; // R3 emulators RoundingMode vector table
+ULONG KiBootFeatureBits;
+
+#if DBG
+UCHAR MsgDpcTrashedEsp[] = "\n*** DPC routine %lx trashed ESP\n";
+UCHAR MsgDpcTimeout[] = "\n*** DPC routine %lx > 1sec (%lx interrupts, %lx isr time)\n";
+UCHAR MsgISRTimeout[] = "\n*** ISR at %lx took over .5 second\n";
+UCHAR MsgISROverflow[] = "\n*** ISR at %lx - %d interrupts per .5 second\n";
+
+ULONG KiDPCTimeout = 110;
+ULONG KiISRTimeout = 55;
+ULONG KiISROverflow = 5500;
+ULONG KiSpinlockTimeout = 55;
+#endif
+
+
+
+VOID
+KiInitializeGDT (
+ IN OUT PKGDTENTRY Gdt,
+ IN USHORT GdtLimit,
+ IN PKPCR Pcr,
+ IN USHORT PcrLimit,
+ IN PKTSS Tss,
+ IN USHORT TssLimit,
+ IN USHORT TebLimit
+ )
+
+/*++
+
+Routine Description:
+
+ This procedure initializes a GDT. It will set standard values
+ for all descriptors.
+
+ It will not set PCR->GDT.
+
+ It will set the PCR address in KGDT_PCR.
+
+ KGDT_R3_TEB will be set to a base of 0, with a limit of 1 page.
+
+
+Arguments:
+
+ Gdt - Supplies a pointer to an array of KGDTENTRYs.
+
+ GdtLimit - Supplies size (in bytes) of Gdt. Used to detect a
+ GDT which is too small (which will cause a BUGCHECK)
+
+ Pcr - FLAT address of Pcr for processor Gdt is for.
+
+ PcrLimit - Size Limit of PCR in bytes.
+
+ Tss - FLAT adderss of TSS for processor Gdt is for.
+
+ TssLimit - Size limit of TSS in bytes.
+
+ TebLimit - Size limit of Teb in bytes.
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+
+ if ((KGDT_NUMBER * 8) > GdtLimit)
+ KeBugCheck(MEMORY_MANAGEMENT);
+
+ KiInitializeGdtEntry(&Gdt[KGDT_NULL], 0, 0, 0, 0, GRAN_PAGE);
+
+ KiInitializeGdtEntry(
+ &Gdt[KGDT_R0_CODE], 0, (ULONG)-1, TYPE_CODE, DPL_SYSTEM, GRAN_PAGE);
+
+ KiInitializeGdtEntry(
+ &Gdt[KGDT_R0_DATA], 0, (ULONG)-1, TYPE_DATA, DPL_SYSTEM, GRAN_PAGE);
+
+ KiInitializeGdtEntry(&Gdt[KGDT_R3_CODE], 0,
+ (ULONG)-1, TYPE_CODE, DPL_USER, GRAN_PAGE);
+
+ KiInitializeGdtEntry(&Gdt[KGDT_R3_DATA], 0,
+ (ULONG)-1, TYPE_DATA, DPL_USER, GRAN_PAGE);
+
+ KiInitializeGdtEntry(
+ &Gdt[KGDT_TSS], (ULONG)Tss, TssLimit-1,
+ TYPE_TSS, DPL_SYSTEM, GRAN_BYTE);
+
+ KiInitializeGdtEntry(
+ &Gdt[KGDT_R0_PCR], (ULONG)Pcr, PcrLimit-1,
+ TYPE_DATA, DPL_SYSTEM, GRAN_BYTE);
+
+ KiInitializeGdtEntry(
+ &Gdt[KGDT_R3_TEB], 0, TebLimit-1, TYPE_DATA, DPL_USER, GRAN_BYTE);
+}
+
+VOID
+KiInitializeGdtEntry (
+ OUT PKGDTENTRY GdtEntry,
+ IN ULONG Base,
+ IN ULONG Limit,
+ IN USHORT Type,
+ IN USHORT Dpl,
+ IN USHORT Granularity
+ )
+
+/*++
+
+Routine Description:
+
+ This function initializes a GDT entry. Base, Limit, Type (code,
+ data), and Dpl (0 or 3) are set according to parameters. All other
+ fields of the entry are set to match standard system values.
+
+Arguments:
+
+ GdtEntry - GDT descriptor to be filled in.
+
+ Base - Linear address of the first byte mapped by the selector.
+
+ Limit - Size of the selector in pages. Note that 0 is 1 page
+ while 0xffffff is 1 megapage = 4 gigabytes.
+
+ Type - Code or Data. All code selectors are marked readable,
+ all data selectors are marked writeable.
+
+ Dpl - User (3) or System (0)
+
+ Granularity - 0 for byte, 1 for page
+
+Return Value:
+
+ Pointer to the GDT entry.
+
+--*/
+
+{
+ GdtEntry->LimitLow = (USHORT)(Limit & 0xffff);
+ GdtEntry->BaseLow = (USHORT)(Base & 0xffff);
+ GdtEntry->HighWord.Bytes.BaseMid = (UCHAR)((Base & 0xff0000) >> 16);
+ GdtEntry->HighWord.Bits.Type = Type;
+ GdtEntry->HighWord.Bits.Dpl = Dpl;
+ GdtEntry->HighWord.Bits.Pres = 1;
+ GdtEntry->HighWord.Bits.LimitHi = (Limit & 0xf0000) >> 16;
+ GdtEntry->HighWord.Bits.Sys = 0;
+ GdtEntry->HighWord.Bits.Reserved_0 = 0;
+ GdtEntry->HighWord.Bits.Default_Big = 1;
+ GdtEntry->HighWord.Bits.Granularity = Granularity;
+ GdtEntry->HighWord.Bytes.BaseHi = (UCHAR)((Base & 0xff000000) >> 24);
+}
+
+VOID
+KiInitializeMachineType (
+ VOID
+ )
+
+/*++
+
+Routine Description:
+
+ This function initializes machine type, i.e. MCA, ABIOS, ISA
+ or EISA.
+ N.B. This is a temporary routine. machine type:
+ Byte 0 - Machine Type, ISA, EISA or MCA
+ Byte 1 - CPU type, i386 or i486
+ Byte 2 - Cpu Step, A or B ... etc.
+ Highest bit indicates if NPX is present.
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ KeI386MachineType = KeLoaderBlock->u.I386.MachineType & 0x000ff;
+}
diff --git a/private/ntos/ke/i386/i386pcr.asm b/private/ntos/ke/i386/i386pcr.asm
new file mode 100644
index 000000000..fed059531
--- /dev/null
+++ b/private/ntos/ke/i386/i386pcr.asm
@@ -0,0 +1,200 @@
+ title "I386 PCR"
+;++
+;
+; Copyright (c) 1989 Microsoft Corporation
+;
+; Module Name:
+;
+; i386pcr.asm
+;
+; Abstract:
+;
+; This module implements routines for accessing and initing the pcr.
+;
+; Author:
+;
+; Bryan Willman (bryanwi) 20 Mar 90
+;
+; Environment:
+;
+; Kernel mode, early init of first processor.
+;
+; Revision History:
+;
+;--
+
+.386p
+ .xlist
+include ks386.inc
+include callconv.inc ; calling convention macros
+ .list
+
+;
+; NOTE - This definition of PCR gives us 2 instructions to get to some
+; variables that need to be addressable in one instruction. Any
+; such variable (such as current thread) must be accessed via its
+; own access procedure (see below), NOT by KeGetPcr()->PbCurrentThread.
+; (This is only an issue on MP machines.)
+;
+
+_TEXT$00 SEGMENT DWORD PUBLIC 'CODE'
+ ASSUME DS:FLAT, ES:FLAT, SS:NOTHING, FS:NOTHING, GS:NOTHING
+
+cPublicProc _KeGetPcr ,0
+
+ mov eax,PCR[PcSelfPcr]
+ stdRET _KeGetPcr
+
+stdENDP _KeGetPcr
+
+
+;++
+;
+; PKPRCB
+; KeGetCurrentPrcb()
+;
+; Return Value:
+;
+; Pointer to current PRCB.
+;
+;--
+cPublicProc _KeGetCurrentPrcb ,0
+
+ mov eax,PCR[PcPrcb]
+ stdRET _KeGetCurrentPrcb
+
+stdENDP _KeGetCurrentPrcb
+
+
+;++
+;
+; PKTHREAD
+; KeGetCurrentThread()
+;
+; Return Value:
+;
+; Pointer to current Thread object.
+;
+;--
+cPublicProc _KeGetCurrentThread ,0
+
+ mov eax,PCR[PcPrcbData+PbCurrentThread]
+ stdRET _KeGetCurrentThread
+
+stdENDP _KeGetCurrentThread
+
+
+;++
+;
+; KPROCESSOR_MODE
+; KeGetPreviousMode()
+;
+; Return Value:
+;
+; PreviousMode of current thread.
+;
+;--
+cPublicProc _KeGetPreviousMode
+
+ mov eax,PCR[PcPrcbData+PbCurrentThread] ; (eax) -> Thread
+ movzx eax,byte ptr [eax]+ThPreviousMode ; (eax) = PreviousMode
+ stdRET _KeGetPreviousMode
+
+stdENDP _KeGetPreviousMode
+
+
+;++
+;
+; BOOLEAN
+; KeIsExecutingDpc(
+; VOID
+; );
+;
+; Return Value:
+;
+; Value of flag which indicates whether we're executing in DPC context
+;
+;--
+
+cPublicProc _KeIsExecutingDpc ,0
+
+ mov eax,PCR[PcPrcbData.PbDpcRoutineActive]
+ stdRET _KeIsExecutingDpc
+
+stdENDP _KeIsExecutingDpc
+
+
+;++
+;
+; VOID
+; GetMachineBootPointers(
+; )
+;
+; Routine Description:
+;
+; This routine is called at system startup to extract the address of
+; the PCR and machine control values. It is useful only for the P0
+; case where the boot loader must already init the machine before it
+; turns on paging and calls us.
+;
+; Pcr address is extracted from the base of KGDT_R0_PCR.
+;
+; Gdt and Idt are extracted from the machine GDTR and IDTR.
+;
+; TSS is derived from the TSR and related descriptor.
+;
+; Arguments:
+;
+; None.
+;
+; Return Value:
+;
+;
+; (edi) -> gdt
+; (esi) -> pcr
+; (edx) -> tss
+; (eax) -> idt
+;
+;--
+
+cPublicProc GetMachineBootPointers
+
+ push ebp
+ mov ebp,esp
+ sub esp,8
+
+ sgdt fword ptr [ebp-8]
+ mov edi,[ebp-6] ; (edi) = gdt address
+
+ mov cx,fs
+ and cx,(NOT RPL_MASK)
+ movzx ecx,cx
+ add ecx,edi ; (ecx) -> pcr descriptor
+
+ mov dh,[ecx+KgdtBaseHi]
+ mov dl,[ecx+KgdtBaseMid]
+ shl edx,16
+ mov dx,[ecx+KgdtBaseLow] ; (edx) -> pcr
+ mov esi,edx ; (esi) -> pcr
+
+ str cx
+ movzx ecx,cx
+ add ecx,edi ; (ecx) -> TSS descriptor
+
+ mov dh,[ecx+KgdtBaseHi]
+ mov dl,[ecx+KgdtBaseMid]
+ shl edx,16
+ mov dx,[ecx+KgdtBaseLow] ; (edx) -> TSS
+
+ sidt fword ptr [ebp-8]
+ mov eax,[ebp-6] ; (eax) -> Idt
+
+ mov esp,ebp
+ pop ebp
+ stdRET GetMachineBootPointers
+
+stdENDP GetMachineBootPointers
+
+_TEXT$00 ENDS
+ end
+
diff --git a/private/ntos/ke/i386/instemul.asm b/private/ntos/ke/i386/instemul.asm
new file mode 100644
index 000000000..2c7da00c4
--- /dev/null
+++ b/private/ntos/ke/i386/instemul.asm
@@ -0,0 +1,2873 @@
+ title "Vdm Instuction Emulation"
+;++
+;
+; Copyright (c) 1989 Microsoft Corporation
+;
+; Module Name:
+;
+; instemul.asm
+;
+; Abstract:
+;
+; This module contains the routines for emulating instructions and
+; faults to a VDM.
+;
+; Author:
+;
+; Dave Hastings (daveh) 29-March-1991
+;
+; Environment:
+;
+; Kernel mode only.
+;
+; Notes:
+;
+;
+;sudeepb 09-Dec-1992 Very Sonn this file will be deleted and protected
+; mode instruction emulation will be merged in
+; emv86.asm. Particularly following routines will
+; simply become OpcodeInvalid.
+; OpcodeIret
+; OpcodePushf
+; OpcodePopf
+; OpcodeHlt
+; Other routines such as
+; OpcodeCli
+; OpcodeSti
+; OpcodeIN/OUT/SB/Immb etc
+; will map exactly like emv86.asm
+; OpcodeInt will be the main differeing routine.
+;
+; OpcodeDispatch Table will be deleted.
+;
+; So before making any major changes in this file please see
+; Sudeepb or Daveh.
+;
+;neilsa 19-Oct-1993 Size and performance enhancements
+;jonle 15-Nov-1993 - The Debug messages for each opcode may no longer work
+; correctly, because interrupts may not have been enabled
+;
+;
+;
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;
+; Revision History:
+;
+;--
+.386p
+ .xlist
+include ks386.inc
+include i386\kimacro.inc
+include mac386.inc
+include i386\mi.inc
+include callconv.inc
+include ..\..\vdm\i386\vdm.inc
+include ..\..\vdm\i386\vdmtb.inc
+ .list
+
+ extrn VdmOpcode0f:proc
+ extrn OpcodeNPXV86:proc
+ extrn VdmDispatchIntAck:proc ;; only OpcodeSti uses this
+ extrn CommonDispatchException:proc ;; trap.asm
+ extrn _DbgPrint:proc
+ extrn _KeI386VdmIoplAllowed:dword
+ extrn _KeI386VirtualIntExtensions:dword
+ EXTRNP _KeBugCheck,1
+ EXTRNP _Ki386GetSelectorParameters,4
+ EXTRNP _Ki386VdmDispatchIo,5
+ EXTRNP _Ki386VdmDispatchStringIo,8
+ EXTRNP _KiDispatchException,5
+ EXTRNP _VdmPrinterStatus,3
+ EXTRNP KfLowerIrql,1,IMPORT, FASTCALL
+ EXTRNP _VdmPrinterWriteData, 3
+
+; JAPAN - SUPPORT Intel CPU/Non PC/AT machine
+ extrn _VdmFixedStateLinear:dword
+
+ page ,132
+
+ifdef VDMDBG
+%out Debugging version
+endif
+
+;
+; Force assume into place
+;
+
+_PAGE SEGMENT DWORD PUBLIC 'CODE'
+ ASSUME DS:NOTHING, ES:NOTHING, SS:NOTHING, FS:NOTHING, GS:NOTHING
+_PAGE ENDS
+
+_TEXT$00 SEGMENT DWORD PUBLIC 'CODE'
+ ASSUME DS:NOTHING, ES:NOTHING, SS:NOTHING, FS:NOTHING, GS:NOTHING
+_TEXT$00 ENDS
+
+_DATA SEGMENT DWORD PUBLIC 'DATA'
+
+
+;
+; Instruction emulation emulates the following instructions.
+; The emulation affects the noted user mode registers.
+;
+; In protected mode, the following instructions are emulated in the kernel
+;
+; Registers (E)Flags (E)SP SS CS
+; INTnn X X X X
+; INTO X X X X
+; CLI X
+; STI X
+;
+; The following instructions are always emulated by reflection to the
+; Usermode VDM monitor
+;
+; INSB
+; INSW
+; OUTSB
+; OUTSW
+; INBimm
+; INWimm
+; OUTBimm
+; OUTWimm
+; INB
+; INW
+; OUTB
+; OUTW
+;
+; WARNING What do we do about 32 bit io instructions??
+
+
+;
+; OpcodeIndex - packed 1st level table to index OpcodeDispatch table
+;
+ public OpcodeIndex
+diBEGIN OpcodeIndex,VDM_INDEX_Invalid
+ dtI 0fh, VDM_INDEX_0F
+ dtI 26h, VDM_INDEX_ESPrefix
+ dtI 2eh, VDM_INDEX_CSPrefix
+ dtI 36h, VDM_INDEX_SSPrefix
+ dtI 3eh, VDM_INDEX_DSPrefix
+ dtI 64h, VDM_INDEX_FSPrefix
+ dtI 65h, VDM_INDEX_GSPrefix
+ dtI 66h, VDM_INDEX_OPER32Prefix
+ dtI 67h, VDM_INDEX_ADDR32Prefix
+ dtI 6ch, VDM_INDEX_INSB
+ dtI 6dh, VDM_INDEX_INSW
+ dtI 6eh, VDM_INDEX_OUTSB
+ dtI 6fh, VDM_INDEX_OUTSW
+ dtI 9bh, VDM_INDEX_NPX
+ dtI 9ch, VDM_INDEX_PUSHF
+ dtI 9dh, VDM_INDEX_POPF
+ dtI 0cdh, VDM_INDEX_INTnn
+ dtI 0ceh, VDM_INDEX_INTO
+ dtI 0cfh, VDM_INDEX_IRET
+ dtI 0d8h, VDM_INDEX_NPX
+ dtI 0d9h, VDM_INDEX_NPX
+ dtI 0dah, VDM_INDEX_NPX
+ dtI 0dbh, VDM_INDEX_NPX
+ dtI 0dch, VDM_INDEX_NPX
+ dtI 0ddh, VDM_INDEX_NPX
+ dtI 0deh, VDM_INDEX_NPX
+ dtI 0dfh, VDM_INDEX_NPX
+ dtI 0e4h, VDM_INDEX_INBimm
+ dtI 0e5h, VDM_INDEX_INWimm
+ dtI 0e6h, VDM_INDEX_OUTBimm
+ dtI 0e7h, VDM_INDEX_OUTWimm
+ dtI 0ech, VDM_INDEX_INB
+ dtI 0edh, VDM_INDEX_INW
+ dtI 0eeh, VDM_INDEX_OUTB
+ dtI 0efh, VDM_INDEX_OUTW
+ dtI 0f0h, VDM_INDEX_LOCKPrefix
+ dtI 0f2h, VDM_INDEX_REPNEPrefix
+ dtI 0f3h, VDM_INDEX_REPPrefix
+ dtI 0f4h, VDM_INDEX_HLT
+ dtI 0fah, VDM_INDEX_CLI
+ dtI 0fbh, VDM_INDEX_STI
+diEND NUM_OPCODE
+
+;
+; OpcodeDispatch - table of routines used to emulate instructions
+;
+
+ public OpcodeDispatch
+dtBEGIN OpcodeDispatch,OpcodeInvalid
+ dtS VDM_INDEX_0F , Opcode0F
+ dtS VDM_INDEX_ESPrefix , OpcodeESPrefix
+ dtS VDM_INDEX_CSPrefix , OpcodeCSPrefix
+ dtS VDM_INDEX_SSPrefix , OpcodeSSPrefix
+ dtS VDM_INDEX_DSPrefix , OpcodeDSPrefix
+ dtS VDM_INDEX_FSPrefix , OpcodeFSPrefix
+ dtS VDM_INDEX_GSPrefix , OpcodeGSPrefix
+ dtS VDM_INDEX_OPER32Prefix, OpcodeOPER32Prefix
+ dtS VDM_INDEX_ADDR32Prefix, OpcodeADDR32Prefix
+ dtS VDM_INDEX_INSB , OpcodeINSB
+ dtS VDM_INDEX_INSW , OpcodeINSW
+ dtS VDM_INDEX_OUTSB , OpcodeOUTSB
+ dtS VDM_INDEX_OUTSW , OpcodeOUTSW
+ dtS VDM_INDEX_INTnn , OpcodeINTnn
+ dtS VDM_INDEX_INTO , OpcodeINTO
+ dtS VDM_INDEX_INBimm , OpcodeINBimm
+ dtS VDM_INDEX_INWimm , OpcodeINWimm
+ dtS VDM_INDEX_OUTBimm , OpcodeOUTBimm
+ dtS VDM_INDEX_OUTWimm , OpcodeOUTWimm
+ dtS VDM_INDEX_INB , OpcodeINB
+ dtS VDM_INDEX_INW , OpcodeINW
+ dtS VDM_INDEX_OUTB , OpcodeOUTB
+ dtS VDM_INDEX_OUTW , OpcodeOUTW
+ dtS VDM_INDEX_LOCKPrefix , OpcodeLOCKPrefix
+ dtS VDM_INDEX_REPNEPrefix , OpcodeREPNEPrefix
+ dtS VDM_INDEX_REPPrefix , OpcodeREPPrefix
+ dtS VDM_INDEX_CLI , OpcodeCLI
+ dtS VDM_INDEX_STI , OpcodeSTI
+dtEND MAX_VDM_INDEX
+
+ public _ExVdmOpcodeDispatchCounts,_ExVdmSegmentNotPresent
+_ExVdmOpcodeDispatchCounts dd MAX_VDM_INDEX dup(0)
+_ExVdmSegmentNotPresent dd 0
+ifdef VDMDBG
+NUM_TRACE_ENTRIES equ 64
+TRACE_ENTRY_SIZE equ 16
+OpcodeTrace dd NUM_TRACE_ENTRIES*TRACE_ENTRY_SIZE dup (0)
+TracePointer dd OpcodeTrace
+endif
+_DATA ENDS
+
+_PAGE SEGMENT DWORD PUBLIC 'CODE'
+ ASSUME DS:NOTHING, ES:NOTHING, SS:FLAT, FS:NOTHING, GS:NOTHING
+
+ page ,132
+ subttl "Overide Prefix Macro"
+;++
+;
+; Routine Description:
+;
+; This macro generates the code for handling override prefixes
+; The routine name generated is OpcodeXXXXPrefix, where XXXX is
+; the name used in the macro invocation. The code will set the
+; PREFIX_XXXX bit in the Prefix flags.
+;
+; Arguments
+; name = name of prefix
+; EBP -> trap frame
+; EBX -> prefix flags, BL = instruction length count
+; ECX -> byte at the faulting address
+; EDX -> pointer to vdm state in DOS arena
+; ESI -> Reginfo struct
+; EDI -> address of faulting instruction
+;
+; Returns
+; user mode Eip advanced
+; eax advanced
+; edx contains next byte of opcode
+;
+; NOTE: This routine exits by dispatching through the table again.
+;--
+opPrefix macro name
+ public Opcode&name&Prefix
+Opcode&name&Prefix proc
+
+ or [esi].RiPrefixFlags,PREFIX_&name
+
+
+ifdef VDMDBG
+_DATA segment
+Msg&name&Prefix db 'NTVDM: Encountered override prefix &name& %lx at '
+ db 'address %lx', 0ah, 0dh, 0
+_DATA ends
+ push [ebp].TsSegCs
+ push offset FLAT:Msg&name&Prefix
+ call _DbgPrint
+ add esp,12
+
+endif
+
+ jmp OpcodeGenericPrefix ; dispatch to next handler
+
+Opcode&name&Prefix endp
+endm
+
+irp prefix, <ES, CS, SS, DS, FS, GS, OPER32, ADDR32, LOCK, REPNE, REP>
+
+ opPrefix prefix
+
+endm
+
+ page ,132
+ subttl "Instruction Emulation Dispatcher"
+;++
+;
+; Routine Description:
+;
+; This routine dispatches to the opcode specific emulation routine,
+; based on the first byte of the opcode. Two byte opcodes, and prefixes
+; result in another level of dispatching, from the handling routine.
+;
+; Arguments:
+;
+; ebp = pointer to trap frame
+;
+; Returns:
+;
+; Nothing
+;
+;
+
+cPublicProc _Ki386DispatchOpcode,0
+
+ifdef VDMDBG
+ push 1
+ call TraceOpcode
+endif
+
+ sub esp,REGINFOSIZE
+ mov esi, esp ; scratch area
+
+ CsToLinearPM [ebp].TsSegCs, doerr ; initialize reginfo
+
+ mov edi,[ebp].TsEip ; get fault instruction address
+ cmp edi,[esi].RiCsLimit ; check eip
+ ja doerr
+
+ add edi,[esi].RiCsBase
+ movzx ecx,byte ptr [edi] ; get faulting opcode
+
+ mov eax,ecx
+ and eax,0F8h ; check for npx instr
+ cmp eax,0D8h
+ je do30 ; dispatch
+
+ movzx eax, OpcodeIndex[ecx]
+ mov ebx,1 ; length count, flags
+
+ ; All handler routines will get the following on entry
+ ; ebp -> trap frame
+ ; ebx -> prefix flags, instruction length count
+ ; ecx -> byte at the faulting address
+ ; edx -> pointer to vdm state in DOS arena
+ ; interrupts enabled and Irql at APC level
+ ; edi -> address of faulting instruction
+ ; esi -> reginfo struct
+ ; All handler routines will return
+ ; EAX = 0 for failure
+ ; EAX = 1 for success
+if DEVL
+ inc _ExVdmOpcodeDispatchCounts[eax * type _ExVdmOpcodeDispatchCounts]
+endif
+
+ call OpcodeDispatch[eax * type OpcodeDispatch]
+do20:
+ add esp,REGINFOSIZE
+ stdRET _Ki386DispatchOpcode
+
+doerr: xor eax,eax
+ jmp do20
+
+ ;
+ ; If we get here, we have executed an NPX instruction in user mode
+ ; with the emulator installed. If the EM bit was not set in CR0, the
+ ; app really wanted to execute the instruction for detection purposes.
+ ; In this case, we need to clear the TS bit, and restart the instruction.
+ ; Otherwise we need to reflect the exception
+ ;
+do30:
+ call OpcodeNPXV86
+ jmp short do20
+
+stdENDP _Ki386DispatchOpcode
+
+
+ page ,132
+ subttl "Invalid Opcode Handler"
+;++
+;
+; Routine Description:
+;
+; This routine emulates an invalid opcode. It prints the invalid
+; opcode message, and causes a GP fault to be reflected to the
+; debuger
+;
+; Arguments:
+; EBP -> trap frame
+; EBX -> prefix flags, BL = instruction length count
+; ECX -> byte at the faulting address
+; EDX -> pointer to vdm state in DOS arena
+; ESI -> Reginfo struct
+; EDI -> address of faulting instruction
+;
+; Returns:
+;
+; nothing
+;
+
+ public OpcodeInvalid
+OpcodeInvalid proc
+ifdef VDMDBG
+_DATA segment
+MsgInvalidOpcode db 'NTVDM: An invalid opcode %lx was encountered at '
+ db 'address %x:%x',0ah, 0dh, 0
+_DATA ends
+
+ push [ebp].TsEip
+ push [ebp].TsSegCs
+ push edx
+ push offset FLAT:MsgInvalidOpcode
+ call _DbgPrint ; display invalid opcode message
+ add esp,16
+endif
+ xor eax,eax ; ret fail
+ ret
+
+OpcodeInvalid endp
+
+
+ page ,132
+ subttl "Generic Prefix Handler"
+;++
+;
+; Routine Description:
+;
+; This routine handles the generic portion of all of the prefixes,
+; and dispatches the next byte of the opcode.
+;
+; Arguments:
+; EBP -> trap frame
+; EBX -> prefix flags, BL = instruction length count
+; ECX -> byte at the faulting address
+; EDX -> pointer to vdm state in DOS arena
+; ESI -> Reginfo struct
+; EDI -> address of faulting instruction
+;
+; Returns:
+;
+; user mode Eip advanced
+; edx contains next byte of opcode
+;
+
+ public OpcodeGenericPrefix
+OpcodeGenericPrefix proc
+
+ inc edi ; increment eip
+ inc ebx ; increment size
+ cmp bl, 128 ; set arbitrary inst size limit
+ ja ogperr ; in case of pointless prefixes
+
+ mov eax,edi ; current linear address
+ sub eax,[esi].RiCsBase ; make address eip
+ cmp eax,[esi].RiCsLimit ; check eip
+ ja ogperr
+
+ mov cl,byte ptr [edi] ; get next opcode
+
+ movzx eax, OpcodeIndex[ecx]
+if DEVL
+ inc _ExVdmOpcodeDispatchCounts[eax * type _ExVdmOpcodeDispatchCounts]
+endif
+ jmp OpcodeDispatch[eax * type OpcodeDispatch]
+
+ogperr:
+ xor eax,eax ; opcode was NOT handled
+ ret
+
+OpcodeGenericPrefix endp
+
+
+ page ,132
+ subttl "0F Opcode Handler"
+;++
+;
+; Routine Description:
+;
+; This routine emulates a 0Fh opcode. It currently prints a message,
+; and causes a GP fault to be reflected to the debugger.
+;
+; Arguments:
+; EBP -> trap frame
+; EBX -> prefix flags, BL = instruction length count
+; ECX -> byte at the faulting address
+; EDX -> pointer to vdm state in DOS arena
+; ESI -> Reginfo struct
+; EDI -> address of faulting instruction
+;
+; Returns:
+;
+; nothing
+;
+
+ public Opcode0F
+Opcode0F proc
+
+ifdef VDMDBG
+_DATA segment
+Msg0FOpcode db 'NTVDM: A 0F opcode %lx was encountered at '
+ db 'address %lx',0ah, 0dh, 0
+_DATA ends
+ push [ebp].TsEip
+ push [ebp].TsSegCs
+ push offset FLAT:Msg0FOpcode
+ call _DbgPrint ; display invalid opcode message
+ add esp,12
+endif
+
+ mov eax,[ebp].TsEip ; get fault instruction address
+ mov [esi].RiEip,eax
+ mov [esi].RiTrapFrame,ebp
+ mov [esi].RiPrefixFlags,ebx
+ mov eax,dword ptr [ebp].TsEFlags
+ mov [esi].RiEFlags,eax
+
+ call VdmOpcode0F ; enables interrupts
+ test eax,0FFFFh
+ jz o0f20
+
+ mov eax,[esi].RiEip
+ mov [ebp].TsEip,eax
+ mov eax,1
+o0f20:
+ ret
+
+Opcode0F endp
+
+ page ,132
+ subttl "Byte string in Opcode Handler"
+;++
+;
+; Routine Description:
+;
+; This routine emulates an INSB opcode. Currently, it prints
+; a message, and ignores the instruction.
+;
+; Arguments:
+; EBP -> trap frame
+; EBX -> prefix flags, BL = instruction length count
+; ECX -> byte at the faulting address
+; EDX -> pointer to vdm state in DOS arena
+; ESI -> Reginfo struct
+; EDI -> address of faulting instruction
+;
+; Returns:
+;
+; nothing
+;
+; WARNING what to do about size override? ds override?
+
+ public OpcodeINSB
+OpcodeINSB proc
+
+ifdef VDMDBG
+_DATA segment
+MsgINSBOpcode db 'NTVDM: An INSB opcode %lx was encountered at '
+ db 'address %lx',0ah, 0dh, 0
+_DATA ends
+ push [ebp].TsEip
+ push [ebp].TsSegCs
+ push offset FLAT:MsgINSBOpcode
+ call _DbgPrint ; display INSB opcode message
+ add esp,12
+endif
+
+ push ebp ; Trap Frame
+ push ebx ; size of insb
+
+ movzx eax,word ptr [ebp].TsSegEs
+ shl eax,16
+ ; WARNING no support for 32bit edi
+ mov ax,word ptr [ebp].TsEdi ; don't support 32bit'ness
+ push eax ; address
+
+ xor eax, eax
+ mov ecx,1
+ test ebx,PREFIX_REP
+ jz @f
+
+ mov eax, 1
+ ; WARNING no support for 32bit ecx
+ movzx ecx,word ptr [ebp].TsEcx
+@@:
+
+ push ecx ; number of io ops
+ push TRUE ; read op
+ push eax ; REP prefix
+ push 1 ; byte op
+ movzx edx,word ptr [ebp].TsEdx
+ push edx ; port number
+ call _Ki386VdmDispatchStringIo@32 ; use retval
+
+ ret
+
+OpcodeINSB endp
+
+ page ,132
+ subttl "Word String In Opcode Handler"
+;++
+;
+; Routine Description:
+;
+; This routine emulates an INSW opcode. Currently, it prints
+; a message, and ignores the instruction.
+;
+; Arguments:
+; EBP -> trap frame
+; EBX -> prefix flags, BL = instruction length count
+; ECX -> byte at the faulting address
+; EDX -> pointer to vdm state in DOS arena
+; ESI -> Reginfo struct
+; EDI -> address of faulting instruction
+;
+; Returns:
+;
+; nothing
+;
+
+ public OpcodeINSW
+OpcodeINSW proc
+
+ifdef VDMDBG
+_DATA segment
+MsgINSWOpcode db 'NTVDM: An INSW opcode %lx was encountered at '
+ db 'address %lx',0ah, 0dh, 0
+_DATA ends
+ push [ebp].TsEip
+ push [ebp].TsSegCs
+ push offset FLAT:MsgINSWOpcode
+ call _DbgPrint ; display INSW opcode message
+ add esp,12
+endif
+
+ push ebp ; Trap frame
+ push ebx ; sizeof insw
+
+ movzx eax,word ptr [ebp].TsSegEs
+ shl eax,16
+ ; WARNING no support for 32bit edi
+ mov ax,word ptr [ebp].TsEdi
+ push eax ; address
+
+ xor eax, eax
+ mov ecx,1
+ test ebx,PREFIX_REP
+ jz @f
+
+ mov eax, 1
+ ; WARNING no support for 32bit ecx
+ movzx ecx,word ptr [ebp].TsEcx
+@@:
+ movzx edx,word ptr [ebp].TsEdx
+ push ecx ; number of io ops
+ push TRUE ; read op
+ push eax ; REP prefix
+ push 2 ; word size
+ push edx ; port number
+ call _Ki386VdmDispatchStringIo@32 ; use retval
+
+ ret
+
+OpcodeINSW endp
+
+ page ,132
+ subttl "Byte String Out Opcode Handler"
+;++
+;
+; Routine Description:
+;
+; This routine emulates an OUTSB opcode. Currently, it prints
+; a message, and ignores the instruction.
+;
+; Arguments:
+; EBP -> trap frame
+; EBX -> prefix flags, BL = instruction length count
+; ECX -> byte at the faulting address
+; EDX -> pointer to vdm state in DOS arena
+; ESI -> Reginfo struct
+; EDI -> address of faulting instruction
+;
+; Returns:
+;
+; nothing
+;
+
+ public OpcodeOUTSB
+OpcodeOUTSB proc
+
+ifdef VDMDBG
+_DATA segment
+MsgOUTSBOpcode db 'NTVDM: An OUTSB opcode %lx was encountered at '
+ db 'address %lx',0ah, 0dh, 0
+_DATA ends
+ push [ebp].TsEip
+ push [ebp].TsSegCs
+ push offset FLAT:MsgOUTSBOpcode
+ call _DbgPrint ; display OUTSB opcode message
+ add esp,12
+endif
+
+ push ebp ; Trap Frame
+ push ebx ; size of outsb
+
+ movzx eax,word ptr [ebp].TsSegDs
+ shl eax,16
+ ; WARNING don't support 32bit'ness, esi
+ mov ax,word ptr [ebp].TsEsi
+ push eax ; address
+
+ xor eax, eax
+ mov ecx,1
+ test ebx,PREFIX_REP
+ jz @f
+
+ mov eax, 1
+ ; WARNING don't support 32bit'ness ecx
+ movzx ecx,word ptr [ebp].TsEcx
+@@:
+ movzx edx,word ptr [ebp].TsEdx
+ push ecx ; number of io ops
+ push FALSE ; write op
+ push eax ; REP prefix
+ push 1 ; byte op
+ push edx ; port number
+ call _Ki386VdmDispatchStringIo@32 ; use retval
+
+ ret
+
+OpcodeOUTSB endp
+
+ page ,132
+ subttl "Word String Out Opcode Handler"
+;++
+;
+; Routine Description:
+;
+; This routine emulates an OUTSW opcode. Currently, it prints
+; a message, and ignores the instruction
+;
+; Arguments:
+; EBP -> trap frame
+; EBX -> prefix flags, BL = instruction length count
+; ECX -> byte at the faulting address
+; EDX -> pointer to vdm state in DOS arena
+; ESI -> Reginfo struct
+; EDI -> address of faulting instruction
+;
+; Returns:
+;
+; nothing
+;
+
+ public OpcodeOUTSW
+OpcodeOUTSW proc
+
+ifdef VDMDBG
+_DATA segment
+MsgOUTSWOpcode db 'NTVDM: An OUTSW opcode %lx was encountered at '
+ db 'address %lx',0ah, 0dh, 0
+_DATA ends
+
+ push [ebp].TsEip
+ push [ebp].TsSegCs
+ push offset FLAT:MsgOUTSWOpcode
+ call _DbgPrint ; display OUTSW opcode message
+ add esp,12
+endif
+ push ebp ; Trap Frame
+ push ebx ; size of outsb
+
+ movzx eax,word ptr [ebp].TsSegDs
+ shl eax,16
+ ; WARNING don't support 32bit'ness esi
+ mov ax,word ptr [ebp].TsEsi
+ push eax ; address
+
+ xor eax, eax
+ mov ecx,1
+ test ebx,PREFIX_REP
+ jz @f
+
+ mov eax, 1
+ ; WARNING don't support 32bit'ness ecx
+ movzx ecx,word ptr [ebp].TsEcx
+@@:
+ movzx edx,word ptr [ebp].TsEdx
+
+ push ecx ; number of io ops
+ push FALSE ; write op
+ push eax ; REP prefix
+ push 2 ; byte op
+ push edx ; port number
+ call _Ki386VdmDispatchStringIo@32 ; use retval
+
+ ret
+
+OpcodeOUTSW endp
+
+ page ,132
+ subttl "INTnn Opcode Handler"
+;++
+;
+; Routine Description:
+;
+; This routine emulates an INTnn opcode. It retrieves the handler
+; from the IVT, pushes the current cs:ip and flags on the stack,
+; and dispatches to the handler.
+;
+; Arguments:
+; EBP -> trap frame
+; EBX -> prefix flags, BL = instruction length count
+; ECX -> byte at the faulting address
+; EDX -> pointer to vdm state in DOS arena
+; ESI -> Reginfo struct
+; EDI -> address of faulting instruction
+;
+; Returns:
+;
+; Current CS:IP on user stack
+; RiCs:RiEip -> handler from IVT
+;
+
+ public OpcodeINTnn
+OpcodeINTnn proc
+
+ifdef VDMDBG
+_DATA segment
+MsgINTnnOpcode db 'NTVDM: An INTnn opcode %lx was encountered at '
+ db 'address %lx',0ah, 0dh, 0
+_DATA ends
+ push [ebp].TsEip
+ push [ebp].TsSegCs
+ push offset FLAT:MsgINTnnOpcode
+ call _DbgPrint ; display INTnn opcode message
+ add esp,12
+endif
+
+ mov eax,dword ptr [ebp].TsEFlags
+ call GetVirtualBits ; set interrupt flag
+ mov [esi].RiEFlags,eax
+ movzx eax,word ptr [ebp].TsHardwareSegSs
+ call SsToLinear
+ test al,0FFh
+ jz oinerr
+
+ inc edi ; point to int #
+ mov eax,edi ; current linear address
+ sub eax,[esi].RiCsBase ; make address eip
+ cmp eax,[esi].RiCsLimit ; check eip
+ ja oinerr
+
+ movzx ecx,byte ptr [edi] ; get int #
+ inc eax ; inc past end of instruction
+ mov [esi].RiEip,eax ; save for pushint's benefit
+ call PushInt ; will return retcode in al
+ test al,0FFh
+ jz oinerr ; error!
+
+ mov eax,[esi].RiEsp
+ mov [ebp].TsHardwareEsp,eax
+ mov ax,word ptr [esi].RiSegCs
+ mov word ptr [ebp].TsSegCs,ax
+ mov eax,[esi].RiEFlags
+ mov [ebp].TsEFlags,eax
+ mov eax,[esi].RiEip
+ mov [ebp].TsEip,eax
+ mov eax,1
+ ret
+
+oinerr:
+ xor eax,eax
+ ret
+
+
+OpcodeINTnn endp
+
+ page ,132
+ subttl "INTO Opcode Handler"
+;++
+;
+; Routine Description:
+;
+; This routine emulates an INTO opcode. Currently, it prints
+; a message, and reflects a GP fault to the debugger.
+;
+; Arguments:
+; EBP -> trap frame
+; EBX -> prefix flags, BL = instruction length count
+; ECX -> byte at the faulting address
+; EDX -> pointer to vdm state in DOS arena
+; ESI -> Reginfo struct
+; EDI -> address of faulting instruction
+;
+; Returns:
+;
+; nothing
+;
+
+ public OpcodeINTO
+OpcodeINTO proc
+
+ifdef VDMDBG
+_DATA segment
+MsgINTOOpcode db 'NTVDM: An INTO opcode %lx was encountered at '
+ db 'address %lx',0ah, 0dh, 0
+_DATA ends
+ push [ebp].TsEip
+ push [ebp].TsSegCs
+ push offset FLAT:MsgINTOOpcode
+ call _DbgPrint ; display INTO opcode message
+ add esp,12
+endif
+ xor eax,eax
+ ret
+
+OpcodeINTO endp
+
+
+ page ,132
+ subttl "In Byte Immediate Opcode Handler"
+;++
+;
+; Routine Description:
+;
+; This routine emulates an in byte immediate opcode. Currently, it
+; prints a message, and ignores the instruction.
+;
+; Arguments:
+; EBP -> trap frame
+; EBX -> prefix flags, BL = instruction length count
+; ECX -> byte at the faulting address
+; EDX -> pointer to vdm state in DOS arena
+; ESI -> Reginfo struct
+; EDI -> address of faulting instruction
+;
+; Returns:
+;
+; nothing
+;
+
+ public OpcodeINBimm
+OpcodeINBimm proc
+
+ifdef VDMDBG
+_DATA segment
+MsgINBimmOpcode db 'NTVDM: An INBimm opcode %lx was encountered at '
+ db 'address %lx',0ah, 0dh, 0
+_DATA ends
+ push [ebp].TsEip
+ push [ebp].TsSegCs
+ push offset FLAT:MsgINBimmOpcode
+ call _DbgPrint ; display INBimm opcode message
+ add esp,12
+endif
+
+ inc ebx ; length count
+ inc edi
+ mov eax,edi ; current linear address
+ sub eax,[esi].RiCsBase ; make address eip
+ cmp eax,[esi].RiCsLimit ; check eip
+ ja oibi20
+
+ movzx ecx,byte ptr [edi]
+
+; (eax) = inst. size
+; read op
+; I/O size = 1
+; (ecx) = port number
+
+ stdCall _Ki386VdmDispatchIo, <ecx, 1, TRUE, ebx, ebp>
+ ret
+oibi20:
+ xor eax, eax ; not handled
+ ret
+
+OpcodeINBimm endp
+
+ page ,132
+ subttl "Word In Immediate Opcode Handler"
+;++
+;
+; Routine Description:
+;
+; This routine emulates an in word immediate opcode. Currently, it
+; prints a message, and ignores the instruction.
+;
+; Arguments:
+; EBP -> trap frame
+; EBX -> prefix flags, BL = instruction length count
+; ECX -> byte at the faulting address
+; EDX -> pointer to vdm state in DOS arena
+; ESI -> Reginfo struct
+; EDI -> address of faulting instruction
+;
+; Returns:
+;
+; nothing
+;
+
+ public OpcodeINWimm
+OpcodeINWimm proc
+
+ifdef VDMDBG
+_DATA segment
+MsgINWimmOpcode db 'NTVDM: An INWimm opcode %lx was encountered at '
+ db 'address %lx',0ah, 0dh, 0
+_DATA ends
+ push [ebp].TsEip
+ push [ebp].TsSegCs
+ push offset FLAT:MsgINWimmOpcode
+ call _DbgPrint ; display INWimm opcode message
+ add esp,12
+endif
+
+ inc ebx ; length count
+ inc edi
+ mov eax,edi ; current linear address
+ sub eax,[esi].RiCsBase ; make address eip
+ cmp eax,[esi].RiCsLimit ; check eip
+ ja oiwi20
+
+ movzx ecx,byte ptr [edi]
+
+; TRUE - read op
+; 2 - word op
+; ecx - port number
+ stdCall _Ki386VdmDispatchIo, <ecx, 2, TRUE, ebx, ebp>
+ ret
+oiwi20:
+ xor eax, eax ; not handled
+ ret
+
+OpcodeINWimm endp
+
+ page ,132
+ subttl "Out Byte Immediate Opcode Handler"
+;++
+;
+; Routine Description:
+;
+; This routine emulates an invalid opcode. Currently, it prints
+; a message, and ignores the instruction.
+;
+; Arguments:
+; EBP -> trap frame
+; EBX -> prefix flags, BL = instruction length count
+; ECX -> byte at the faulting address
+; EDX -> pointer to vdm state in DOS arena
+; ESI -> Reginfo struct
+; EDI -> address of faulting instruction
+;
+; Returns:
+;
+; nothing
+;
+
+ public OpcodeOUTBimm
+OpcodeOUTBimm proc
+
+ifdef VDMDBG
+_DATA segment
+MsgOUTBimmOpcode db 'NTVDM: An OUTBimm opcode %lx was encountered at '
+ db 'address %lx',0ah, 0dh, 0
+_DATA ends
+ push [ebp].TsEip
+ push [ebp].TsSegCs
+ push offset FLAT:MsgOUTBimmOpcode
+ call _DbgPrint ; display OUTBimm opcode message
+ add esp,12
+endif
+
+ inc ebx ; length count
+ inc edi
+ mov eax,edi ; current linear address
+ sub eax,[esi].RiCsBase ; make address eip
+ cmp eax,[esi].RiCsLimit ; check eip
+ ja oobi20
+
+ movzx ecx,byte ptr [edi]
+
+; FALSE - write op
+; 1 - byte op
+; ecx - port #
+
+ stdCall _Ki386VdmDispatchIo, <ecx, 1, FALSE, ebx, ebp>
+ ret
+oobi20:
+ xor eax, eax ; not handled
+ ret
+
+OpcodeOUTBimm endp
+
+ page ,132
+ subttl "Out Word Immediate Opcode Handler"
+;++
+;
+; Routine Description:
+;
+; This routine emulates an out word immediate opcode. Currently,
+; it prints a message, and ignores the instruction.
+;
+; Arguments:
+; EBP -> trap frame
+; EBX -> prefix flags, BL = instruction length count
+; ECX -> byte at the faulting address
+; EDX -> pointer to vdm state in DOS arena
+; ESI -> Reginfo struct
+; EDI -> address of faulting instruction
+;
+; Returns:
+;
+; nothing
+;
+
+ public OpcodeOUTWimm
+OpcodeOUTWimm proc
+
+ifdef VDMDBG
+_DATA segment
+MsgOUTWimmOpcode db 'NTVDM: An OUTWimm opcode %lx was encountered at '
+ db 'address %lx',0ah, 0dh, 0
+_DATA ends
+
+ push [ebp].TsEip
+ push [ebp].TsSegCs
+ push offset FLAT:MsgOUTWimmOpcode
+ call _DbgPrint ; display OUTWimm opcode message
+ add esp,12
+endif
+
+ inc ebx ; length count
+ inc edi
+ mov eax,edi ; current linear address
+ sub eax,[esi].RiCsBase ; make address eip
+ cmp eax,[esi].RiCsLimit ; check eip
+ ja oowi20
+
+ movzx ecx,byte ptr [edi]
+
+; FALSE - write op
+; 2 - word op
+; ecx - port number
+ stdCall _Ki386VdmDispatchIo, <ecx, 2, FALSE, ebx, ebp>
+ ret
+
+oowi20:
+ xor eax, eax ; not handled
+ ret
+
+OpcodeOUTWimm endp
+
+ page ,132
+ subttl "INB Opcode Handler"
+;++
+;
+; Routine Description:
+;
+; This routine emulates an INB opcode. Currently, it prints
+; a message, and ignores the instruction.
+;
+; Arguments:
+; EBP -> trap frame
+; EBX -> prefix flags, BL = instruction length count
+; ECX -> byte at the faulting address
+; EDX -> pointer to vdm state in DOS arena
+; ESI -> Reginfo struct
+; EDI -> address of faulting instruction
+;
+; Returns:
+;
+; nothing
+;
+
+ public OpcodeINB
+OpcodeINB proc
+
+ifdef VDMDBG
+_DATA segment
+MsgINBOpcode db 'NTVDM: An INB opcode %lx was encountered at '
+ db 'address %lx',0ah, 0dh, 0
+_DATA ends
+
+ push [ebp].TsEip
+ push [ebp].TsSegCs
+ push offset FLAT:MsgINBOpcode
+ call _DbgPrint ; display INB opcode message
+ add esp,12
+endif
+
+ movzx eax,word ptr [ebp].TsEdx
+
+; TRUE - read op
+; 1 - byte op
+; eax - port number
+
+ cmp eax, 3bdh
+ jz oib_prt1
+ cmp eax, 379h
+ jz oib_prt1
+ cmp eax, 279h
+ jz oib_prt1
+
+oib_reflect:
+ stdCall _Ki386VdmDispatchIo, <eax, 1, TRUE, ebx, ebp>
+ ret
+oib20:
+ xor eax, eax ; not handled
+ ret
+
+oib_prt1:
+ ; call printer status routine with port number, size, trap frame
+ movzx ebx, bl ;clear prefix flags
+ push eax
+ stdCall _VdmPrinterStatus, <eax, ebx, ebp>
+ or al,al
+ pop eax
+ jz short oib_reflect
+ mov al, 1
+ ret
+
+OpcodeINB endp
+
+ page ,132
+ subttl "INW Opcode Handler"
+;++
+;
+; Routine Description:
+;
+; This routine emulates an INW opcode. Currently, it prints
+; a message, and ignores the instruction.
+;
+; Arguments:
+; EBP -> trap frame
+; EBX -> prefix flags, BL = instruction length count
+; ECX -> byte at the faulting address
+; EDX -> pointer to vdm state in DOS arena
+; ESI -> Reginfo struct
+; EDI -> address of faulting instruction
+;
+; Returns:
+;
+; nothing
+;
+
+ public OpcodeINW
+OpcodeINW proc
+
+ifdef VDMDBG
+_DATA segment
+MsgINWOpcode db 'NTVDM: An INW opcode %lx was encountered at '
+ db 'address %lx',0ah, 0dh, 0
+_DATA ends
+ push [ebp].TsEip
+ push [ebp].TsSegCs
+ push offset FLAT:MsgINWOpcode
+ call _DbgPrint ; display INW opcode message
+ add esp,12
+endif
+
+ movzx eax,word ptr [ebp].TsEdx
+
+; TRUE - read operation
+; 2 - word op
+; eax - port number
+ stdCall _Ki386VdmDispatchIo, <eax, 2, TRUE, ebx, ebp>
+ ret
+
+OpcodeINW endp
+
+ page ,132
+ subttl "OUTB Opcode Handler"
+;++
+;
+; Routine Description:
+;
+; This routine emulates an OUTB opcode. Currently, it prints
+; a message, and ignores the instruction.
+;
+; Arguments:
+; EBP -> trap frame
+; EBX -> prefix flags, BL = instruction length count
+; ECX -> byte at the faulting address
+; EDX -> pointer to vdm state in DOS arena
+; ESI -> Reginfo struct
+; EDI -> address of faulting instruction
+;
+; Returns:
+;
+; nothing
+;
+
+ public OpcodeOUTB
+OpcodeOUTB proc
+
+ifdef VDMDBG
+_DATA segment
+MsgOUTBOpcode db 'NTVDM: An OUTB opcode %lx was encountered at '
+ db 'address %lx',0ah, 0dh, 0
+_DATA ends
+
+ push [ebp].TsEip
+ push [ebp].TsSegCs
+ push offset FLAT:MsgOUTBOpcode
+ call _DbgPrint ; display OUTB opcode message
+ add esp,12
+endif
+
+ movzx eax,word ptr [ebp].TsEdx
+ cmp eax, 03BCh
+ je short oob_printerVDD
+ cmp eax, 0378h
+ je short oob_printerVDD
+ cmp eax, 0278h
+ jz short oob_printerVDD
+
+oob_reflect:
+; FALSE - write op
+; 1 - byte op
+; eax - port number
+ stdCall _Ki386VdmDispatchIo, <eax, 1, FALSE, ebx, ebp>
+ ret
+
+oob_printerVDD:
+ movzx ebx, bl ; instruction size
+ push eax ; save port address
+ stdCall _VdmPrinterWriteData, <eax, ebx, ebp>
+ or al,al ;
+ pop eax
+ jz short oob_reflect
+ mov al, 1
+ ret
+
+OpcodeOUTB endp
+
+ page ,132
+ subttl "OUTW Opcode Handler"
+;++
+;
+; Routine Description:
+;
+; This routine emulates an OUTW opcode. Currently, it prints
+; a message, and ignores the instruction.
+;
+; Arguments:
+; EBP -> trap frame
+; EBX -> prefix flags, BL = instruction length count
+; ECX -> byte at the faulting address
+; EDX -> pointer to vdm state in DOS arena
+; ESI -> Reginfo struct
+; EDI -> address of faulting instruction
+;
+; Returns:
+;
+; nothing
+;
+
+ public OpcodeOUTW
+OpcodeOUTW proc
+
+ifdef VDMDBG
+_DATA segment
+MsgOUTWOpcode db 'NTVDM: An OUTW opcode %lx was encountered at '
+ db 'address %lx',0ah, 0dh, 0
+_DATA ends
+
+ push [ebp].TsEip
+ push [ebp].TsSegCs
+ push offset FLAT:MsgOUTWOpcode
+ call _DbgPrint ; display OUTW opcode message
+ add esp,12
+endif
+
+ movzx eax,word ptr [ebp].TsEdx
+
+; FALSE - write op
+; 2 - word op
+; edi - port #
+ stdCall _Ki386VdmDispatchIo, <eax, 2, FALSE, ebx, ebp>
+ ret
+
+OpcodeOUTW endp
+
+ page ,132
+ subttl "CLI Opcode Handler"
+;++
+;
+; Routine Description:
+;
+; This routine emulates an CLI opcode. Currently, it prints
+; a message, and clears the virtual interrupt flag in the VdmTeb.
+;
+; Arguments:
+; EBP -> trap frame
+; EBX -> prefix flags, BL = instruction length count
+; ECX -> byte at the faulting address
+; EDX -> pointer to vdm state in DOS arena
+; ESI -> Reginfo struct
+; EDI -> address of faulting instruction
+;
+; Returns:
+;
+; nothing
+;
+
+ public OpcodeCLI
+OpcodeCLI proc
+
+ifdef VDMDBG
+_DATA segment
+MsgCLIOpcode db 'NTVDM: An CLI opcode %lx was encountered at '
+ db 'address %lx',0ah, 0dh, 0
+_DATA ends
+ push [ebp].TsEip
+ push [ebp].TsSegCs
+ push offset FLAT:MsgCLIOpcode
+ call _DbgPrint ; display CLI opcode message
+ add esp,12
+endif
+
+ mov eax,[ebp].TsEFlags
+ and eax,NOT EFLAGS_INTERRUPT_MASK
+ call SetVirtualBits
+ inc dword ptr [ebp].TsEip
+
+ mov eax,1
+ ret
+
+OpcodeCLI endp
+
+ page ,132
+ subttl "STI Opcode Handler"
+;++
+;
+; Routine Description:
+;
+; This routine emulates an STI opcode. Currently, it prints
+; a message, and sets the virtual interrupt flag in the VDM teb.
+;
+; Arguments:
+; EBP -> trap frame
+; EBX -> prefix flags, BL = instruction length count
+; ECX -> byte at the faulting address
+; EDX -> pointer to vdm state in DOS arena
+; ESI -> Reginfo struct
+; EDI -> address of faulting instruction
+;
+; Returns:
+;
+; nothing
+;
+
+ public OpcodeSTI
+OpcodeSTI proc
+
+ifdef VDMDBG
+_DATA segment
+MsgSTIOpcode db 'NTVDM: An STI opcode %lx was encountered at '
+ db 'address %lx',0ah, 0dh, 0
+_DATA ends
+ push [ebp].TsEip
+ push [ebp].TsSegCs
+ push offset FLAT:MsgSTIOpcode
+ call _DbgPrint ; display STI opcode message
+ add esp,12
+endif
+
+ mov eax,[ebp].TsEFlags
+ or eax,EFLAGS_INTERRUPT_MASK
+ call SetVirtualBits
+ inc dword ptr [ebp].TsEip
+ mov eax,_VdmFixedStateLinear
+ mov eax,dword ptr [eax]
+ test eax,VDM_INTERRUPT_PENDING
+ jz os10
+
+ call VdmDispatchIntAck
+os10:
+ mov eax,1
+ ret
+
+OpcodeSTI endp
+
+ page ,132
+ subttl "Check Vdm Flags"
+;++
+;
+; Routine Description:
+;
+; This routine checks the flags that are going to be used for the
+; dos or windows application.
+;
+; Arguments:
+;
+; ecx = EFlags to be set
+; esi = address of reg info
+;
+; Returns:
+;
+; ecx = fixed flags
+;
+ public CheckVdmFlags
+CheckVdmFlags proc
+
+ push eax
+ mov eax,[esi].RiEFlags
+ and eax,EFLAGS_V86_MASK
+ test _KeI386VdmIoplAllowed,1
+ jnz cvf30
+
+ test _KeI386VirtualIntExtensions, V86_VIRTUAL_INT_EXTENSIONS OR PM_VIRTUAL_INT_EXTENSIONS
+ jnz cvf40
+
+cvf10: or ecx,EFLAGS_INTERRUPT_MASK
+cvf20: and ecx,NOT (EFLAGS_IOPL_MASK OR EFLAGS_NT_MASK OR EFLAGS_V86_MASK OR EFLAGS_VIF OR EFLAGS_VIP)
+ or ecx,eax ; restore original v86 bit
+ pop eax
+ ret
+
+cvf30: test eax,EFLAGS_V86_MASK
+ jz cvf10
+
+ jmp cvf20
+
+cvf40: test eax,EFLAGS_V86_MASK
+ jz cvf60
+
+ test _KeI386VirtualIntExtensions,V86_VIRTUAL_INT_EXTENSIONS
+ jz cvf10
+
+cvf50: push eax
+ mov eax,ecx
+ and eax,EFLAGS_INTERRUPT_MASK
+ shl eax,0ah
+ pop eax
+ jmp cvf10
+
+cvf60: test _KeI386VirtualIntExtensions,PM_VIRTUAL_INT_EXTENSIONS
+ jz cvf10
+
+ jmp cvf50
+CheckVdmFlags endp
+
+ page ,132
+ subttl "Get Virtual Interrupt Flag"
+;++
+;
+; Routine Description:
+;
+; This routine correctly gets the VDMs virtual interrupt flag and
+; puts it into an EFlags image to be put on the stack.
+;
+; Arguments:
+;
+; eax = EFlags value
+;
+; Returns:
+;
+; eax = EFlags value with correct setting for IF
+;
+; Uses:
+; ecx
+;
+ public GetVirtualBits
+GetVirtualBits proc
+
+ test _KeI386VdmIoplAllowed,1
+ jnz gvb60
+
+ test _KeI386VirtualIntExtensions, V86_VIRTUAL_INT_EXTENSIONS OR PM_VIRTUAL_INT_EXTENSIONS
+ jnz gvb30
+
+gvb10: and eax,NOT EFLAGS_INTERRUPT_MASK
+ mov ecx,_VdmFixedStateLinear ; get pointer to Teb
+ mov ecx,dword ptr [ecx] ; get virtual int flag
+ and ecx,VDM_VIRTUAL_INTERRUPTS OR VDM_VIRTUAL_AC
+ or eax,ecx ; put virtual int flag into flags
+ or eax,EFLAGS_IOPL_MASK ; make it look like a 386
+ ret
+
+gvb30: test eax, EFLAGS_V86_MASK
+ jz gvb50
+
+ test _KeI386VirtualIntExtensions, V86_VIRTUAL_INT_EXTENSIONS
+ jz gvb10
+
+gvb40: mov ecx,eax
+ and ecx,EFLAGS_VIF
+ shr ecx,0ah ; mov vif to if posn
+ and eax,NOT EFLAGS_INTERRUPT_MASK
+ or eax,ecx
+
+ mov ecx,_VdmFixedStateLinear
+ mov ecx,dword ptr [ecx]
+ and ecx,VDM_VIRTUAL_AC
+ and eax,NOT EFLAGS_ALIGN_CHECK
+ or eax,ecx
+ or eax,EFLAGS_IOPL_MASK
+ ret
+
+gvb50: test _KeI386VirtualIntExtensions, PM_VIRTUAL_INT_EXTENSIONS
+ jz gvb10
+
+ jmp gvb40
+
+gvb60: test eax,EFLAGS_V86_MASK
+ jz gvb10
+
+ mov ecx,_VdmFixedStateLinear
+ mov ecx,dword ptr [ecx]
+ and ecx,VDM_VIRTUAL_AC
+ and eax,NOT EFLAGS_ALIGN_CHECK
+ or eax,ecx
+ or eax,EFLAGS_IOPL_MASK
+ ret
+
+GetVirtualBits endp
+
+ page ,132
+ subttl "Set Virtual Interrupt Flag"
+;++
+;
+; Routine Description:
+;
+; This routine correctly sets the VDMs virtual interrupt flag.
+;
+; Arguments:
+;
+; eax = EFlags value
+;
+; Returns:
+;
+; Virtual interrupt flag set
+;
+ public SetVirtualBits
+SetVirtualBits proc
+Flags equ [ebp - 4]
+
+ push ebp
+ mov ebp,esp
+ sub esp,4
+
+ push edx
+ mov Flags,eax
+ mov edx,_VdmFixedStateLinear ; get pointer to Teb
+ and eax,EFLAGS_INTERRUPT_MASK ; isolate int flag
+ MPLOCK and [edx],NOT VDM_VIRTUAL_INTERRUPTS
+ MPLOCK or [edx],eax ; place virtual int flag value
+ test _KeI386VirtualIntExtensions, V86_VIRTUAL_INT_EXTENSIONS OR PM_VIRTUAL_INT_EXTENSIONS
+ jnz svb40
+svb20:
+ ; WARNING 32 bit support!
+ test ebx,PREFIX_OPER32
+ jz svb30 ; 16 bit instr
+
+ mov eax,Flags
+ and eax,EFLAGS_ALIGN_CHECK
+ MPLOCK and dword ptr [edx],NOT EFLAGS_ALIGN_CHECK
+ MPLOCK or [edx],eax
+svb30: pop edx
+ mov esp,ebp
+ pop ebp
+ ret
+
+svb40: test Flags,dword ptr EFLAGS_V86_MASK
+ jz svb60
+
+ test _KeI386VirtualIntExtensions,V86_VIRTUAL_INT_EXTENSIONS
+ jz svb20
+
+svb50: shl eax,0ah
+ jmp svb20
+
+svb60: test _KeI386VirtualIntExtensions,PM_VIRTUAL_INT_EXTENSIONS
+ jz svb20
+
+ jmp svb50
+SetVirtualBits endp
+
+
+ page ,132
+ subttl "Reflect Exception to a Vdm"
+;++
+;
+; Routine Description:
+;
+; This routine reflects an exception to a VDM. It uses the information
+; in the trap frame to determine what exception to reflect, and updates
+; the trap frame with the new CS, EIP, SS, and SP values
+;
+; Arguments:
+;
+; ebp -> Trap frame
+; ss:esp + 4 = trap number
+;
+; Returns
+;
+; Nothing
+;
+; Notes:
+; Interrupts are enable upon entry, Irql is at APC level
+; This routine may not preserve all of the non-volatile registers if
+; a fault occurs.
+;
+cPublicProc _Ki386VdmReflectException,1
+
+RI equ [ebp - REGINFOSIZE]
+
+ push ebp
+ mov ebp,esp
+ sub esp,REGINFOSIZE
+
+ pushad
+
+ mov esi,_VdmFixedStateLinear
+
+ cmp word ptr [ebp + 8],0dh
+ jne vre00
+
+ test dword ptr [esi],VDM_BREAK_EXCEPTIONS
+ jnz vrexcd ; reflect the exception to 32
+vre00:
+ test dword ptr [esi],VDM_BREAK_DEBUGGER
+ jz vre04 ; reflect to vdm
+
+ cmp word ptr [ebp + 8],1
+ je vrexc1
+
+ cmp word ptr [ebp + 8],3
+ je vrexc3
+vre04:
+ mov esi,[ebp]
+ cmp word ptr [esi].TsSegCs, KGDT_R3_CODE OR RPL_MASK ; int sim after fault?
+ je vre28
+if DEVL
+ cmp word ptr [ebp + 8],11
+ jne xyzzy1
+ inc _ExVdmSegmentNotPresent
+xyzzy1:
+endif
+
+ mov RI.RiTrapFrame,esi
+ mov eax,[esi].TsHardwareSegSs
+ mov RI.RiSegSs,eax
+ mov eax,[esi].TsHardwareEsp
+ mov RI.RiEsp,eax
+ mov eax,[esi].TsEFlags
+ mov RI.RiEFlags,eax
+ mov eax,[esi].TsEip
+ mov RI.RiEip,eax
+ mov eax,[esi].TsSegCs
+ mov RI.RiSegCs,eax
+ lea esi,RI
+ call CsToLinear ; uses eax as selector
+ test al,0FFh
+ jz vrerr
+
+ mov eax,[esi].RiSegSs
+ call SsToLinear
+ test al,0FFh
+ jz vrerr
+
+ mov ecx,[ebp + 8]
+ call PushException
+ test al,0FFh
+ jz vrerr
+
+ mov esi,RI.RiTrapFrame
+ mov eax,RI.RiEsp
+ mov [esi].TsHardwareEsp,eax
+ mov eax,RI.RiSegSs
+ mov [esi].TsHardwareSegSs,eax
+ mov eax,RI.RiEFlags
+ mov [esi].TsEFlags,eax
+ mov eax,RI.RiSegCs
+ mov [esi].TsSegCs,eax
+ mov eax,RI.RiEip
+ mov [esi].TsEip,eax
+ cmp word ptr [ebp + 8],1
+ jne vre28
+ and dword ptr [esi].TsEFlags, NOT EFLAGS_TF_MASK
+
+vre28:
+ popad
+ mov eax,1 ; handled
+
+vre30:
+ mov esp,ebp
+ pop ebp
+ stdRET _Ki386VdmReflectException
+
+vrerr:
+ popad
+ xor eax,eax
+ jmp vre30
+
+vrexc1:
+ mov eax, [ebp]
+ and dword ptr [eax]+TsEflags, not EFLAGS_TF_MASK
+ mov eax, [ebp]+TsEip ; (eax)-> faulting instruction
+ stdCall _VdmDispatchException <[ebp],STATUS_SINGLE_STEP,eax,0,0,0,0>
+ jmp short vre28
+
+vrexc3:
+ mov eax,BREAKPOINT_BREAK
+ mov ebx, [ebp]
+ mov ebx, [ebx]+TsEip
+ dec ebx ; (eax)-> int3 instruction
+ stdCall _VdmDispatchException <[ebp],STATUS_BREAKPOINT,ebx,3,eax,ecx,edx>
+ jmp short vre28
+
+vrexcd:
+ mov eax, [ebp]
+ mov eax, [eax]+TsEip
+ stdCall _VdmDispatchException <[ebp],STATUS_ACCESS_VIOLATION,eax,2,0,-1,0>
+ jmp short vre28
+
+stdENDP _Ki386VdmReflectException
+
+
+ page ,132
+ subttl "Reflect Segment Not Present Exception to a Vdm"
+;++
+;
+; Routine Description:
+;
+; This routine reflects an TRAP B to a VDM. It uses the information
+; in the trap frame to determine what exception to reflect, and updates
+; the trap frame with the new CS, EIP, SS, and SP values
+;
+; Arguments:
+;
+; ebp -> Trap frame
+;
+; Returns
+;
+; 0 is returned if the reflection fails.
+;
+
+cPublicProc _Ki386VdmSegmentNotPresent,0
+
+ mov edi,PCR[PcTeb]
+ mov ecx,VDM_FAULT_HANDLER_SIZE * 0Bh
+ mov edi,[edi].TbVdm
+ xor ebx, ebx
+ lea esi,[edi].VtPmStackInfo ; (edi)->dpmi stack struct
+ lea edi,[edi+ecx].VtFaultHandlers ; (edi)->FaultHandler
+ cmp word ptr [esi].VpLockCount, 0 ; switching stacks?
+ jz short @f ; yes, we can handle it
+ ; no, let normal code check
+ ; for stack faults
+
+ pop eax ; (eax) = return addr
+ push 0bh
+ push eax
+ jmp _Ki386VdmReflectException
+
+@@:
+if DEVL
+ inc _ExVdmSegmentNotPresent
+endif
+ inc word ptr [esi].VpLockCount
+
+; save stuff just like SwitchToHandlerStack does
+ mov eax, [ebp].TsEip
+ mov [esi].VpSaveEip, eax
+ mov eax, [ebp].TsHardwareEsp
+ mov [esi].VpSaveEsp, eax
+ mov ax, [ebp].TsHardwareSegSs
+ mov [esi].VpSaveSsSelector, ax
+
+ mov bx,word ptr [esi].VpSsSelector
+ mov eax, PCR[PcGdt]
+ lea eax, [eax]+KGDT_LDT
+ mov ch, [eax+KgdtBaseHi]
+ mov cl, [eax+KgdtBaseMid]
+ shl ecx, 16
+ and ebx, 0fffffff8h
+ mov cx, [eax+KgdtBaseLow]
+ lea eax, [ecx+ebx]
+ mov bh, [eax+KgdtBaseHi]
+ mov bl, [eax+KgdtBaseMid]
+ shl ebx, 16
+ mov bx, [eax+KgdtBaseLow] ; (ebx) = Base of SS
+
+ mov eax, [ebp].TsEFlags
+ call GetVirtualBits ; (eax) = app's eflags
+ push esi
+ mov edx, 0fe0h ; dpmistack offset (per win31)
+ test word ptr [esi].VpFlags, 1 ; 32-bit frame?
+ jz short @f
+ sub edx, 8 * 4
+ add edx, ebx
+ mov esi, [ebp].TsHardwareEsp
+ mov ecx, [ebp].TsHardwareSegSs
+ mov [edx + 20], eax ; push flags
+ mov [edx + 24], esi ; put esp on new stack
+ mov [edx + 28], ecx ; put ss on new stack
+ mov ecx, [ebp].TsSegCs
+ mov eax, [ebp].TsEip
+ mov esi, [ebp].TsErrCode
+ mov [edx + 16], ecx ; push cs
+ mov [edx + 12], eax ; push ip
+ mov [edx], esi ; push error code
+ pop esi
+ mov ecx, [esi].VpDosxFaultIretD
+ mov eax, ecx
+ shr eax, 16
+ and ecx, 0ffffh
+ mov [edx + 4], eax ; push fault iret seg
+ mov [edx], ecx ; push fault iret offset
+ jmp short vsnp_update
+@@:
+ sub edx, 8 * 2
+ add edx, ebx
+ mov esi, [ebp].TsHardwareEsp
+ mov ecx, [ebp].TsHardwareSegSs
+ mov [edx + 10], ax ; push flags
+ mov [edx + 12], si ; put esp on new stack
+ mov [edx + 14], cx ; put ss on new stack
+ mov ecx, [ebp].TsSegCs
+ mov eax, [ebp].TsEip
+ mov esi, [ebp].TsErrCode
+ mov [edx + 8], cx ; push cs
+ mov [edx + 6], ax ; push ip
+ mov [edx + 4], si ; push error code
+ pop esi
+ mov ecx, [esi].VpDosxFaultIret
+ mov eax, ecx
+ shr eax, 16
+ mov [edx + 2], ax ; push fault iret seg
+ mov [edx], cx ; push fault iret offset
+
+vsnp_update:
+ mov eax,[edi].VfEip
+ sub edx, ebx
+ mov cx, word ptr [edi].VfCsSelector
+ mov bx, word ptr [esi].VpSsSelector
+ test dword ptr [edi].VfFlags, VDM_INT_INT_GATE
+ jz short @f
+
+ mov esi,_VdmFixedStateLinear ; get pointer to Teb
+ MPLOCK and [esi],NOT VDM_VIRTUAL_INTERRUPTS
+ and dword ptr [ebp].TsEflags, 0FFF7FFFFH ; clear VIF
+@@:
+ mov [ebp].TsSegCs, cx
+ mov [ebp].TsEip, eax
+ mov [ebp].TsHardwareEsp,edx
+ mov [ebp].TsHardwareSegSs,bx
+
+ mov eax, 1
+ stdRET _Ki386VdmSegmentNotPresent
+
+stdENDP _Ki386VdmSegmentNotPresent
+
+ page ,132
+ subttl "Dispatch UserMode Exception to a Vdm"
+;++
+;
+; Routine Description:
+;
+; Dispatches exception for vdm in from the kernel, by invoking
+; CommonDispatchException.
+;
+; Arguments: See CommonDispatchException for parameter description
+;
+; VOID
+; VdmDispatchException(
+; PKTRAP_FRAME TrapFrame,
+; NTSTATUS ExcepCode,
+; PVOID ExcepAddr,
+; ULONG NumParms,
+; ULONG Parm1,
+; ULONG Parm2,
+; ULONG Parm3
+; )
+;
+; Returns
+;
+; Nothing
+;
+; Notes:
+;
+; This routine may not preserve all of the non-volatile registers if
+; a fault occurs.
+;
+cPublicProc _VdmDispatchException,7
+
+TrapFrame equ [ebp+8]
+ExcepCode equ [ebp+12]
+ExcepAddr equ [ebp+16]
+NumParms equ [ebp+20]
+Parm1 equ [ebp+24]
+Parm2 equ [ebp+28]
+Parm3 equ [ebp+32]
+
+ push ebp
+ mov ebp,esp
+ pushad
+
+ xor ecx, ecx ; lower irql to 0
+ fstCall KfLowerIrql ; allow apc's and debuggers in!
+
+ mov eax, ExcepCode
+ mov ebx, ExcepAddr
+ mov ecx, NumParms
+ mov edx, Parm1
+ mov esi, Parm2
+ mov edi, Parm3
+ mov ebp, TrapFrame
+ call CommonDispatchException
+
+ popad
+ stdRET _VdmDispatchException
+
+stdENDP _VdmDispatchException
+
+
+
+
+ page ,132
+ subttl "Push Interrupt frame on user stack"
+;++
+;
+; Routine Description:
+;
+; This routine pushes an interrupt frame on the user stack
+;
+; Arguments:
+;
+; ecx = interrupt #
+; esi = address of reg info
+; Returns:
+;
+; interrupt frame pushed on stack
+; reg info updated
+;
+ public PushInt
+PushInt proc
+
+ push ebx
+ push edi
+
+pi100:
+;
+; Handle dispatching interrupts directly to the handler, rather than
+; to the dos extender
+;
+ ;
+ ; Get a the information on the interrupt handler
+ ;
+ .errnz (VDM_INTERRUPT_HANDLER_SIZE - 8)
+ mov eax,PCR[PcTeb]
+ mov eax,[eax].TbVdm
+ lea eax,[eax].VtInterruptHandlers[ecx*8]
+
+ ;
+ ; Get SP
+ ;
+ mov edi,[ebp].TsHardwareEsp
+ test [esi].RiSsFlags,SEL_TYPE_BIG
+ jnz pi110
+
+ movzx edi,di ; zero high bits for 64k stack
+
+ ;
+ ; Update SP
+ ;
+pi110: test [eax].ViFlags,dword ptr VDM_INT_32
+ jz pi120
+
+ ;
+ ; 32 bit iret frame
+ ;
+ cmp edi,12 ; enough space on stack?
+ jb pierr ; no, go fault
+
+ sub edi,12
+ mov [esi].RiEsp,edi
+ jmp pi130
+
+ ;
+ ; 16 bit iret frame
+ ;
+pi120: cmp edi,6 ; enough space on stack?
+ jb pierr ; no, go fault
+
+ sub edi,6
+ mov [esi].RiEsp,edi
+
+ ;
+ ; Check limit
+ ;
+pi130: test [esi].RiSsFlags,SEL_TYPE_ED
+ jz pi140
+
+ ;
+ ; Expand down, Sp must be above limit
+ ;
+ cmp edi,[esi].RiSsLimit
+ jna pierr
+ jmp pi150
+
+ ;
+ ; Normal, Sp must be below limit
+ ;
+pi140: cmp edi,[esi].RiSsLimit
+ jnb pierr
+
+ ;
+ ; Get base of ss
+ ;
+pi150: mov ebx,[esi].RiSsBase
+ test [eax].ViFlags,dword ptr VDM_INT_32
+ jz pi160
+
+ ;
+ ; "push" 32 bit iret frame
+ ;
+ mov edx,[esi].RiEip
+ mov [edi + ebx],edx
+ mov dx,word ptr [ebp].TsSegCs
+ mov [edi + ebx] + 4,edx
+ push eax
+ mov eax,[esi].RiEFlags
+ call GetVirtualBits
+
+ mov [edi + ebx] + 8,eax
+ pop eax
+ jmp pi170
+
+ ;
+ ; push 16 bit iret frame
+ ;
+pi160: mov dx,word ptr [esi].RiEip
+ mov [edi + ebx],dx
+ mov dx,word ptr [ebp].TsSegCs
+ mov [edi + ebx] + 2,dx
+ push eax
+ mov eax,[esi].RiEFlags
+ call GetVirtualBits
+
+ mov [edi + ebx] + 4,ax
+ pop eax
+
+ ;
+ ; Update CS and IP
+ ;
+pi170: mov ebx,eax ; save int info
+ mov dx,[eax].ViCsSelector
+ mov word ptr [esi].RiSegCs,dx
+ mov edx,[eax].ViEip
+ mov [esi].RiEip,edx
+
+ movzx eax, word ptr [esi].RiSegCs
+ call CsToLinear ; uses eax as selector
+
+ test al,0ffh
+ jnz pi175
+
+ ;
+ ; Check for destination not present
+ ;
+ test [esi].RiCsFlags,SEL_TYPE_NP
+ jz pierr
+
+ mov al,0ffh ; succeeded
+ jmp pi180
+
+ ;
+ ; Check handler address
+ ;
+pi175: mov edx,[esi].RiEip
+ cmp edx,[esi].RiCsLimit
+ jnb pierr
+
+ ;
+ ; Turn off the trap flag
+ ;
+pi180: and [esi].RiEFlags,NOT EFLAGS_TF_MASK
+
+ ;
+ ; Turn off virtual interrupts if necessary
+ ;
+ test [ebx].ViFlags,dword ptr VDM_INT_INT_GATE
+ ; n.b. We know al is non-zero, because we succeeded in cstolinear
+ jz pi80
+
+ test _KeI386VirtualIntExtensions,PM_VIRTUAL_INT_EXTENSIONS
+ jz pi75
+
+ and [esi].RiEFlags, NOT (EFLAGS_VIF)
+
+pi75: mov ebx,_VdmFixedStateLinear
+ MPLOCK and [ebx], NOT EFLAGS_INTERRUPT_MASK
+
+pi80: and [esi].RiEFlags,NOT (EFLAGS_IOPL_MASK OR EFLAGS_NT_MASK OR EFLAGS_V86_MASK)
+ or [esi].RiEFlags,EFLAGS_INTERRUPT_MASK
+
+pi90: pop edi
+ pop ebx
+ ret
+
+pierr: xor eax,eax
+ jmp pi90
+
+PushInt endp
+
+ page ,132
+ subttl "Convert CS Segment or selector to linear address"
+;++
+;
+; Routine Description:
+;
+; Convert CS segment or selector to linear address as appropriate
+; for the curret user mode processor mode.
+;
+; Arguments:
+;
+; esi = reg info
+;
+; Returns:
+;
+; reg info updated
+;
+ public CsToLinear
+CsToLinear proc
+
+ test [esi].RiEFlags,EFLAGS_V86_MASK
+ jz ctl10
+
+;;; selector now passed in eax
+;;; movzx eax,word ptr [esi].RiSegCs
+ shl eax,4
+ mov [esi].RiCsBase,eax
+ mov [esi].RiCsLimit,0FFFFh
+ mov [esi].RiCsFlags,0
+ mov eax,1
+ ret
+
+ifdef NOT_USED_ANYMORE
+ctl10: push edx ; WARNING volatile regs!!!
+ lea eax,[esi].RiCsLimit
+ push eax
+ lea eax,[esi].RiCsBase
+ push eax
+ lea eax,[esi].RiCsFlags
+ push eax
+ push [esi].RiSegCs
+
+endif
+
+ctl10:
+ push edx ; WARNING volatile regs!!!
+ lea edx,[esi].RiCsLimit
+ push edx
+ lea edx,[esi].RiCsBase
+ push edx
+ lea edx,[esi].RiCsFlags
+ push edx
+ push eax ; push selector
+
+IFDEF STD_CALL
+ call _Ki386GetSelectorParameters@16
+ELSE
+ call _Ki386GetSelectorParameters
+ add esp,10h
+ENDIF
+ pop edx
+
+ or al,al
+ jz ctlerr
+
+ test [esi].RiCsFlags,SEL_TYPE_EXECUTE
+ jz ctlerr
+
+ test [esi].RiCsFlags,SEL_TYPE_2GIG
+ jz ctl30
+
+ ; Correct limit value for granularity
+ shl [esi].RiCsLimit,12
+ or [esi].RiCsLimit,0FFFh
+ctl30:
+ mov eax,1
+ctl40: ret
+
+ctlerr: xor eax,eax
+ jmp ctl40
+
+CsToLinear endp
+
+
+ page ,132
+ subttl "Verify that EIP is still valid"
+;++
+;
+; Routine Description:
+;
+; Verify that Eip is still valid and put it into the trap frame
+;
+; Arguments:
+;
+; esi = address of reg info
+;
+; Returns:
+;
+;
+ public CheckEip
+CheckEip proc
+ mov eax,[esi].RiEip
+ test [esi].RiEFlags,EFLAGS_V86_MASK
+ jz ce20
+
+ and eax,[esi].RiCsLimit
+ mov [esi].RiEip,eax
+ jmp ce40
+
+ce20: cmp eax,[esi].RiCsLimit
+ ja ceerr
+ce40: mov eax,1
+ce50: ret
+
+ceerr: xor eax,eax
+ jmp ce50
+
+CheckEip endp
+
+ page ,132
+ subttl "Convert Ss Segment or selector to linear address"
+;++
+;
+; Routine Description:
+;
+; Convert Ss segment or selector to linear address as appropriate
+; for the curret user mode processor mode.
+;
+; Arguments:
+;
+; eax = selector to convert
+; esi = address of reg info
+;
+; Returns:
+;
+; reg info updated
+;
+ public SsToLinear
+SsToLinear proc
+
+ test [esi].RiEFlags,EFLAGS_V86_MASK
+ jz stl10
+
+ shl eax,4
+ mov [esi].RiSsBase,eax
+ mov [esi].RiSsLimit,0FFFFh
+ mov [esi].RiSsFlags,0
+ mov eax,1
+ ret
+
+stl10: push ecx
+ lea ecx,[esi].RiSsLimit
+ push ecx
+ lea ecx,[esi].RiSsBase
+ push ecx
+ lea ecx,[esi].RiSsFlags
+ push ecx
+ push eax ;selector
+
+IFDEF STD_CALL
+ call _Ki386GetSelectorParameters@16
+ELSE
+ call _Ki386GetSelectorParameters
+ add esp,10h
+ENDIF
+ pop ecx
+
+ or al,al
+ jz stlerr
+
+ test [esi].RiSsFlags,SEL_TYPE_WRITE
+ jz stlerr
+
+ test [esi].RiSsFlags,SEL_TYPE_2GIG
+ jz stl30
+
+ ; Correct limit value for granularity
+
+ mov eax,[esi].RiSsLimit
+ shl eax,12
+ or eax,0FFFh
+ mov [esi].RiSsLimit,eax
+stl30:
+ mov eax,1
+stl40: ret
+
+stlerr: xor eax,eax
+ jmp stl40
+
+SsToLinear endp
+
+ page ,132
+ subttl "Verify that Esp is still valid"
+;++
+;
+; Routine Description:
+;
+; Verify that Esp is still valid
+;
+; Arguments:
+;
+; ecx = # of bytes needed for stack frame
+; esi = address of reg info
+;
+; Returns:
+;
+;
+;
+ public CheckEsp
+CheckEsp proc
+ mov eax,[esi].RiEsp
+ test [esi].RiEFlags,EFLAGS_V86_MASK
+ jz cs20
+
+ and eax,[esi].RiSsLimit
+ mov [esi].RiEsp,eax
+ jmp cs40
+
+cs20: test [esi].RiSsFlags,SEL_TYPE_BIG
+ jnz cs25
+
+ and eax,0FFFFh ; only use 16 bit for 16 bit
+cs25:
+ cmp ecx, eax ; StackOffset > SP?
+ ja cserr ; yes error
+ dec eax ; make limit checks work
+ test [esi].RiSsFlags,SEL_TYPE_ED ; Expand down?
+ jz cs30 ; jif no
+
+;
+; Expand Down
+;
+ sub eax, ecx ; New SP
+ cmp eax,[esi].RiSsLimit ; NewSp < Limit?
+ jb cserr
+ jmp cs40
+
+;
+; Not Expand Down
+;
+cs30: cmp eax,[esi].RiSsLimit
+ ja cserr
+
+cs40: mov eax,1
+cs50: ret
+
+
+cserr: xor eax,eax
+ jmp cs50
+
+CheckEsp endp
+
+ page ,132
+ subttl "Switch to protected mode interrupt stack"
+;++
+;
+; Routine Description:
+;
+; Switch to protected mode interrupt handler stack
+;
+; Arguments:
+;
+; ecx = interrupt number
+; esi = address of reg info
+; edi = address of PM Stack info
+;
+; Returns:
+;
+; reg info updated
+;
+ public SwitchToHandlerStack
+SwitchToHandlerStack proc
+
+
+ cmp word ptr [edi].VpLockCount, 0 ; already switched?
+ jnz short @f ; yes
+
+ mov eax, [esi].RiEip
+ mov [edi].VpSaveEip, eax
+ mov eax, [esi].RiEsp
+ mov [edi].VpSaveEsp, eax
+ mov eax, [esi].RiSegSs
+ mov [edi].VpSaveSsSelector, ax
+
+ movzx eax,word ptr [edi].VpSsSelector
+ mov [esi].RiSegSs,eax
+ mov dword ptr [esi].RiEsp,1000h ; dpmi stack offset
+
+ movzx eax, word ptr [esi].RiSegSs
+ push ecx
+ call SsToLinear ; compute new base
+ pop ecx
+ test al,0FFh
+ jz shserr
+@@:
+ inc word ptr [edi].VpLockCount ; maintain lock count
+ mov eax,1
+ ret
+
+shserr:
+ xor eax,eax
+ ret
+
+SwitchToHandlerStack endp
+
+
+ page ,132
+ subttl "Get protected mode interrupt handler address"
+;++
+;
+; Routine Description:
+;
+; Get the address of the interrupt handler for the sepcified interrupt
+;
+; Arguments:
+;
+; ecx = interrupt number
+; esi = address of reg info
+;
+; Returns:
+;
+; reg info updated
+;
+ public GetHandlerAddress
+GetHandlerAddress proc
+
+ push ecx
+ push edx
+ mov eax,VDM_FAULT_HANDLER_SIZE
+ mul ecx
+ mov edi,PCR[PcTeb]
+ mov edi,[edi].TbVdm
+ lea edi,[edi].VtFaultHandlers
+ movzx ecx,word ptr [edi + eax].VfCsSelector
+ mov [esi].RiSegCs,ecx
+ mov ecx,[edi + eax].VfEip
+ mov [esi].RiEip,ecx
+ pop edx
+ pop ecx
+ mov eax,1
+ ret
+GetHandlerAddress endp
+
+ page ,132
+ subttl "Push processor exception"
+;++
+;
+; Routine Description:
+;
+; Update the stack and registers to emulate the specified exception
+;
+; Arguments:
+;
+; ecx = interrupt number
+; esi = address of reg info
+;
+; Returns:
+;
+; reg info updated
+;
+ public PushException
+PushException Proc
+
+ push ebx
+ push edi
+
+ test [esi].RiEflags,EFLAGS_V86_MASK
+ jz pe40
+
+;
+; Push V86 mode exception
+;
+ cmp ecx, 7 ; device not available fault
+ ja peerr ; per win3.1, no exceptions
+ ; above 7 for v86 mode
+ mov edx,[esi].RiEsp
+ mov ebx,[esi].RiSsBase
+ and edx,0FFFFh ; only use a 16 bit sp
+ sub dx,2
+ mov eax,[esi].RiEFlags
+ push ecx
+ call GetVirtualBits
+ pop ecx
+ mov [ebx+edx],ax ; push flags
+ sub dx,2
+ mov ax,word ptr [esi].RiSegCs
+ mov [ebx+edx],ax ; push cs
+ sub dx,2
+ mov ax,word ptr [esi].RiEip
+ mov [ebx+edx],ax ; push ip
+ mov eax,[ecx*4] ; get new cs:ip value
+ push eax
+ movzx eax,ax
+ mov [esi].RiEip,eax
+ pop eax
+ shr eax,16
+ mov [esi].RiSegCs,eax
+ mov word ptr [esi].RiEsp,dx
+ jmp pe60
+
+;
+; Push PM exception
+;
+pe40:
+ push [esi].RiEsp ; save for stack frame
+ push [esi].RiSegSs
+
+ mov edi,PCR[PcTeb]
+ mov edi,[edi].TbVdm
+ lea edi,[edi].VtPmStackInfo
+ call SwitchToHandlerStack
+ test al,0FFh
+ jz peerr1 ; pop off stack and exit
+
+ sub [esi].RiEsp, 20h ; win31 undocumented feature
+
+ mov ebx,[esi].RiSsBase
+ mov edx,[esi].RiEsp
+ test [esi].RiSsFlags,SEL_TYPE_BIG
+ jnz short @f
+ movzx edx,dx ; zero high bits for 64k stack
+@@:
+
+ test word ptr [edi].VpFlags, 1 ; 32 bit app?
+ jnz short pe45 ; yes
+
+;
+; push 16-bit frame
+;
+ push ecx
+ mov ecx, 8*2 ; room for 8 words?
+ call CheckEsp
+ pop ecx
+ test al,0FFh
+ jz peerr1 ; pop off stack and exit
+
+ sub edx,8*2
+ mov [esi].RiEsp,edx
+
+ pop eax ; ss
+ mov [ebx+edx+14], ax
+ pop eax ; esp
+ mov [ebx+edx+12], ax
+
+ mov eax,[esi].RiEFlags
+ push ecx
+ call GetVirtualBits
+ pop ecx
+ mov [ebx+edx+10],ax ; push flags
+ movzx eax,word ptr [esi].RiSegCs
+ mov [ebx+edx+8],ax ; push cs
+ mov eax,[esi].RiEip
+ mov [ebx+edx+6],ax ; push ip
+ mov eax,RI.RiTrapFrame
+ mov eax,[eax].TsErrCode
+ mov [ebx+edx+4],ax ; push error code
+ mov eax,[edi].VpDosxFaultIret
+ mov [ebx+edx],eax ; push iret address
+ jmp pe50
+pe45:
+;
+; push 32-bit frame
+;
+ push ecx
+ mov ecx, 8*4 ; room for 8 dwords?
+ call CheckEsp
+ pop ecx
+ test al,0FFh
+ jz peerr1 ; pop off stack and exit
+
+ sub edx,8*4
+ mov [esi].RiEsp,edx
+
+ pop eax ; ss
+ mov [ebx+edx+28], eax
+ pop eax ; esp
+ mov [ebx+edx+24], eax
+
+ mov eax,[esi].RiEFlags
+ push ecx
+ call GetVirtualBits
+ pop ecx
+ mov [ebx+edx+20],eax ; push flags
+ movzx eax,word ptr [esi].RiSegCs
+ mov [ebx+edx+16],eax ; push cs
+ mov eax,[esi].RiEip
+ mov [ebx+edx+12],eax ; push ip
+ mov eax,RI.RiTrapFrame
+ mov eax,[eax].TsErrCode
+ mov [ebx+edx+8],eax ; push error code
+ mov eax,[edi].VpDosxFaultIretD
+ shr eax, 16
+ mov [ebx+edx+4],eax ; push iret seg
+ mov eax,[edi].VpDosxFaultIretD
+ and eax, 0ffffh
+ mov [ebx+edx],eax ; push iret offset
+pe50:
+ call GetHandlerAddress
+ test al,0FFh
+ jz peerr
+
+pe60: push ecx
+ movzx eax,word ptr [esi].RiSegCs
+ call CsToLinear ; uses eax as selector
+ pop ecx
+ test al,0FFh
+ jz peerr
+
+ mov eax,[esi].RiEip
+ cmp eax,[esi].RiCsLimit
+ ja peerr
+
+ mov eax,VDM_FAULT_HANDLER_SIZE
+ push edx
+ mul ecx
+ pop edx
+ mov edi,PCR[PcTeb]
+ mov edi,[edi].TbVdm
+ lea edi,[edi].VtFaultHandlers
+ add edi,eax
+ mov eax,[esi].RiEFlags ;WARNING 16 vs 32
+ test dword ptr [edi].VfFlags,VDM_INT_INT_GATE
+ jz pe70
+
+ and eax,NOT (EFLAGS_INTERRUPT_MASK OR EFLAGS_TF_MASK)
+ push eax
+ xor ebx, ebx ; clear prefix flags
+ call SetVirtualBits
+ pop eax
+pe70: push ecx
+ mov ecx,eax
+ call CheckVdmFlags
+ and ecx,NOT EFLAGS_TF_MASK
+ mov [esi].RiEFlags,ecx
+ pop ecx
+ mov eax,1 ; success
+pe80: pop edi
+ pop ebx
+ ret
+
+peerr1: add esp, 8 ;throw away esp, ss
+peerr: xor eax,eax
+ jmp pe80
+
+PushException endp
+
+
+ifdef VDMDBG
+ public TraceOpcode
+TraceOpcode Proc
+
+ push eax
+ push edx
+ mov eax, TracePointer
+
+ mov edx,dword ptr [esp+12] ;pushed code
+ mov dword ptr [eax],edx
+ mov edx,dword ptr [esp+8] ;ret addr
+ mov dword ptr [eax+4],edx
+ mov edx,-1
+ mov dword ptr [eax+8],edx
+ mov dword ptr [eax+12],edx
+
+ mov edx,[ebp].TsEax
+ mov dword ptr [eax+16],edx
+ mov edx,[ebp].TsEbx
+ mov dword ptr [eax+20],edx
+ mov edx,[ebp].TsEcx
+ mov dword ptr [eax+24],edx
+ mov edx,[ebp].TsEdx
+ mov dword ptr [eax+28],edx
+
+ mov edx,[ebp].TsEip
+ mov dword ptr [eax+36],edx
+ mov edx,[ebp].TsHardwareEsp
+ mov dword ptr [eax+32],edx
+ mov edx,[ebp].TsEdi
+ mov dword ptr [eax+40],edx
+ mov edx,[ebp].TsEsi
+ mov dword ptr [eax+44],edx
+
+ mov edx,[ebp].TsHardwareSegSs
+ mov dword ptr [eax+48],edx
+ mov edx,[ebp].TsSegCs
+ mov dword ptr [eax+52],edx
+ mov edx,[ebp].TsSegDs
+ mov dword ptr [eax+56],edx
+ mov edx,[ebp].TsSegEs
+ mov dword ptr [eax+60],edx
+
+ add eax, TRACE_ENTRY_SIZE*4
+ cmp eax, Offset OpcodeTrace + NUM_TRACE_ENTRIES*TRACE_ENTRY_SIZE*4
+ jb @f
+ mov eax, Offset OpcodeTrace
+@@:
+ mov TracePointer, eax
+
+ pop edx
+ pop eax
+ ret 4
+TraceOpcode endp
+endif
+
+_PAGE ends
+
+
+_TEXT$00 SEGMENT DWORD PUBLIC 'CODE'
+ ASSUME DS:NOTHING, ES:NOTHING, SS:FLAT, FS:NOTHING, GS:NOTHING
+
+;
+; Non-pagable code
+;
+
+ page ,132
+ subttl "Ipi worker for enabling Pentium extensions"
+;++
+;
+; Routine Description:
+;
+; This routine sets or resets the VME bit in CR4 for each processor
+;
+; Arguments:
+;
+; [esp + 4] -- 1 if VME is to be set, 0 if it is to be reset
+; Returns:
+;
+; 0
+;
+cPublicProc _Ki386VdmEnablePentiumExtentions, 1
+
+Enable equ [ebp + 8]
+ push ebp
+ mov ebp,esp
+;
+; Insure we do not get an interrupt in here. We may
+; be called at IPI_LEVEL - 1 by KiIpiGenericCall.
+;
+ pushf
+ cli
+
+; mov eax,cr4
+ db 0fh, 020h,0e0h
+
+ test Enable,1
+ je vepe20
+
+ or eax,CR4_VME
+ jmp vepe30
+
+vepe20: and eax,NOT CR4_VME
+
+; mov cr4,eax
+vepe30: db 0fh,022h,0e0h
+
+ popf
+ xor eax,eax
+
+ mov esp,ebp
+ pop ebp
+ stdRET _Ki386VdmEnablePentiumExtentions
+stdENDP _Ki386VdmEnablePentiumExtentions
+
+_TEXT$00 ends
+ end
diff --git a/private/ntos/ke/i386/int.asm b/private/ntos/ke/i386/int.asm
new file mode 100644
index 000000000..d4bf4fa8e
--- /dev/null
+++ b/private/ntos/ke/i386/int.asm
@@ -0,0 +1,132 @@
+ title "Trap Processing"
+;++
+;
+; Copyright (c) 1989 Microsoft Corporation
+;
+; Module Name:
+;
+; int.asm
+;
+; Abstract:
+;
+; This module implements the code necessary to field and process i386
+; interrupt.
+;
+; Author:
+;
+; Shie-Lin Tzong (shielint) 8-Jan-1990
+;
+; Environment:
+;
+; Kernel mode only.
+;
+; Revision History:
+;
+;--
+
+.386p
+ .xlist
+include ks386.inc
+include i386\kimacro.inc
+include callconv.inc
+ .list
+
+;
+; Interrupt flag bit maks for EFLAGS
+;
+
+EFLAGS_IF equ 200H
+EFLAGS_SHIFT equ 9
+
+_TEXT SEGMENT DWORD PUBLIC 'CODE'
+ ASSUME DS:FLAT, ES:FLAT, SS:FLAT, FS:NOTHING, GS:NOTHING
+
+; NOTE This routine is never actually called on standard x86 hardware,
+; because passive level doesn't actually exist. It's here to
+; fill out the portable skeleton.
+;
+; The following code is called when a passive release occurs and there is
+; no interrupt to process.
+;
+
+cPublicProc _KiPassiveRelease ,0
+ stdRET _KiPassiveRelease ; cReturn
+stdENDP _KiPassiveRelease
+
+
+ page ,132
+ subttl "Disable Processor Interrupts"
+;++
+;
+; BOOLEAN
+; KiDisableInterrupts(
+; VOID
+; )
+;
+; Routine Description:
+;
+; This routine disables interrupts at the processor level. It does not
+; edit the PICS or adjust IRQL, it is for use in the debugger only.
+;
+; Arguments:
+;
+; None
+;
+; Return Value:
+;
+; (eax) = !0 if interrupts were on, 0 if they were off
+;
+;--
+cPublicProc _KiDisableInterrupts ,0
+cPublicFpo 0, 0
+ pushfd
+ pop eax
+ and eax,EFLAGS_IF ; (eax) = the interrupt bit
+ shr eax,EFLAGS_SHIFT ; low bit of (eax) == interrupt bit
+ cli
+ stdRET _KiDisableInterrupts
+
+stdENDP _KiDisableInterrupts
+
+
+ page ,132
+ subttl "Restore Processor Interrupts"
+;++
+;
+; VOID
+; KiRestoreInterrupts(
+; BOOLEAN Restore
+; )
+;
+; Routine Description:
+;
+; This routine restores interrupts at the processor level. It does not
+; edit the PICS or adjust IRQL, it is for use in the debugger only.
+;
+; Arguments:
+;
+; Restore (esp+4) - a "boolean" returned by KiDisableInterrupts, if
+; !0 interrupts will be turned on, else left off.
+;
+; NOTE: We don't actually test the boolean as such, we just or
+; it directly into the flags!
+;
+; Return Value:
+;
+; none.
+;
+;--
+cPublicProc _KiRestoreInterrupts ,1
+cPublicFpo 1, 0
+ xor eax, eax
+ mov al, byte ptr [esp]+4
+ shl eax,EFLAGS_SHIFT ; (eax) == the interrupt bit
+ pushfd
+ or [esp],eax ; or EI into flags
+ popfd
+ stdRET _KiRestoreInterrupts
+
+stdENDP _KiRestoreInterrupts
+
+_TEXT ends
+ end
diff --git a/private/ntos/ke/i386/intobj.c b/private/ntos/ke/i386/intobj.c
new file mode 100644
index 000000000..f4da6ca33
--- /dev/null
+++ b/private/ntos/ke/i386/intobj.c
@@ -0,0 +1,767 @@
+/*++
+
+Copyright (c) 1989 Microsoft Corporation
+
+Module Name:
+
+ intobj.c
+
+Abstract:
+
+ This module implements the kernel interrupt object. Functions are provided
+ to initialize, connect, and disconnect interrupt objects.
+
+Author:
+
+ David N. Cutler (davec) 30-Jul-1989
+
+Environment:
+
+ Kernel mode only.
+
+Revision History:
+
+ 23-Jan-1990 shielint
+
+ Modified for NT386 interrupt manager
+
+--*/
+
+#include "ki.h"
+
+//
+// Externs from trap.asm used to compute and set handlers for unexpected
+// hardware interrupts.
+//
+
+extern ULONG KiStartUnexpectedRange(VOID);
+extern ULONG KiEndUnexpectedRange(VOID);
+extern ULONG KiUnexpectedEntrySize;
+
+
+VOID
+KiInterruptDispatch2ndLvl(
+ VOID
+ );
+
+
+VOID
+KiChainedDispatch2ndLvl(
+ VOID
+ );
+
+
+typedef enum {
+ NoConnect,
+ NormalConnect,
+ ChainConnect,
+ UnkownConnect
+} CONNECT_TYPE, *PCONNECT_TYPE;
+
+typedef struct {
+ CONNECT_TYPE Type;
+ PKINTERRUPT Interrupt;
+ PKINTERRUPT_ROUTINE NoDispatch;
+ PKINTERRUPT_ROUTINE InterruptDispatch;
+ PKINTERRUPT_ROUTINE FloatingDispatch;
+ PKINTERRUPT_ROUTINE ChainedDispatch;
+ PKINTERRUPT_ROUTINE *FlatDispatch;
+} DISPATCH_INFO, *PDISPATCH_INFO;
+
+
+VOID
+KiGetVectorInfo (
+ IN ULONG Vector,
+ OUT PDISPATCH_INFO DispatchInfo
+ );
+
+VOID
+KiConnectVectorAndInterruptObject (
+ IN PKINTERRUPT Interrupt,
+ IN CONNECT_TYPE Type
+ );
+
+
+VOID
+KeInitializeInterrupt (
+ IN PKINTERRUPT Interrupt,
+ IN PKSERVICE_ROUTINE ServiceRoutine,
+ IN PVOID ServiceContext,
+ IN PKSPIN_LOCK SpinLock OPTIONAL,
+ IN ULONG Vector,
+ IN KIRQL Irql,
+ IN KIRQL SynchronizeIrql,
+ IN KINTERRUPT_MODE InterruptMode,
+ IN BOOLEAN ShareVector,
+ IN CCHAR ProcessorNumber,
+ IN BOOLEAN FloatingSave
+ )
+
+/*++
+
+Routine Description:
+
+ This function initializes a kernel interrupt object. The service routine,
+ service context, spin lock, vector, IRQL, SynchronizeIrql, and floating
+ context save flag are initialized.
+
+Arguments:
+
+ Interrupt - Supplies a pointer to a control object of type interrupt.
+
+ ServiceRoutine - Supplies a pointer to a function that is to be
+ executed when an interrupt occurs via the specified interrupt
+ vector.
+
+ ServiceContext - Supplies a pointer to an arbitrary data structure which is
+ to be passed to the function specified by the ServiceRoutine parameter.
+
+ SpinLock - Supplies a pointer to an executive spin lock.
+
+ Vector - Supplies the index of the entry in the Interrupt Dispatch Table
+ that is to be associated with the ServiceRoutine function.
+
+ Irql - Supplies the request priority of the interrupting source.
+
+ SynchronizeIrql - The request priority that the interrupt should be
+ synchronized with.
+
+ InterruptMode - Supplies the mode of the interrupt; LevelSensitive or
+
+ ShareVector - Supplies a boolean value that specifies whether the
+ vector can be shared with other interrupt objects or not. If FALSE
+ then the vector may not be shared, if TRUE it may be.
+ Latched.
+
+ ProcessorNumber - Supplies the number of the processor to which the
+ interrupt will be connected.
+
+ FloatingSave - Supplies a boolean value that determines whether the
+ floating point registers and pipe line are to be saved before calling
+ the ServiceRoutine function.
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+
+ LONG Index;
+ PULONG pl;
+ PULONG NormalDispatchCode;
+
+ //
+ // Initialize standard control object header.
+ //
+
+ Interrupt->Type = InterruptObject;
+ Interrupt->Size = sizeof(KINTERRUPT);
+
+ //
+ // Initialize the address of the service routine,
+ // the service context, the address of the spin lock, the vector
+ // number, the IRQL of the interrupting source, the Irql used for
+ // synchronize execution, the interrupt mode, the processor
+ // number, and the floating context save flag.
+ //
+
+ Interrupt->ServiceRoutine = ServiceRoutine;
+ Interrupt->ServiceContext = ServiceContext;
+
+ if (ARGUMENT_PRESENT(SpinLock)) {
+ Interrupt->ActualLock = SpinLock;
+ } else {
+ KeInitializeSpinLock (&Interrupt->SpinLock);
+ Interrupt->ActualLock = &Interrupt->SpinLock;
+ }
+
+ Interrupt->Vector = Vector;
+ Interrupt->Irql = Irql;
+ Interrupt->SynchronizeIrql = SynchronizeIrql;
+ Interrupt->Mode = InterruptMode;
+ Interrupt->ShareVector = ShareVector;
+ Interrupt->Number = ProcessorNumber;
+ Interrupt->FloatingSave = FloatingSave;
+
+ //
+ // Copy the interrupt dispatch code template into the interrupt object
+ // and edit the machine code stored in the structure (please see
+ // _KiInterruptTemplate in intsup.asm.) Finally, flush the dcache
+ // on all processors that the current thread can
+ // run on to ensure that the code is actually in memory.
+ //
+
+ NormalDispatchCode = &(Interrupt->DispatchCode[0]);
+
+ pl = NormalDispatchCode;
+
+ for (Index = 0; Index < NORMAL_DISPATCH_LENGTH; Index += 1) {
+ *NormalDispatchCode++ = KiInterruptTemplate[Index];
+ }
+
+ //
+ // The following two instructions set the address of current interrupt
+ // object the the NORMAL dispatching code.
+ //
+
+ pl = (PULONG)((PUCHAR)pl + ((PUCHAR)&KiInterruptTemplateObject -
+ (PUCHAR)KiInterruptTemplate));
+ *pl = (ULONG)Interrupt;
+
+ KeSweepDcache(FALSE);
+
+ //
+ // Set the connected state of the interrupt object to FALSE.
+ //
+
+ Interrupt->Connected = FALSE;
+ return;
+}
+
+BOOLEAN
+KeConnectInterrupt (
+ IN PKINTERRUPT Interrupt
+ )
+
+/*++
+
+Routine Description:
+
+ This function connects an interrupt object to the interrupt vector
+ specified by the interrupt object. If the interrupt object is already
+ connected, or an attempt is made to connect to an interrupt that cannot
+ be connected, then a value of FALSE is returned. Else the specified
+ interrupt object is connected to the interrupt vector, the connected
+ state is set to TRUE, and TRUE is returned as the function value.
+
+Arguments:
+
+ Interrupt - Supplies a pointer to a control object of type interrupt.
+
+Return Value:
+
+ If the interrupt object is already connected or an attempt is made to
+ connect to an interrupt vector that cannot be connected, then a value
+ of FALSE is returned. Else a value of TRUE is returned.
+
+--*/
+
+{
+ DISPATCH_INFO DispatchInfo;
+ BOOLEAN Connected;
+ BOOLEAN ConnectError;
+ BOOLEAN Enabled;
+ KIRQL Irql;
+ CCHAR Number;
+ KIRQL OldIrql;
+ ULONG Vector;
+
+ //
+ // If the interrupt object is already connected, the interrupt vector
+ // number is invalid, an attempt is being made to connect to a vector
+ // that cannot be connected, the interrupt request level is invalid, or
+ // the processor number is invalid, then do not connect the interrupt
+ // object. Else connect interrupt object to the specified vector and
+ // establish the proper interrupt dispatcher.
+ //
+
+ Connected = FALSE;
+ ConnectError = FALSE;
+ Irql = Interrupt->Irql;
+ Number = Interrupt->Number;
+ Vector = Interrupt->Vector;
+ if ( !((Irql > HIGH_LEVEL) ||
+ (Number >= KeNumberProcessors) ||
+ (Interrupt->SynchronizeIrql < Irql) ||
+ (Interrupt->FloatingSave) // R0 x87 usage not supported on x86
+ )
+ ) {
+
+ //
+ //
+ // Set system affinity to the specified processor.
+ //
+
+ KeSetSystemAffinityThread((KAFFINITY)(1<<Number));
+
+ //
+ // Raise IRQL to dispatcher level and lock dispatcher database.
+ //
+
+ KiLockDispatcherDatabase(&OldIrql);
+
+ //
+ // Is interrupt object already connected?
+ //
+
+ if (!Interrupt->Connected) {
+
+ //
+ // Determine interrupt dispatch vector
+ //
+
+ KiGetVectorInfo (
+ Vector,
+ &DispatchInfo
+ );
+
+ //
+ // If dispatch vector is not connected, then connect it
+ //
+
+ if (DispatchInfo.Type == NoConnect) {
+ Connected = TRUE;
+ Interrupt->Connected = TRUE;
+
+ //
+ // Connect interrupt dispatch to interrupt object dispatch code
+ //
+
+ InitializeListHead(&Interrupt->InterruptListEntry);
+ KiConnectVectorAndInterruptObject (Interrupt, NormalConnect);
+
+ //
+ // Enabled system vector
+ //
+
+ Enabled = HalEnableSystemInterrupt(Vector, Irql, Interrupt->Mode);
+ if (!Enabled) {
+ ConnectError = TRUE;
+ }
+
+
+ } else if (DispatchInfo.Type != UnkownConnect &&
+ Interrupt->ShareVector &&
+ DispatchInfo.Interrupt->ShareVector &&
+ DispatchInfo.Interrupt->Mode == Interrupt->Mode) {
+
+ //
+ // Vector is already connected as sharable. New vector is sharable
+ // and modes match. Chain new vector.
+ //
+
+ Connected = TRUE;
+ Interrupt->Connected = TRUE;
+
+ ASSERT (Irql <= SYNCH_LEVEL);
+
+ //
+ // If not already using chained dispatch handler, set it up
+ //
+
+ if (DispatchInfo.Type != ChainConnect) {
+ KiConnectVectorAndInterruptObject (DispatchInfo.Interrupt, ChainConnect);
+ }
+
+ //
+ // Add to tail of chained dispatch
+ //
+
+ InsertTailList(
+ &DispatchInfo.Interrupt->InterruptListEntry,
+ &Interrupt->InterruptListEntry
+ );
+
+ }
+ }
+
+ //
+ // Unlock dispatcher database and lower IRQL to its previous value.
+ //
+
+ KiUnlockDispatcherDatabase(OldIrql);
+
+ //
+ // Set system affinity back to the original value.
+ //
+
+ KeRevertToUserAffinityThread();
+ }
+
+ if (Connected && ConnectError) {
+#if DBG
+ DbgPrint ("HalEnableSystemInterrupt failed\n");
+#endif
+ KeDisconnectInterrupt (Interrupt);
+ Connected = FALSE;
+ }
+
+ //
+ // Return whether interrupt was connected to the specified vector.
+ //
+
+ return Connected;
+}
+
+BOOLEAN
+KeDisconnectInterrupt (
+ IN PKINTERRUPT Interrupt
+ )
+
+/*++
+
+Routine Description:
+
+ This function disconnects an interrupt object from the interrupt vector
+ specified by the interrupt object. If the interrupt object is not
+ connected, then a value of FALSE is returned. Else the specified interrupt
+ object is disconnected from the interrupt vector, the connected state is
+ set to FALSE, and TRUE is returned as the function value.
+
+Arguments:
+
+ Interrupt - Supplies a pointer to a control object of type interrupt.
+
+Return Value:
+
+ If the interrupt object is not connected, then a value of FALSE is
+ returned. Else a value of TRUE is returned.
+
+--*/
+
+{
+
+ DISPATCH_INFO DispatchInfo;
+ BOOLEAN Connected;
+ PKINTERRUPT Interrupty;
+ KIRQL Irql;
+ KIRQL OldIrql;
+ ULONG Vector;
+
+ //
+ // Set system affinity to the specified processor.
+ //
+
+ KeSetSystemAffinityThread((KAFFINITY)(1<<Interrupt->Number));
+
+ //
+ // Raise IRQL to dispatcher level and lock dispatcher database.
+ //
+
+ KiLockDispatcherDatabase(&OldIrql);
+
+ //
+ // If the interrupt object is connected, then disconnect it from the
+ // specified vector.
+ //
+
+ Connected = Interrupt->Connected;
+ if (Connected) {
+ Irql = Interrupt->Irql;
+ Vector = Interrupt->Vector;
+
+ //
+ // If the specified interrupt vector is not connected to the chained
+ // interrupt dispatcher, then disconnect it by setting its dispatch
+ // address to the unexpected interrupt routine. Else remove the
+ // interrupt object from the interrupt chain. If there is only
+ // one entry remaining in the list, then reestablish the dispatch
+ // address.
+ //
+
+ //
+ // Determine interrupt dispatch vector
+ //
+
+ KiGetVectorInfo (
+ Vector,
+ &DispatchInfo
+ );
+
+
+ //
+ // Is dispatch a chained handler?
+ //
+
+ if (DispatchInfo.Type == ChainConnect) {
+
+ ASSERT (Irql <= SYNCH_LEVEL);
+
+ //
+ // Is interrupt being removed from head?
+ //
+
+ if (Interrupt == DispatchInfo.Interrupt) {
+
+ //
+ // Update next interrupt object to be head
+ //
+
+ DispatchInfo.Interrupt = CONTAINING_RECORD(
+ DispatchInfo.Interrupt->InterruptListEntry.Flink,
+ KINTERRUPT,
+ InterruptListEntry
+ );
+
+ KiConnectVectorAndInterruptObject (DispatchInfo.Interrupt, ChainConnect);
+ }
+
+ //
+ // Remove interrupt object
+ //
+
+ RemoveEntryList(&Interrupt->InterruptListEntry);
+
+ //
+ // If there's only one interrupt object left on this vector,
+ // determine proper interrupt dispatcher
+ //
+
+ Interrupty = CONTAINING_RECORD(
+ DispatchInfo.Interrupt->InterruptListEntry.Flink,
+ KINTERRUPT,
+ InterruptListEntry
+ );
+
+ if (DispatchInfo.Interrupt == Interrupty) {
+ KiConnectVectorAndInterruptObject (Interrupty, NormalConnect);
+ }
+
+ } else {
+
+ //
+ // Removing last interrupt object from the vector. Disable the
+ // vector, and set it to unconnected
+ //
+
+ HalDisableSystemInterrupt(Interrupt->Vector, Irql);
+ KiConnectVectorAndInterruptObject (Interrupt, NoConnect);
+ }
+
+
+ KeSweepIcache(TRUE);
+ Interrupt->Connected = FALSE;
+ }
+
+ //
+ // Unlock dispatcher database and lower IRQL to its previous value.
+ //
+
+ KiUnlockDispatcherDatabase(OldIrql);
+
+ //
+ // Set system affinity back to the original value.
+ //
+
+ KeRevertToUserAffinityThread();
+
+ //
+ // Return whether interrupt was disconnected from the specified vector.
+ //
+
+ return Connected;
+}
+
+VOID
+KiGetVectorInfo (
+ IN ULONG Vector,
+ OUT PDISPATCH_INFO DispatchInfo
+ )
+{
+ PKINTERRUPT_ROUTINE Dispatch;
+ ULONG CurrentDispatch;
+ ULONG DispatchType;
+
+ //
+ // Get second level dispatch point
+ //
+
+
+ DispatchType = HalSystemVectorDispatchEntry (
+ Vector,
+ &DispatchInfo->FlatDispatch,
+ &DispatchInfo->NoDispatch
+ );
+
+ //
+ // Get vector info
+ //
+
+ switch (DispatchType) {
+ case 0:
+ //
+ // Primary dispatch
+ //
+
+ DispatchInfo->NoDispatch = (PKINTERRUPT_ROUTINE) (((ULONG) &KiStartUnexpectedRange) +
+ (Vector - PRIMARY_VECTOR_BASE) * KiUnexpectedEntrySize);
+
+ DispatchInfo->InterruptDispatch = KiInterruptDispatch;
+ DispatchInfo->FloatingDispatch = KiFloatingDispatch;
+ DispatchInfo->ChainedDispatch = KiChainedDispatch;
+ DispatchInfo->FlatDispatch = NULL;
+
+ CurrentDispatch = (ULONG) KiReturnHandlerAddressFromIDT(Vector);
+ DispatchInfo->Interrupt = CONTAINING_RECORD (
+ CurrentDispatch,
+ KINTERRUPT,
+ DispatchCode
+ );
+ break;
+
+ case 1:
+ //
+ // Secondardy dispatch.
+ //
+
+ DispatchInfo->InterruptDispatch = KiInterruptDispatch2ndLvl;
+ DispatchInfo->FloatingDispatch = KiInterruptDispatch2ndLvl;
+ DispatchInfo->ChainedDispatch = KiChainedDispatch2ndLvl;
+
+ CurrentDispatch = (ULONG) *DispatchInfo->FlatDispatch;
+ DispatchInfo->Interrupt = (PKINTERRUPT) ( (PUCHAR) CurrentDispatch -
+ (PUCHAR) KiInterruptTemplate +
+ (PUCHAR) &KiInterruptTemplate2ndDispatch
+ );
+ break;
+
+ default:
+ // Other values reserved
+ KeBugCheck (MISMATCHED_HAL);
+ }
+
+
+ //
+ // Determine dispatch type
+ //
+
+ if (((PKINTERRUPT_ROUTINE) CurrentDispatch) == DispatchInfo->NoDispatch) {
+
+ //
+ // Is connected to the NoDispatch function
+ //
+
+ DispatchInfo->Type = NoConnect;
+
+ } else {
+ Dispatch = DispatchInfo->Interrupt->DispatchAddress;
+
+ if (Dispatch == DispatchInfo->ChainedDispatch) {
+ //
+ // Is connected to the chained handler
+ //
+
+ DispatchInfo->Type = ChainConnect;
+
+ } else if (Dispatch == DispatchInfo->InterruptDispatch ||
+ Dispatch == DispatchInfo->FloatingDispatch) {
+ //
+ // If connection to the non-chained handler
+ //
+
+ DispatchInfo->Type = NormalConnect;
+
+ } else {
+
+ //
+ // Unkown connection
+ //
+
+ DispatchInfo->Type = UnkownConnect;
+#if DBG
+ DbgPrint ("KiGetVectorInfo not understood\n");
+#endif
+ }
+ }
+}
+
+VOID
+KiConnectVectorAndInterruptObject (
+ IN PKINTERRUPT Interrupt,
+ IN CONNECT_TYPE Type
+ )
+{
+ PKINTERRUPT_ROUTINE DispatchAddress;
+ DISPATCH_INFO DispatchInfo;
+ PULONG pl;
+
+ //
+ // Get current connect info
+ //
+
+ KiGetVectorInfo (
+ Interrupt->Vector,
+ &DispatchInfo
+ );
+
+ //
+ // If disconnecting, set vector to NoDispatch
+ //
+
+ if (Type == NoConnect) {
+
+ DispatchAddress = DispatchInfo.NoDispatch;
+
+ } else {
+
+ //
+ // Set interrupt objects dispatch for new type
+ //
+
+ DispatchAddress = DispatchInfo.ChainedDispatch;
+
+ if (Type == NormalConnect) {
+ DispatchAddress = DispatchInfo.InterruptDispatch;
+ if (Interrupt->FloatingSave) {
+ DispatchAddress = DispatchInfo.FloatingDispatch;
+ }
+ }
+
+ Interrupt->DispatchAddress = DispatchAddress;
+
+ //
+ // Set interrupt objects dispatch code to kernel dispatcher
+ //
+
+ pl = &(Interrupt->DispatchCode[0]);
+ pl = (PULONG)((PUCHAR)pl +
+ ((PUCHAR)&KiInterruptTemplateDispatch -
+ (PUCHAR)KiInterruptTemplate));
+
+ *pl = (ULONG)DispatchAddress-(ULONG)((PUCHAR)pl+4);
+
+ //
+ // Set dispatch vector to proper address dispatch code location
+ //
+
+ if (DispatchInfo.FlatDispatch) {
+
+ //
+ // Connect to flat dispatch
+ //
+
+ DispatchAddress = (PKINTERRUPT_ROUTINE)
+ ((PUCHAR) &(Interrupt->DispatchCode[0]) +
+ ((PUCHAR) &KiInterruptTemplate2ndDispatch -
+ (PUCHAR) KiInterruptTemplate));
+
+ } else {
+
+ //
+ // Connect to enter_all dispatch
+ //
+
+ DispatchAddress = (PKINTERRUPT_ROUTINE) &Interrupt->DispatchCode;
+ }
+ }
+
+
+ if (DispatchInfo.FlatDispatch) {
+
+ //
+ // Connect to flat dispatch
+ //
+
+ *DispatchInfo.FlatDispatch = DispatchAddress;
+
+ } else {
+
+ //
+ // Connect to IDT
+ //
+
+ KiSetHandlerAddressToIDT (Interrupt->Vector, DispatchAddress);
+ }
+}
diff --git a/private/ntos/ke/i386/intsup.asm b/private/ntos/ke/i386/intsup.asm
new file mode 100644
index 000000000..8b2db6cc3
--- /dev/null
+++ b/private/ntos/ke/i386/intsup.asm
@@ -0,0 +1,774 @@
+ TITLE "Interrupt Object Support Routines"
+;++
+;
+; Copyright (c) 1989 Microsoft Corporation
+;
+; Module Name:
+;
+; intsup.asm
+;
+; Abstract:
+;
+; This module implements the code necessary to support interrupt objects.
+; It contains the interrupt dispatch code and the code template that gets
+; copied into an interrupt object.
+;
+; Author:
+;
+; Shie-Lin Tzong (shielint) 20-Jan-1990
+;
+; Environment:
+;
+; Kernel mode only.
+;
+; Revision History:
+;
+;--
+.386p
+ .xlist
+KERNELONLY equ 1
+include ks386.inc
+include i386\kimacro.inc
+include mac386.inc
+include callconv.inc
+ .list
+
+ EXTRNP KfRaiseIrql,1,IMPORT,FASTCALL
+ EXTRNP KfLowerIrql,1,IMPORT,FASTCALL
+ EXTRNP _KeBugCheck,1
+ EXTRNP _KiDeliverApc,3
+ EXTRNP _HalBeginSystemInterrupt,3,IMPORT
+ EXTRNP _HalEndSystemInterrupt,2,IMPORT
+ EXTRNP Kei386EoiHelper
+if DBG
+ extrn _DbgPrint:near
+ extrn _MsgISRTimeout:BYTE
+ extrn _MsgISROverflow:BYTE
+ extrn _KeTickCount:DWORD
+ extrn _KiISRTimeout:DWORD
+ extrn _KiISROverflow:DWORD
+endif
+
+MI_MOVEDI EQU 0BFH ; op code for mov edi, constant
+MI_DIRECTJMP EQU 0E9H ; op code for indirect jmp
+ ; or index registers
+
+_DATA SEGMENT DWORD PUBLIC 'DATA'
+
+if DBG
+ public KiInterruptCounts
+KiInterruptCounts dd 256*2 dup (0)
+endif
+
+_DATA ends
+
+ page ,132
+ subttl "Synchronize Execution"
+
+_TEXT$00 SEGMENT PARA PUBLIC 'CODE'
+
+;++
+;
+; BOOLEAN
+; KeSynchronizeExecution (
+; IN PKINTERRUPT Interrupt,
+; IN PKSYNCHRONIZE_ROUTINE SynchronizeRoutine,
+; IN PVOID SynchronizeContext
+; )
+;
+; Routine Description:
+;
+; This function synchronizes the execution of the specified routine with the
+; execution of the service routine associated with the specified interrupt
+; object.
+;
+; Arguments:
+;
+; Interrupt - Supplies a pointer to a control object of type interrupt.
+;
+; SynchronizeRoutine - Supplies a pointer to a function whose execution
+; is to be synchronized with the execution of the service routine associated
+; with the specified interrupt object.
+;
+; SynchronizeContext - Supplies a pointer to an arbitrary data structure
+; which is to be passed to the function specified by the SynchronizeRoutine
+; parameter.
+;
+; Return Value:
+;
+; The value returned by the SynchronizeRoutine function is returned as the
+; function value.
+;
+;--
+ ASSUME DS:FLAT, ES:FLAT, SS:NOTHING, FS:NOTHING, GS:NOTHING
+cPublicProc _KeSynchronizeExecution ,3
+
+; equates of Local variables
+
+KsePreviousIrql equ [ebp - 4] ; previous IRQL
+KseStackSize = 4 * 1
+
+; equates for arguments
+
+KseInterrupt equ [ebp +8]
+KseSynchronizeRoutine equ [ebp + 12]
+KseSynchronizeContext equ [ebp + 16]
+
+ push ebp ; save ebp
+ mov ebp, esp ; (ebp)-> base of local variable frame
+ sub esp, KseStackSize ; allocate local variables space
+ push ebx ; save ebx
+ push esi ; save esi
+
+; Acquire the associated spin lock and raise IRQL to the interrupting source.
+
+ mov ebx, KseInterrupt ; (ebx)->interrupt object
+
+ mov ecx, InSynchronizeIrql[ebx] ; (ecx) = Synchronize Irql
+ fstCall KfRaiseIrql
+ mov KsePreviousIrql, al
+
+kse10: mov esi,[ebx+InActualLock] ; (esi)->Spin lock variable
+ ACQUIRE_SPINLOCK esi,<short kse20>
+
+; Call specified routine passing the specified context parameter.
+ mov eax,KseSynchronizeContext
+ push eax
+ call KseSynchronizeRoutine
+ mov ebx, eax ; save function returned value
+
+; Unlock spin lock, lower IRQL to its previous level, and return the value
+; returned by the specified routine.
+
+ RELEASE_SPINLOCK esi
+
+ mov ecx, KsePreviousIrql
+ fstCall KfLowerIrql
+
+ mov eax, ebx ; (eax) = returned value
+ pop esi ; restore esi
+ pop ebx ; restore ebx
+ leave ; will clear stack
+ stdRET _KeSynchronizeExecution
+
+; Lock is already owned; spin until free and then attempt to acquire lock
+; again.
+
+ifndef NT_UP
+kse20: SPIN_ON_SPINLOCK esi,<short kse10>,,DbgMp
+endif
+
+stdENDP _KeSynchronizeExecution
+
+ page ,132
+ subttl "Chained Dispatch"
+;++
+;
+; Routine Description:
+;
+; This routine is entered as the result of an interrupt being generated
+; via a vector that is connected to more than one interrupt object.
+;
+; Arguments:
+;
+; edi - Supplies a pointer to the interrupt object.
+; esp - Supplies a pointer to the top of trap frame
+; ebp - Supplies a pointer to the top of trap frame
+;
+; Return Value:
+;
+; None.
+;
+;--
+
+
+align 16
+cPublicProc _KiChainedDispatch ,0
+.FPO (2, 0, 0, 0, 0, 1)
+
+;
+; update statistic
+;
+
+ inc dword ptr PCR[PcPrcbData+PbInterruptCount]
+
+;
+; set ebp to the top of trap frame. We don't need to save ebp because
+; it is saved in trap frame already.
+;
+
+ mov ebp, esp ; (ebp)->trap frame
+
+;
+; Save previous IRQL and set new priority level
+;
+
+ mov eax, [edi].InVector ; save vector
+ push eax
+ sub esp, 4 ; make room for OldIrql
+ mov ecx, [edi].InIrql ; Irql
+
+;
+; esp - pointer to OldIrql
+; eax - vector
+; ecx - Irql
+;
+
+ stdCall _HalBeginSystemInterrupt, <ecx, eax, esp>
+ or eax, eax ; check for spurious int.
+ jz kid_spuriousinterrupt
+
+ stdCall _KiChainedDispatch2ndLvl
+
+ INTERRUPT_EXIT ; will do an iret
+
+stdENDP _KiChainedDispatch
+
+
+ page ,132
+ subttl "Chained Dispatch 2nd Level"
+;++
+;
+; Routine Description:
+;
+; This routine is entered as the result of an interrupt being generated
+; via a vector that is either connected to more than one interrupt object,
+; or is being 2nd level dispatched. Its function is to walk the list
+; of connected interrupt objects and call each interrupt service routine.
+; If the mode of the interrupt is latched, then a complete traversal of
+; the chain must be performed. If any of the routines require saving the
+; floating point machine state, then it is only saved once.
+;
+; Arguments:
+;
+; edi - Supplies a pointer to the interrupt object.
+;
+; Return Value:
+;
+; None.
+; Uses all registers
+;
+;--
+
+
+public _KiInterruptDispatch2ndLvl@0
+_KiInterruptDispatch2ndLvl@0:
+ nop
+
+cPublicProc _KiChainedDispatch2ndLvl,0
+cPublicFpo 0, 3
+
+ push ebp
+ sub esp, 8 ; Make room for scratch value
+ xor ebp, ebp ; init (ebp) = Interrupthandled = FALSE
+ lea ebx, [edi].InInterruptListEntry
+ ; (ebx)->Interrupt Head List
+
+;
+; Walk the list of connected interrupt objects and call the appropriate dispatch
+; routine.
+;
+
+kcd40:
+
+;
+; Raise irql level to the SynchronizeIrql level if it is not equal to current
+; irql.
+;
+
+ mov cl, [edi+InIrql] ; [cl] = Current Irql
+ mov esi,[edi+InActualLock]
+ cmp [edi+InSynchronizeIrql], cl ; Is SyncIrql > current IRQL?
+ je short kcd50 ; if e, no, go kcd50
+
+;
+; Arg2 esp : Address of OldIrql
+; Arg1 eax : Irql to raise to
+;
+
+ mov ecx, [edi+InSynchronizeIrql] ; (eax) = Irql to raise to
+ fstCall KfRaiseIrql
+ mov [esp], eax ; Save OldIrql
+
+
+;
+; Acquire the service routine spin lock and call the service routine.
+;
+
+kcd50:
+ ACQUIRE_SPINLOCK esi,kcd110
+if DBG
+ mov eax, _KeTickCount ; Grab ISR start time
+ mov [esp+4], eax ; save to local varible
+endif
+ mov eax, InServiceContext[edi] ; set parameter value
+ push eax
+ push edi ; pointer to interrupt object
+ call InServiceRoutine[edi] ; call specified routine
+
+if DBG
+ mov ecx, [esp+4] ; (ecx) = time isr started
+ add ecx, _KiISRTimeout ; adjust for timeout
+ cmp _KeTickCount, ecx ; Did ISR timeout?
+ jnc kcd200
+kcd51:
+endif
+
+;
+; Release the service routine spin lock and check to determine if end of loop.
+;
+
+ RELEASE_SPINLOCK esi
+
+;
+; Lower IRQL to earlier level if we raised it to SynchronizedLevel.
+;
+
+ mov cl, [edi+InIrql]
+ cmp [edi+InSynchronizeIrql], cl ; Is SyncIrql > current IRQL?
+ je short kcd55 ; if e, no, go kcd55
+
+ mov esi, eax ; save ISR returned value
+
+;
+; Arg1 : Irql to Lower to
+;
+
+ mov ecx, [esp]
+ fstCall KfLowerIrql
+
+ mov eax, esi ; [eax] = ISR returned value
+kcd55:
+ or al,al ; Is interrupt handled?
+ je short kcd60 ; if eq, interrupt not handled
+ cmp word ptr InMode[edi], InLevelSensitive
+ je short kcd70 ; if eq, level sensitive interrupt
+
+ mov ebp, eax ; else edge shared int is handled. Remember it.
+kcd60: mov edi, [edi].InInterruptListEntry
+ ; (edi)->next obj's addr of listentry
+ cmp ebx, edi ; Are we at end of interrupt list?
+ je short kcd65 ; if eq, reach end of list
+ sub edi, InInterruptListEntry; (edi)->addr of next interrupt object
+ jmp kcd40
+
+kcd65:
+;
+; If this is edge shared interrupt, we need to loop till no one handle the
+; interrupt. In theory only shared edge triggered interrupts come here.
+;
+
+ sub edi, InInterruptListEntry; (edi)->addr of next interrupt object
+ cmp word ptr InMode[edi], InLevelSensitive
+ je short kcd70 ; if level, exit. No one handle the interrupt?
+
+ test ebp, 0fh ; does anyone handle the interrupt?
+ je short kcd70 ; if e, no one, we can exit.
+
+ xor ebp, ebp ; init local var to no one handle the int
+ jmp kcd40 ; restart the loop.
+
+;
+; Either the interrupt is level sensitive and has been handled or the end of
+; the interrupt object chain has been reached.
+;
+
+; restore frame pointer, and deallocate trap frame.
+
+kcd70:
+ add esp, 8 ; clear local variable space
+ pop ebp
+ stdRet _KiChainedDispatch2ndLvl
+
+
+; Service routine Lock is currently owned, spin until free and then
+; attempt to acquire lock again.
+
+ifndef NT_UP
+kcd110: SPIN_ON_SPINLOCK esi, kcd50,,DbgMp
+endif
+
+;
+; ISR took a long time to complete, abort to debugger
+;
+
+if DBG
+kcd200: push eax ; save return code
+ push InServiceRoutine[edi]
+ push offset FLAT:_MsgISRTimeout
+ call _DbgPrint
+ add esp,8
+ pop eax
+ int 3
+ jmp kcd51 ; continue
+endif
+
+stdENDP _KiChainedDispatch2ndLvl
+
+
+ page ,132
+ subttl "Floating Dispatch"
+;++
+;
+; Routine Description:
+;
+; This routine is entered as the result of an interrupt being generated
+; via a vector that is connected to an interrupt object. Its function is
+; to save the machine state and floating state and then call the specified
+; interrupt service routine.
+;
+; Arguments:
+;
+; edi - Supplies a pointer to the interrupt object.
+; esp - Supplies a pointer to the top of trap frame
+; ebp - Supplies a pointer to the top of trap frame
+;
+; Return Value:
+;
+; None.
+;
+;--
+
+align 16
+cPublicProc _KiFloatingDispatch ,0
+.FPO (2, 0, 0, 0, 0, 1)
+
+;
+; update statistic
+;
+ inc dword ptr PCR[PcPrcbData+PbInterruptCount]
+
+; set ebp to the top of trap frame. We don't need to save ebp because
+; it is saved in trap frame already.
+;
+
+ mov ebp, esp ; (ebp)->trap frame
+
+;
+; Save previous IRQL and set new priority level to interrupt obj's SyncIrql
+;
+ mov eax, [edi].InVector
+ mov ecx, [edi].InSynchronizeIrql ; Irql
+ push eax ; save vector
+ sub esp, 4 ; make room for OldIrql
+
+; arg3 - ptr to OldIrql
+; arg2 - vector
+; arg1 - Irql
+ stdCall _HalBeginSystemInterrupt, <ecx, eax, esp>
+
+ or eax, eax ; check for spurious int.
+ jz kid_spuriousinterrupt
+
+;
+; Acquire the service routine spin lock and call the service routine.
+;
+
+kfd30: mov esi,[edi+InActualLock]
+ ACQUIRE_SPINLOCK esi,kfd100
+
+if DBG
+ mov ebx, _KeTickCount ; Grab current tick time
+endif
+ mov eax, InServiceContext[edi] ; set parameter value
+ push eax
+ push edi ; pointer to interrupt object
+ call InServiceRoutine[edi] ; call specified routine
+if DBG
+ add ebx, _KiISRTimeout ; adjust for ISR timeout
+ cmp _KeTickCount, ebx ; Did ISR timeout?
+ jnc kfd200
+kfd31:
+endif
+
+;
+; Release the service routine spin lock.
+;
+
+ RELEASE_SPINLOCK esi
+
+;
+; Do interrupt exit processing
+;
+ INTERRUPT_EXIT ; will do an iret
+
+;
+; Service routine Lock is currently owned; spin until free and
+; then attempt to acquire lock again.
+;
+
+ifndef NT_UP
+kfd100: SPIN_ON_SPINLOCK esi,kfd30,,DbgMp
+endif
+
+;
+; ISR took a long time to complete, abort to debugger
+;
+
+if DBG
+kfd200: push InServiceRoutine[edi] ; timed out
+ push offset FLAT:_MsgISRTimeout
+ call _DbgPrint
+ add esp,8
+ int 3
+ jmp kfd31 ; continue
+endif
+
+stdENDP _KiFloatingDispatch
+
+ page ,132
+ subttl "Interrupt Dispatch"
+;++
+;
+; Routine Description:
+;
+; This routine is entered as the result of an interrupt being generated
+; via a vector that is connected to an interrupt object. Its function is
+; to directly call the specified interrupt service routine.
+;
+; Arguments:
+;
+; edi - Supplies a pointer to the interrupt object.
+; esp - Supplies a pointer to the top of trap frame
+; ebp - Supplies a pointer to the top of trap frame
+;
+; Return Value:
+;
+; None.
+;
+;--
+
+align 16
+cPublicProc _KiInterruptDispatch ,0
+.FPO (2, 0, 0, 0, 0, 1)
+
+;
+; update statistic
+;
+ inc dword ptr PCR[PcPrcbData+PbInterruptCount]
+
+;
+; set ebp to the top of trap frame. We don't need to save ebp because
+; it is saved in trap frame already.
+;
+
+ mov ebp, esp ; (ebp)->trap frame
+
+;
+; Save previous IRQL and set new priority level
+;
+ mov eax, [edi].InVector ; save vector
+ mov ecx, [edi].InSynchronizeIrql ; Irql to raise to
+ push eax
+ sub esp, 4 ; make room for OldIrql
+
+ stdCall _HalBeginSystemInterrupt,<ecx, eax, esp>
+
+ or eax, eax ; check for spurious int.
+ jz kid_spuriousinterrupt
+
+;
+; Acquire the service routine spin lock and call the service routine.
+;
+
+kid30: mov esi,[edi+InActualLock]
+ ACQUIRE_SPINLOCK esi,kid100
+if DBG
+ mov ebx, [edi].InVector ; this vector
+ mov eax, _KeTickCount ; current time
+ and eax, NOT 31 ; mask to closest 1/2 second
+ shl ebx, 3 ; eax = eax * 8
+ cmp eax, [KiInterruptCounts+ebx] ; in same 1/2 range?
+ jne kid_overflowreset
+
+ mov eax, _KiISROverflow
+ inc [KiInterruptCounts+ebx+4]
+ cmp [KiInterruptCounts+ebx+4], eax
+ jnc kid_interruptoverflow
+kid_dbg2:
+ mov ebx, _KeTickCount
+endif
+ mov eax, InServiceContext[edi] ; set parameter value
+ push eax
+ push edi ; pointer to interrupt object
+ call InServiceRoutine[edi] ; call specified routine
+
+if DBG
+ add ebx, _KiISRTimeout ; adjust for ISR timeout
+ cmp _KeTickCount, ebx ; Did ISR timeout?
+ jnc kid200
+kid31:
+endif
+
+;
+; Release the service routine spin lock, retrieve the return address,
+; deallocate stack storage, and return.
+;
+
+ RELEASE_SPINLOCK esi
+
+;
+; Do interrupt exit processing
+;
+
+ INTERRUPT_EXIT ; will do an iret
+
+ add esp, 8 ; clean stack
+
+kid_spuriousinterrupt:
+ add esp, 8 ; Irql wasn't raised, exit interrupt
+ SPURIOUS_INTERRUPT_EXIT ; without eoi or lower irql
+
+;
+; Lock is currently owned; spin until free and then attempt to acquire
+; lock again.
+;
+
+ifndef NT_UP
+kid100: SPIN_ON_SPINLOCK esi,kid30,,DbgMp
+endif
+
+;
+; ISR took a long time to complete, abort to debugger
+;
+
+if DBG
+kid200: push InServiceRoutine[edi] ; timed out
+ push offset FLAT:_MsgISRTimeout
+ call _DbgPrint
+ add esp,8
+ int 3
+ jmp kid31 ; continue
+
+kid_interruptoverflow:
+ push [KiInterruptCounts+ebx+4]
+ push InServiceRoutine[edi]
+ push offset FLAT:_MsgISROverflow
+ call _DbgPrint
+ add esp,12
+ int 3
+
+ mov eax, _KeTickCount ; current time
+ and eax, NOT 31 ; mask to closest 1/2 second
+
+kid_overflowreset:
+ mov [KiInterruptCounts+ebx], eax ; initialize time
+ mov [KiInterruptCounts+ebx+4], 0 ; reset count
+ jmp kid_dbg2
+endif
+
+
+
+stdENDP _KiInterruptDispatch
+
+ page ,132
+ subttl "Interrupt Template"
+;++
+;
+; Routine Description:
+;
+; This routine is a template that is copied into each interrupt object. Its
+; function is to save machine state and pass the address of the respective
+; interrupt object and transfer control to the appropriate interrupt
+; dispatcher.
+;
+; Control comes here through i386 interrupt gate and, upon entry, the
+; interrupt is disabled.
+;
+; Note: If the length of this template changed, the corresponding constant
+; defined in Ki.h needs to be updated accordingly.
+;
+; Arguments:
+;
+; None
+;
+; Return Value:
+;
+; edi - addr of interrupt object
+; esp - top of trap frame
+; interrupts are disabled
+;
+;--
+
+_KiShutUpAssembler proc
+
+ public _KiInterruptTemplate
+_KiInterruptTemplate label byte
+
+; Save machine state on trap frame
+
+ ENTER_INTERRUPT kit_a, kit_t
+
+;
+; the following instruction gets the addr of associated interrupt object.
+; the value ? will be replaced by REAL interrupt object address at
+; interrupt object initialization time.
+; mov edi, addr of interrupt object
+
+ public _KiInterruptTemplate2ndDispatch
+_KiInterruptTemplate2ndDispatch equ this dword
+ db MI_MOVEDI ; MOV EDI opcode
+
+ public _KiInterruptTemplateObject
+_KiInterruptTemplateObject equ this dword
+ dd ? ; addr of interrupt object
+
+; the following instruction transfers control to the appropriate dispatcher
+; code. The value ? will be replaced by real InterruptObj.DispatchAddr
+; at interrupt initialization time. The dispatcher routine will be any one
+; of _KiInterruptDispatch, _KiFloatingDispatch, or _KiChainDispatch.
+; jmp [IntObj.DispatchAddr]
+
+ db MI_DIRECTJMP ; indirect near jump opcode
+
+ public _KiInterruptTemplateDispatch
+_KiInterruptTemplateDispatch equ this dword
+ dd -5 ; addr of IntObj.DispatchAddr
+
+ ENTER_DR_ASSIST kit_a, kit_t
+
+; end of _KiInterruptTemplate
+
+if ($ - _KiInterruptTemplate) GT DISPATCH_LENGTH
+ .err
+ %out <InterruptTemplate greater than dispatch_length>
+endif
+
+_KiShutUpAssembler endp
+
+ page ,132
+ subttl "Unexpected Interrupt"
+;++
+;
+; Routine Description:
+;
+; This routine is entered as the result of an interrupt being generated
+; via a vector that is not connected to an interrupt object.
+;
+; For any unconnected vector, its associated 8259 irq is masked out at
+; Initialization time. So, this routine should NEVER be called.
+; If somehow, this routine gets control we simple raise a BugCheck and
+; stop the system.
+;
+; Arguments:
+;
+; None
+; Interrupt is disabled
+;
+; Return Value:
+;
+; None.
+;
+;--
+ public _KiUnexpectedInterrupt
+_KiUnexpectedInterrupt proc
+cPublicFpo 0,0
+
+; stop the system
+ stdCall _KeBugCheck, <TRAP_CAUSE_UNKNOWN>
+ nop
+
+_KiUnexpectedInterrupt endp
+
+_TEXT$00 ends
+ end
diff --git a/private/ntos/ke/i386/iopm.c b/private/ntos/ke/i386/iopm.c
new file mode 100644
index 000000000..c6e7260e2
--- /dev/null
+++ b/private/ntos/ke/i386/iopm.c
@@ -0,0 +1,529 @@
+/*++
+
+Copyright (c) 1991 Microsoft Corporation
+
+Module Name:
+
+ iopm.c
+
+Abstract:
+
+ This module implements interfaces that support manipulation of i386
+ i/o access maps (IOPMs).
+
+ These entry points only exist on i386 machines.
+
+Author:
+
+ Bryan M. Willman (bryanwi) 18-Sep-91
+
+Environment:
+
+ Kernel mode only.
+
+Revision History:
+
+--*/
+
+#include "ki.h"
+
+//
+// Our notion of alignment is different, so force use of ours
+//
+
+#undef ALIGN_UP
+#undef ALIGN_DOWN
+#define ALIGN_DOWN(address,amt) ((ULONG)(address) & ~(( amt ) - 1))
+#define ALIGN_UP(address,amt) (ALIGN_DOWN( (address + (amt) - 1), (amt) ))
+
+//
+// Note on synchronization:
+//
+// IOPM edits are always done by code running at synchronization level on
+// the processor whose TSS (map) is being edited.
+//
+// IOPM only affects user mode code. User mode code can never interrupt
+// synchronization level code, therefore, edits and user code never race.
+//
+// Likewise, switching from one map to another occurs on the processor
+// for which the switch is being done by IPI_LEVEL code. The active
+// map could be switched in the middle of an edit of some map, but
+// the edit will always complete before any user code gets run on that
+// processor, therefore, there is no race.
+//
+// Multiple simultaneous calls to Ke386SetIoAccessMap *could* produce
+// weird mixes. Therefore, KiIopmLock must be acquired to
+// globally serialize edits.
+//
+
+//
+// Define forward referenced function prototypes.
+//
+
+VOID
+KiSetIoMap(
+ IN PKIPI_CONTEXT SignalDone,
+ IN PVOID MapSource,
+ IN PVOID MapNumber,
+ IN PVOID Parameter3
+ );
+
+VOID
+KiLoadIopmOffset(
+ IN PKIPI_CONTEXT SignalDone,
+ IN PVOID Parameter1,
+ IN PVOID Parameter2,
+ IN PVOID Parameter3
+ );
+
+BOOLEAN
+Ke386SetIoAccessMap (
+ ULONG MapNumber,
+ PKIO_ACCESS_MAP IoAccessMap
+ )
+
+/*++
+
+Routine Description:
+
+ The specified i/o access map will be set to match the
+ definition specified by IoAccessMap (i.e. enable/disable
+ those ports) before the call returns. The change will take
+ effect on all processors.
+
+ Ke386SetIoAccessMap does not give any process enhanced I/O
+ access, it merely defines a particular access map.
+
+Arguments:
+
+ MapNumber - Number of access map to set. Map 0 is fixed.
+
+ IoAccessMap - Pointer to bitvector (64K bits, 8K bytes) which
+ defines the specified access map. Must be in
+ non-paged pool.
+
+Return Value:
+
+ TRUE if successful. FALSE if failure (attempt to set a map
+ which does not exist, attempt to set map 0)
+
+--*/
+
+{
+
+ PKPROCESS CurrentProcess;
+ KIRQL OldIrql;
+ PKPRCB Prcb;
+ PVOID pt;
+ KAFFINITY TargetProcessors;
+
+ //
+ // Reject illegal requests
+ //
+
+ if ((MapNumber > IOPM_COUNT) || (MapNumber == IO_ACCESS_MAP_NONE)) {
+ return FALSE;
+ }
+
+ //
+ // Acquire the context swap lock so a context switch will not occur.
+ //
+
+ KiLockContextSwap(&OldIrql);
+
+ //
+ // Compute set of active processors other than this one, if non-empty
+ // IPI them to set their maps.
+ //
+
+ Prcb = KeGetCurrentPrcb();
+
+#if !defined(NT_UP)
+
+ TargetProcessors = KeActiveProcessors & ~Prcb->SetMember;
+ if (TargetProcessors != 0) {
+ KiIpiSendPacket(TargetProcessors,
+ KiSetIoMap,
+ IoAccessMap,
+ (PVOID)MapNumber,
+ NULL);
+ }
+
+#endif
+
+ //
+ // Copy the IOPM map and load the map for the current process.
+ //
+
+ pt = &(KiPcr()->TSS->IoMaps[MapNumber-1].IoMap);
+ RtlMoveMemory(pt, (PVOID)IoAccessMap, IOPM_SIZE);
+ CurrentProcess = Prcb->CurrentThread->ApcState.Process;
+ KiPcr()->TSS->IoMapBase = CurrentProcess->IopmOffset;
+
+ //
+ // Wait until all of the target processors have finished copying the
+ // new map.
+ //
+
+#if !defined(NT_UP)
+
+ if (TargetProcessors != 0) {
+ KiIpiStallOnPacketTargets();
+ }
+
+#endif
+
+ //
+ // Restore IRQL and unlock the context swap lock.
+ //
+
+ KiUnlockContextSwap(OldIrql);
+ return TRUE;
+}
+
+#if !defined(NT_UP)
+
+
+VOID
+KiSetIoMap(
+ IN PKIPI_CONTEXT SignalDone,
+ IN PVOID MapSource,
+ IN PVOID MapNumber,
+ IN PVOID Parameter3
+ )
+/*++
+
+Routine Description:
+
+ copy the specified map into this processor's TSS.
+ This procedure runs at IPI level.
+
+Arguments:
+
+ Argument - actually a pointer to a KIPI_SET_IOPM structure
+ ReadyFlag - pointer to flag to set once setiopm has completed
+
+Return Value:
+
+ none
+
+--*/
+
+{
+
+ PKPROCESS CurrentProcess;
+ PKPRCB Prcb;
+ PVOID pt;
+
+ //
+ // Copy the IOPM map and load the map for the current process.
+ //
+
+ Prcb = KeGetCurrentPrcb();
+ pt = &(KiPcr()->TSS->IoMaps[((ULONG) MapNumber)-1].IoMap);
+ RtlMoveMemory(pt, MapSource, IOPM_SIZE);
+ CurrentProcess = Prcb->CurrentThread->ApcState.Process;
+ KiPcr()->TSS->IoMapBase = CurrentProcess->IopmOffset;
+ KiIpiSignalPacketDone(SignalDone);
+ return;
+}
+
+#endif
+
+
+BOOLEAN
+Ke386QueryIoAccessMap (
+ ULONG MapNumber,
+ PKIO_ACCESS_MAP IoAccessMap
+ )
+
+/*++
+
+Routine Description:
+
+ The specified i/o access map will be dumped into the buffer.
+ map 0 is a constant, but will be dumped anyway.
+
+Arguments:
+
+ MapNumber - Number of access map to set. map 0 is fixed.
+
+ IoAccessMap - Pointer to buffer (64K bits, 8K bytes) which
+ is to receive the definition of the access map.
+ Must be in non-paged pool.
+
+Return Value:
+
+ TRUE if successful. FALSE if failure (attempt to query a map
+ which does not exist)
+
+--*/
+
+{
+
+ ULONG i;
+ PVOID Map;
+ KIRQL OldIrql;
+ PUCHAR p;
+
+ //
+ // Reject illegal requests
+ //
+
+ if (MapNumber > IOPM_COUNT) {
+ return FALSE;
+ }
+
+ //
+ // Acquire the context swap lock so a context switch will not occur.
+ //
+
+ KiLockContextSwap(&OldIrql);
+
+ //
+ // Copy out the map
+ //
+
+ if (MapNumber == IO_ACCESS_MAP_NONE) {
+
+ //
+ // no access case, simply return a map of all 1s
+ //
+
+ p = (PUCHAR)IoAccessMap;
+ for (i = 0; i < IOPM_SIZE; i++) {
+ p[i] = (UCHAR)-1;
+ }
+
+ } else {
+
+ //
+ // normal case, just copy the bits
+ //
+
+ Map = (PVOID)&(KiPcr()->TSS->IoMaps[MapNumber-1].IoMap);
+ RtlMoveMemory((PVOID)IoAccessMap, Map, IOPM_SIZE);
+ }
+
+ //
+ // Restore IRQL and unlock the context swap lock.
+ //
+
+ KiUnlockContextSwap(OldIrql);
+ return TRUE;
+}
+
+
+BOOLEAN
+Ke386IoSetAccessProcess (
+ PKPROCESS Process,
+ ULONG MapNumber
+ )
+/*++
+
+Routine Description:
+
+ Set the i/o access map which controls user mode i/o access
+ for a particular process.
+
+Arguments:
+
+ Process - Pointer to kernel process object describing the
+ process which for which a map is to be set.
+
+ MapNumber - Number of the map to set. Value of map is
+ defined by Ke386IoSetAccessProcess. Setting MapNumber
+ to IO_ACCESS_MAP_NONE will disallow any user mode i/o
+ access from the process.
+
+Return Value:
+
+ TRUE if success, FALSE if failure (illegal MapNumber)
+
+--*/
+
+{
+
+ USHORT MapOffset;
+ KIRQL OldIrql;
+ PKPRCB Prcb;
+ KAFFINITY TargetProcessors;
+
+ //
+ // Reject illegal requests
+ //
+
+ if (MapNumber > IOPM_COUNT) {
+ return FALSE;
+ }
+
+ MapOffset = KiComputeIopmOffset(MapNumber);
+
+ //
+ // Acquire the context swap lock so a context switch will not occur.
+ //
+
+ KiLockContextSwap(&OldIrql);
+
+ //
+ // Store new offset in process object, compute current set of
+ // active processors for process, if this cpu is one, set IOPM.
+ //
+
+ Process->IopmOffset = MapOffset;
+
+ TargetProcessors = Process->ActiveProcessors;
+ Prcb = KeGetCurrentPrcb();
+ if (TargetProcessors & Prcb->SetMember) {
+ KiPcr()->TSS->IoMapBase = MapOffset;
+ }
+
+ //
+ // Compute set of active processors other than this one, if non-empty
+ // IPI them to load their IOPMs, wait for them.
+ //
+
+#if !defined(NT_UP)
+
+ TargetProcessors = TargetProcessors & ~Prcb->SetMember;
+ if (TargetProcessors != 0) {
+ KiIpiSendPacket(TargetProcessors,
+ KiLoadIopmOffset,
+ NULL,
+ NULL,
+ NULL);
+
+ KiIpiStallOnPacketTargets();
+ }
+
+#endif
+
+ //
+ // Restore IRQL and unlock the context swap lock.
+ //
+
+ KiUnlockContextSwap(OldIrql);
+ return TRUE;
+}
+
+#if !defined(NT_UP)
+
+
+VOID
+KiLoadIopmOffset(
+ IN PKIPI_CONTEXT SignalDone,
+ IN PVOID Parameter1,
+ IN PVOID Parameter2,
+ IN PVOID Parameter3
+ )
+
+/*++
+
+Routine Description:
+
+ Edit IopmBase of Tss to match that of currently running process.
+
+Arguments:
+
+ Argument - actually a pointer to a KIPI_LOAD_IOPM_OFFSET structure
+ ReadyFlag - Pointer to flag to be set once we are done
+
+Return Value:
+
+ none
+
+--*/
+
+{
+
+ PKPROCESS CurrentProcess;
+ PKPRCB Prcb;
+
+ //
+ // Update IOPM field in TSS from current process
+ //
+
+ Prcb = KeGetCurrentPrcb();
+ CurrentProcess = Prcb->CurrentThread->ApcState.Process;
+ KiPcr()->TSS->IoMapBase = CurrentProcess->IopmOffset;
+ KiIpiSignalPacketDone(SignalDone);
+ return;
+}
+
+#endif
+
+
+VOID
+Ke386SetIOPL(
+ IN PKPROCESS Process
+ )
+
+/*++
+
+Routine Description:
+
+ Gives IOPL to the specified process.
+
+ All threads created from this point on will get IOPL. The current
+ process will get IOPL. Must be called from context of thread and
+ process that are to have IOPL.
+
+ Iopl (to be made a boolean) in KPROCESS says all
+ new threads to get IOPL.
+
+ Iopl (to be made a boolean) in KTHREAD says given
+ thread to get IOPL.
+
+ N.B. If a kernel mode only thread calls this procedure, the
+ result is (a) poinless and (b) will break the system.
+
+Arguments:
+
+ Process - Pointer to the process == IGNORED!!!
+
+Return Value:
+
+ none
+
+--*/
+
+{
+
+ PKTHREAD Thread;
+ PKPROCESS Process2;
+ PKTRAP_FRAME TrapFrame;
+ CONTEXT Context;
+
+ //
+ // get current thread and Process2, set flag for IOPL in both of them
+ //
+
+ Thread = KeGetCurrentThread();
+ Process2 = Thread->ApcState.Process;
+
+ Process2->Iopl = 1;
+ Thread->Iopl = 1;
+
+ //
+ // Force IOPL to be on for current thread
+ //
+
+ TrapFrame = (PKTRAP_FRAME)((PUCHAR)Thread->InitialStack -
+ ALIGN_UP(sizeof(KTRAP_FRAME),KTRAP_FRAME_ALIGN) -
+ sizeof(FLOATING_SAVE_AREA));
+
+ Context.ContextFlags = CONTEXT_CONTROL;
+ KeContextFromKframes(TrapFrame,
+ NULL,
+ &Context);
+
+ Context.EFlags |= (EFLAGS_IOPL_MASK & -1); // IOPL == 3
+
+ KeContextToKframes(TrapFrame,
+ NULL,
+ &Context,
+ CONTEXT_CONTROL,
+ UserMode);
+
+ return;
+}
diff --git a/private/ntos/ke/i386/kernlini.c b/private/ntos/ke/i386/kernlini.c
new file mode 100644
index 000000000..cce9b5248
--- /dev/null
+++ b/private/ntos/ke/i386/kernlini.c
@@ -0,0 +1,1581 @@
+/*++
+
+Copyright (c) 1989 Microsoft Corporation
+
+Module Name:
+
+ kernlini.c
+
+Abstract:
+
+ This module contains the code to initialize the kernel data structures
+ and to initialize the idle thread, its process, and the processor control
+ block.
+
+ For the i386, it also contains code to initialize the PCR.
+
+Author:
+
+ David N. Cutler (davec) 21-Apr-1989
+
+Environment:
+
+ Kernel mode only.
+
+Revision History:
+
+ 24-Jan-1990 shielin
+
+ Changed for NT386
+
+ 20-Mar-1990 bryanwi
+
+ Added KiInitializePcr
+
+--*/
+
+#include "ki.h"
+#include "ki386.h"
+
+#define TRAP332_GATE 0xEF00
+
+VOID
+KiSetProcessorType(
+ VOID
+ );
+
+VOID
+KiSetCR0Bits(
+ VOID
+ );
+
+BOOLEAN
+KiIsNpxPresent(
+ VOID
+ );
+
+VOID
+KiInitializeDblFaultTSS(
+ IN PKTSS Tss,
+ IN ULONG Stack,
+ IN PKGDTENTRY TssDescriptor
+ );
+
+VOID
+KiInitializeTSS2 (
+ IN PKTSS Tss,
+ IN PKGDTENTRY TssDescriptor
+ );
+
+VOID
+KiSwapIDT (
+ VOID
+ );
+
+VOID
+KeSetup80387OrEmulate (
+ IN PVOID *R3EmulatorTable
+ );
+
+ULONG
+KiGetFeatureBits (
+ VOID
+ );
+
+NTSTATUS
+KiMoveRegTree(
+ HANDLE Source,
+ HANDLE Dest
+ );
+
+VOID
+Ki386EnableGlobalPage (
+ IN volatile PLONG Number
+ );
+
+BOOLEAN
+KiInitMachineDependent (
+ VOID
+ );
+
+VOID
+KiInitializeMTRR (
+ IN BOOLEAN LastProcessor
+ );
+
+#ifdef ALLOC_PRAGMA
+#pragma alloc_text(INIT,KiInitializeKernel)
+#pragma alloc_text(INIT,KiInitializePcr)
+#pragma alloc_text(INIT,KiInitializeDblFaultTSS)
+#pragma alloc_text(INIT,KiInitializeTSS2)
+#pragma alloc_text(INIT,KiSwapIDT)
+#pragma alloc_text(INIT,KeSetup80387OrEmulate)
+#pragma alloc_text(INIT,KiGetFeatureBits)
+#pragma alloc_text(INIT,KiMoveRegTree)
+#pragma alloc_text(INIT,KiInitMachineDependent)
+#endif
+
+
+#if 0
+PVOID KiTrap08;
+#endif
+
+extern PVOID Ki387RoundModeTable;
+extern PVOID Ki386IopmSaveArea;
+extern ULONG KeI386ForceNpxEmulation;
+extern WCHAR CmDisabledFloatingPointProcessor[];
+extern UCHAR CmpCyrixID[];
+
+#define CPU_NONE 0
+#define CPU_INTEL 1
+#define CPU_AMD 2
+#define CPU_CYRIX 3
+
+
+
+
+//
+// Profile vars
+//
+
+extern KIDTENTRY IDT[];
+
+VOID
+KiInitializeKernel (
+ IN PKPROCESS Process,
+ IN PKTHREAD Thread,
+ IN PVOID IdleStack,
+ IN PKPRCB Prcb,
+ IN CCHAR Number,
+ PLOADER_PARAMETER_BLOCK LoaderBlock
+ )
+
+/*++
+
+Routine Description:
+
+ This function gains control after the system has been bootstrapped and
+ before the system has been initialized. Its function is to initialize
+ the kernel data structures, initialize the idle thread and process objects,
+ initialize the processor control block, call the executive initialization
+ routine, and then return to the system startup routine. This routine is
+ also called to initialize the processor specific structures when a new
+ processor is brought on line.
+
+Arguments:
+
+ Process - Supplies a pointer to a control object of type process for
+ the specified processor.
+
+ Thread - Supplies a pointer to a dispatcher object of type thread for
+ the specified processor.
+
+ IdleStack - Supplies a pointer the base of the real kernel stack for
+ idle thread on the specified processor.
+
+ Prcb - Supplies a pointer to a processor control block for the specified
+ processor.
+
+ Number - Supplies the number of the processor that is being
+ initialized.
+
+ LoaderBlock - Supplies a pointer to the loader parameter block.
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+
+#define INITIAL_KERNEL_STACK_SIZE (((sizeof(FLOATING_SAVE_AREA)+KTRAP_FRAME_LENGTH+KTRAP_FRAME_ROUND) & ~KTRAP_FRAME_ROUND)/sizeof(ULONG))+1
+
+ ULONG KernelStack[INITIAL_KERNEL_STACK_SIZE];
+ LONG Index;
+ ULONG DirectoryTableBase[2];
+ KIRQL OldIrql;
+ PKPCR Pcr;
+ BOOLEAN NpxFlag;
+ ULONG FeatureBits;
+
+ KiSetProcessorType();
+ KiSetCR0Bits();
+ NpxFlag = KiIsNpxPresent();
+
+ Pcr = KeGetPcr();
+
+ //
+ // Initialize DPC listhead and lock.
+ //
+
+ InitializeListHead(&Prcb->DpcListHead);
+ KeInitializeSpinLock(&Prcb->DpcLock);
+ Prcb->DpcRoutineActive = 0;
+ Prcb->DpcQueueDepth = 0;
+ Prcb->MaximumDpcQueueDepth = KiMaximumDpcQueueDepth;
+ Prcb->MinimumDpcRate = KiMinimumDpcRate;
+ Prcb->AdjustDpcThreshold = KiAdjustDpcThreshold;
+
+ //
+ // Check for unsupported processor revision
+ //
+
+ if (Prcb->CpuType == 3) {
+ KeBugCheckEx(UNSUPPORTED_PROCESSOR,0x386,0,0,0);
+ }
+
+ //
+ // If the initial processor is being initialized, then initialize the
+ // per system data structures.
+ //
+
+ if (Number == 0) {
+
+ //
+ // Initial setting for global Cpu & Stepping levels
+ //
+
+ KeI386NpxPresent = NpxFlag;
+ KeI386CpuType = Prcb->CpuType;
+ KeI386CpuStep = Prcb->CpuStep;
+
+ KeProcessorArchitecture = PROCESSOR_ARCHITECTURE_INTEL;
+ KeProcessorLevel = (USHORT)Prcb->CpuType;
+ if (Prcb->CpuID == 0) {
+ KeProcessorRevision = 0xFF00 |
+ (((Prcb->CpuStep >> 4) + 0xa0 ) & 0x0F0) |
+ (Prcb->CpuStep & 0xf);
+ } else {
+ KeProcessorRevision = Prcb->CpuStep;
+ }
+
+ KeFeatureBits = KiGetFeatureBits();
+
+ //
+ // If cmpxchg8b was available at boot, verify its still available
+ //
+
+ if ((KiBootFeatureBits & KF_CMPXCHG8B) && !(KeFeatureBits & KF_CMPXCHG8B)) {
+ KeBugCheckEx (MULTIPROCESSOR_CONFIGURATION_NOT_SUPPORTED, KF_CMPXCHG8B, 0, 0, 0);
+ }
+
+ //
+ // Lower IRQL to APC level.
+ //
+
+ KeLowerIrql(APC_LEVEL);
+
+
+ //
+ // Initialize kernel internal spinlocks
+ //
+
+ KeInitializeSpinLock(&KiContextSwapLock);
+ KeInitializeSpinLock(&KiDispatcherLock);
+ KeInitializeSpinLock(&KiFreezeExecutionLock);
+
+
+ //
+ // Performance architecture independent initialization.
+ //
+
+ KiInitSystem();
+
+ //
+ // Initialize idle thread process object and then set:
+ //
+ // 1. all the quantum values to the maximum possible.
+ // 2. the process in the balance set.
+ // 3. the active processor mask to the specified process.
+ //
+
+ DirectoryTableBase[0] = 0;
+ DirectoryTableBase[1] = 0;
+ KeInitializeProcess(Process,
+ (KPRIORITY)0,
+ (KAFFINITY)(0xffffffff),
+ &DirectoryTableBase[0],
+ FALSE);
+
+ Process->ThreadQuantum = MAXCHAR;
+
+ } else {
+
+ FeatureBits = KiGetFeatureBits();
+
+ //
+ // Adjust global cpu setting to represent lowest of all processors
+ //
+
+ if (NpxFlag != KeI386NpxPresent) {
+ //
+ // NPX support must be available on all processors or on none
+ //
+
+ KeBugCheckEx (MULTIPROCESSOR_CONFIGURATION_NOT_SUPPORTED, 0x387, 0, 0, 0);
+ }
+
+ if ((ULONG)(Prcb->CpuType) != KeI386CpuType) {
+
+ if ((ULONG)(Prcb->CpuType) < KeI386CpuType) {
+
+ //
+ // What is the lowest CPU type
+ //
+
+ KeI386CpuType = (ULONG)Prcb->CpuType;
+ KeProcessorLevel = (USHORT)Prcb->CpuType;
+ }
+ }
+
+ if ((KiBootFeatureBits & KF_CMPXCHG8B) && !(FeatureBits & KF_CMPXCHG8B)) {
+ //
+ // cmpxchg8b must be available on all processors, if installed at boot
+ //
+
+ KeBugCheckEx (MULTIPROCESSOR_CONFIGURATION_NOT_SUPPORTED, KF_CMPXCHG8B, 0, 0, 0);
+ }
+
+ if ((KeFeatureBits & KF_GLOBAL_PAGE) && !(FeatureBits & KF_GLOBAL_PAGE)) {
+ //
+ // Global page support must be available on all processors, if on boot processor
+ //
+
+ KeBugCheckEx (MULTIPROCESSOR_CONFIGURATION_NOT_SUPPORTED, KF_GLOBAL_PAGE, 0, 0, 0);
+ }
+
+ //
+ // Use lowest stepping value
+ //
+
+ if (Prcb->CpuStep < KeI386CpuStep) {
+ KeI386CpuStep = Prcb->CpuStep;
+ if (Prcb->CpuID == 0) {
+ KeProcessorRevision = 0xFF00 |
+ ((Prcb->CpuStep >> 8) + 'A') |
+ (Prcb->CpuStep & 0xf);
+ } else {
+ KeProcessorRevision = Prcb->CpuStep;
+ }
+ }
+
+ //
+ // Use subset of all NT feature bits available on each processor
+ //
+
+ KeFeatureBits &= FeatureBits;
+
+ //
+ // Lower IRQL to DISPATCH level.
+ //
+
+ KeLowerIrql(DISPATCH_LEVEL);
+
+ }
+
+ //
+ // Update processor features
+ //
+
+ SharedUserData->ProcessorFeatures[PF_MMX_INSTRUCTIONS_AVAILABLE] =
+ (KeFeatureBits & KF_MMX) ? TRUE : FALSE;
+
+ SharedUserData->ProcessorFeatures[PF_COMPARE_EXCHANGE_DOUBLE] =
+ (KeFeatureBits & KF_CMPXCHG8B) ? TRUE : FALSE;
+
+ //
+ // Initialize idle thread object and then set:
+ //
+ // 1. the initial kernel stack to the specified idle stack.
+ // 2. the next processor number to the specified processor.
+ // 3. the thread priority to the highest possible value.
+ // 4. the state of the thread to running.
+ // 5. the thread affinity to the specified processor.
+ // 6. the specified processor member in the process active processors
+ // set.
+ //
+
+ KeInitializeThread(Thread, (PVOID)&KernelStack[INITIAL_KERNEL_STACK_SIZE],
+ (PKSYSTEM_ROUTINE)NULL, (PKSTART_ROUTINE)NULL,
+ (PVOID)NULL, (PCONTEXT)NULL, (PVOID)NULL, Process);
+ Thread->InitialStack = (PVOID)(((ULONG)IdleStack) &0xfffffff0);
+ Thread->StackBase = Thread->InitialStack;
+ Thread->StackLimit = (PVOID)((ULONG)Thread->InitialStack - KERNEL_STACK_SIZE);
+ Thread->NextProcessor = Number;
+ Thread->Priority = HIGH_PRIORITY;
+ Thread->State = Running;
+ Thread->Affinity = (KAFFINITY)(1<<Number);
+ Thread->WaitIrql = DISPATCH_LEVEL;
+ SetMember(Number, Process->ActiveProcessors);
+
+ //
+ // Initialize the processor block. (Note that some fields have been
+ // initialized at KiInitializePcr().
+ //
+
+ Prcb->CurrentThread = Thread;
+ Prcb->NextThread = (PKTHREAD)NULL;
+ Prcb->IdleThread = Thread;
+ Pcr->NtTib.StackBase = Thread->InitialStack;
+
+ //
+ // The following operations need to be done atomically. So we
+ // grab the DispatcherDatabase.
+ //
+
+ KiAcquireSpinLock(&KiDispatcherLock);
+
+ //
+ // Release DispatcherDatabase
+ //
+
+ KiReleaseSpinLock(&KiDispatcherLock);
+
+ //
+ // call the executive initialization routine.
+ //
+
+ try {
+ ExpInitializeExecutive(Number, LoaderBlock);
+
+ } except (EXCEPTION_EXECUTE_HANDLER) {
+ KeBugCheck (PHASE0_EXCEPTION);
+ }
+
+ //
+ // If the initial processor is being initialized, then compute the
+ // timer table reciprocal value and reset the PRCB values for the
+ // controllable DPC behavior in order to reflect any registry
+ // overrides.
+ //
+
+ if (Number == 0) {
+ KiTimeIncrementReciprocal = KiComputeReciprocal((LONG)KeMaximumIncrement,
+ &KiTimeIncrementShiftCount);
+
+ Prcb->MaximumDpcQueueDepth = KiMaximumDpcQueueDepth;
+ Prcb->MinimumDpcRate = KiMinimumDpcRate;
+ Prcb->AdjustDpcThreshold = KiAdjustDpcThreshold;
+ }
+
+ //
+ // Allocate 8k IOPM bit map saved area to allow BiosCall swap
+ // bit maps.
+ //
+
+ if (Number == 0) {
+ Ki386IopmSaveArea = ExAllocatePool(PagedPool, PAGE_SIZE * 2);
+ if (Ki386IopmSaveArea == NULL) {
+ KeBugCheck(NO_PAGES_AVAILABLE);
+ }
+ }
+
+ //
+ // Set the priority of the specified idle thread to zero, set appropriate
+ // member in KiIdleSummary and return to the system start up routine.
+ //
+
+ KeRaiseIrql(DISPATCH_LEVEL, &OldIrql);
+ KeSetPriorityThread(Thread, (KPRIORITY)0);
+
+ //
+ // if a thread has not been selected to run on the current processors,
+ // check to see if there are any ready threads; otherwise add this
+ // processors to the IdleSummary
+ //
+
+ KiAcquireSpinLock(&KiDispatcherLock);
+ if (Prcb->NextThread == (PKTHREAD)NULL) {
+ SetMember(Number, KiIdleSummary);
+ }
+ KiReleaseSpinLock(&KiDispatcherLock);
+
+ KeRaiseIrql(HIGH_LEVEL, &OldIrql);
+
+ //
+ // This processor has initialized
+ //
+
+ LoaderBlock->Prcb = (ULONG)NULL;
+
+ return;
+}
+
+VOID
+KiInitializePcr (
+ IN ULONG Processor,
+ IN PKPCR Pcr,
+ IN PKIDTENTRY Idt,
+ IN PKGDTENTRY Gdt,
+ IN PKTSS Tss,
+ IN PKTHREAD Thread
+ )
+
+/*++
+
+Routine Description:
+
+ This function is called to initialize the PCR for a processor. It
+ simply stuffs values into the PCR. (The PCR is not inited statically
+ because the number varies with the number of processors.)
+
+ Note that each processor has its own IDT, GDT, and TSS as well as PCR!
+
+Arguments:
+
+ Processor - Processor whoes Pcr to initialize.
+
+ Pcr - Linear address of PCR.
+
+ Idt - Linear address of i386 IDT.
+
+ Gdt - Linear address of i386 GDT.
+
+ Tss - Linear address (NOT SELECTOR!) of the i386 TSS.
+
+ Thread - Dummy thread object to use very early on.
+
+Return Value:
+
+ None.
+
+--*/
+{
+ // set version values
+
+ Pcr->MajorVersion = PCR_MAJOR_VERSION;
+ Pcr->MinorVersion = PCR_MINOR_VERSION;
+
+ Pcr->PrcbData.MajorVersion = PRCB_MAJOR_VERSION;
+ Pcr->PrcbData.MinorVersion = PRCB_MINOR_VERSION;
+
+ Pcr->PrcbData.BuildType = 0;
+
+#if DBG
+ Pcr->PrcbData.BuildType |= PRCB_BUILD_DEBUG;
+#endif
+
+#ifdef NT_UP
+ Pcr->PrcbData.BuildType |= PRCB_BUILD_UNIPROCESSOR;
+#endif
+
+ // Basic addressing fields
+
+ Pcr->SelfPcr = Pcr;
+ Pcr->Prcb = &(Pcr->PrcbData);
+
+ // Thread control fields
+
+ Pcr->NtTib.ExceptionList = EXCEPTION_CHAIN_END;
+ Pcr->NtTib.StackBase = 0;
+ Pcr->NtTib.StackLimit = 0;
+ Pcr->NtTib.Self = 0;
+
+ Pcr->PrcbData.CurrentThread = Thread;
+
+ //
+ // Init Prcb.Number and ProcessorBlock such that Ipi will work
+ // as early as possible.
+ //
+
+ Pcr->PrcbData.Number = (UCHAR)Processor;
+ Pcr->PrcbData.SetMember = 1 << Processor;
+ KiProcessorBlock[Processor] = Pcr->Prcb;
+
+ Pcr->Irql = 0;
+
+ // Machine structure addresses
+
+ Pcr->GDT = Gdt;
+ Pcr->IDT = Idt;
+ Pcr->TSS = Tss;
+
+ return;
+}
+
+#if 0
+VOID
+KiInitializeDblFaultTSS(
+ IN PKTSS Tss,
+ IN ULONG Stack,
+ IN PKGDTENTRY TssDescriptor
+ )
+
+/*++
+
+Routine Description:
+
+ This function is called to initialize the double-fault TSS for a
+ processor. It will set the static fields of the TSS to point to
+ the double-fault handler and the appropriate double-fault stack.
+
+ Note that the IOPM for the double-fault TSS grants access to all
+ ports. This is so the standard HAL's V86-mode callback to reset
+ the display to text mode will work.
+
+Arguments:
+
+ Tss - Supplies a pointer to the double-fault TSS
+
+ Stack - Supplies a pointer to the double-fault stack.
+
+ TssDescriptor - Linear address of the descriptor for the TSS.
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ PUCHAR p;
+ ULONG i;
+ ULONG j;
+
+ //
+ // Set limit for TSS
+ //
+
+ if (TssDescriptor != NULL) {
+ TssDescriptor->LimitLow = sizeof(KTSS) - 1;
+ TssDescriptor->HighWord.Bits.LimitHi = 0;
+ }
+
+ //
+ // Initialize IOPMs
+ //
+
+ for (i = 0; i < IOPM_COUNT; i++) {
+ p = (PUCHAR)(Tss->IoMaps[i]);
+
+ for (j = 0; j < PIOPM_SIZE; j++) {
+ p[j] = 0;
+ }
+ }
+
+ // Set IO Map base address to indicate no IO map present.
+
+ // N.B. -1 does not seem to be a valid value for the map base. If this
+ // value is used, byte immediate in's and out's will actually go
+ // the hardware when executed in V86 mode.
+
+ Tss->IoMapBase = KiComputeIopmOffset(IO_ACCESS_MAP_NONE);
+
+ // Set flags to 0, which in particular dispables traps on task switches.
+
+ Tss->Flags = 0;
+
+
+ // Set LDT and Ss0 to constants used by NT.
+
+ Tss->LDT = 0;
+ Tss->Ss0 = KGDT_R0_DATA;
+ Tss->Esp0 = Stack;
+ Tss->Eip = (ULONG)KiTrap08;
+ Tss->Cs = KGDT_R0_CODE || RPL_MASK;
+ Tss->Ds = KGDT_R0_DATA;
+ Tss->Es = KGDT_R0_DATA;
+ Tss->Fs = KGDT_R0_DATA;
+
+
+ return;
+
+}
+#endif
+
+
+VOID
+KiInitializeTSS (
+ IN PKTSS Tss
+ )
+
+/*++
+
+Routine Description:
+
+ This function is called to intialize the TSS for a processor.
+ It will set the static fields of the TSS. (ie Those fields that
+ the part reads, and for which NT uses constant values.)
+
+ The dynamic fiels (Esp0 and CR3) are set in the context swap
+ code.
+
+Arguments:
+
+ Tss - Linear address of the Task State Segment.
+
+Return Value:
+
+ None.
+
+--*/
+{
+
+ // Set IO Map base address to indicate no IO map present.
+
+ // N.B. -1 does not seem to be a valid value for the map base. If this
+ // value is used, byte immediate in's and out's will actually go
+ // the hardware when executed in V86 mode.
+
+ Tss->IoMapBase = KiComputeIopmOffset(IO_ACCESS_MAP_NONE);
+
+ // Set flags to 0, which in particular dispables traps on task switches.
+
+ Tss->Flags = 0;
+
+
+ // Set LDT and Ss0 to constants used by NT.
+
+ Tss->LDT = 0;
+ Tss->Ss0 = KGDT_R0_DATA;
+
+ return;
+}
+
+VOID
+KiInitializeTSS2 (
+ IN PKTSS Tss,
+ IN PKGDTENTRY TssDescriptor
+ )
+
+/*++
+
+Routine Description:
+
+ Do part of TSS init we do only once.
+
+Arguments:
+
+ Tss - Linear address of the Task State Segment.
+
+ TssDescriptor - Linear address of the descriptor for the TSS.
+
+Return Value:
+
+ None.
+
+--*/
+{
+ PUCHAR p;
+ ULONG i;
+ ULONG j;
+
+ //
+ // Set limit for TSS
+ //
+
+ if (TssDescriptor != NULL) {
+ TssDescriptor->LimitLow = sizeof(KTSS) - 1;
+ TssDescriptor->HighWord.Bits.LimitHi = 0;
+ }
+
+ //
+ // Initialize IOPMs
+ //
+
+ for (i = 0; i < IOPM_COUNT; i++) {
+ p = (PUCHAR)(Tss->IoMaps[i].IoMap);
+
+ for (j = 0; j < PIOPM_SIZE; j++) {
+ p[j] = (UCHAR)-1;
+ }
+ }
+
+ //
+ // Initialize Software Interrupt Direction Maps
+ //
+
+ for (i = 0; i < IOPM_COUNT; i++) {
+ p = (PUCHAR)(Tss->IoMaps[i].DirectionMap);
+ for (j = 0; j < INT_DIRECTION_MAP_SIZE; j++) {
+ p[j] = 0;
+ }
+ }
+
+ //
+ // Initialize the map for IO_ACCESS_MAP_NONE
+ //
+ p = (PUCHAR)(Tss->IntDirectionMap);
+ for (j = 0; j < INT_DIRECTION_MAP_SIZE; j++) {
+ p[j] = 0;
+ }
+
+ return;
+}
+
+VOID
+KiSwapIDT (
+ )
+
+/*++
+
+Routine Description:
+
+ This function is called to edit the IDT. It swaps words of the address
+ and access fields around into the format the part actually needs.
+ This allows for easy static init of the IDT.
+
+ Note that this procedure edits the current IDT.
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ None.
+
+--*/
+{
+ LONG Index;
+ USHORT Temp;
+
+ //
+ // Rearrange the entries of IDT to match i386 interrupt gate structure
+ //
+
+ for (Index = 0; Index <= MAXIMUM_IDTVECTOR; Index += 1) {
+ Temp = IDT[Index].Selector;
+ IDT[Index].Selector = IDT[Index].ExtendedOffset;
+ IDT[Index].ExtendedOffset = Temp;
+ }
+}
+
+ULONG
+KiGetFeatureBits ()
+/*++
+
+ Return the NT feature bits supported by this processors
+
+--*/
+{
+ UCHAR Buffer[50];
+ ULONG Junk, ProcessorFeatures, NtBits;
+ ULONG CpuVendor;
+ PKPRCB Prcb;
+
+ NtBits = 0;
+
+ Prcb = KeGetCurrentPrcb();
+ Prcb->VendorString[0] = 0;
+
+ if (!Prcb->CpuID) {
+ return NtBits;
+ }
+
+ //
+ // Determine the processor type
+ //
+
+ CPUID (0, &Junk, (PULONG) Buffer+0, (PULONG) Buffer+2, (PULONG) Buffer+1);
+ Buffer[12] = 0;
+
+ //
+ // Copy vendor string to Prcb for debugging
+ //
+
+ strcpy (Prcb->VendorString, Buffer);
+
+ //
+ // Determine OEM type
+ //
+
+ CpuVendor = CPU_NONE;
+ if (strcmp (Buffer, "GenuineIntel") == 0) {
+ CpuVendor = CPU_INTEL;
+ } else if (strcmp (Buffer, "AuthenticAMD") == 0) {
+ CpuVendor = CPU_AMD;
+ } else if (strcmp (Buffer, CmpCyrixID) == 0) {
+ CpuVendor = CPU_CYRIX;
+ }
+
+ //
+ // Determine which NT compatible features are present
+ //
+
+ CPUID (1, &Junk, &Junk, &Junk, &ProcessorFeatures);
+
+ if (CpuVendor == CPU_INTEL || CpuVendor == CPU_AMD || CpuVendor == CPU_CYRIX) {
+ if (ProcessorFeatures & 0x100) {
+ NtBits |= KF_CMPXCHG8B;
+ }
+
+ if (ProcessorFeatures & 0x10) {
+ NtBits |= KF_RDTSC;
+ }
+
+ if (ProcessorFeatures & 0x02) {
+ NtBits |= KF_V86_VIS | KF_CR4;
+ }
+
+ if (ProcessorFeatures & 0x00800000) {
+ NtBits |= KF_MMX;
+ }
+ }
+
+
+ if (CpuVendor == CPU_INTEL || CpuVendor == CPU_CYRIX) {
+
+ if (ProcessorFeatures & 0x08) {
+ NtBits |= KF_LARGE_PAGE | KF_CR4;
+ }
+
+ if (ProcessorFeatures & 0x2000) {
+ NtBits |= KF_GLOBAL_PAGE | KF_CR4;
+ }
+
+ if (ProcessorFeatures & 0x8000) {
+ NtBits |= KF_CMOV;
+ }
+ }
+
+ //
+ // Intel specific stuff
+ //
+
+ if (CpuVendor == CPU_INTEL) {
+ if (ProcessorFeatures & 0x1000) {
+ NtBits |= KF_MTRR;
+ }
+
+ if (Prcb->CpuType == 6) {
+ WRMSR (0x8B, 0);
+ CPUID (1, &Junk, &Junk, &Junk, &ProcessorFeatures);
+ Prcb->UpdateSignature.QuadPart = RDMSR (0x8B);
+ }
+ }
+
+ return NtBits;
+}
+
+#define MAX_ATTEMPTS 10
+
+BOOLEAN
+KiInitMachineDependent (
+ VOID
+ )
+{
+ KAFFINITY ActiveProcessors, CurrentAffinity;
+ ULONG NumberProcessors;
+ IDENTITY_MAP IdentityMap;
+ ULONG Index;
+ ULONG Average;
+ ULONG Junk;
+ struct {
+ LARGE_INTEGER PerfStart;
+ LARGE_INTEGER PerfEnd;
+ LONGLONG PerfDelta;
+ LARGE_INTEGER PerfFreq;
+ LONGLONG TSCStart;
+ LONGLONG TSCEnd;
+ LONGLONG TSCDelta;
+ ULONG MHz;
+ } Samples[MAX_ATTEMPTS], *pSamp;
+
+ //
+ // If PDE large page is supported, enable it.
+ //
+ // We enable large pages before global pages to make TLB invalidation
+ // easier while turning on large pages.
+ //
+
+ if (KeFeatureBits & KF_LARGE_PAGE) {
+ if (Ki386CreateIdentityMap(&IdentityMap)) {
+
+ KiIpiGenericCall (
+ (PKIPI_BROADCAST_WORKER) Ki386EnableTargetLargePage,
+ (ULONG)(&IdentityMap)
+ );
+ }
+
+ //
+ // Always call Ki386ClearIdentityMap() to free any memory allocated
+ //
+
+ Ki386ClearIdentityMap(&IdentityMap);
+ }
+
+ //
+ // If PDE/PTE global page is supported, enable it
+ //
+
+ if (KeFeatureBits & KF_GLOBAL_PAGE) {
+ NumberProcessors = KeNumberProcessors;
+ KiIpiGenericCall (
+ (PKIPI_BROADCAST_WORKER) Ki386EnableGlobalPage,
+ (ULONG)(&NumberProcessors)
+ );
+ }
+
+ ActiveProcessors = KeActiveProcessors;
+ for (CurrentAffinity=1; ActiveProcessors; CurrentAffinity <<= 1) {
+
+ if (ActiveProcessors & CurrentAffinity) {
+
+ //
+ // Switch to that processor, and remove it from the
+ // remaining set of processors
+ //
+
+ ActiveProcessors &= ~CurrentAffinity;
+ KeSetSystemAffinityThread(CurrentAffinity);
+
+ //
+ // Determine the MHz for the processor
+ //
+
+ KeGetCurrentPrcb()->MHz = 0;
+
+ if (KeFeatureBits & KF_RDTSC) {
+
+ Index = 0;
+ pSamp = Samples;
+
+ for (; ;) {
+
+ //
+ // Collect a new sample
+ // Delay the thread a "long" amount and time it with
+ // a time source and RDTSC.
+ //
+
+ CPUID (0, &Junk, &Junk, &Junk, &Junk);
+ pSamp->PerfStart = KeQueryPerformanceCounter (NULL);
+ pSamp->TSCStart = RDTSC();
+ pSamp->PerfFreq.QuadPart = -50000;
+
+ KeDelayExecutionThread (KernelMode, FALSE, &pSamp->PerfFreq);
+
+ CPUID (0, &Junk, &Junk, &Junk, &Junk);
+ pSamp->PerfEnd = KeQueryPerformanceCounter (&pSamp->PerfFreq);
+ pSamp->TSCEnd = RDTSC();
+
+ //
+ // Calculate processors MHz
+ //
+
+ pSamp->PerfDelta = pSamp->PerfEnd.QuadPart - pSamp->PerfStart.QuadPart;
+ pSamp->TSCDelta = pSamp->TSCEnd - pSamp->TSCStart;
+
+ pSamp->MHz = (ULONG) ((pSamp->TSCDelta * pSamp->PerfFreq.QuadPart + 500000L) /
+ (pSamp->PerfDelta * 1000000L));
+
+
+ //
+ // If last 2 samples matched, done
+ //
+
+ if (Index && pSamp->MHz == pSamp[-1].MHz) {
+ break;
+ }
+
+ //
+ // Advance to next sample
+ //
+
+ pSamp += 1;
+ Index += 1;
+
+ //
+ // If too many samples, then something is wrong
+ //
+
+ if (Index >= MAX_ATTEMPTS) {
+
+#if DBG
+ //
+ // Temp breakpoint to see where this is failing
+ // and why
+ //
+
+ DbgBreakPoint();
+#endif
+
+ Average = 0;
+ for (Index = 0; Index < MAX_ATTEMPTS; Index++) {
+ Average += Samples[Index].MHz;
+ }
+ pSamp[-1].MHz = Average / MAX_ATTEMPTS;
+ break;
+ }
+
+ }
+
+ KeGetCurrentPrcb()->MHz = (USHORT) pSamp[-1].MHz;
+ }
+
+ //
+ // If MTRR is supported, initialize per processor
+ //
+
+ if (KeFeatureBits & KF_MTRR) {
+ KiInitializeMTRR ( (BOOLEAN) (ActiveProcessors ? FALSE : TRUE));
+ }
+ }
+ }
+
+ KeRevertToUserAffinityThread();
+ return TRUE;
+}
+
+
+VOID
+KeOptimizeProcessorControlState (
+ VOID
+ )
+{
+ Ke386ConfigureCyrixProcessor ();
+}
+
+
+
+VOID
+KeSetup80387OrEmulate (
+ IN PVOID *R3EmulatorTable
+ )
+
+/*++
+
+Routine Description:
+
+ This routine is called by PS initialization after loading UDLL.
+
+ If this is a 386 system without 387s (all processors must be
+ symmetrical) then this function will set the trap 07 vector on all
+ processors to point to the address passed in (which should be the
+ entry point of the 80387 emulator in UDLL, NPXNPHandler).
+
+Arguments:
+
+ HandlerAddress - Supplies the address of the trap07 handler.
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ PKINTERRUPT_ROUTINE HandlerAddress;
+ KAFFINITY ActiveProcessors, CurrentAffinity;
+ KIRQL OldIrql;
+ ULONG disposition;
+ HANDLE SystemHandle, SourceHandle, DestHandle;
+ NTSTATUS Status;
+ UNICODE_STRING unicodeString;
+ OBJECT_ATTRIBUTES ObjectAttributes;
+ double Dividend, Divisor;
+ BOOLEAN PrecisionErrata;
+
+ if (KeI386NpxPresent) {
+
+ //
+ // A coprocessor is present - check to see if the precision errata exists
+ //
+
+ PrecisionErrata = FALSE;
+
+ ActiveProcessors = KeActiveProcessors;
+ for (CurrentAffinity = 1; ActiveProcessors; CurrentAffinity <<= 1) {
+
+ if (ActiveProcessors & CurrentAffinity) {
+ ActiveProcessors &= ~CurrentAffinity;
+
+ //
+ // Run calculation on each processor.
+ //
+
+ KeSetSystemAffinityThread(CurrentAffinity);
+ _asm {
+
+ ;
+ ; This is going to destroy the state in the coprocesssor,
+ ; but we know that there's no state currently in it.
+ ;
+
+ cli
+ mov eax, cr0
+ mov ecx, eax ; hold original cr0 value
+ and eax, not (CR0_TS+CR0_MP+CR0_EM)
+ mov cr0, eax
+
+ fninit ; to known state
+ }
+
+ Dividend = 4195835.0;
+ Divisor = 3145727.0;
+
+ _asm {
+ fld Dividend
+ fdiv Divisor ; test known faulty divison
+ fmul Divisor ; Multiple quotient by divisor
+ fcomp Dividend ; Compare product and dividend
+ fstsw ax ; Move float conditions to ax
+ sahf ; move to eflags
+
+ mov cr0, ecx ; restore cr0
+ sti
+
+ jc short em10
+ jz short em20
+em10: mov PrecisionErrata, TRUE
+em20:
+ }
+ }
+ }
+
+
+ //
+ // Check to see if the emulator should be used anyway
+ //
+
+ switch (KeI386ForceNpxEmulation) {
+ case 0:
+ //
+ // Use the emulator based on the value in KeI386NpxPresent
+ //
+
+ break;
+
+ case 1:
+ //
+ // Only use the emulator if any processor has the known
+ // Pentium floating point division problem.
+ //
+
+ if (PrecisionErrata) {
+ KeI386NpxPresent = FALSE;
+ }
+ break;
+
+ default:
+
+ //
+ // Unkown setting - use the emulator
+ //
+
+ KeI386NpxPresent = FALSE;
+ break;
+ }
+ }
+
+ //
+ // Setup processor features, and install emulator if needed
+ //
+
+ SharedUserData->ProcessorFeatures[PF_FLOATING_POINT_EMULATED] = KeI386NpxPresent;
+ SharedUserData->ProcessorFeatures[PF_FLOATING_POINT_PRECISION_ERRATA] = PrecisionErrata;
+
+ if (!KeI386NpxPresent) {
+
+ //
+ // MMx not available when emulator is used
+ //
+
+ KeFeatureBits &= ~KF_MMX;
+ SharedUserData->ProcessorFeatures[PF_MMX_INSTRUCTIONS_AVAILABLE] = FALSE;
+
+ //
+ // Errata not present when using emulator
+ //
+
+ SharedUserData->ProcessorFeatures[PF_FLOATING_POINT_PRECISION_ERRATA] = FALSE;
+
+ //
+ // Use the user mode floating point emulator
+ //
+
+ HandlerAddress = (PKINTERRUPT_ROUTINE) ((PULONG) R3EmulatorTable)[0];
+ Ki387RoundModeTable = (PVOID) ((PULONG) R3EmulatorTable)[1];
+
+ ActiveProcessors = KeActiveProcessors;
+ for (CurrentAffinity = 1; ActiveProcessors; CurrentAffinity <<= 1) {
+
+ if (ActiveProcessors & CurrentAffinity) {
+ ActiveProcessors &= ~CurrentAffinity;
+
+ //
+ // Run this code on each processor.
+ //
+
+ KeSetSystemAffinityThread(CurrentAffinity);
+
+ //
+ // Raise IRQL and lock dispatcher database.
+ //
+
+ KiLockDispatcherDatabase(&OldIrql);
+
+ //
+ // Make the trap 07 IDT entry point at the passed-in handler
+ //
+
+ KiSetHandlerAddressToIDT(I386_80387_NP_VECTOR, HandlerAddress);
+ KeGetPcr()->IDT[I386_80387_NP_VECTOR].Selector = KGDT_R3_CODE;
+ KeGetPcr()->IDT[I386_80387_NP_VECTOR].Access = TRAP332_GATE;
+
+
+ //
+ // Unlock dispatcher database and lower IRQL to its previous value.
+ //
+
+ KiUnlockDispatcherDatabase(OldIrql);
+ }
+ }
+
+ //
+ // Move any entries from ..\System\FloatingPointProcessor to
+ // ..\System\DisabledFloatingPointProcessor.
+ //
+
+ //
+ // Open system tree
+ //
+
+ InitializeObjectAttributes(
+ &ObjectAttributes,
+ &CmRegistryMachineHardwareDescriptionSystemName,
+ OBJ_CASE_INSENSITIVE,
+ NULL,
+ NULL
+ );
+
+ Status = ZwOpenKey( &SystemHandle,
+ KEY_ALL_ACCESS,
+ &ObjectAttributes
+ );
+
+ if (NT_SUCCESS(Status)) {
+
+ //
+ // Open FloatingPointProcessor key
+ //
+
+ InitializeObjectAttributes(
+ &ObjectAttributes,
+ &CmTypeName[FloatingPointProcessor],
+ OBJ_CASE_INSENSITIVE,
+ SystemHandle,
+ NULL
+ );
+
+ Status = ZwOpenKey ( &SourceHandle,
+ KEY_ALL_ACCESS,
+ &ObjectAttributes
+ );
+
+ if (NT_SUCCESS(Status)) {
+
+ //
+ // Create DisabledFloatingPointProcessor key
+ //
+
+ RtlInitUnicodeString (
+ &unicodeString,
+ CmDisabledFloatingPointProcessor
+ );
+
+ InitializeObjectAttributes(
+ &ObjectAttributes,
+ &unicodeString,
+ OBJ_CASE_INSENSITIVE,
+ SystemHandle,
+ NULL
+ );
+
+ Status = ZwCreateKey( &DestHandle,
+ KEY_ALL_ACCESS,
+ &ObjectAttributes,
+ 0,
+ NULL,
+ REG_OPTION_VOLATILE,
+ &disposition
+ );
+
+ if (NT_SUCCESS(Status)) {
+
+ //
+ // Move it
+ //
+
+ KiMoveRegTree (SourceHandle, DestHandle);
+ ZwClose (DestHandle);
+ }
+ ZwClose (SourceHandle);
+ }
+ ZwClose (SystemHandle);
+ }
+ }
+
+ //
+ // Set affinity back to the original value.
+ //
+
+ KeRevertToUserAffinityThread();
+}
+
+
+
+NTSTATUS
+KiMoveRegTree(
+ HANDLE Source,
+ HANDLE Dest
+ )
+{
+ NTSTATUS Status;
+ PKEY_BASIC_INFORMATION KeyInformation;
+ PKEY_VALUE_FULL_INFORMATION KeyValue;
+ OBJECT_ATTRIBUTES ObjectAttributes;
+ HANDLE SourceChild;
+ HANDLE DestChild;
+ ULONG ResultLength;
+ UCHAR buffer[1024]; // hmm....
+ UNICODE_STRING ValueName;
+ UNICODE_STRING KeyName;
+
+
+ KeyValue = (PKEY_VALUE_FULL_INFORMATION)buffer;
+
+ //
+ // Move values from source node to dest node
+ //
+
+ for (; ;) {
+ //
+ // Get first value
+ //
+
+ Status = ZwEnumerateValueKey(Source,
+ 0,
+ KeyValueFullInformation,
+ buffer,
+ sizeof (buffer),
+ &ResultLength);
+
+ if (!NT_SUCCESS(Status)) {
+ break;
+ }
+
+
+ //
+ // Write value to dest node
+ //
+
+ ValueName.Buffer = KeyValue->Name;
+ ValueName.Length = (USHORT) KeyValue->NameLength;
+ ZwSetValueKey( Dest,
+ &ValueName,
+ KeyValue->TitleIndex,
+ KeyValue->Type,
+ buffer+KeyValue->DataOffset,
+ KeyValue->DataLength
+ );
+
+ //
+ // Delete value and get first value again
+ //
+
+ Status = ZwDeleteValueKey (Source, &ValueName);
+ if (!NT_SUCCESS(Status)) {
+ break;
+ }
+ }
+
+
+ //
+ // Enumerate node's children and apply ourselves to each one
+ //
+
+ KeyInformation = (PKEY_BASIC_INFORMATION)buffer;
+ for (; ;) {
+
+ //
+ // Open node's first key
+ //
+
+ Status = ZwEnumerateKey(
+ Source,
+ 0,
+ KeyBasicInformation,
+ KeyInformation,
+ sizeof (buffer),
+ &ResultLength
+ );
+
+ if (!NT_SUCCESS(Status)) {
+ break;
+ }
+
+ KeyName.Buffer = KeyInformation->Name;
+ KeyName.Length = (USHORT) KeyInformation->NameLength;
+
+ InitializeObjectAttributes(
+ &ObjectAttributes,
+ &KeyName,
+ OBJ_CASE_INSENSITIVE,
+ Source,
+ NULL
+ );
+
+ Status = ZwOpenKey(
+ &SourceChild,
+ KEY_ALL_ACCESS,
+ &ObjectAttributes
+ );
+
+ if (!NT_SUCCESS(Status)) {
+ break;
+ }
+
+ //
+ // Create key in dest tree
+ //
+
+ InitializeObjectAttributes(
+ &ObjectAttributes,
+ &KeyName,
+ OBJ_CASE_INSENSITIVE,
+ Dest,
+ NULL
+ );
+
+ Status = ZwCreateKey(
+ &DestChild,
+ KEY_ALL_ACCESS,
+ &ObjectAttributes,
+ 0,
+ NULL,
+ REG_OPTION_VOLATILE,
+ NULL
+ );
+
+ if (!NT_SUCCESS(Status)) {
+ break;
+ }
+
+ //
+ // Move subtree
+ //
+
+ Status = KiMoveRegTree(SourceChild, DestChild);
+
+ ZwClose(DestChild);
+ ZwClose(SourceChild);
+
+ if (!NT_SUCCESS(Status)) {
+ break;
+ }
+
+ //
+ // Loop and get first key. (old first key was delete by the
+ // call to KiMoveRegTree).
+ //
+ }
+
+ //
+ // Remove source node
+ //
+
+ return NtDeleteKey (Source);
+}
diff --git a/private/ntos/ke/i386/ki386.h b/private/ntos/ke/i386/ki386.h
new file mode 100644
index 000000000..9819abd0f
--- /dev/null
+++ b/private/ntos/ke/i386/ki386.h
@@ -0,0 +1,34 @@
+
+
+typedef struct _IDENTITY_MAP {
+ unsigned long IdentityCR3;
+ unsigned long IdentityLabel;
+ PHARDWARE_PTE PageDirectory;
+ PHARDWARE_PTE IdentityMapPT;
+ PHARDWARE_PTE CurrentMapPT;
+} IDENTITY_MAP, *PIDENTITY_MAP;
+
+
+VOID
+Ki386ClearIdentityMap(
+ PIDENTITY_MAP IdentityMap
+ );
+
+VOID
+Ki386EnableTargetLargePage(
+ PIDENTITY_MAP IdentityMap
+ );
+
+BOOLEAN
+Ki386CreateIdentityMap(
+ PIDENTITY_MAP IdentityMap
+ );
+
+VOID
+Ki386EnableCurrentLargePage (
+ IN ULONG IdentityAddr,
+ IN ULONG IdentityCr3
+ );
+
+#define KiGetPdeOffset(va) (((ULONG)(va)) >> 22)
+#define KiGetPteOffset(va) ((((ULONG)(va)) << 10) >> 22)
diff --git a/private/ntos/ke/i386/kimacro.inc b/private/ntos/ke/i386/kimacro.inc
new file mode 100644
index 000000000..1618578e7
--- /dev/null
+++ b/private/ntos/ke/i386/kimacro.inc
@@ -0,0 +1,1288 @@
+;++
+;
+; Copyright (c) 1989 Microsoft Corporation
+;
+; Module Name:
+;
+; kimacro.inc
+;
+; Abstract:
+;
+; This module contains the macros used by kernel assembler code.
+; It includes macros to manipulate interrupts, support system
+; entry and exit for syscalls, faults, and interrupts, and
+; manipulate floating point state.
+;
+; Author:
+;
+; Shie-Lin (shielint) 24-Jan-1990
+;
+; Revision History:
+;
+; BryanWi 17-Aug-90
+; Replace GENERATE_MACHINE... and RESTORE... with ENTER_...
+; and EXIT_ALL macros.
+;
+;--
+
+;++
+;
+; These constants are used by the fpo directives in this file.
+; This directive causes the assembler to output a .debug$f segment
+; in the obj file. The segment will contain 1 fpo record for each
+; directive present during assembly.
+;
+; Although the assembler will accept all valid values, the value of 7
+; in the FPO_REGS field indicates to the debugger that a trap frame is
+; generated by the function. The value of 7 can be used because the
+; C/C++ compiler puts a maximum value of 3 in the field.
+;
+FPO_LOCALS equ 0 ; 32 bits, size of locals in dwords
+FPO_PARAMS equ 0 ; 32 bits, size of parameters in dwords
+FPO_PROLOG equ 0 ; 12 bits, 0-4095, # of bytes in prolog
+FPO_REGS equ 0 ; 3 bits, 0-7, # regs saved in prolog
+FPO_USE_EBP equ 0 ; 1 bit, 0-1, is ebp used?
+FPO_TRAPFRAME equ 1 ; 2 bits, 0=fpo, 1=trap frame, 2=tss
+;
+;--
+
+
+;++
+;
+; POLL_DEBUGGER
+;
+; Macro Description:
+;
+; Call the debugger so it can check for control-c. If it finds
+; it, it will report our iret address as address of break-in.
+;
+; N.B. This macro should be used when all the caller's registers
+; have been restored. (Otherwise, the kernel debugger register
+; dump will not have correct state.) The only exception is
+; fs. This is because Kd may need to access PCR or PRCB.
+;
+; Arguments:
+;
+; There MUST be an iret frame on the stack when this macro
+; is invoked.
+;
+; Exit:
+;
+; Debugger will iret for us, so we don't usually return from
+; this macro, but remember that it generates nothing for non-DEVL
+; kernels.
+;--
+
+POLL_DEBUGGER macro
+local a, b, c_
+
+if DEVL
+ EXTRNP _DbgBreakPointWithStatus,1
+ stdCall _KdPollBreakIn
+ or al,al
+ jz short c_
+ stdCall _DbgBreakPointWithStatus,<DBG_STATUS_CONTROL_C>
+c_:
+endif ; DEVL
+endm
+
+;++
+;
+; ASSERT_FS
+;
+; Try to catch funky condition wherein we get FS=r3 value while
+; running in kernel mode.
+;
+;--
+
+ASSERT_FS macro
+local a,b
+
+if DBG
+ EXTRNP _KeBugCheck,1
+
+ mov bx,fs
+ cmp bx,KGDT_R0_PCR
+ jnz short a
+
+ cmp dword ptr fs:[0], 0
+ jne short b
+
+a:
+ stdCall _KeBugCheck,<-1>
+align 4
+b:
+endif
+endm
+
+
+
+;++
+;
+;
+; Copy data from various places into base of TrapFrame, net effect
+; is to allow dbg KB command to trace accross trap frame, and to
+; allow user to find arguments to system calls.
+;
+; USE ebx and edi.
+;--
+
+SET_DEBUG_DATA macro
+
+ife FPO
+
+;
+; This macro is used by ENTER_SYSTEM_CALL, ENTER_TRAP and ENTER_INTERRUPT
+; and is used at the end of above macros. It is safe to destroy ebx, edi.
+;
+
+ mov ebx,[ebp]+TsEbp
+ mov edi,[ebp]+TsEip
+ mov [ebp]+TsDbgArgPointer,edx
+ mov [ebp]+TsDbgArgMark,0BADB0D00h
+ mov [ebp]+TsDbgEbp,ebx
+ mov [ebp]+TsDbgEip,edi
+endif
+
+endm
+
+
+;++
+;
+; ENTER_DR_ASSIST EnterLabel, ExitLabel, NoAbiosAssist, NoV86Assist
+;
+; Macro Description:
+;
+; Jumped to by ENTER_ macros to deal with DR register work,
+; abios work and v86 work. The main purpose of this macro is
+; that interrupt/trap/systemCall EnterMacros can jump here to
+; deal with some special cases such that most of the times the
+; main ENTER_ execution flow can proceed without being branched.
+;
+; If (previousmode == usermode) {
+; save DR* in trapframe
+; load DR* from Prcb
+; }
+;
+; Arguments:
+; EnterLabel - label to emit
+; ExitLabel - label to branch to when done
+;
+; Entry-conditions:
+; Dr work:
+; DebugActive == TRUE
+; (esi)->Thread object
+; (esp)->base of trap frame
+; (ebp)->base of trap frame
+;
+; Abios work:
+; v86 work:
+;
+; Exit-conditions:
+; Dr work:
+; Interrupts match input state (this routine doesn't change IEF)
+; (esp)->base of trap frame
+; (ebp)->base of trap frame
+; Preserves entry eax, edx
+; Abios work:
+; v86 work:
+;
+;--
+
+ENTER_DR_ASSIST macro EnterLabel, ExitLabel, NoAbiosAssist, NoV86Assist, V86R
+ local a,b
+
+ public Dr_&EnterLabel
+align 4
+Dr_&EnterLabel:
+
+;
+; Test if we came from user-mode. If not, do nothing.
+;
+
+ test dword ptr [ebp]+TsEFlags,EFLAGS_V86_MASK
+ jnz short a
+
+ test dword ptr [ebp]+TsSegCs,MODE_MASK
+ jz Dr_&ExitLabel ; called from kmode, go continue
+
+
+;
+; Save user-mode Dr* regs in TrapFrame
+;
+; We are safe to destroy ebx, ecx, edi because in ENTER_INTERRUPT and
+; ENTER_TRAP these registers are saved already. In ENTER_SYSTEMCALL
+; ebx, edi is saved and ecx is don't-care.
+;
+
+a: mov ebx,dr0
+ mov ecx,dr1
+ mov edi,dr2
+ mov [ebp]+TsDr0,ebx
+ mov [ebp]+TsDr1,ecx
+ mov [ebp]+TsDr2,edi
+ mov ebx,dr3
+ mov ecx,dr6
+ mov edi,dr7
+ mov [ebp]+TsDr3,ebx
+ mov [ebp]+TsDr6,ecx
+ mov [ebp]+TsDr7,edi
+
+;
+; Load KernelDr* into processor
+;
+
+ mov edi,dword ptr fs:[PcPrcb]
+ mov ebx,[edi].PbProcessorState.PsSpecialRegisters.SrKernelDr0
+ mov ecx,[edi].PbProcessorState.PsSpecialRegisters.SrKernelDr1
+ mov dr0,ebx
+ mov dr1,ecx
+ mov ebx,[edi].PbProcessorState.PsSpecialRegisters.SrKernelDr2
+ mov ecx,[edi].PbProcessorState.PsSpecialRegisters.SrKernelDr3
+ mov dr2,ebx
+ mov dr3,ecx
+ mov ebx,[edi].PbProcessorState.PsSpecialRegisters.SrKernelDr6
+ mov ecx,[edi].PbProcessorState.PsSpecialRegisters.SrKernelDr7
+ mov dr6,ebx
+ mov dr7,ecx
+
+ifnb <V86R>
+ test dword ptr [ebp]+TsEFlags,EFLAGS_V86_MASK
+ jz short b
+ jmp Dr_&V86R
+endif
+b:
+ jmp Dr_&ExitLabel
+
+
+ifb <NoAbiosAssist>
+
+ public Abios_&EnterLabel
+align 4
+Abios_&EnterLabel:
+
+;
+; INTERRUPT_STACK16_TO_STACK32
+;
+; This macro remaps current 32bit stack to 16bit stack at interrupt
+; time.
+;
+; Arguments:
+;
+; (esp)->trap frame.
+; (eax)->Entry Esp.
+;
+
+ mov eax, [esp].TsErrCode ; (eax) = Entry Esp
+ mov ecx, KGDT_R0_DATA
+ mov edx, esp
+ shl eax, 16
+ add edx, fs:[PcstackLimit]
+ mov [esp].TsErrCode, eax
+ mov ss, cx
+ mov esp, edx ; Interrupts are off
+ mov ebp, edx
+ jmp Abios_&ExitLabel
+
+endif ; NoAbiosAssist
+
+ifb <NoV86Assist>
+
+ public V86_&EnterLabel
+align 4
+V86_&EnterLabel:
+
+;
+; Move the V86 segment registers to the correct place in the frame
+;
+ mov eax,dword ptr [ebp].TsV86Fs
+ mov ebx,dword ptr [ebp].TsV86Gs
+ mov ecx,dword ptr [ebp].TsV86Es
+ mov edx,dword ptr [ebp].TsV86Ds
+ mov [ebp].TsSegFs,ax
+ mov [ebp].TsSegGs,bx
+ mov [ebp].TsSegEs,cx
+ mov [ebp].TsSegDs,dx
+ jmp V86_&ExitLabel
+
+endif ; NoV86Assist
+
+ endm
+
+;++
+;
+; ENTER_SYSCALL AssistLabel, TagetLabel
+;
+; Macro Description:
+;
+; Build the frame and set registers needed by a system call.
+;
+; Save:
+; Errorpad,
+; Non-volatile regs,
+; FS,
+; ExceptionList,
+; PreviousMode
+;
+; Don't Save:
+; Volatile regs
+; Seg regs
+; Floating point state
+;
+; Set:
+; FS,
+; ExceptionList,
+; PreviousMode,
+; Direction
+;
+; Arguments:
+; AssistLabel - label ENTER_ASSIST macro is at
+; TargetLabel - label to emit for ENTER_ASSIST to jump to
+;
+; Exit-conditions:
+; Interrupts match input state (this routine doesn't change IEF)
+; (esp)->base of trap frame
+; (ebp)->base of trap frame
+; Preserves entry eax, edx
+;
+; Note:
+; The DS: reference to PreviousMode is *required* for correct
+; functioning of lazy selector loads. If you remove this use
+; of DS:, put a DS: override on something.
+;
+;--
+
+ENTER_SYSCALL macro AssistLabel, TargetLabel
+
+
+.FPO ( FPO_LOCALS, FPO_PARAMS, FPO_PROLOG, FPO_REGS, FPO_USE_EBP, FPO_TRAPFRAME )
+
+ifdef KERNELONLY
+
+;
+; Construct trap frame.
+;
+; N.B. The initial part of the trap frame is constructed by pushing values
+; on the stack. If the format of the trap frame is changed, then the
+; following code must alos be changed.
+;
+
+ push 0 ; put pad dword for error on stack
+ push ebp ; save the non-volatile registers
+ push ebx ;
+ push esi ;
+ push edi ;
+ push fs ; save and set FS to PCR.
+ mov ebx,KGDT_R0_PCR ; set PCR segment number
+ mov fs,bx ;
+
+;
+; Save the old exception list in trap frame and initialize a new empty
+; exception list.
+;
+
+ push PCR[PcExceptionList] ; save old exception list
+ mov PCR[PcExceptionList],EXCEPTION_CHAIN_END ; set new empty list
+
+;
+; Save the old previous mode in trap frame, allocate remainder of trap frame,
+; and set the new previous mode.
+;
+
+ mov esi,PCR[PcPrcbData+PbCurrentThread] ; get current thread address
+ push [esi]+ThPreviousMode ; save old previous mode
+ sub esp,TsPreviousPreviousMode ; allocate remainder of trap frame
+ mov ebx,[esp+TsSegCS] ; compute new previous mode
+ and ebx,MODE_MASK ;
+ mov [esi]+ThPreviousMode,bl ; set new previous mode
+
+;
+; Save the old trap frame address and set the new trap frame address.
+;
+
+ mov ebp,esp ; set trap frame address
+ mov ebx,[esi].ThTrapFrame ; save current trap frame address
+ mov [ebp].TsEdx,ebx ;
+ mov [esi].ThTrapFrame,ebp ; set new trap frame address
+ cld ; make sure direction is forward
+
+ SET_DEBUG_DATA ; Note this destroys edi
+
+ test byte ptr [esi]+ThDebugActive,-1 ; test if debugging active
+ jnz Dr_&AssistLabel ; if nz, debugging is active on thread
+
+Dr_&TargetLabel: ;
+ sti ; enable interrupts
+
+else
+ %out ENTER_SYSCAL outside of kernel
+ .err
+endif
+ endm
+
+;++
+;
+; ENTER_INTERRUPT AssistLabel, TargetLabel
+;
+; Macro Description:
+;
+; Build the frame and set registers needed by an interrupt.
+;
+; Save:
+; Errorpad,
+; Non-volatile regs,
+; FS,
+; ExceptionList,
+; PreviousMode
+; Volatile regs
+; Seg regs from V86 mode
+; DS, ES, GS
+;
+; Don't Save:
+; Floating point state
+;
+; Set:
+; FS,
+; ExceptionList,
+; Direction,
+; DS, ES
+;
+; Don't Set:
+; PreviousMode
+;
+; Arguments:
+; AssistLabel - label ENTER_ASSIST macro is at
+; TargetLabel - label to emit for ENTER_ASSIST to jump to
+;
+; Exit-conditions:
+; Interrupts match input state (this routine doesn't change IEF)
+; (esp)->base of trap frame
+; (ebp)->base of trap frame
+; Preserves entry eax, ecx, edx
+;
+;--
+
+ENTER_INTERRUPT macro AssistLabel, TargetLabel, PassParm
+ local b
+
+.FPO ( FPO_LOCALS+2, FPO_PARAMS, FPO_PROLOG, FPO_REGS, FPO_USE_EBP, FPO_TRAPFRAME )
+
+;
+; Fill in parts of frame we care about
+;
+
+ifb <PassParm>
+ push esp ; Use Error code field to save 16bit esp
+endif
+ push ebp ; Save the non-volatile registers
+ push ebx
+ push esi
+ push edi
+
+ sub esp, TsEdi
+ mov ebp,esp
+
+ mov [esp]+TsEax, eax ; Save volatile registers
+ mov [esp]+TsEcx, ecx
+ mov [esp]+TsEdx, edx
+if DBG
+ mov dword ptr [esp]+TsPreviousPreviousMode, -1 ; ThPreviousMode not pushed on interrupt
+endif
+
+ test dword ptr [esp].TsEflags,EFLAGS_V86_MASK
+ jnz V86_&AssistLabel
+
+ cmp word ptr [esp]+TsSegCs, KGDT_R0_CODE
+ jz short @f
+
+ mov [esp]+TsSegFs, fs ; Save and set FS to PCR.
+ mov [esp]+TsSegDs, ds
+ mov [esp]+TsSegEs, es
+ mov [esp]+TsSegGs, gs
+
+V86_&TargetLabel:
+ mov ebx,KGDT_R0_PCR
+ mov eax,KGDT_R3_DATA OR RPL_MASK
+ mov fs, bx
+ mov ds, ax
+ mov es, ax
+@@:
+ mov ebx, fs:[PcExceptionList] ;Save, set ExceptionList
+ mov fs:[PcExceptionList],EXCEPTION_CHAIN_END
+ mov [esp]+TsExceptionList, ebx
+
+ifnb <PassParm>
+ lea eax, [esp].TsErrCode
+ lea ecx, [esp].TsEip ; Move eax to EIP field
+ mov ebx, ss:[eax] ; (ebx) = parameter to pass
+ mov ss:[eax], ecx ; save 16bit esp
+endif
+
+;
+; Remap ABIOS 16 bit stack to 32 bit stack, if necessary.
+;
+
+ cmp esp, 10000h
+ jb Abios_&AssistLabel
+
+ mov dword ptr [esp].TsErrCode, 0 ; Indicate no remapping.
+Abios_&TargetLabel:
+
+;
+; end of Abios stack checking
+;
+
+ cld
+
+ifnb <PassParm>
+ push ebx ; push parameter as argument
+endif
+
+
+ SET_DEBUG_DATA
+
+ test byte ptr PCR[PcDebugActive], -1
+ jnz Dr_&AssistLabel
+
+Dr_&TargetLabel:
+
+ endm
+
+
+;++
+;
+; ENTER_TRAP AssistLabel, TargetLabel
+;
+; Macro Description:
+;
+; Build the frame and set registers needed by a trap or exception.
+;
+; Save:
+; Non-volatile regs,
+; FS,
+; ExceptionList,
+; PreviousMode,
+; Volatile regs
+; Seg Regs from V86 mode
+; DS, ES, GS
+;
+; Don't Save:
+; Floating point state
+;
+; Set:
+; FS,
+; Direction,
+; DS, ES
+;
+; Don't Set:
+; PreviousMode,
+; ExceptionList
+;
+; Arguments:
+; AssistLabel - label ENTER_ASSIST macro is at
+; TargetLabel - label to emit for ENTER_ASSIST to jump to
+;
+; Exit-conditions:
+; Interrupts match input state (this routine doesn't change IEF)
+; (esp)->base of trap frame
+; (ebp)->base of trap frame
+; Preserves entry eax
+;
+;--
+
+ENTER_TRAP macro AssistLabel, TargetLabel
+ local b
+
+.FPO ( FPO_LOCALS, FPO_PARAMS, FPO_PROLOG, FPO_REGS, FPO_USE_EBP, FPO_TRAPFRAME )
+
+;
+; Fill in parts of frame we care about
+;
+
+if DBG
+ifndef _Ki16BitStackException
+ EXTRNP _Ki16BitStackException
+endif
+endif ; DBG
+
+ mov word ptr [esp+2], 0 ; Clear upper word of ErrorCode
+
+ push ebp ; Save the non-volatile registers
+ push ebx
+ push esi
+ push edi
+
+ push fs ; Save and set FS to PCR.
+ mov ebx,KGDT_R0_PCR
+ mov fs,bx
+ mov ebx, fs:[PcExceptionList] ;Save ExceptionList
+ push ebx
+if DBG
+ push -1 ; Don't need to save ThPreviousMode from trap
+else
+ sub esp, 4 ; pad dword
+endif
+ push eax ; Save the volatile registers
+ push ecx
+ push edx
+
+ push ds ; Save segments
+ push es
+ push gs
+
+;
+; Skip allocate reset of trap frame and Set up DS/ES, they may be trash
+;
+
+ mov ax,KGDT_R3_DATA OR RPL_MASK
+ sub esp,TsSegGs
+ mov ds,ax
+ mov es,ax
+
+if DBG
+;
+; The code here check if the exception occurred in ring 0
+; ABIOS code. If yes, this is a fatal condition. We will
+; put out message and bugcheck.
+;
+
+ cmp esp, 10000h ; Is the trap in abios?
+ jb _Ki16BitStackException ; if b, yes, switch stack and bugcheck.
+
+endif ; DBG
+
+ mov ebp,esp
+ test dword ptr [esp].TsEflags,EFLAGS_V86_MASK
+ jnz V86_&AssistLabel
+
+V86_&TargetLabel:
+
+ cld
+ SET_DEBUG_DATA
+
+ test byte ptr PCR[PcDebugActive], -1
+ jnz Dr_&AssistLabel
+
+Dr_&TargetLabel:
+
+ endm
+;++
+;
+; EXIT_ALL NoRestoreSegs, NoRestoreVolatiles, NoPreviousMode
+;
+; Macro Description:
+;
+; Load a syscall frame back into the machine.
+;
+; Restore:
+; Volatile regs, IF NoRestoreVolatiles blank
+; NoPreviousMode,
+; ExceptionList,
+; FS,
+; Non-volatile regs
+;
+; If the frame is a kernel mode frame, AND esp has been edited,
+; then TsSegCs will have a special value. Test for that value
+; and execute special code for that case.
+;
+; N.B. This macro generates an IRET! (i.e. It exits!)
+;
+; Arguments:
+;
+; NoRestoreSegs - non-blank if DS, ES, GS are NOT to be restored
+;
+; NoRestoreVolatiles - non-blank if Volatile regs are NOT to be restored
+;
+; NoPreviousMode - if nb pop ThPreviousMode
+;
+; Entry-conditions:
+;
+; (esp)->base of trap frame
+; (ebp)->Base of trap frame
+;
+; Exit-conditions:
+;
+; Does not exit, returns.
+; Preserves eax, ecx, edx, IFF NoRestoreVolatiles is set
+;
+;--
+
+?adjesp = 0
+?RestoreAll = 1
+
+EXIT_ALL macro NoRestoreSegs, NoRestoreVolatiles, NoPreviousMode
+local a, b, f, x
+local Abios_ExitHelp, Abios_ExitHelp_Target1, Abios_ExitHelp_Target2
+local Dr_ExitHelp, Dr_ExitHelp_Target, V86_ExitHelp, V86_ExitHelp_Target
+local Db_NotATrapFrame, Db_A, Db_NotValidEntry, NonFlatPm_Target
+
+;
+; Sanity check some values and setup globals for macro
+;
+
+?adjesp = TsSegGs
+?RestoreAll = 1
+
+ifnb <NoRestoreSegs>
+ ?RestoreAll = 0
+ ?adjesp = ?adjesp + 12
+endif
+
+ifnb <NoRestoreVolatiles>
+ if ?RestoreAll eq 1
+ %out "EXIT_ALL NoRestoreVolatiles requires NoRestoreSegs"
+ .err
+ endif
+ ?adjesp = ?adjesp + 12
+endif
+
+ifb <NoPreviousMode>
+ifndef KERNELONLY
+ %out EXIT_ALL can not restore previousmode outside kernel
+ .err
+endif
+endif
+
+; All callers are responsible for getting here with interrupts disabled.
+
+if DBG
+ pushfd
+ pop edx
+
+ test edx, EFLAGS_INTERRUPT_MASK
+ jnz Db_NotValidEntry
+
+ cmp esp, ebp ; make sure esp = ebp
+ jne Db_NotValidEntry
+
+; Make sure BADB0D00 sig is present. If not this isn't a trap frame!
+Db_A: sub [esp]+TsDbgArgMark,0BADB0D00h
+ jne Db_NotATrapFrame
+endif
+
+ ASSERT_FS
+
+ mov edx, [esp]+TsExceptionList
+if DBG
+ or edx, edx
+ jnz short @f
+ int 3
+@@:
+endif
+ mov ebx, fs:[PcDebugActive] ; (ebx) = DebugActive flag
+ mov fs:[PcExceptionList], edx ; Restore ExceptionList
+
+ifb <NoPreviousMode>
+ mov ecx, [esp]+TsPreviousPreviousMode ; Restore PreviousMode
+if DBG
+ cmp ecx, -1 ; temporary debugging code
+ jne @f ; to make sure no one tries to pop ThPreviousMode
+ int 3 ; when it wasn't saved
+@@:
+endif
+ mov esi,fs:[PcPrcbData+PbCurrentThread]
+ mov [esi]+ThPreviousMode,cl
+else
+if DBG
+ mov ecx, [esp]+TsPreviousPreviousMode
+ cmp ecx, -1 ; temporary debugging code
+ je @f ; to make sure no one pushed ThPreviousMode and
+ int 3 ; is now exiting without restoreing it
+@@:
+endif
+endif
+
+ test ebx, 0fh
+ jnz Dr_ExitHelp
+
+Dr_ExitHelp_Target:
+
+ test dword ptr [esp].TsEflags,EFLAGS_V86_MASK
+ jnz V86_ExitHelp
+
+ test word ptr [esp]+TsSegCs,FRAME_EDITED
+ jz b ; Edited frame pop out.
+
+
+if ?RestoreAll eq 0
+.errnz MODE_MASK-1
+ cmp word ptr [esp]+TsSegCs,KGDT_R3_CODE OR RPL_MASK ; set/clear ZF
+ bt word ptr [esp]+TsSegCs,0 ; test MODE_MASK set/clear CF
+ cmc ; (CF=1 and ZF=0)
+ ja f ; jmp if CF=0 and ZF=0
+endif
+ifb <NoRestoreVolatiles>
+ifb <NoRestoreSegs> ; must restore eax before any
+ mov eax, [esp].TsEax ; selectors! (see trap0e handler)
+endif
+endif
+
+ifb <NoRestoreVolatiles>
+ mov edx, [ebp]+TsEdx ; Restore volitales
+ mov ecx, [ebp]+TsEcx
+ifb <NoRestoreSegs>
+else
+ mov eax, [ebp]+TsEax
+endif
+endif ; NoRestoreVolatiles
+
+ cmp word ptr [ebp]+TsSegCs, KGDT_R0_CODE
+ jz short @f
+
+ifb <NoRestoreSegs>
+ lea esp, [ebp]+TsSegGs
+ pop gs ; Restore Segs
+ pop es
+ pop ds
+endif
+NonFlatPm_Target:
+ lea esp, [ebp]+TsSegFs
+ pop fs
+@@:
+V86_ExitHelp_Target:
+
+ lea esp, [ebp]+TsEdi ; Skip PreMode, ExceptList and fs
+
+ pop edi ; restore non-volatiles
+ pop esi
+ pop ebx
+ pop ebp
+
+;
+; Esp MUST point to the Error Code on the stack. Because we use it to
+; store the entering esp.
+;
+
+ cmp word ptr [esp+8], 80h ; check for abios code segment?
+ ja Abios_ExitHelp
+
+Abios_ExitHelp_Target1:
+
+ add esp, 4 ; remove error code from trap frame
+
+Abios_ExitHelp_Target2:
+
+;
+; End of ABIOS stack check
+;
+
+ iretd ; return
+
+if DBG
+Db_NotATrapFrame:
+ add [esp]+TsDbgArgMark,0BADB0D00h ; put back the orig value
+Db_NotValidEntry:
+ int 3
+ jmp Db_A
+endif
+
+;
+; EXIT_HELPER
+;
+; if (PreviousMode == UserMode) {
+; DR* regs = TF.Dr* regs
+; }
+;
+; Entry-Conditions:
+;
+; DebugActive == TRUE
+; (ebp)->TrapFrame
+;
+;--
+
+align dword
+Dr_ExitHelp:
+
+ test dword ptr [ebp]+TsEFlags,EFLAGS_V86_MASK
+ jnz short x
+
+ test dword ptr [ebp]+TsSegCs,MODE_MASK
+ jz Dr_ExitHelp_Target
+
+x: mov esi,[ebp]+TsDr0
+ mov edi,[ebp]+TsDr1
+ mov ebx,[ebp]+TsDr2
+ mov dr0,esi
+ mov dr1,edi
+ mov dr2,ebx
+ mov esi,[ebp]+TsDr3
+ mov edi,[ebp]+TsDr6
+ mov ebx,[ebp]+TsDr7
+ mov dr3,esi
+ mov dr6,edi
+ mov dr7,ebx
+
+ jmp Dr_ExitHelp_Target
+
+align dword
+Abios_ExitHelp:
+
+;
+; INTERRUPT_STACK32_TO_STACK16
+;
+; This macro remaps current 32bit stack to 16bit stack at interrupt
+; time.
+;
+; Arguments:
+;
+; (esp)->TsEip.
+;
+;
+; PERFNOTE shielint We should check if there is any other H/W interrupt
+; pending. If yes, don't switch back to 16 bit stack. This way
+; we can get better performance.
+;
+
+ cmp word ptr [esp+2], 0 ; (esp+2) = Low word of error code
+ jz short Abios_ExitHelp_Target1
+ cmp word ptr [esp], 0 ; (esp) = High word of error code
+ jnz short Abios_ExitHelp_Target1
+
+ shr dword ptr [esp], 16
+ mov word ptr [esp + 2], KGDT_STACK16
+ lss sp, dword ptr [esp]
+ movzx esp, sp
+ jmp short Abios_ExitHelp_Target2
+
+;
+; Restore volatiles for V86 mode, and move seg regs
+;
+
+align dword
+V86_ExitHelp:
+
+ add esp,TsEdx
+ pop edx
+ pop ecx
+ pop eax
+ jmp V86_ExitHelp_Target
+
+;
+if ?RestoreAll eq 0
+;
+; Restore segs and volatiles for non-flat R3 PM (VDM in PM)
+;
+
+f: mov eax,[esp].TsEax ; restore eax before any selectors
+ ; (see trap0e handler)
+ add esp,TsSegGs
+
+ pop gs
+ pop es
+ pop ds
+
+ pop edx
+ pop ecx
+ jmp NonFlatPm_Target
+
+endif ; not ?RestoreAll
+
+
+;
+; TsSegCs contains the special value that means the frame was edited
+; in a way that affected esp, AND it's a kernel mode frame.
+; (Special value is null selector except for RPL.)
+;
+; Put back the real CS.
+; push eflags, eip onto target stack
+; restore
+; switch to target stack
+; iret
+;
+
+b: mov ebx,[esp]+TsTempSegCs
+ mov [esp]+TsSegCs,ebx
+
+;
+; There is no instruction that will load esp with an arbitrary value
+; (i.e. one out of a frame) and do a return, if no privledge transition
+; is occuring. Therefore, if we are returning to kernel mode, and
+; esp has been edited, we must "emulate" a kind of iretd.
+;
+; We do this by logically pushing the eip,cs,eflags onto the new
+; logical stack, loading that stack, and doing an iretd. This
+; requires that the new logical stack is at least 1 dword higher
+; than the unedited esp would have been. (i.e. It is not legal
+; to edit esp to have a new value < the old value.)
+;
+; KeContextToKframes enforces this rule.
+;
+
+;
+; Compute new logical stack address
+;
+
+ mov ebx,[esp]+TsTempEsp
+ sub ebx,12
+ mov [esp]+TsErrCode,ebx
+
+;
+; Copy eip,cs,eflags to new stack. note we do this high to low
+;
+
+ mov esi,[esp]+TsEflags
+ mov [ebx+8],esi
+ mov esi,[esp]+TsSegCs
+ mov [ebx+4],esi
+ mov esi,[esp]+TsEip
+ mov [ebx],esi
+
+;
+; Do a standard restore sequence.
+;
+; Observe that RestoreVolatiles is honored. Editing a volatile
+; register has no effect when returning from a system call.
+;
+ifb <NoRestoreVolatiles>
+ mov eax,[esp].TsEax
+endif
+; add esp,TsSegGs
+;
+;ifb <NoRestoreSegs>
+; pop gs
+; pop es
+; pop ds
+;else
+; add esp,12
+;endif
+
+ifb <NoRestoreVolatiles>
+ mov edx, [esp]+TsEdx
+ mov ecx, [esp]+TsEcx
+endif
+
+;ifnb <NoPreviousMode>
+; add esp, 4 ; Skip previous mode
+;else
+; pop ebx ; Restore PreviousMode
+; mov esi,fs:[PcPrcbData+PbCurrentThread]
+; mov ss:[esi]+ThPreviousMode,bl
+;endif
+;
+; pop ebx
+;
+; mov fs:[PcExceptionList], ebx ;Restore ExceptionList
+; pop fs
+
+ add esp, TsEdi
+ pop edi ; restore non-volatiles
+ pop esi
+ pop ebx
+ pop ebp
+
+;
+; (esp)->TsErrCode, where we saved the new esp
+;
+
+ mov esp,[esp] ; Do move not push to avoid increment
+ iretd
+
+ endm
+
+
+;++
+;
+; INTERRUPT_EXIT
+;
+; Macro Description:
+;
+; This macro is executed on return from an interrupt vector service
+; service routine. Its function is to restore privileged processor
+; state, and continue thread execution. If control is returning to
+; user mode and there is a user APC pending, then APC level interupt
+; will be requested and control is transfered to the user APC delivery
+; routine, if no higher level interrupt pending.
+;
+; Arguments:
+;
+; (TOS) = previous irql
+; (TOS+4) = irq vector to eoi
+; (TOS+8 ...) = machine_state frame
+; (ebp)-> machine state frame (trap frame)
+;
+;--
+
+INTERRUPT_EXIT macro DebugCheck
+local a
+
+ifnb <DebugCheck>
+ POLL_DEBUGGER
+endif
+if DBG ; save current eip for
+a: mov esi, offset a ; debugging bad trap frames
+endif
+
+ifdef __imp_Kei386EoiHelper@0
+ cli
+ call _HalEndSystemInterrupt@8
+ jmp dword ptr [__imp_Kei386EoiHelper@0]
+
+else
+ cli
+ call dword ptr [__imp__HalEndSystemInterrupt@8]
+ jmp Kei386EoiHelper@0
+endif
+endm
+
+
+;++
+;
+; SPURIOUS_INTERRUPT_EXIT
+;
+; Macro Description:
+;
+; To exit an interrupt without performing the EOI.
+;
+; Arguments:
+;
+; (TOS) = machine_state frame
+; (ebp)-> machine state frame (trap frame)
+;
+;--
+
+SPURIOUS_INTERRUPT_EXIT macro
+local a
+if DBG ; save current eip for
+a: mov esi, offset a ; debugging bad trap frames
+endif
+ifdef __imp_Kei386EoiHelper@0
+ jmp dword ptr [__imp_Kei386EoiHelper@0]
+else
+ jmp Kei386EoiHelper@0
+endif
+endm
+
+;++
+;
+; ENTER_TRAPV86
+;
+; Macro Description:
+;
+; Construct trap frame for v86 mode traps.
+;
+;--
+
+ENTER_TRAPV86 macro DRENTER,V86ENTER,LOADES
+ sub esp, TsErrCode
+ mov word ptr [esp].TsErrCode + 2, 0
+ mov [esp].TsEbx, ebx
+ mov [esp].TsEax, eax
+ mov [esp].TsEbp, ebp
+ mov [esp].TsEsi, esi
+ mov [esp].TsEdi, edi
+ mov ebx, KGDT_R0_PCR
+ mov eax, KGDT_R3_DATA OR RPL_MASK
+ mov [esp].TsEcx, ecx
+ mov [esp].TsEdx, edx
+if DBG
+ mov [esp].TsPreviousPreviousMode, -1
+ mov [esp]+TsDbgArgMark, 0BADB0D00h
+endif
+ mov fs, bx
+ mov ds, ax
+ifnb <LOADES>
+ mov es, ax
+endif
+ mov ebp, esp
+ cld ; CHECKIT_SUDEEP ; do we really need it
+ test byte ptr PCR[PcDebugActive], -1
+ jnz Dr_&DRENTER
+
+Dr_&V86ENTER:
+endm
+
+
+;
+; Taken from ntos\vdm\i386\vdmtb.inc
+;
+
+FIXED_NTVDMSTATE_LINEAR_PC_AT equ 0714H
+FIXED_NTVDMSTATE_LINEAR_PC_98 equ 0614H
+MACHINE_TYPE_MASK equ 0ff00H
+VDM_VIRTUAL_INTERRUPTS equ 0200H
+
+;++
+;
+; EXIT_TRAPV86
+;
+; Macro Description:
+;
+; if UserApc is pending deliver it
+; if User Context is v86 mode
+; Exit from kernel (does not return)
+; else
+; return (expected to execute EXIT_ALL)
+;--
+
+EXIT_TRAPV86 macro
+ local w, x, y, z
+
+z: mov ebx, PCR[PcPrcbData+PbCurrentThread]
+ mov byte ptr [ebx]+ThAlerted, 0
+ cmp byte ptr [ebx]+ThApcState.AsUserApcPending, 0
+ jne short w
+
+ ;
+ ; Kernel exit to V86 mode
+ ;
+
+ add esp,TsEdx
+ pop edx
+ pop ecx
+ pop eax
+ test byte ptr PCR[PcDebugActive], -1
+ jnz short x
+y:
+ add esp,12 ; unused fields
+ pop edi
+ pop esi
+ pop ebx
+ pop ebp
+ add esp,4 ; clear error code
+ iretd
+
+x: mov esi,[ebp]+TsDr0
+ mov edi,[ebp]+TsDr1
+ mov ebx,[ebp]+TsDr2
+ mov dr0,esi
+ mov dr1,edi
+ mov dr2,ebx
+ mov esi,[ebp]+TsDr3
+ mov edi,[ebp]+TsDr6
+ mov ebx,[ebp]+TsDr7
+ mov dr3,esi
+ mov dr6,edi
+ mov dr7,ebx
+ jmp short y
+
+w:
+ ;
+ ; Dispatch user mode APC
+ ; The APC routine runs with interrupts on and at APC level
+ ;
+
+ mov ecx, APC_LEVEL
+ fstCall KfRaiseIrql
+ push eax ; Save OldIrql
+ sti
+
+ stdCall _KiDeliverApc, <1, 0, ebp> ; ebp - Trap frame
+ ; 0 - Null exception frame
+ ; 1 - Previous mode
+
+ pop ecx ; (TOS) = OldIrql
+ fstCall KfLowerIrql
+
+ cli
+
+ ;
+ ; UserApc may have changed to vdm Monitor context (user flat 32)
+ ; If it has cannot use the v86 only kernel exit
+ ;
+
+ test dword ptr [ebp]+TsEFlags,EFLAGS_V86_MASK
+ jnz short z
+
+ ; Exit to do EXIT_ALL
+endm
+
diff --git a/private/ntos/ke/i386/largepag.c b/private/ntos/ke/i386/largepag.c
new file mode 100644
index 000000000..22760cb6f
--- /dev/null
+++ b/private/ntos/ke/i386/largepag.c
@@ -0,0 +1,179 @@
+#include "ki.h"
+#include "ki386.h"
+
+#ifdef ALLOC_PRAGMA
+
+#pragma alloc_text(INIT,Ki386CreateIdentityMap)
+#pragma alloc_text(INIT,Ki386ClearIdentityMap)
+#pragma alloc_text(INIT,Ki386EnableTargetLargePage)
+
+#endif
+
+extern PVOID Ki386LargePageIdentityLabel;
+
+
+BOOLEAN
+Ki386CreateIdentityMap(
+ IN OUT PIDENTITY_MAP IdentityMap
+ )
+{
+/*++
+
+ This function creates a page directory and page tables such that the
+ address "Ki386LargePageIdentityLabel" is mapped with 2 different mappings.
+ The first mapping is the current kernel mapping being used for the label.
+ The second mapping is an identity mapping, such that the physical address
+ of "Ki386LargePageIdentityLabel" is also its linear address.
+ Both mappings are created for 2 code pages.
+
+ This function assumes that the mapping does not require 2 PDE entries.
+ This will happen only while mapping 2 pages from
+ "Ki386LargePageIdentityLabel", if we cross a 4 meg boundary.
+
+Arguments:
+ IdentityMap - Pointer to the structure which will be filled with the newly
+ created Page Directory address and physical = linear address
+ for the label "Ki386LargePageIdentityLabel". It also provides
+ storage for the pointers used in allocating and freeing the
+ memory.
+Return Value:
+
+ TRUE if the function succeeds, FALSE otherwise.
+
+ Note - Ki386ClearIdentityMap() should be called even on FALSE return to
+ free any memory allocated.
+
+--*/
+
+ PHYSICAL_ADDRESS PageDirPhysical, CurrentMapPTPhysical,
+ IdentityMapPTPhysical, IdentityLabelPhysical;
+ PHARDWARE_PTE Pte;
+ ULONG Index;
+
+ IdentityMap->IdentityMapPT = NULL; // set incase of failure
+ IdentityMap->CurrentMapPT = NULL; // set incase of failure
+
+ IdentityMap->PageDirectory = ExAllocatePool(NonPagedPool, PAGE_SIZE);
+ if (IdentityMap->PageDirectory == NULL ) {
+ return(FALSE);
+ }
+
+ // The Page Directory and page tables must be aligned to page boundaries.
+ ASSERT((((ULONG) IdentityMap->PageDirectory) & (PAGE_SIZE-1)) == 0);
+
+ IdentityMap->IdentityMapPT = ExAllocatePool(NonPagedPool, PAGE_SIZE);
+ if (IdentityMap->IdentityMapPT == NULL ) {
+ return(FALSE);
+ }
+
+ ASSERT((((ULONG) IdentityMap->IdentityMapPT) & (PAGE_SIZE-1)) == 0);
+
+ IdentityMap->CurrentMapPT = ExAllocatePool(NonPagedPool, PAGE_SIZE);
+ if (IdentityMap->CurrentMapPT == NULL ) {
+ return(FALSE);
+ }
+
+ ASSERT((((ULONG) IdentityMap->CurrentMapPT) & (PAGE_SIZE-1)) == 0);
+
+ PageDirPhysical = MmGetPhysicalAddress(IdentityMap->PageDirectory);
+ IdentityMapPTPhysical = MmGetPhysicalAddress(IdentityMap->IdentityMapPT);
+ CurrentMapPTPhysical = MmGetPhysicalAddress(IdentityMap->CurrentMapPT);
+ IdentityLabelPhysical = MmGetPhysicalAddress(&Ki386LargePageIdentityLabel);
+
+ if ( (PageDirPhysical.LowPart == 0) ||
+ (IdentityMapPTPhysical.LowPart == 0) ||
+ (CurrentMapPTPhysical.LowPart == 0) ||
+ (IdentityLabelPhysical.LowPart == 0) ) {
+ return(FALSE);
+ }
+
+ // Write the pfn address of current map for Ki386LargePageIdentityLabel in PDE
+ Index = KiGetPdeOffset(&Ki386LargePageIdentityLabel);
+ Pte = &IdentityMap->PageDirectory[Index];
+ *(PULONG)Pte = 0;
+ Pte->PageFrameNumber = (CurrentMapPTPhysical.LowPart >> PAGE_SHIFT);
+ Pte->Valid = 1;
+
+ // Write the pfn address of current map for Ki386LargePageIdentityLabel in PTE
+ Index = KiGetPteOffset(&Ki386LargePageIdentityLabel);
+ Pte = &IdentityMap->CurrentMapPT[Index];
+ *(PULONG)Pte = 0;
+ Pte->PageFrameNumber = (IdentityLabelPhysical.LowPart >> PAGE_SHIFT);
+ Pte->Valid = 1;
+
+ // Map a second page, just in case the code crosses a page boundary
+ Pte = &IdentityMap->CurrentMapPT[Index+1];
+ *(PULONG)Pte = 0;
+ Pte->PageFrameNumber = ((IdentityLabelPhysical.LowPart >> PAGE_SHIFT) + 1);
+ Pte->Valid = 1;
+
+ // Write the pfn address of identity map for Ki386LargePageIdentityLabel in PDE
+ Index = KiGetPdeOffset(IdentityLabelPhysical.LowPart);
+ Pte = &IdentityMap->PageDirectory[Index];
+ *(PULONG)Pte = 0;
+ Pte->PageFrameNumber = (IdentityMapPTPhysical.LowPart >> PAGE_SHIFT);
+ Pte->Valid = 1;
+
+ // Write the pfn address of identity map for Ki386LargePageIdentityLabel in PTE
+ Index = KiGetPteOffset(IdentityLabelPhysical.LowPart);
+ Pte = &IdentityMap->IdentityMapPT[Index];
+ *(PULONG)Pte = 0;
+ Pte->PageFrameNumber = (IdentityLabelPhysical.LowPart >> PAGE_SHIFT);
+ Pte->Valid = 1;
+
+ // Map a second page, just in case the code crosses a page boundary
+ Pte = &IdentityMap->IdentityMapPT[Index+1];
+ *(PULONG)Pte = 0;
+ Pte->PageFrameNumber = ((IdentityLabelPhysical.LowPart >> PAGE_SHIFT) + 1);
+ Pte->Valid = 1;
+
+ IdentityMap->IdentityCR3 = PageDirPhysical.LowPart;
+ IdentityMap->IdentityLabel = IdentityLabelPhysical.LowPart;
+
+ return(TRUE);
+
+}
+
+
+VOID
+Ki386ClearIdentityMap(
+ IN PIDENTITY_MAP IdentityMap
+ )
+{
+/*++
+
+ This function just frees the page directory and page tables created in
+ Ki386CreateIdentityMap().
+
+--*/
+
+ if (IdentityMap->PageDirectory != NULL ) {
+
+ ExFreePool(IdentityMap->PageDirectory);
+ }
+
+ if (IdentityMap->IdentityMapPT != NULL ) {
+
+ ExFreePool(IdentityMap->IdentityMapPT);
+ }
+
+ if (IdentityMap->CurrentMapPT != NULL ) {
+
+ ExFreePool(IdentityMap->CurrentMapPT);
+ }
+}
+
+VOID
+Ki386EnableTargetLargePage(
+ IN PIDENTITY_MAP IdentityMap
+ )
+{
+/*++
+
+ This function just passes info on to the assembly routine
+ Ki386EnableLargePage().
+
+--*/
+
+ Ki386EnableCurrentLargePage(IdentityMap->IdentityLabel, IdentityMap->IdentityCR3);
+}
diff --git a/private/ntos/ke/i386/ldtsup.c b/private/ntos/ke/i386/ldtsup.c
new file mode 100644
index 000000000..3df09295f
--- /dev/null
+++ b/private/ntos/ke/i386/ldtsup.c
@@ -0,0 +1,392 @@
+/*++
+
+Copyright (c) 1991 Microsoft Corporation
+
+Module Name:
+
+ ldtsup.c
+
+Abstract:
+
+ This module implements interfaces that support manipulation of i386 Ldts.
+ These entry points only exist on i386 machines.
+
+Author:
+
+ Bryan M. Willman (bryanwi) 14-May-1991
+
+Environment:
+
+ Kernel mode only.
+
+Revision History:
+
+--*/
+
+#include "ki.h"
+
+//
+// Low level assembler support procedures
+//
+
+VOID
+KiLoadLdtr(
+ VOID
+ );
+
+VOID
+KiFlushDescriptors(
+ VOID
+ );
+
+//
+// Local service procedures
+//
+
+VOID
+Ki386LoadTargetLdtr (
+ IN PKIPI_CONTEXT SignalDone,
+ IN PVOID Parameter1,
+ IN PVOID Parameter2,
+ IN PVOID Parameter3
+ );
+
+VOID
+Ki386FlushTargetDescriptors (
+ IN PKIPI_CONTEXT SignalDone,
+ IN PVOID Parameter1,
+ IN PVOID Parameter2,
+ IN PVOID Parameter3
+ );
+
+VOID
+Ke386SetLdtProcess (
+ IN PKPROCESS Process,
+ IN PLDT_ENTRY Ldt,
+ IN ULONG Limit
+ )
+/*++
+
+Routine Description:
+
+ The specified LDT (which may be null) will be made the active Ldt of
+ the specified process, for all threads thereof, on whichever
+ processors they are running. The change will take effect before the
+ call returns.
+
+ An Ldt address of NULL or a Limit of 0 will cause the process to
+ receive the NULL Ldt.
+
+ This function only exists on i386 and i386 compatible processors.
+
+ No checking is done on the validity of Ldt entries.
+
+
+ N.B.
+
+ While a single Ldt structure can be shared amoung processes, any
+ edits to the Ldt of one of those processes will only be synchronized
+ for that process. Thus, processes other than the one the change is
+ applied to may not see the change correctly.
+
+Arguments:
+
+ Process - Pointer to KPROCESS object describing the process for
+ which the Ldt is to be set.
+
+ Ldt - Pointer to an array of LDT_ENTRYs (that is, a pointer to an
+ Ldt.)
+
+ Limit - Ldt limit (must be 0 mod 8)
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+
+ KGDTENTRY LdtDescriptor;
+ BOOLEAN LocalProcessor;
+ KIRQL OldIrql;
+ PKPRCB Prcb;
+ KAFFINITY TargetProcessors;
+
+ //
+ // Compute the contents of the Ldt descriptor
+ //
+
+ if ((Ldt == NULL) || (Limit == 0)) {
+
+ //
+ // Set up an empty descriptor
+ //
+
+ LdtDescriptor.LimitLow = 0;
+ LdtDescriptor.BaseLow = 0;
+ LdtDescriptor.HighWord.Bytes.BaseMid = 0;
+ LdtDescriptor.HighWord.Bytes.Flags1 = 0;
+ LdtDescriptor.HighWord.Bytes.Flags2 = 0;
+ LdtDescriptor.HighWord.Bytes.BaseHi = 0;
+
+ } else {
+
+ //
+ // Insure that the unfilled fields of the selector are zero
+ // N.B. If this is not done, random values appear in the high
+ // portion of the Ldt limit.
+ //
+
+ LdtDescriptor.HighWord.Bytes.Flags1 = 0;
+ LdtDescriptor.HighWord.Bytes.Flags2 = 0;
+
+ //
+ // Set the limit and base
+ //
+
+ LdtDescriptor.LimitLow = (USHORT) ((ULONG) Limit - 1);
+ LdtDescriptor.BaseLow = (USHORT) ((ULONG) Ldt & 0xffff);
+ LdtDescriptor.HighWord.Bytes.BaseMid = (UCHAR) (((ULONG)Ldt & 0xff0000) >> 16);
+ LdtDescriptor.HighWord.Bytes.BaseHi = (UCHAR) (((ULONG)Ldt & 0xff000000) >> 24);
+
+ //
+ // Type is LDT, DPL = 0
+ //
+
+ LdtDescriptor.HighWord.Bits.Type = TYPE_LDT;
+ LdtDescriptor.HighWord.Bits.Dpl = DPL_SYSTEM;
+
+ //
+ // Make it present
+ //
+
+ LdtDescriptor.HighWord.Bits.Pres = 1;
+
+ }
+
+ //
+ // Acquire the context swap lock so a context switch cannot occur.
+ //
+
+ KiLockContextSwap(&OldIrql);
+
+ //
+ // Set the Ldt fields in the process object.
+ //
+
+ Process->LdtDescriptor = LdtDescriptor;
+
+ //
+ // Tell all processors active for this process to reload their LDTs
+ //
+
+#ifdef NT_UP
+
+ KiLoadLdtr();
+
+#else
+
+ Prcb = KeGetCurrentPrcb();
+ TargetProcessors = Process->ActiveProcessors & ~Prcb->SetMember;
+ if (TargetProcessors != 0) {
+ KiIpiSendPacket(TargetProcessors,
+ Ki386LoadTargetLdtr,
+ NULL,
+ NULL,
+ NULL);
+ }
+
+ KiLoadLdtr();
+ if (TargetProcessors != 0) {
+
+ //
+ // Stall until target processor(s) release us
+ //
+
+ KiIpiStallOnPacketTargets();
+ }
+
+#endif
+
+ //
+ // Restore IRQL and release the context swap lock.
+ //
+
+ KiUnlockContextSwap(OldIrql);
+ return;
+}
+
+VOID
+Ki386LoadTargetLdtr (
+ IN PKIPI_CONTEXT SignalDone,
+ IN PVOID Parameter1,
+ IN PVOID Parameter2,
+ IN PVOID Parameter3
+ )
+/*++
+
+Routine Description:
+
+ Reload local Ldt register and clear signal bit in TargetProcessor mask
+
+Arguments:
+
+ Argument - pointer to a ipi packet structure.
+ ReadyFlag - Pointer to flag to be set once LDTR has been reloaded
+
+Return Value:
+
+ none.
+
+--*/
+{
+
+ //
+ // Reload the LDTR register from currently active process object
+ //
+
+ KiLoadLdtr();
+ KiIpiSignalPacketDone(SignalDone);
+ return;
+}
+
+VOID
+Ke386SetDescriptorProcess (
+ IN PKPROCESS Process,
+ IN ULONG Offset,
+ IN LDT_ENTRY LdtEntry
+ )
+/*++
+
+Routine Description:
+
+ The specified LdtEntry (which could be 0, not present, etc) will be
+ edited into the specified Offset in the Ldt of the specified Process.
+ This will be synchronzied accross all the processors executing the
+ process. The edit will take affect on all processors before the call
+ returns.
+
+ N.B.
+
+ Editing an Ldt descriptor requires stalling all processors active
+ for the process, to prevent accidental loading of descriptors in
+ an inconsistent state.
+
+Arguments:
+
+ Process - Pointer to KPROCESS object describing the process for
+ which the descriptor edit is to be performed.
+
+ Offset - Byte offset into the Ldt of the descriptor to edit.
+ Must be 0 mod 8.
+
+ LdtEntry - Value to edit into the descriptor in hardware format.
+ No checking is done on the validity of this item.
+
+Return Value:
+
+ none.
+
+--*/
+
+{
+
+ PLDT_ENTRY Ldt;
+ KIRQL OldIrql;
+ PKPRCB Prcb;
+ KAFFINITY TargetProcessors;
+
+ //
+ // Compute address of descriptor to edit.
+ //
+
+ Ldt =
+ (PLDT_ENTRY)
+ ((Process->LdtDescriptor.HighWord.Bytes.BaseHi << 24) |
+ ((Process->LdtDescriptor.HighWord.Bytes.BaseMid << 16) & 0xff0000) |
+ (Process->LdtDescriptor.BaseLow & 0xffff));
+ Offset = Offset / 8;
+ MmLockPagedPool(&Ldt[Offset], sizeof(LDT_ENTRY));
+ KiLockContextSwap(&OldIrql);
+
+#ifdef NT_UP
+
+ //
+ // Edit the Ldt.
+ //
+
+ Ldt[Offset] = LdtEntry;
+
+#else
+
+ Prcb = KeGetCurrentPrcb();
+ TargetProcessors = Process->ActiveProcessors & ~Prcb->SetMember;
+ if (TargetProcessors != 0) {
+ KiIpiSendPacket(TargetProcessors,
+ Ki386FlushTargetDescriptors,
+ (PVOID)&Prcb->ReverseStall,
+ NULL,
+ NULL);
+
+ KiIpiStallOnPacketTargets();
+ }
+
+ //
+ // All target processors have flushed the segment descriptors and
+ // are waiting to proceed. Edit the ldt on the current processor,
+ // then continue the execution of target processors.
+ //
+
+ Ldt[Offset] = LdtEntry;
+ if (TargetProcessors != 0) {
+ Prcb->ReverseStall += 1;
+ }
+
+#endif
+
+ //
+ // Restore IRQL and release the context swap lock.
+ //
+
+ KiUnlockContextSwap(OldIrql);
+ MmUnlockPagedPool(&Ldt[Offset], sizeof(LDT_ENTRY));
+ return;
+}
+
+VOID
+Ki386FlushTargetDescriptors (
+ IN PKIPI_CONTEXT SignalDone,
+ IN PVOID Proceed,
+ IN PVOID Parameter2,
+ IN PVOID Parameter3
+ )
+
+/*++
+
+Routine Description:
+
+ This function flushes the segment descriptors on the current processor.
+
+Arguments:
+
+ Argument - pointer to a _KIPI_FLUSH_DESCRIPTOR structure.
+
+ ReadyFlag - pointer to flag to syncroize with
+
+Return Value:
+
+ none.
+
+--*/
+
+{
+ //
+ // Flush the segment descriptors on the current processor and signal that
+ // the descriptors have been flushed.
+ //
+
+ KiFlushDescriptors();
+ KiIpiSignalPacketDoneAndStall (SignalDone, Proceed);
+ return;
+}
diff --git a/private/ntos/ke/i386/ldtsup2.asm b/private/ntos/ke/i386/ldtsup2.asm
new file mode 100644
index 000000000..5ef9fcd43
--- /dev/null
+++ b/private/ntos/ke/i386/ldtsup2.asm
@@ -0,0 +1,164 @@
+ title "Ldt Support 2 - Low Level"
+;++
+;
+; Copyright (c) 1991 Microsoft Corporation
+;
+; Module Name:
+;
+; ldtsup2.asm
+;
+; Abstract:
+;
+; This module implements procedures to load a new ldt and to flush
+; segment descriptors.
+;
+; Author:
+;
+; Bryan M. Willman (bryanwi) 14-May-1991
+;
+; Environment:
+;
+; Kernel mode only.
+;
+; Revision History:
+;
+;--
+
+.386p
+ .xlist
+include ks386.inc
+include i386\kimacro.inc
+include mac386.inc
+include callconv.inc
+ .list
+
+_TEXT$00 SEGMENT DWORD PUBLIC 'CODE'
+ ASSUME DS:FLAT, ES:FLAT, SS:NOTHING, FS:NOTHING, GS:NOTHING
+;++
+;
+; VOID
+; KiLoadLdtr(
+; VOID
+; )
+;
+; Routine Description:
+;
+; This routine copies the Ldt descriptor image out of the currently
+; executing process object into the Ldt descriptor, and reloads the
+; the Ldt descriptor into the Ldtr. The effect of this is to provide
+; a new Ldt.
+;
+; If the Ldt descriptor image has a base or limit of 0, then NULL will
+; be loaded into the Ldtr, and no copy to the Gdt will be done.
+;
+; Arguments:
+;
+; None.
+;
+; Return Value:
+;
+; None.
+;
+;--
+
+cPublicProc _KiLoadLdtr, 0
+
+ push esi
+ push edi
+
+ mov eax,fs:PcPrcbData+PbCurrentThread ; (eax)->CurrentThread
+ mov eax,[eax]+(ThApcState+AsProcess) ; (eax)->CurrentProcess
+
+ lea esi,[eax]+PrLdtDescriptor ; (esi)->Ldt value
+ xor dx,dx ; assume null value
+ cmp word ptr [esi],0 ; limit == 0?
+ jz kill10 ; yes limit 0, go load null
+
+;
+; We have a non-null Ldt Descriptor, copy it into the Gdt
+;
+
+ mov edi,fs:PcGdt
+ add edi,KGDT_LDT ; (edi)->Ldt descriptor
+
+ movsd
+ movsd ; descrip. now matches value
+
+ mov dx,KGDT_LDT
+
+kill10: lldt dx
+
+ pop edi
+ pop esi
+
+ stdCall _KiFlushDescriptors
+
+ stdRET _KiLoadLdtr
+
+stdENDP _KiLoadLdtr
+
+
+
+;++
+;
+; VOID
+; KiFlushDescriptors(
+; VOID
+; )
+;
+; Routine Description:
+;
+; Flush the in-processor descriptor registers for the segment registers.
+; We do this by reloading each segment register.
+;
+; N.B.
+;
+; This procedure is only intended to support Ldt operations.
+; It does not support operations on the Gdt. In particular,
+; neither it nor Ke386SetDescriptorProcess are appropriate for
+; editing descriptors used by 16bit kernel code (i.e. ABIOS.)
+;
+; Since we are in kernel mode, we know that CS and SS do NOT
+; contain Ldt selectors, any such selectors will be save/restored
+; by the interrupt that brought us here from user space.
+;
+; Since we are in kernel mode, DS must contain a flat GDT descriptor,
+; since all entry sequences would have forced a reference to it.
+;
+; Since we are in kernel mode, FS points to the PCR, since all
+; entry sequences force it to.
+;
+; Therefore, only ES and GS need to be flushed.
+;
+; Since no inline kernel code ever uses GS, we know it will be
+; restored from a frame of some caller, or nobody cares. Therefore,
+; we load null into GS. (Fastest possible load.)
+;
+; ES is restored to KGDT_R3_DATA, because kernel exit will not restore
+; it for us. If we do not put the correct value in ES, we may wind
+; up with zero in ES in user mode.
+;
+; Arguments:
+;
+; None.
+;
+; Return Value:
+;
+; None.
+;
+;--
+
+cPublicProc _KiFlushDescriptors ,0
+
+ xor ax,ax
+ mov gs,ax
+ push ds
+ pop es
+ stdRET _KiFlushDescriptors
+
+stdENDP _KiFlushDescriptors
+
+
+_TEXT$00 ends
+ end
+
diff --git a/private/ntos/ke/i386/mi.inc b/private/ntos/ke/i386/mi.inc
new file mode 100644
index 000000000..93678d6f9
--- /dev/null
+++ b/private/ntos/ke/i386/mi.inc
@@ -0,0 +1,43 @@
+;++
+;
+; Copyright (c) 1989 Microsoft Corporation
+;
+; Module Name:
+;
+; MI.INC
+;
+; Abstract:
+;
+; This module contains equates for x86 machine instructions
+;
+; Author:
+;
+; Dave Hastings 2 May 1991
+;
+; Notes:
+;
+; This information used to reside in Trap.asm, but is now needed in
+; multiple source files.
+;
+; Revision History:
+;--
+
+
+MAX_INSTRUCTION_LENGTH EQU 15
+MAX_INSTRUCTION_PREFIX_LENGTH EQU 4
+MI_LOCK_PREFIX EQU 0F0H
+MI_ADDR_PREFIX EQU 067H
+MI_TWO_BYTE EQU 0FH
+MI_HLT EQU 0F4H
+MI_LTR_LLDT EQU 0
+MI_LGDT_LIDT_LMSW EQU 01H
+MI_MODRM_MASK EQU 38H
+MI_LLDT_MASK EQU 10h
+MI_LTR_MASK EQU 18H
+MI_LGDT_MASK EQU 10H
+MI_LIDT_MASK EQU 18H
+MI_LMSW_MASK EQU 30H
+MI_SPECIAL_MOV_MASK EQU 20H
+MI_REP_INS_OUTS EQU 0F3H
+MI_MIN_INS_OUTS EQU 06CH
+MI_MAX_INS_OUTS EQU 06FH
diff --git a/private/ntos/ke/i386/misc.c b/private/ntos/ke/i386/misc.c
new file mode 100644
index 000000000..7449e7a74
--- /dev/null
+++ b/private/ntos/ke/i386/misc.c
@@ -0,0 +1,164 @@
+/*++
+
+Copyright (c) 1989 Microsoft Corporation
+
+Module Name:
+
+ misc.c
+
+Abstract:
+
+ This module implements machine dependent miscellaneous kernel functions.
+
+Author:
+
+ Ken Reneris 7-5-95
+
+Environment:
+
+ Kernel mode only.
+
+Revision History:
+
+--*/
+
+#include "ki.h"
+
+#ifdef ALLOC_PRAGMA
+#pragma alloc_text(PAGE,KeSaveFloatingPointState)
+#pragma alloc_text(PAGE,KeRestoreFloatingPointState)
+#endif
+
+NTSTATUS
+KeSaveFloatingPointState (
+ OUT PKFLOATING_SAVE FloatSave
+ )
+/*++
+
+Routine Description:
+
+ This routine saves the thread's current non-volatile NPX state,
+ and sets a new initial floating point state for the caller.
+
+Arguments:
+
+ FloatSave - receives the current non-volatile npx state for the thread
+
+Return Value:
+
+--*/
+{
+ PKTHREAD Thread;
+ PFLOATING_SAVE_AREA NpxFrame;
+
+ PAGED_CODE ();
+
+ Thread = KeGetCurrentThread();
+ NpxFrame = (PFLOATING_SAVE_AREA)(((ULONG)(Thread->InitialStack) -
+ sizeof(FLOATING_SAVE_AREA)));
+
+ //
+ // If the system is using floating point emulation, then
+ // return an error
+ //
+
+ if (!KeI386NpxPresent) {
+ return STATUS_ILLEGAL_FLOAT_CONTEXT;
+ }
+
+ //
+ // Ensure the thread's current NPX state is in memory
+ //
+
+ KiFlushNPXState ();
+
+ //
+ // Save the non-volatile portion of the thread's NPX state
+ //
+
+ FloatSave->ControlWord = NpxFrame->ControlWord;
+ FloatSave->StatusWord = NpxFrame->StatusWord;
+ FloatSave->ErrorOffset = NpxFrame->ErrorOffset;
+ FloatSave->ErrorSelector = NpxFrame->ErrorSelector;
+ FloatSave->DataOffset = NpxFrame->DataOffset;
+ FloatSave->DataSelector = NpxFrame->DataSelector;
+ FloatSave->Cr0NpxState = NpxFrame->Cr0NpxState;
+
+ //
+ // Load new initial floating point state
+ //
+
+ NpxFrame->ControlWord = 0x27f; // like fpinit but 64bit mode
+ NpxFrame->StatusWord = 0;
+ NpxFrame->TagWord = 0xffff;
+ NpxFrame->ErrorOffset = 0;
+ NpxFrame->ErrorSelector = 0;
+ NpxFrame->DataOffset = 0;
+ NpxFrame->DataSelector = 0;
+ NpxFrame->Cr0NpxState = 0;
+
+ return STATUS_SUCCESS;
+}
+
+
+NTSTATUS
+KeRestoreFloatingPointState (
+ IN PKFLOATING_SAVE FloatSave
+ )
+/*++
+
+Routine Description:
+
+ This routine retores the thread's current non-volatile NPX state,
+ to the passed in state.
+
+Arguments:
+
+ FloatSave - the non-volatile npx state for the thread to restore
+
+Return Value:
+
+--*/
+{
+ PKTHREAD Thread;
+ PFLOATING_SAVE_AREA NpxFrame;
+
+ PAGED_CODE ();
+ ASSERT (KeI386NpxPresent);
+
+ Thread = KeGetCurrentThread();
+ NpxFrame = (PFLOATING_SAVE_AREA)(((ULONG)(Thread->InitialStack) -
+ sizeof(FLOATING_SAVE_AREA)));
+
+ if (FloatSave->Cr0NpxState & ~(CR0_PE|CR0_MP|CR0_EM|CR0_TS)) {
+ ASSERT (FALSE);
+ return STATUS_UNSUCCESSFUL;
+ }
+
+ //
+ // Ensure the thread's current NPX state is in memory
+ //
+
+ KiFlushNPXState ();
+
+ //
+ // Restore the non-volatile portion of the thread's NPX state
+ //
+
+ NpxFrame->ControlWord = FloatSave->ControlWord;
+ NpxFrame->StatusWord = FloatSave->StatusWord;
+ NpxFrame->ErrorOffset = FloatSave->ErrorOffset;
+ NpxFrame->ErrorSelector = FloatSave->ErrorSelector;
+ NpxFrame->DataOffset = FloatSave->DataOffset;
+ NpxFrame->DataSelector = FloatSave->DataSelector;
+ NpxFrame->Cr0NpxState = FloatSave->Cr0NpxState;
+ FloatSave->Cr0NpxState = 0xffffffff;
+
+ //
+ // Clear the volatile floating point state
+ //
+
+ NpxFrame->TagWord = 0xffff;
+
+ return STATUS_SUCCESS;
+}
diff --git a/private/ntos/ke/i386/mpipia.asm b/private/ntos/ke/i386/mpipia.asm
new file mode 100644
index 000000000..8d86d86a2
--- /dev/null
+++ b/private/ntos/ke/i386/mpipia.asm
@@ -0,0 +1,435 @@
+ title "mpipia"
+;++
+;
+; Copyright (c) 1989-1995 Microsoft Corporation
+;
+; Module Name:
+;
+; mpipia.asm
+;
+; Abstract:
+;
+; This module implements the x86 specific fucntions required to
+; support multiprocessor systems.
+;
+; Author:
+;
+; David N. Cutler (davec) 5-Feb-1995
+;
+; Environment:
+;
+; Krnel mode only.
+;
+; Revision History:
+;
+;--
+
+.486p
+ .xlist
+include ks386.inc
+include mac386.inc
+include callconv.inc
+ .list
+
+ EXTRNP HalRequestSoftwareInterrupt,1,IMPORT,FASTCALL
+ EXTRNP HalRequestSoftwareInterrupt,1,IMPORT,FASTCALL
+ EXTRNP _HalRequestIpi,1,IMPORT
+ EXTRNP _KiFreezeTargetExecution, 2
+ifdef DBGMP
+ EXTRNP _KiPollDebugger
+endif
+ extrn _KiProcessorBlock:DWORD
+
+
+DELAYCOUNT equ 2000h
+
+
+
+_TEXT SEGMENT DWORD PUBLIC 'CODE'
+ ASSUME DS:FLAT, ES:FLAT, SS:NOTHING, FS:NOTHING, GS:NOTHING
+
+;++
+;
+; BOOLEAN
+; KiIpiServiceRoutine (
+; IN PKTRAP_FRAME TrapFrame,
+; IN PKEXCEPTION_FRAME ExceptionFrame
+; )
+;
+; Routine Description:
+;
+; This routine is called at IPI level to process any outstanding
+; interporcessor requests for the current processor.
+;
+; Arguments:
+;
+; TrapFrame - Supplies a pointer to a trap frame.
+;
+; ExceptionFrame - Not used.
+;
+; Return Value:
+;
+; A value of TRUE is returned, if one of more requests were service.
+; Otherwise, FALSE is returned.
+;
+;--
+
+cPublicProc _KiIpiServiceRoutine, 2
+
+ifndef NT_UP
+cPublicFpo 2, 1
+ push ebx
+
+ mov ecx, PCR[PcPrcb] ; get current processor block address
+
+ xor ebx, ebx ; get request summary flags
+ cmp [ecx].PbRequestSummary, ebx
+ jz short isr10
+
+ xchg [ecx].PbRequestSummary, ebx
+
+;
+; Check for freeze request.
+;
+
+ test bl, IPI_FREEZE ; check if freeze requested
+ jnz short isr50 ; if nz, freeze requested
+
+;
+; For RequestSummary's other then IPI_FREEZE set return to TRUE
+;
+
+ mov bh, 1
+
+;
+; Check for Packet ready.
+;
+; If a packet is ready, then get the address of the requested function
+; and call the function passing the address of the packet address as a
+; parameter.
+;
+
+isr10: mov eax, [ecx].PbSignalDone ; get source processor block address
+ or eax, eax ; check if packet ready
+ jz short isr20 ; if z set, no packet ready
+
+ mov edx, [esp + 8] ; Current trap frame
+
+ push [eax].PbCurrentPacket + 8 ; push parameters on stack
+ push [eax].PbCurrentPacket + 4 ;
+ push [eax].PbCurrentPacket + 0 ;
+ push eax ; push source processor block address
+ mov eax, [eax]+PbWorkerRoutine ; get worker routine address
+ mov [ecx].PbSignalDone, 0 ; clear packet address
+ mov [ecx].PbIpiFrame, edx ; Save frame address
+ call eax ; call worker routine
+ mov bh, 1 ; return TRUE
+
+;
+; Check for APC interrupt request.
+;
+
+isr20: test bl, IPI_APC ; check if APC interrupt requested
+ jz short isr30 ; if z, APC interrupt not requested
+
+ mov ecx, APC_LEVEL ; request APC interrupt
+ fstCall HalRequestSoftwareInterrupt
+
+;
+; Check for DPC interrupt request.
+;
+
+isr30: test bl, IPI_DPC ; check if DPC interrupt requested
+ jz short isr40 ; if z, DPC interrupt not requested
+
+ mov ecx, DISPATCH_LEVEL ; request DPC interrupt
+ fstCall HalRequestSoftwareInterrupt ;
+
+isr40: mov al, bh ; return status
+ pop ebx
+ stdRET _KiIpiServiceRoutine
+
+;
+; Freeze request is requested
+;
+
+isr50: mov ecx, [esp] + 12 ; get exception frame address
+ mov edx, [esp] + 8 ; get trap frame address
+ stdCall _KiFreezeTargetExecution, <edx, ecx> ; freeze execution
+ mov ecx, PCR[PcPrcb] ; get current processor block address
+ test bl, not IPI_FREEZE ; Any other IPI RequestSummary?
+ setnz bh ; Set return code accordingly
+ jmp short isr10
+else
+ xor eax, eax ; return FALSE
+ stdRET _KiIpiServiceRoutine
+endif
+
+stdENDP _KiIpiServiceRoutine
+
+;++
+;
+; VOID
+; FASTCALL
+; KiIpiSend (
+; IN KAFFINITY TargetProcessors,
+; IN KIPI_REQUEST Request
+; )
+;
+; Routine Description:
+;
+; This function requests the specified operation on the targt set of
+; processors.
+;
+; Arguments:
+;
+; TargetProcessors (ecx) - Supplies the set of processors on which the
+; specified operation is to be executed.
+;
+; IpiRequest (edx) - Supplies the request operation code.
+;
+; Return Value:
+;
+; None.
+;
+;--
+
+cPublicFastCall KiIpiSend, 2
+
+ifndef NT_UP
+
+cPublicFpo 0, 2
+ push esi ; save registers
+ push edi ;
+ mov esi, ecx ; save target processor set
+
+ shr ecx, 1 ; shift out first bit
+ lea edi, _KiProcessorBlock ; get processor block array address
+ jnc short is20 ; if nc, not in target set
+
+is10: mov eax, [edi] ; get processor block address
+ lock or [eax].PbRequestSummary, edx ; set request summary bit
+
+is20: shr ecx, 1 ; shift out next bit
+ lea edi, [edi+4] ; advance to next processor
+ jc short is10 ; if target, go set summary bit
+ jnz short is20 ; if more, check next
+
+ stdCall _HalRequestIpi, <esi> ; request IPI interrupts on targets
+
+ pop edi ; restore registers
+ pop esi ;
+endif
+ fstRet KiIpiSend
+
+fstENDP KiIpiSend
+
+;++
+;
+; VOID
+; KiIpiSendPacket (
+; IN KAFFINITY TargetProcessors,
+; IN PKIPI_WORKER WorkerFunction,
+; IN PVOID Parameter1,
+; IN PVOID Parameter2,
+; IN PVOID Parameter3
+; )
+;
+; Routine Description:
+;
+; This routine executes the specified worker function on the specified
+; set of processors.
+;
+; Arguments:
+;
+; TargetProcessors [esp + 4] - Supplies the set of processors on which the
+; specfied operation is to be executed.
+;
+; WorkerFunction [esp + 8] - Supplies the address of the worker function.
+;
+; Parameter1 - Parameter3 [esp + 12] - Supplies worker function specific
+; paramters.
+;
+; Return Value:
+;
+; None.
+;
+;--*/
+
+cPublicProc _KiIpiSendPacket, 5
+
+ifndef NT_UP
+
+cPublicFpo 5, 2
+ push esi ; save registers
+ push edi ;
+
+;
+; Store function address and parameters in the packet area of the PRCB on
+; the current processor.
+;
+
+ mov edx, PCR[PcPrcb] ; get current processor block address
+ mov ecx, [esp] + 12 ; set target processor set
+ mov eax, [esp] + 16 ; set worker function address
+ mov edi, [esp] + 20 ; store worker function parameters
+ mov esi, [esp] + 24 ;
+
+ mov [edx].PbTargetSet, ecx
+ mov [edx].PbWorkerRoutine, eax
+
+ mov eax, [esp] + 28
+ mov [edx].PbCurrentPacket, edi
+ mov [edx].PbCurrentPacket + 4, esi
+ mov [edx].PbCurrentPacket + 8, eax
+
+;
+; Loop through the target processors and send the packet to the specified
+; recipients.
+;
+
+ shr ecx, 1 ; shift out first bit
+ lea edi, _KiProcessorBlock ; get processor block array address
+ jnc short isp30 ; if nc, not in target set
+isp10: mov esi, [edi] ; get processor block address
+isp20: mov eax, [esi].PbSignalDone ; check if packet being processed
+ or eax, eax ;
+ jne short isp20 ; if ne, packet being processed
+
+ lock cmpxchg [esi].PbSignalDone, edx ; compare and exchange
+
+ jnz short isp20 ; if nz, exchange failed
+
+isp30: shr ecx, 1 ; shift out next bit
+ lea edi, [edi+4] ; advance to next processor
+ jc short isp10 ; if c, in target set
+ jnz short isp30 ; if nz, more target processors
+
+ mov ecx, [esp] + 12 ; set target processor set
+ stdCall _HalRequestIpi, <ecx> ; send IPI to targets
+
+ pop edi ; restore register
+ pop esi ;
+endif
+
+ stdRet _KiIpiSendPacket
+
+stdENDP _KiIpiSendPacket
+
+;++
+;
+; VOID
+; FASTCALL
+; KiIpiSignalPacketDone (
+; IN PKIPI_CONTEXT Signaldone
+; )
+;
+; Routine Description:
+;
+; This routine signals that a processor has completed a packet by
+; clearing the calling processor's set member of the requesting
+; processor's packet.
+;
+; Arguments:
+;
+; SignalDone (ecx) - Supplies a pointer to the processor block of the
+; sending processor.
+;
+; Return Value:
+;
+; None.
+;
+;--
+
+cPublicFastCall KiIpiSignalPacketDone, 1
+
+ifndef NT_UP
+
+ mov edx, PCR[PcPrcb] ; get current processor block address
+ mov eax, [edx].PbSetMember ; get processor bit
+
+ lock xor [ecx].PbTargetSet, eax ; clear processor set member
+endif
+ fstRET KiIpiSignalPacketDone
+
+fstENDP KiIpiSignalPacketDone
+
+
+;++
+;
+; VOID
+; FASTCALL
+; KiIpiSignalPacketDoneAndStall (
+; IN PKIPI_CONTEXT Signaldone
+; IN PULONG ReverseStall
+; )
+;
+; Routine Description:
+;
+; This routine signals that a processor has completed a packet by
+; clearing the calling processor's set member of the requesting
+; processor's packet, and then stalls of the reverse stall value
+;
+; Arguments:
+;
+; SignalDone (ecx) - Supplies a pointer to the processor block of the
+; sending processor.
+;
+; ReverseStall (edx) - Supplies a pointer to the reverse stall barrier
+;
+; Return Value:
+;
+; None.
+;
+;--
+
+cPublicFastCall KiIpiSignalPacketDoneAndStall, 2
+cPublicFpo 0, 2
+
+ifndef NT_UP
+ push ebx
+ push esi
+
+ mov esi, PCR[PcPrcb] ; get current processor block address
+ mov eax, [esi].PbSetMember ; get processor bit
+ mov ebx, dword ptr [edx] ; get current value of barrier
+
+ lock xor [ecx].PbTargetSet, eax ; clear processor set member
+
+sps10: mov eax, DELAYCOUNT
+sps20: cmp ebx, dword ptr [edx] ; barrier set?
+ jne short sps90 ; yes, all done
+
+ dec eax ; P54C pre C2 workaround
+ jnz short sps20 ; if eax = 0, generate bus cycle
+
+ifdef DBGMP
+ stdCall _KiPollDebugger ; Check for debugger ^C
+endif
+
+;
+; There could be a freeze execution outstanding. Check and clear
+; freeze flag.
+;
+
+.errnz IPI_FREEZE - 4
+ lock btr [esi].PbRequestSummary, 2 ; Generate bus cycle
+ jnc short sps10 ; Freeze pending?
+
+cPublicFpo 0,4
+ push ecx ; save TargetSet address
+ push edx
+ stdCall _KiFreezeTargetExecution, <[esi].PbIpiFrame, 0>
+ pop edx
+ pop ecx
+ jmp short sps10
+
+sps90: pop esi
+ pop ebx
+endif
+ fstRET KiIpiSignalPacketDoneAndStall
+
+fstENDP KiIpiSignalPacketDoneAndStall
+
+_TEXT ends
+ end
diff --git a/private/ntos/ke/i386/mtrr.c b/private/ntos/ke/i386/mtrr.c
new file mode 100644
index 000000000..0156cf908
--- /dev/null
+++ b/private/ntos/ke/i386/mtrr.c
@@ -0,0 +1,1887 @@
+/*++
+
+Copyright (c) 1991 Microsoft Corporation
+
+Module Name:
+
+ mtrr.c
+
+Abstract:
+
+ This module implements interfaces that support manipulation of
+ memory range type registers.
+
+ These entry points only exist on i386 machines.
+
+Author:
+
+ Ken Reneris (kenr) 11-Oct-95
+
+Environment:
+
+ Kernel mode only.
+
+Revision History:
+
+--*/
+
+#include "ki.h"
+#include "mtrr.h"
+
+#define STATIC
+#define IDBG DBG
+
+#if DBG
+#define DBGMSG(a) DbgPrint(a)
+#else
+#define DBGMSG(a)
+#endif
+
+//
+// Internal declarations
+//
+
+//
+// Range in generic terms
+//
+
+typedef struct _ONE_RANGE {
+ ULONGLONG Base;
+ ULONGLONG Limit;
+ UCHAR Type;
+} ONE_RANGE, *PONE_RANGE;
+
+#define GROW_RANGE_TABLE 4
+
+//
+// Range in specific mtrr terms
+//
+
+typedef struct _MTRR_RANGE {
+ MTRR_VARIABLE_BASE Base;
+ MTRR_VARIABLE_MASK Mask;
+} MTRR_RANGE, *PMTRR_RANGE;
+
+//
+// System static information concerning cached range types
+//
+
+typedef struct _RANGE_INFO {
+
+ //
+ // Global MTRR info
+ //
+
+ MTRR_DEFAULT Default; // h/w mtrr default
+ MTRR_CAPABILITIES Capabilities; // h/w mtrr Capabilities
+ UCHAR DefaultCachedType; // default type for MmCached
+
+ //
+ // Variable MTRR information
+ //
+
+ BOOLEAN RangesValid; // Ranges initialized and valid.
+ BOOLEAN MtrrWorkaround; // Work Around needed/not.
+ UCHAR NoRange; // No ranges currently in Ranges
+ UCHAR MaxRange; // Max size of Ranges
+ PONE_RANGE Ranges; // Current ranges as set into h/w
+
+} RANGE_INFO, *PRANGE_INFO;
+
+
+//
+// Structure used while processing range database
+//
+
+typedef struct _NEW_RANGE {
+ //
+ // Current Status
+ //
+
+ NTSTATUS Status;
+
+ //
+ // Generic info on new range
+ //
+
+ ULONGLONG Base;
+ ULONGLONG Limit;
+ UCHAR Type;
+
+ //
+ // MTRR image to be set into h/w
+ //
+
+ PMTRR_RANGE MTRR;
+
+ //
+ // RangeDatabase before edits were started
+ //
+
+ UCHAR NoRange;
+ PONE_RANGE Ranges;
+
+ //
+ // IPI context to coordinate concurrent processor update
+ //
+
+ ULONG NoMTRR;
+ ULONG Processor;
+ volatile ULONG TargetCount;
+ volatile ULONG *TargetPhase;
+
+} NEW_RANGE, *PNEW_RANGE;
+
+//
+// Prototypes
+//
+
+VOID
+KiInitializeMTRR (
+ IN BOOLEAN LastProcessor
+ );
+
+BOOLEAN
+KiRemoveRange (
+ IN PNEW_RANGE NewRange,
+ IN ULONGLONG Base,
+ IN ULONGLONG Limit,
+ IN PBOOLEAN RemoveThisType
+ );
+
+VOID
+KiAddRange (
+ IN PNEW_RANGE NewRange,
+ IN ULONGLONG Base,
+ IN ULONGLONG Limit,
+ IN UCHAR Type
+ );
+
+VOID
+KiStartEffectiveRangeChange (
+ IN PNEW_RANGE NewRange
+ );
+
+VOID
+KiCompleteEffectiveRangeChange (
+ IN PNEW_RANGE NewRange
+ );
+
+STATIC ULONG
+KiRangeWeight (
+ IN PONE_RANGE Range
+ );
+
+STATIC ULONG
+KiFindFirstSetLeftBit (
+ IN ULONGLONG Set
+ );
+
+STATIC ULONG
+KiFindFirstSetRightBit (
+ IN ULONGLONG Set
+ );
+
+
+VOID
+KiLoadMTRRTarget (
+ IN PKIPI_CONTEXT SignalDone,
+ IN PVOID Context,
+ IN PVOID Parameter2,
+ IN PVOID Parameter3
+ );
+
+NTSTATUS
+KiLoadMTRR (
+ IN PNEW_RANGE Context
+ );
+
+VOID
+KiSynchronizeMTRRLoad (
+ IN PNEW_RANGE Context
+ );
+
+ULONGLONG
+KiMaskToLength (
+ IN ULONGLONG Mask
+ );
+
+ULONGLONG
+KiLengthToMask (
+ IN ULONGLONG Length
+ );
+
+#if IDBG
+VOID
+KiDumpMTRR (
+ PUCHAR DebugString,
+ PMTRR_RANGE MTRR
+ );
+#endif
+
+#ifdef ALLOC_PRAGMA
+#pragma alloc_text(INIT,KiInitializeMTRR)
+#pragma alloc_text(PAGELK,KiRemoveRange)
+#pragma alloc_text(PAGELK,KiAddRange)
+#pragma alloc_text(PAGELK,KiStartEffectiveRangeChange)
+#pragma alloc_text(PAGELK,KiCompleteEffectiveRangeChange)
+#pragma alloc_text(PAGELK,KiRangeWeight)
+#pragma alloc_text(PAGELK,KiFindFirstSetLeftBit)
+#pragma alloc_text(PAGELK,KiFindFirstSetRightBit)
+#pragma alloc_text(PAGELK,KiLoadMTRR)
+#pragma alloc_text(PAGELK,KiLoadMTRRTarget)
+#pragma alloc_text(PAGELK,KiSynchronizeMTRRLoad)
+#pragma alloc_text(PAGELK,KiLengthToMask)
+#pragma alloc_text(PAGELK,KiMaskToLength)
+
+#if IDBG
+#pragma alloc_text(PAGELK,KiDumpMTRR)
+#endif
+
+#endif
+
+//
+// KiRangeLock - Used to synchronize accesses to KiRangeInfo
+//
+
+KSPIN_LOCK KiRangeLock;
+
+//
+// KiRangeInfo - Range type mapping inforation. Details specific h/w support
+// and contains the current range database of how physical
+// addresses have been set
+
+RANGE_INFO KiRangeInfo;
+
+
+BOOLEAN bUseFrameBufferCaching;
+
+VOID
+KiInitializeMTRR (
+ IN BOOLEAN LastProcessor
+ )
+/*++
+
+Routine Description:
+
+ Called to incrementally initialize the physical range
+ database feature. First processor's MTRR set is read into the
+ physical range database.
+
+Arguments:
+
+ LastProcessor - If set this is the last processor to execute this routine
+ such that when this processor finishes, the initialization is complete.
+
+Return Value:
+
+ None - if there was a problem the function
+ KeSetPhysicalCacheTypeRange type is disabled.
+
+--*/
+{
+ BOOLEAN Status;
+ ULONG Index, Size;
+ MTRR_DEFAULT Default;
+ MTRR_CAPABILITIES Capabilities;
+ NEW_RANGE NewRange;
+ MTRR_VARIABLE_BASE MtrrBase;
+ MTRR_VARIABLE_MASK MtrrMask;
+ ULONGLONG Base, Mask, Length;
+ BOOLEAN RemoveThisType[MTRR_TYPE_MAX];
+ NTSTATUS NtStatus;
+ PKPRCB Prcb;
+
+ Status = TRUE;
+ RtlZeroMemory (&NewRange, sizeof (NewRange));
+ NewRange.Status = STATUS_UNSUCCESSFUL;
+
+ //
+ // If this is the first processor, initialize some fields
+ //
+
+ if (KeGetPcr()->Number == 0) {
+ KeInitializeSpinLock (&KiRangeLock);
+
+ KiRangeInfo.Capabilities.u.QuadPart = RDMSR(MTRR_MSR_CAPABILITIES);
+ KiRangeInfo.Default.u.QuadPart = RDMSR(MTRR_MSR_DEFAULT);
+ KiRangeInfo.DefaultCachedType = MTRR_TYPE_MAX;
+
+ //
+ // If h/w mtrr support is not enabled, disable OS support
+ //
+
+ if (!KiRangeInfo.Default.u.hw.MtrrEnabled ||
+ KiRangeInfo.Capabilities.u.hw.VarCnt == 0) {
+ Status = FALSE;
+ } else {
+ if (KiRangeInfo.Capabilities.u.hw.UswcSupported) {
+ //
+ // If USWC type is supported by the hardware, check the HAL
+ // to see if we are on a "Shared Memory Cluster" machine and
+ // do not want USWC supported.
+ //
+ NtStatus = HalQuerySystemInformation(
+ HalFrameBufferCachingInformation,
+ sizeof (BOOLEAN),
+ &bUseFrameBufferCaching,
+ &Size
+ );
+
+ if (NT_SUCCESS(NtStatus)) {
+
+ if (bUseFrameBufferCaching == FALSE) {
+ DBGMSG ("KiInitializeMTRR: HAL set UseFrameBufferCaching FALSE\n");
+ KiRangeInfo.Capabilities.u.hw.UswcSupported = 0;
+ }
+ }
+ }
+ }
+
+ //
+ // Allocate initial range type database
+ //
+
+ KiRangeInfo.NoRange = 0;
+ KiRangeInfo.MaxRange = (UCHAR) KiRangeInfo.Capabilities.u.hw.VarCnt + GROW_RANGE_TABLE;
+ KiRangeInfo.Ranges = ExAllocatePool (NonPagedPool,
+ sizeof(ONE_RANGE) * KiRangeInfo.MaxRange);
+ RtlZeroMemory (KiRangeInfo.Ranges, sizeof(ONE_RANGE) * KiRangeInfo.MaxRange);
+ }
+
+ //
+ // Workaround for cpu signatures 611, 612, 616 and 617
+ // - if the request for setting a variable MTRR specifies
+ // an address which is not 4M aligned or length is not
+ // a multiple of 4M then possible problem for INVLPG inst.
+ // Detect if workaround is required
+ //
+
+ Prcb = KeGetCurrentPrcb();
+ if (Prcb->CpuType == 6 &&
+ (Prcb->CpuStep == 0x0101 || Prcb->CpuStep == 0x0102 ||
+ Prcb->CpuStep == 0x0106 || Prcb->CpuStep == 0x0107 )) {
+
+ KiRangeInfo.MtrrWorkaround = TRUE;
+ }
+
+ //
+ // If MTRR support disabled on first processor or if
+ // buffer not allocated then fall through
+ //
+
+ if (!KiRangeInfo.Ranges){
+ Status = FALSE;
+ } else {
+
+ //
+ // Verify MTRR support is symmetric
+ //
+
+ Capabilities.u.QuadPart = RDMSR(MTRR_MSR_CAPABILITIES);
+
+ if ((Capabilities.u.hw.UswcSupported) &&
+ (bUseFrameBufferCaching == FALSE)) {
+ DBGMSG ("KiInitializeMTRR: setting UswcSupported FALSE\n");
+ Capabilities.u.hw.UswcSupported = 0;
+ }
+
+ Default.u.QuadPart = RDMSR(MTRR_MSR_DEFAULT);
+
+ if (Default.u.QuadPart != KiRangeInfo.Default.u.QuadPart ||
+ Capabilities.u.QuadPart != KiRangeInfo.Capabilities.u.QuadPart) {
+ DBGMSG ("KiInitializeMTRR: asymmtric mtrr support\n");
+ Status = FALSE;
+ }
+ }
+
+ NewRange.Status = STATUS_SUCCESS;
+
+ //
+ // MTRR registers should be identically set on each processor.
+ // Ranges should be added to the range database only for one
+ // processor.
+ //
+
+ if (Status && (KeGetPcr()->Number == 0)) {
+#if IDBG
+ KiDumpMTRR ("Processor MTRR:", NULL);
+#endif
+
+ //
+ // Read current MTRR settings for various cached range types
+ // and add them to the range database
+ //
+
+ for (Index=0; Index < Capabilities.u.hw.VarCnt; Index++) {
+
+ MtrrBase.u.QuadPart = RDMSR(MTRR_MSR_VARIABLE_BASE+Index*2);
+ MtrrMask.u.QuadPart = RDMSR(MTRR_MSR_VARIABLE_MASK+Index*2);
+
+ Mask = MtrrMask.u.QuadPart & MTRR_MASK_MASK;
+ Base = MtrrBase.u.QuadPart & MTRR_MASK_BASE;
+
+ //
+ // Note - the variable MTRR Mask does NOT contain the length
+ // spanned by the variable MTRR. Thus just checking the Valid
+ // Bit should be sufficient for identifying a valid MTRR.
+ //
+
+ if (MtrrMask.u.hw.Valid) {
+
+ Length = KiMaskToLength(Mask);
+
+ //
+ // Check for non-contigous MTRR mask.
+ //
+
+ if ((Mask + Length) & MASK_OVERFLOW_MASK) {
+ DBGMSG ("KiInitializeMTRR: Found non-contiguous MTRR mask!\n");
+ Status = FALSE;
+ }
+
+ //
+ // If this is an uncachable range, handle it in the next pass
+ //
+
+ if (MtrrBase.u.hw.Type == MTRR_TYPE_UC) {
+ continue;
+ }
+
+ //
+ // Add this MTRR to the range database
+ //
+
+ Base &= Mask;
+ KiAddRange (
+ &NewRange,
+ Base,
+ Base + Length - 1,
+ (UCHAR) MtrrBase.u.hw.Type
+ );
+
+ //
+ // Check for default cache type
+ //
+
+ if (MtrrBase.u.hw.Type == MTRR_TYPE_WB) {
+ KiRangeInfo.DefaultCachedType = MTRR_TYPE_WB;
+ }
+
+ if (KiRangeInfo.DefaultCachedType == MTRR_TYPE_MAX &&
+ MtrrBase.u.hw.Type == MTRR_TYPE_WT) {
+ KiRangeInfo.DefaultCachedType = MTRR_TYPE_WT;
+ }
+ }
+ }
+
+ //
+ // Read current MTRR settings, for uncachable ranges and remove
+ // them from the range database
+ //
+
+ memset (RemoveThisType, TRUE, MTRR_TYPE_MAX);
+ for (Index=0; Index < Capabilities.u.hw.VarCnt; Index++) {
+
+ MtrrBase.u.QuadPart = RDMSR(MTRR_MSR_VARIABLE_BASE+Index*2);
+ MtrrMask.u.QuadPart = RDMSR(MTRR_MSR_VARIABLE_MASK+Index*2);
+
+ Mask = MtrrMask.u.QuadPart & MTRR_MASK_MASK;
+ Base = MtrrBase.u.QuadPart & MTRR_MASK_BASE;
+
+ if (MtrrMask.u.hw.Valid && MtrrBase.u.hw.Type == MTRR_TYPE_UC) {
+
+ //
+ // Remove uncachable region from range database
+ //
+
+ Base &= Mask;
+ Length = KiMaskToLength(Mask);
+
+ KiRemoveRange (
+ &NewRange,
+ Base,
+ Base + Length - 1,
+ RemoveThisType
+ );
+ }
+ }
+
+ //
+ // If a default type for "cached" was not found, assume write-back
+ //
+
+ if (KiRangeInfo.DefaultCachedType == MTRR_TYPE_MAX) {
+ DBGMSG ("KiInitalizeMTRR: assume write-back\n");
+ KiRangeInfo.DefaultCachedType = MTRR_TYPE_WB;
+ }
+ }
+
+ //
+ // Done
+ //
+
+ if (!NT_SUCCESS(NewRange.Status)) {
+ Status = FALSE;
+ }
+
+ if (!Status) {
+ DBGMSG ("KiInitializeMTRR: OS support for MTRRs disabled\n");
+ if (KiRangeInfo.Ranges != NULL) {
+ ExFreePool (KiRangeInfo.Ranges);
+ KiRangeInfo.Ranges = NULL;
+ }
+ } else {
+
+ // if last processor indicate initialization complete
+ if (LastProcessor) {
+ KiRangeInfo.RangesValid = TRUE;
+ }
+ }
+}
+
+NTSTATUS
+KeSetPhysicalCacheTypeRange (
+ IN PHYSICAL_ADDRESS PhysicalAddress,
+ IN ULONG NumberOfBytes,
+ IN MEMORY_CACHING_TYPE CacheType
+ )
+/*++
+
+Routine Description:
+
+ This function sets a physical range to a particular cache type.
+ If the system does not support setting cache policies based on
+ physical ranges, no action is taken.
+
+Arguments:
+
+ PhysicalAddress - The starting address of the range being set
+
+ NumberOfBytes - The length, in bytes, of the range being set
+
+ CacheType - The caching type for which the physical range is
+ to be set to.
+
+ NonCached:
+ Setting ranges to be NonCached is done for
+ book keeping reasons. A return of SUCCESS when
+ setting a range NonCached does not mean it has
+ been physically set to as NonCached. The caller
+ must use a cache-disabled virtual pointer for
+ any NonCached range.
+
+ Cached:
+ A sucessful return indicates that the physical
+ range has been set to cache. This mode requires
+ the caller to be at irql < dispatch_level.
+
+ FrameBuffer:
+ A sucessful return indicates that the physical
+ range has been set to be framebuffer cached.
+ This mode requires the caller to be at irql <
+ dispatch_level.
+
+Return Value:
+
+ STATUS_SUCCESS - if success, the cache attributes of the physical range
+ have been set (or feature not supported or not yet
+ initialized).
+
+ STATUS_NO_SUCH_DEVICE - if FrameBuffer type is requested and is not
+ supported.
+
+ STATUS_UNSUCCESSFUL/STATUS_INTERNAL_ERROR - Requested Physical Range is
+ below 1M or other error.
+
+--*/
+{
+ KIRQL OldIrql;
+ NEW_RANGE NewRange;
+ BOOLEAN RemoveThisType[MTRR_TYPE_MAX];
+ BOOLEAN EffectRangeChange, AddToRangeDatabase;
+
+ //
+ // If processor doesn't have the memory type range feature, or
+ // if request isn't supported return not supported.
+ //
+
+ if (!KiRangeInfo.RangesValid ||
+ PhysicalAddress.LowPart < 1 * 1024 * 1024) {
+
+ return STATUS_NOT_SUPPORTED;
+ }
+
+ //
+ // Workaround for cpu signatures 611, 612, 616 and 617
+ // - if the request for setting a variable MTRR specifies
+ // an address which is not 4M aligned or length is not
+ // a multiple of 4M then return status not supported
+ //
+
+ if ((KiRangeInfo.MtrrWorkaround) &&
+ ((PhysicalAddress.LowPart & 0x3fffff) ||
+ (NumberOfBytes & 0x3fffff))) {
+
+ return STATUS_NOT_SUPPORTED;
+ }
+
+
+ ASSERT (NumberOfBytes != 0);
+
+ RtlZeroMemory (&NewRange, sizeof (NewRange));
+ NewRange.Base = PhysicalAddress.QuadPart;
+ NewRange.Limit = NewRange.Base + NumberOfBytes - 1;
+
+ //
+ // Determine what the new mtrr range type is. If setting NonCached then
+ // the database need not be updated to reflect the virtual change. This
+ // is because non-cached virtual pointers are mapped as cache disabled.
+ //
+
+ EffectRangeChange = TRUE;
+ AddToRangeDatabase = TRUE;
+ switch (CacheType) {
+ case MmNonCached:
+ NewRange.Type = MTRR_TYPE_UC;
+
+ //
+ // NonCached ranges do not need to be reflected into the h/w state
+ // as all non-cached ranges are mapped with cache-disabled pointers.
+ // This also means that cache-disabled ranges do not need to
+ // be put into mtrrs, or held in the range, regardless of the default
+ // range type.
+ //
+
+ EffectRangeChange = FALSE;
+ AddToRangeDatabase = FALSE;
+ break;
+
+ case MmCached:
+ NewRange.Type = KiRangeInfo.DefaultCachedType;
+ break;
+
+ case MmFrameBufferCached:
+ NewRange.Type = MTRR_TYPE_USWC;
+
+ //
+ // If USWC type isn't supported, then request can not be honored
+ //
+
+ if (!KiRangeInfo.Capabilities.u.hw.UswcSupported) {
+ DBGMSG ("KeSetPhysicalCacheTypeRange: USWC not supported\n");
+ return STATUS_NOT_SUPPORTED;
+ }
+ break;
+
+ default:
+ DBGMSG ("KeSetPhysicalCacheTypeRange: no such cache type\n");
+ return STATUS_INVALID_PARAMETER;
+ break;
+ }
+
+ NewRange.Status = STATUS_SUCCESS;
+
+ ASSERT(KiRangeInfo.Default.u.hw.Type == MTRR_TYPE_UC);
+
+ //
+ // The default type is UC thus the range is still mapped using
+ // a Cache Disabled VirtualPointer and hence it need not be added.
+ //
+
+ //
+ // If h/w needs updated, lock down the code required to effect the change
+ //
+
+ if (EffectRangeChange) {
+ if (KeGetCurrentIrql() == DISPATCH_LEVEL) {
+
+ //
+ // Code can not be locked down. Supplying a new range type requires
+ // that the caller calls at irql < dispatch_level.
+ //
+
+ DBGMSG ("KeSetPhysicalCacheTypeRange failed due to calling IRQL == DISPATCH_LEVEL\n");
+ return STATUS_UNSUCCESSFUL;
+ }
+
+ MmLockPagableSectionByHandle(ExPageLockHandle);
+ }
+
+ //
+ // Serialize the range type database
+ //
+
+ KeAcquireSpinLock (&KiRangeLock, &OldIrql);
+
+ //
+ // If h/w is going to need updated, then start an effective range change
+ //
+
+ if (EffectRangeChange) {
+ KiStartEffectiveRangeChange (&NewRange);
+ }
+
+ if (NT_SUCCESS (NewRange.Status)) {
+
+ //
+ // If the new range is NonCached, then don't remove standard memory
+ // cahcing types
+ //
+
+ memset (RemoveThisType, TRUE, MTRR_TYPE_MAX);
+ if (NewRange.Type != MTRR_TYPE_UC) {
+ //
+ // If the requested type is uncached then the physical
+ // memory region is mapped using a cache disabled virtual pointer.
+ // The effective memory type for that region will be the lowest
+ // common denominator of the MTRR type and the cache type in the
+ // PTE. Therefore for a request of type UC, the effective type
+ // will be UC irrespective of the MTRR settings in that range.
+ // Hence it is not necessary to remove the existing MTRR settings
+ // (if any) for that range.
+ //
+
+ //
+ // Clip/remove any ranges in the target area
+ //
+
+ KiRemoveRange (&NewRange, NewRange.Base, NewRange.Limit, RemoveThisType);
+ }
+
+ //
+ // If needed, add new range type
+ //
+
+ if (AddToRangeDatabase) {
+ ASSERT (EffectRangeChange == TRUE);
+ KiAddRange (&NewRange, NewRange.Base, NewRange.Limit, NewRange.Type);
+ }
+
+ //
+ // If this is an effect range change, then complete it
+ //
+
+ if (EffectRangeChange) {
+ KiCompleteEffectiveRangeChange (&NewRange);
+ }
+ }
+
+ KeReleaseSpinLock (&KiRangeLock, OldIrql);
+ if (EffectRangeChange) {
+ MmUnlockPagableImageSection(ExPageLockHandle);
+ }
+
+ return NewRange.Status;
+}
+
+BOOLEAN
+KiRemoveRange (
+ IN PNEW_RANGE NewRange,
+ IN ULONGLONG Base,
+ IN ULONGLONG Limit,
+ IN PBOOLEAN RemoveThisType
+ )
+/*++
+
+Routine Description:
+
+ This function removes any range overlapping with the passed range, of
+ type supplied in RemoveThisType from the global range database.
+
+Arguments:
+
+ NewRange - Context information
+
+ Base - Base & Limit signify the first & last address of a range
+ Limit - which is to be removed from the range database
+
+ RemoveThisType - A TRUE flag for each type which can not overlap the
+ target range
+
+
+Return Value:
+
+ TRUE - if the range database was altered such that it may no longer
+ be sorted.
+
+--*/
+{
+ ULONG i;
+ PONE_RANGE Range;
+ BOOLEAN DatabaseNeedsSorted;
+
+
+ DatabaseNeedsSorted = FALSE;
+
+ //
+ // Check each range
+ //
+
+ for (i=0, Range=KiRangeInfo.Ranges; i < KiRangeInfo.NoRange; i++, Range++) {
+
+ //
+ // If this range type doesn't need to be altered, skip it
+ //
+
+ if (!RemoveThisType[Range->Type]) {
+ continue;
+ }
+
+ //
+ // Check range to see if it overlaps with range being removed
+ //
+
+ if (Range->Base < Base) {
+
+ if (Range->Limit >= Base && Range->Limit <= Limit) {
+
+ //
+ // Truncate range to not overlap with area being removed
+ //
+
+ Range->Limit = Base - 1;
+ }
+
+ if (Range->Limit > Limit) {
+
+ //
+ // Target area is contained totally within this area.
+ // Split into two ranges
+ //
+
+ //
+ // Add range at end
+ //
+
+ DatabaseNeedsSorted = TRUE;
+ KiAddRange (
+ NewRange,
+ Limit+1,
+ Range->Limit,
+ Range->Type
+ );
+
+ //
+ // Turn current range into range at begining
+ //
+
+ Range->Limit = Base - 1;
+ }
+
+ } else {
+
+ // Range->Base >= Base
+
+ if (Range->Base <= Limit) {
+ if (Range->Limit <= Limit) {
+ //
+ // This range is totally within the target area. Remove it.
+ //
+
+ DatabaseNeedsSorted = TRUE;
+ KiRangeInfo.NoRange -= 1;
+ Range->Base = KiRangeInfo.Ranges[KiRangeInfo.NoRange].Base;
+ Range->Limit = KiRangeInfo.Ranges[KiRangeInfo.NoRange].Limit;
+ Range->Type = KiRangeInfo.Ranges[KiRangeInfo.NoRange].Type;
+
+ //
+ // recheck at current location
+ //
+
+ i -= 1;
+ Range -= 1;
+
+ } else {
+
+ //
+ // Bump begining past area being removed
+ //
+
+ Range->Base = Limit + 1;
+ }
+ }
+ }
+ }
+
+ if (!NT_SUCCESS (NewRange->Status)) {
+ DBGMSG ("KiRemoveRange: failure\n");
+ }
+
+ return DatabaseNeedsSorted;
+}
+
+
+VOID
+KiAddRange (
+ IN PNEW_RANGE NewRange,
+ IN ULONGLONG Base,
+ IN ULONGLONG Limit,
+ IN UCHAR Type
+ )
+/*++
+
+Routine Description:
+
+ This function adds the passed range to the global range database.
+
+Arguments:
+
+ NewRange - Context information
+
+ Base - Base & Limit signify the first & last address of a range
+ Limit - which is to be added to the range database
+
+ Type - Type of caching required for this range
+
+Return Value:
+
+ None - Context is updated with an error if the table has overflowed
+
+--*/
+{
+ PONE_RANGE Range, OldRange;
+ ULONG size;
+
+ if (KiRangeInfo.NoRange >= KiRangeInfo.MaxRange) {
+
+ //
+ // Table is out of space, get a bigger one
+ //
+
+ OldRange = KiRangeInfo.Ranges;
+ size = sizeof(ONE_RANGE) * (KiRangeInfo.MaxRange + GROW_RANGE_TABLE);
+ Range = ExAllocatePool (NonPagedPool, size);
+
+ if (!Range) {
+ NewRange->Status = STATUS_INSUFFICIENT_RESOURCES;
+ return ;
+ }
+
+ //
+ // Grow table
+ //
+
+ RtlZeroMemory (Range, size);
+ RtlCopyMemory (Range, OldRange, sizeof(ONE_RANGE) * KiRangeInfo.MaxRange);
+ KiRangeInfo.Ranges = Range;
+ KiRangeInfo.MaxRange += GROW_RANGE_TABLE;
+ ExFreePool (OldRange);
+ }
+
+ //
+ // Add new entry to table
+ //
+
+ KiRangeInfo.Ranges[KiRangeInfo.NoRange].Base = Base;
+ KiRangeInfo.Ranges[KiRangeInfo.NoRange].Limit = Limit;
+ KiRangeInfo.Ranges[KiRangeInfo.NoRange].Type = Type;
+ KiRangeInfo.NoRange += 1;
+}
+
+
+VOID
+KiStartEffectiveRangeChange (
+ IN PNEW_RANGE NewRange
+ )
+/*++
+
+Routine Description:
+
+ This functions sets up the context information required to
+ track & later effect a range change in hardware
+
+Arguments:
+
+ NewRange - Context information
+
+Return Value:
+
+ None
+
+--*/
+{
+ ULONG size;
+
+ //
+ // Allocate working space for MTRR image
+ //
+
+ size = sizeof(MTRR_RANGE) * ((ULONG) KiRangeInfo.Capabilities.u.hw.VarCnt + 1);
+ NewRange->MTRR = ExAllocatePool (NonPagedPool, size);
+ if (!NewRange->MTRR) {
+ NewRange->Status = STATUS_INSUFFICIENT_RESOURCES;
+ return ;
+ }
+
+ RtlZeroMemory (NewRange->MTRR, size);
+
+ //
+ // Save current range information in case of an error
+ //
+
+ size = sizeof(ONE_RANGE) * KiRangeInfo.NoRange;
+ NewRange->NoRange = KiRangeInfo.NoRange;
+ NewRange->Ranges = ExAllocatePool (NonPagedPool, size);
+ if (!NewRange->Ranges) {
+ NewRange->Status = STATUS_INSUFFICIENT_RESOURCES;
+ return ;
+ }
+
+ RtlCopyMemory (NewRange->Ranges, KiRangeInfo.Ranges, size);
+}
+
+
+VOID
+KiCompleteEffectiveRangeChange (
+ IN PNEW_RANGE NewRange
+ )
+/*++
+
+Routine Description:
+
+ This functions commits the range database to hardware, or backs
+ out the current changes to it.
+
+Arguments:
+
+ NewRange - Context information
+
+Return Value:
+
+ None
+
+--*/
+{
+ BOOLEAN Restart;
+ ULONG Index, Index2, NoMTRR;
+ ULONGLONG BestLength, WhichMtrr;
+ ULONGLONG CurrLength;
+ ULONGLONG l, Base, Length, MLength;
+ PONE_RANGE Range;
+ ONE_RANGE OneRange;
+ PMTRR_RANGE MTRR;
+ BOOLEAN RoundDown;
+ BOOLEAN RemoveThisType[MTRR_TYPE_MAX];
+ PKPRCB Prcb;
+ KIRQL OldIrql, OldIrql2;
+ KAFFINITY TargetProcessors;
+
+
+ ASSERT (KeGetCurrentIrql() == DISPATCH_LEVEL);
+ Prcb = KeGetCurrentPrcb();
+
+ //
+ // Round all ranges, according to type, to match what h/w can support
+ //
+
+ for (Index=0; Index < KiRangeInfo.NoRange; Index++) {
+ Range = &KiRangeInfo.Ranges[Index];
+
+ //
+ // Determine rounding for this range type
+ //
+
+ RoundDown = TRUE;
+ if (Range->Type == MTRR_TYPE_UC) {
+ RoundDown = FALSE;
+ }
+
+ //
+ // Apply rounding
+ //
+
+ if (RoundDown) {
+ Range->Base = (Range->Base + MTRR_PAGE_SIZE - 1) & MTRR_PAGE_MASK;
+ Range->Limit = ((Range->Limit+1) & MTRR_PAGE_MASK)-1;
+ } else {
+ Range->Base = (Range->Base & MTRR_PAGE_MASK);
+ Range->Limit = ((Range->Limit + MTRR_PAGE_SIZE) & MTRR_PAGE_MASK)-1;
+ }
+ }
+
+ do {
+ Restart = FALSE;
+
+ //
+ // Sort the ranges by base address
+ //
+
+ for (Index=0; Index < KiRangeInfo.NoRange; Index++) {
+ Range = &KiRangeInfo.Ranges[Index];
+
+ for (Index2=Index+1; Index2 < KiRangeInfo.NoRange; Index2++) {
+
+ if (KiRangeInfo.Ranges[Index2].Base < Range->Base) {
+
+ //
+ // Swap KiRangeInfo.Ranges[Index] with KiRangeInfo.Ranges[Index2]
+ //
+
+ OneRange = *Range;
+ *Range = KiRangeInfo.Ranges[Index2];
+ KiRangeInfo.Ranges[Index2] = OneRange;
+ }
+ }
+ }
+
+ //
+ // Check for adjacent/overlapping ranges
+ //
+
+ for (Index=0; Index < (ULONG) KiRangeInfo.NoRange-1 && !Restart; Index++) {
+ Range = &KiRangeInfo.Ranges[Index];
+
+ l = Range[0].Limit + 1;
+ if (l < Range[0].Limit) {
+ l = Range[0].Limit;
+ }
+
+ //
+ // If ranges overlap or are adjacent and are of the same type, combine them
+ //
+
+ if (l >= Range[1].Base && Range[0].Type == Range[1].Type) {
+
+ //
+ // Increase Range[0] limit to cover Range[1]
+ //
+
+ if (Range[1].Limit > Range[0].Limit) {
+ Range[0].Limit = Range[1].Limit;
+ }
+
+ //
+ // Remove Range[1]
+ //
+
+ if (Index+2 < KiRangeInfo.NoRange) {
+
+ //
+ // Copy everything from current index till end
+ // of range list.
+ //
+
+ RtlCopyMemory(
+ &(Range[1]),
+ &(Range[2]),
+ sizeof(ONE_RANGE) * (KiRangeInfo.NoRange-Index-2)
+ );
+ }
+
+ KiRangeInfo.NoRange -= 1;
+
+ //
+ // Recheck current location
+ //
+
+ Index -= 1;
+ continue;
+ }
+
+ //
+ // If ranges overlap and are not of same type,
+ // then carve them to the best cache type available.
+ //
+
+ if (l > Range[1].Base) {
+
+ //
+ // Pick range which has the cache type which should be used for
+ // the overlapped area
+ //
+
+ Index2 = KiRangeWeight(&Range[0]) > KiRangeWeight(&Range[1]) ? 0 : 1;
+
+ //
+ // Remove ranges of type which do not belong in the overlapped area
+ //
+
+ RtlZeroMemory (RemoveThisType, MTRR_TYPE_MAX);
+ RemoveThisType[Range[Index2 ^ 1].Type] = TRUE;
+
+ //
+ // Remove just the overlapped portion of the range.
+ //
+
+ Restart = KiRemoveRange (
+ NewRange,
+ Range[1].Base,
+ (Range[0].Limit < Range[1].Limit ? Range[0].Limit : Range[1].Limit),
+ RemoveThisType
+ );
+
+ }
+ }
+ } while (Restart);
+
+ //
+ // The range database is now rounded to fit in the h/w and sorted.
+ // Attempt to build MTRR settings which exactly describe the ranges
+ //
+
+ MTRR = NewRange->MTRR;
+ NoMTRR = 0;
+ for (Index=0;NT_SUCCESS(NewRange->Status)&& Index<KiRangeInfo.NoRange;Index++) {
+ Range = &KiRangeInfo.Ranges[Index];
+
+ //
+ // Build MTRRs to fit this range
+ //
+
+ Base = Range->Base;
+ Length = Range->Limit - Base + 1;
+
+ while (Length) {
+
+ //
+ // Compute MTRR length for current range base & length
+ //
+
+ if (Base == 0) {
+ MLength = Length;
+ } else {
+ MLength = (ULONGLONG) 1 << KiFindFirstSetRightBit(Base);
+ }
+ if (MLength > Length) {
+ MLength = Length;
+ }
+
+ l = (ULONGLONG) 1 << KiFindFirstSetLeftBit (MLength);
+ if (MLength > l) {
+ MLength = l;
+ }
+
+ //
+ // Store it in the next MTRR
+ //
+
+ MTRR[NoMTRR].Base.u.QuadPart = Base;
+ MTRR[NoMTRR].Base.u.hw.Type = Range->Type;
+ MTRR[NoMTRR].Mask.u.QuadPart = KiLengthToMask(MLength);
+ MTRR[NoMTRR].Mask.u.hw.Valid = 1;
+ NoMTRR += 1;
+
+ //
+ // Adjust off amount of data covered by that last MTRR
+ //
+
+ Base += MLength;
+ Length -= MLength;
+
+ //
+ // If there are too many MTRRs, try to removing a USWC MTRR.
+ // (ie, convert some MmFrameBufferCached to MmNonCached).
+ //
+
+ if (NoMTRR > (ULONG) KiRangeInfo.Capabilities.u.hw.VarCnt) {
+
+ //
+ // Find smallest USWC type and drop it
+ //
+ // This is okay only if the default type is C.
+ // Default type should always be UC unless BIOS changes
+ // it. Still ASSERT!
+ //
+
+ ASSERT(KiRangeInfo.Default.u.hw.Type == MTRR_TYPE_UC);
+
+ BestLength = (ULONGLONG) 1 << (MTRR_MAX_RANGE_SHIFT + 1);
+ for (Index2=0; Index2 < NoMTRR; Index2++) {
+
+ if (MTRR[Index2].Base.u.hw.Type == MTRR_TYPE_USWC) {
+
+ CurrLength = KiMaskToLength(MTRR[Index2].Mask.u.QuadPart &
+ MTRR_MASK_MASK);
+
+ if (CurrLength < BestLength) {
+ WhichMtrr = Index2;
+ BestLength = Length;
+ }
+ }
+ }
+
+ if (BestLength == ((ULONGLONG) 1 << (MTRR_MAX_RANGE_SHIFT + 1))) {
+ //
+ // Range was not found which could be dropped. Abort process
+ //
+
+ NewRange->Status = STATUS_INSUFFICIENT_RESOURCES;
+ Length = 0;
+
+ } else {
+ //
+ // Remove WhichMtrr
+ //
+
+ MTRR[WhichMtrr] = MTRR[NoMTRR];
+ NoMTRR -= 1;
+ }
+ }
+ }
+ }
+
+ //
+ // Done building new MTRRs
+ //
+
+ if (NT_SUCCESS(NewRange->Status)) {
+
+ //
+ // Update the MTRRs on all processors
+ //
+
+#if IDBG
+ KiDumpMTRR ("Loading the following MTRR:", NewRange->MTRR);
+#endif
+
+ NewRange->TargetCount = 0;
+ NewRange->TargetPhase = &Prcb->ReverseStall;
+ NewRange->Processor = Prcb->Number;
+
+ //
+ // Previously enabled MTRR's with index > NoMTRR
+ // which could conflict with existing setting should be disabled
+ // This is taken care of by setting NewRange->NoMTRR to total
+ // number of variable MTRR's.
+ //
+
+ NewRange->NoMTRR = (ULONG) KiRangeInfo.Capabilities.u.hw.VarCnt;
+
+ //
+ // Synchronize with other IPI functions which may stall
+ //
+
+ KiLockContextSwap(&OldIrql);
+
+#if !defined(NT_UP)
+ //
+ // Collect all the (other) processors
+ //
+
+ TargetProcessors = KeActiveProcessors & ~Prcb->SetMember;
+ if (TargetProcessors != 0) {
+
+ KiIpiSendPacket (
+ TargetProcessors,
+ KiLoadMTRRTarget,
+ (PVOID) NewRange,
+ NULL,
+ NULL
+ );
+
+ //
+ // Wait for all processors to be collected
+ //
+
+ KiIpiStallOnPacketTargets();
+
+ //
+ // All processors are now waiting. Rasie to high level to
+ // ensure this processor doesn't enter the debugger due to
+ // some interrupt service routine.
+ //
+
+ KeRaiseIrql (HIGH_LEVEL, &OldIrql2);
+
+ //
+ // There's no reason for any debug events now, so signal
+ // the other processors that they can all disable interrupts
+ // and being the MTRR update
+ //
+
+ Prcb->ReverseStall += 1;
+ }
+#endif
+
+ //
+ // Update MTRRs
+ //
+
+ KiLoadMTRR (NewRange);
+
+ //
+ // Release ContextSwap lock
+ //
+
+ KiUnlockContextSwap(OldIrql);
+
+
+#if IDBG
+ KiDumpMTRR ("Processor MTRR:", NewRange->MTRR);
+#endif
+
+ } else {
+
+ //
+ // There was an error, put original range database back
+ //
+
+ DBGMSG ("KiCompleteEffectiveRangeChange: mtrr update did not occur\n");
+
+ if (NewRange->Ranges) {
+ KiRangeInfo.NoRange = NewRange->NoRange;
+
+ RtlCopyMemory (
+ KiRangeInfo.Ranges,
+ NewRange->Ranges,
+ sizeof (ONE_RANGE) * KiRangeInfo.NoRange
+ );
+ }
+ }
+
+ //
+ // Cleanup
+ //
+
+ ExFreePool (NewRange->Ranges);
+ ExFreePool (NewRange->MTRR);
+}
+
+
+STATIC ULONG
+KiRangeWeight (
+ IN PONE_RANGE Range
+ )
+/*++
+
+Routine Description:
+
+ This functions returns a weighting of the passed in range's cache
+ type. When two or more regions collide within the same h/w region
+ the types are weighted and that cache type of the higher weight
+ is used for the collision area.
+
+Arguments:
+
+ Range - Range to obtain weighting for
+
+Return Value:
+
+ The weight of the particular cache type
+
+--*/
+{
+ ULONG Weight;
+
+ switch (Range->Type) {
+ case MTRR_TYPE_UC: Weight = 5; break;
+ case MTRR_TYPE_USWC: Weight = 4; break;
+ case MTRR_TYPE_WP: Weight = 3; break;
+ case MTRR_TYPE_WT: Weight = 2; break;
+ case MTRR_TYPE_WB: Weight = 1; break;
+ default: Weight = 0; break;
+ }
+
+ return Weight;
+}
+
+
+STATIC ULONGLONG
+KiMaskToLength (
+ IN ULONGLONG Mask
+ )
+/*++
+
+Routine Description:
+
+ This function returns the length specified by a particular
+ mtrr variable register mask.
+
+--*/
+{
+ if (Mask == 0) {
+ // Zero Mask signifies a length of 2**36
+ return(((ULONGLONG) 1 << MTRR_MAX_RANGE_SHIFT));
+ } else {
+ return(((ULONGLONG) 1 << KiFindFirstSetRightBit(Mask)));
+ }
+}
+
+STATIC ULONGLONG
+KiLengthToMask (
+ IN ULONGLONG Length
+ )
+/*++
+
+Routine Description:
+
+ This function constructs the mask corresponding to the input length
+ to be set in a variable MTRR register. The length is assumed to be
+ a multiple of 4K.
+
+--*/
+{
+ ULONGLONG FullMask = 0xffffff;
+
+ if (Length == ((ULONGLONG) 1 << MTRR_MAX_RANGE_SHIFT)) {
+ return(0);
+ } else {
+ return(((FullMask << KiFindFirstSetRightBit(Length)) &
+ MTRR_RESVBIT_MASK));
+ }
+}
+
+STATIC ULONG
+KiFindFirstSetRightBit (
+ IN ULONGLONG Set
+ )
+/*++
+
+Routine Description:
+
+ This function returns a bit position of the least significant
+ bit set in the passed ULONGLONG parameter. Passed parameter
+ must be non-zero.
+
+--*/
+{
+ ULONG bitno;
+
+ ASSERT(Set != 0);
+ for (bitno=0; !(Set & 0xFF); bitno += 8, Set >>= 8) ;
+ return KiFindFirstSetRight[Set & 0xFF] + bitno;
+}
+
+STATIC ULONG
+KiFindFirstSetLeftBit (
+ IN ULONGLONG Set
+ )
+/*++
+
+Routine Description:
+
+ This function returns a bit position of the most significant
+ bit set in the passed ULONGLONG parameter. Passed parameter
+ must be non-zero.
+
+--*/
+{
+ ULONG bitno;
+
+ ASSERT(Set != 0);
+ for (bitno=56;!(Set & 0xFF00000000000000); bitno -= 8, Set <<= 8) ;
+ return KiFindFirstSetLeft[Set >> 56] + bitno;
+}
+
+#if IDBG
+VOID
+KiDumpMTRR (
+ PUCHAR DebugString,
+ PMTRR_RANGE MTRR
+ )
+/*++
+
+Routine Description:
+
+ This function dumps the MTRR information to the debugger
+
+--*/
+{
+ static PUCHAR Type[] = {
+ // 0 1 2 3 4 5 6
+ "UC ", "USWC", "????", "????", "WT ", "WP ", "WB " };
+ MTRR_VARIABLE_BASE Base;
+ MTRR_VARIABLE_MASK Mask;
+ ULONG Index;
+ ULONG i;
+ PUCHAR p;
+
+ DbgPrint ("%s\n", DebugString);
+ for (Index=0; Index < (ULONG) KiRangeInfo.Capabilities.u.hw.VarCnt; Index++) {
+ if (MTRR) {
+ Base = MTRR[Index].Base;
+ Mask = MTRR[Index].Mask;
+ } else {
+ Base.u.QuadPart = RDMSR(MTRR_MSR_VARIABLE_BASE+2*Index);
+ Mask.u.QuadPart = RDMSR(MTRR_MSR_VARIABLE_MASK+2*Index);
+ }
+
+ DbgPrint (" %d. ", Index);
+ if (Mask.u.hw.Valid) {
+ p = "????";
+ if (Base.u.hw.Type < 7) {
+ p = Type[Base.u.hw.Type];
+ }
+
+ DbgPrint ("%s %08x:%08x %08x:%08x",
+ p,
+ (ULONG) (Base.u.QuadPart >> 32),
+ ((ULONG) (Base.u.QuadPart & MTRR_MASK_BASE)),
+ (ULONG) (Mask.u.QuadPart >> 32),
+ ((ULONG) (Mask.u.QuadPart & MTRR_MASK_MASK))
+ );
+
+ }
+ DbgPrint ("\n");
+ }
+}
+#endif
+
+
+VOID
+KiLoadMTRRTarget (
+ IN PKIPI_CONTEXT SignalDone,
+ IN PVOID NewRange,
+ IN PVOID Parameter2,
+ IN PVOID Parameter3
+ )
+{
+ PNEW_RANGE Context;
+
+ Context = (PNEW_RANGE) NewRange;
+
+ //
+ // Wait for all processors to be ready
+ //
+
+ KiIpiSignalPacketDoneAndStall (SignalDone, Context->TargetPhase);
+
+ //
+ // Update MTRRs
+ //
+
+ KiLoadMTRR (Context);
+}
+
+
+
+#define MOV_EAX_CR4 _emit { 0Fh, 20h, E0h }
+#define MOV_CR4_EAX _emit { 0Fh, 22h, E0h }
+
+NTSTATUS
+KiLoadMTRR (
+ IN PNEW_RANGE Context
+ )
+/*++
+
+Routine Description:
+
+ This function loads the memory type range registers into all processors
+
+Arguments:
+
+ Context - Context which include the MTRRs to load
+
+Return Value:
+
+ All processors are set into the new state
+
+--*/
+{
+ MTRR_DEFAULT Default;
+ BOOLEAN Enable;
+ ULONG HldCr0, HldCr4;
+ ULONG Index;
+
+ //
+ // Disable interrupts
+ //
+
+ Enable = KiDisableInterrupts();
+
+ //
+ // Synchronize all processors
+ //
+
+ KiSynchronizeMTRRLoad (Context);
+
+ _asm {
+ ;
+ ; Get current CR0
+ ;
+
+ mov eax, cr0
+ mov HldCr0, eax
+
+ ;
+ ; Disable caching & line fill
+ ;
+
+ and eax, not CR0_NW
+ or eax, CR0_CD
+ mov cr0, eax
+
+ ;
+ ; Flush caches
+ ;
+
+ ;
+ ; wbinvd
+ ;
+
+ _emit 0Fh
+ _emit 09h
+
+ ;
+ ; Get current cr4
+ ;
+
+ _emit 0Fh
+ _emit 20h
+ _emit 0E0h ; mov eax, cr4
+ mov HldCr4, eax
+
+ ;
+ ; Disable global page
+ ;
+
+ and eax, not CR4_PGE
+ _emit 0Fh
+ _emit 22h
+ _emit 0E0h ; mov cr4, eax
+
+ ;
+ ; Flush TLB
+ ;
+
+ mov eax, cr3
+ mov cr3, eax
+ }
+
+ //
+ // Disable MTRRs
+ //
+
+ Default.u.QuadPart = RDMSR(MTRR_MSR_DEFAULT);
+ Default.u.hw.MtrrEnabled = 0;
+ WRMSR (MTRR_MSR_DEFAULT, Default.u.QuadPart);
+
+ //
+ // Synchronize all processors
+ //
+
+ KiSynchronizeMTRRLoad (Context);
+
+ //
+ // Load new MTRRs
+ //
+
+ for (Index=0; Index < Context->NoMTRR; Index++) {
+ WRMSR (MTRR_MSR_VARIABLE_BASE+2*Index, Context->MTRR[Index].Base.u.QuadPart);
+ WRMSR (MTRR_MSR_VARIABLE_MASK+2*Index, Context->MTRR[Index].Mask.u.QuadPart);
+ }
+
+ //
+ // Synchronize all processors
+ //
+
+ KiSynchronizeMTRRLoad (Context);
+
+ _asm {
+
+ ;
+ ; Flush caches (this should be a "nop", but it was in the Intel reference algorithm)
+ ; This is required because of aggressive prefetch of both instr + data
+ ;
+
+ ;
+ ; wbinvd
+ ;
+
+ _emit 0Fh
+ _emit 09h
+
+ ;
+ ; Flush TLBs (same comment as above)
+ ; Same explanation as above
+ ;
+
+ mov eax, cr3
+ mov cr3, eax
+ }
+
+ //
+ // Enable MTRRs
+ //
+
+ Default.u.hw.MtrrEnabled = 1;
+ WRMSR (MTRR_MSR_DEFAULT, Default.u.QuadPart);
+
+ //
+ // Synchronize all processors
+ //
+
+ KiSynchronizeMTRRLoad (Context);
+
+ _asm {
+ ;
+ ; Restore CR4 (global page enable)
+ ;
+
+ mov eax, HldCr4
+ _emit 0Fh
+ _emit 22h
+ _emit 0E0h ; mov cr4, eax
+
+ ;
+ ; Restore CR0 (cache enable)
+ ;
+
+ mov eax, HldCr0
+ mov cr0, eax
+ }
+
+ //
+ // Restore interrupts and return
+ //
+
+ KiRestoreInterrupts (Enable);
+ return STATUS_SUCCESS;
+}
+
+
+VOID
+KiSynchronizeMTRRLoad (
+ IN PNEW_RANGE Context
+ )
+{
+ ULONG CurrentPhase;
+ volatile ULONG *TargetPhase;
+ PKPRCB Prcb;
+
+ TargetPhase = Context->TargetPhase;
+ Prcb = KeGetCurrentPrcb();
+
+ if (Prcb->Number == (CCHAR) Context->Processor) {
+
+ //
+ // Wait for all processors to signal
+ //
+
+ while (Context->TargetCount != (ULONG) KeNumberProcessors - 1) ;
+
+ //
+ // Reset count for next time
+ //
+
+ Context->TargetCount = 0;
+
+ //
+ // Let waiting processor go to next synchronzation point
+ //
+
+ InterlockedIncrement ((PULONG) TargetPhase);
+
+
+ } else {
+
+ //
+ // Get current phase
+ //
+
+ CurrentPhase = *TargetPhase;
+
+ //
+ // Signal that we have completed the current phase
+ //
+
+ InterlockedIncrement ((PULONG) &Context->TargetCount);
+
+ //
+ // Wait for new phase to begin
+ //
+
+ while (*TargetPhase == CurrentPhase) ;
+ }
+}
diff --git a/private/ntos/ke/i386/mtrr.h b/private/ntos/ke/i386/mtrr.h
new file mode 100644
index 000000000..f7cce0557
--- /dev/null
+++ b/private/ntos/ke/i386/mtrr.h
@@ -0,0 +1,124 @@
+/*++
+
+Copyright (c) 1991 Microsoft Corporation
+
+Module Name:
+
+ mtrr.h
+
+Abstract:
+
+ This module contains the i386 specific mtrr register
+ hardware definitions.
+
+Author:
+
+ Ken Reneris (kenr) 11-Oct-95
+
+Environment:
+
+ Kernel mode only.
+
+Revision History:
+
+--*/
+
+//
+// MTRR MSR architecture definitions
+//
+
+#define MTRR_MSR_CAPABILITIES 0x0fe
+#define MTRR_MSR_DEFAULT 0x2ff
+#define MTRR_MSR_VARIABLE_BASE 0x200
+#define MTRR_MSR_VARIABLE_MASK (MTRR_MSR_VARIABLE_BASE+1)
+
+#define MTRR_PAGE_SIZE 4096
+#define MTRR_PAGE_MASK (~(MTRR_PAGE_SIZE-1))
+
+//
+// Memory range types
+//
+
+#define MTRR_TYPE_UC 0
+#define MTRR_TYPE_USWC 1
+#define MTRR_TYPE_WT 4
+#define MTRR_TYPE_WP 5
+#define MTRR_TYPE_WB 6
+#define MTRR_TYPE_MAX 7
+
+//
+// MTRR specific registers - capability register, default
+// register, and variable mask and base register
+//
+
+#include "pshpack1.h"
+
+typedef struct _MTRR_CAPABILITIES {
+ union {
+ struct {
+ ULONG VarCnt:8;
+ ULONG FixSupported:1;
+ ULONG Reserved_0:1;
+ ULONG UswcSupported:1;
+ ULONG Reserved_1:21;
+ ULONG Reserved_2;
+ } hw;
+ ULONGLONG QuadPart;
+ } u;
+} MTRR_CAPABILITIES, *PMTRR_CAPABILITIES;
+
+typedef struct _MTRR_DEFAULT {
+ union {
+ struct {
+ ULONG Type:8;
+ ULONG Reserved_0:2;
+ ULONG FixedEnabled:1;
+ ULONG MtrrEnabled:1;
+ ULONG Reserved_1:20;
+ ULONG Reserved_2;
+ } hw;
+ ULONGLONG QuadPart;
+ } u;
+} MTRR_DEFAULT, *PMTRR_DEFAULT;
+
+typedef struct _MTRR_VARIABLE_BASE {
+ union {
+ struct {
+ ULONG Type:8;
+ ULONG Reserved_0:4;
+ ULONG PhysBase_1:20;
+ ULONG PhysBase_2:4;
+ ULONG Reserved_1:28;
+ } hw;
+ ULONGLONG QuadPart;
+ } u;
+} MTRR_VARIABLE_BASE, *PMTRR_VARIABLE_BASE;
+
+#define MTRR_MASK_BASE 0x0000000ffffff000
+
+typedef struct _MTRR_VARIABLE_MASK {
+ union {
+ struct {
+ ULONG Reserved_0:11;
+ ULONG Valid:1;
+ ULONG PhysMask_1:20;
+ ULONG PhysMask_2:4;
+ ULONG Reserved_1:28;
+ } hw;
+ ULONGLONG QuadPart;
+ } u;
+} MTRR_VARIABLE_MASK, *PMTRR_VARIABLE_MASK;
+
+#define MTRR_MASK_MASK 0x0000000ffffff000
+
+//
+// Masks/constants to check for non-contiguous masks,
+// mask out reserved bits of variable MTRR's,
+// and construct MTRR variable register masks
+//
+
+#define MASK_OVERFLOW_MASK (~0x1000000000)
+#define MTRR_RESVBIT_MASK 0xfffffffff
+#define MTRR_MAX_RANGE_SHIFT 36
+
+#include "poppack.h"
diff --git a/private/ntos/ke/i386/newsysbg.asm b/private/ntos/ke/i386/newsysbg.asm
new file mode 100644
index 000000000..4256f096d
--- /dev/null
+++ b/private/ntos/ke/i386/newsysbg.asm
@@ -0,0 +1,1150 @@
+ title "System Startup"
+;++
+;
+; Copyright (c) 1989 Microsoft Corporation
+;
+; Module Name:
+;
+; systembg.asm
+;
+; Abstract:
+;
+; This module implements the code necessary to initially startup the
+; NT system.
+;
+; Author:
+;
+; Shie-Lin Tzong (shielint) 07-Mar-1990
+;
+; Environment:
+;
+; Kernel mode only.
+;
+; Revision History:
+;
+; John Vert (jvert) 25-Jun-1991
+; Major overhaul in order to move into new osloader architecture
+; Removed old debugger hacks
+;
+;--
+.386p
+ .xlist
+include i386\cpu.inc
+include ks386.inc
+include i386\kimacro.inc
+include mac386.inc
+include callconv.inc
+ .list
+
+ option segment:flat
+
+ extrn @ExfInterlockedPopEntrySList@8:DWORD
+ extrn @ExfInterlockedPushEntrySList@12:DWORD
+ extrn @ExInterlockedCompareExchange64@16:DWORD
+ extrn @ExInterlockedPopEntrySList@8:DWORD
+ extrn @ExInterlockedPushEntrySList@12:DWORD
+ extrn @ExpInterlockedCompareExchange64@16:DWORD
+ extrn _ExInterlockedAddLargeInteger@16:DWORD
+ extrn _ExInterlockedExchangeAddLargeInteger@16:DWORD
+ extrn _KiBootFeatureBits:DWORD
+ EXTRNP _KdInitSystem,2
+ EXTRNP KfRaiseIrql,1,IMPORT,FASTCALL
+ EXTRNP KfLowerIrql,1,IMPORT,FASTCALL
+ EXTRNP _KiInitializeKernel,6
+ extrn SwapContext:PROC
+ EXTRNP GetMachineBootPointers
+ EXTRNP _KiInitializePcr,6
+ EXTRNP _KiSwapIDT
+ EXTRNP _KiInitializeTSS,1
+ EXTRNP _KiInitializeTSS2,2
+ EXTRNP _KiInitializeGdtEntry,6
+ extrn _KiTrap08:PROC
+ extrn _KiTrap02:PROC
+ EXTRNP _HalDisplayString,1,IMPORT
+ EXTRNP _KiInitializeAbios,1
+ EXTRNP _KiInitializeMachineType
+ EXTRNP _KeGetCurrentIrql,0,IMPORT
+ EXTRNP _KeBugCheck, 1
+ EXTRNP _KeBugCheckEx, 5
+ EXTRNP _HalInitializeProcessor,1,IMPORT
+ EXTRNP _HalProcessorIdle,0,IMPORT
+ EXTRNP HalClearSoftwareInterrupt,1,IMPORT,FASTCALL
+ EXTRNP _ZwAcceptConnectPort,6
+ EXTRNP _ZwUnmapViewOfSection,2
+
+if NT_INST
+ EXTRNP _KiAcquireSpinLock, 1
+ EXTRNP _KiReleaseSpinLock, 1
+endif
+ extrn _KiFreezeExecutionLock:DWORD
+ extrn _KiDispatcherLock:DWORD
+
+ extrn _IDT:BYTE
+ extrn _IDTLEN:BYTE ; NOTE - really an ABS, linker problems
+
+ extrn _KeNumberProcessors:BYTE
+ extrn _KeActiveProcessors:DWORD
+ extrn _KiIdleSummary:DWORD
+ extrn _KiProcessorBlock:DWORD
+ extrn _KiFindFirstSetRight:BYTE
+
+ EXTRNP _KdPollBreakIn,0
+ extrn _KeLoaderBlock:DWORD
+ extrn _KeI386NpxPresent:DWORD
+ extrn _KeI386CpuType:DWORD
+ extrn _KeI386CpuStep:DWORD
+ extrn _KeTickCount:DWORD
+
+ifndef NT_UP
+ extrn _KiBarrierWait:DWORD
+endif
+
+if DBG
+ extrn _KdDebuggerEnabled:BYTE
+ EXTRNP _DbgBreakPoint,0
+ extrn _DbgPrint:near
+ extrn _KiDPCTimeout:DWORD
+ extrn _MsgDpcTrashedEsp:BYTE
+ extrn _MsgDpcTimeout:BYTE
+endif
+
+;
+; Constants for various variables
+;
+
+_DATA SEGMENT PARA PUBLIC 'DATA'
+
+;
+; Idle thread process object
+;
+
+ align 4
+
+ public _KiIdleProcess
+_KiIdleProcess label byte
+ db ExtendedProcessObjectLength dup(?) ; sizeof (EPROCESS)
+
+;
+; Staticly allocated structures for Bootstrap processor
+; idle thread object for P0
+; idle thread stack for P0
+;
+ align 4
+ public P0BootThread
+P0BootThread label byte
+ db ExtendedThreadObjectLength dup(?) ; sizeof (ETHREAD)
+
+;
+; I don't think it is safe to overlap P0 stack and NMI/DoubleFault stack.
+; The NMI handler may decide to continue. We need to make sure the original
+; stack content is complete.
+; [shielint]
+;
+ align 16
+if DBG
+ public _KiDoubleFaultStack
+ db DOUBLE_FAULT_STACK_SIZE dup (?)
+_KiDoubleFaultStack label byte
+endif
+
+ public P0BootStack
+ db KERNEL_STACK_SIZE dup (?)
+P0BootStack label byte
+
+
+;
+; Double fault task stack
+;
+
+MINIMUM_TSS_SIZE EQU TssIoMaps
+
+ align 16
+
+ public _KiDoubleFaultTSS
+_KiDoubleFaultTSS label byte
+ db MINIMUM_TSS_SIZE dup(0)
+
+ public _KiNMITSS
+_KiNMITSS label byte
+ db MINIMUM_TSS_SIZE dup(0)
+
+;
+; Abios specific definitions
+;
+
+ public _KiCommonDataArea, _KiAbiosPresent
+_KiCommonDataArea dd 0
+_KiAbiosPresent dd 0
+
+_DATA ends
+
+ page ,132
+ subttl "System Startup"
+INIT SEGMENT DWORD PUBLIC 'CODE'
+ ASSUME DS:FLAT, ES:FLAT, SS:NOTHING, FS:NOTHING, GS:NOTHING
+
+DFInternalError db 'DF Stack internal error', 0
+
+
+;++
+;
+; For processor 0, Routine Description:
+;
+; This routine is called when the NT system begins execution.
+; Its function is to initialize system hardware state, call the
+; kernel initialization routine, and then fall into code that
+; represents the idle thread for all processors.
+;
+; Entry state created by the boot loader:
+; A short-form IDT (0-1f) exists and is active.
+; A complete GDT is set up and loaded.
+; A complete TSS is set up and loaded.
+; Page map is set up with minimal start pages loaded
+; The lower 4Mb of virtual memory are directly mapped into
+; physical memory.
+;
+; The system code (ntoskrnl.exe) is mapped into virtual memory
+; as described by its memory descriptor.
+; DS=ES=SS = flat
+; ESP->a useable boot stack
+; Interrupts OFF
+;
+; For processor > 0, Routine Description:
+;
+; This routine is called when each additional processor begins execution.
+; The entry state for the processor is:
+; IDT, GDT, TSS, stack, selectors, PCR = all valid
+; Page directory is set to the current running direcroty
+; LoaderBlock - parameters for this processors
+;
+; Arguments:
+;
+; PLOADER_PARAMETER_BLOCK LoaderBlock
+;
+; Return Value:
+;
+; None.
+;
+;--
+;
+; Arguments for KiSystemStartupPx
+;
+
+
+KissLoaderBlock equ [ebp+8]
+
+;
+; Local variables
+;
+
+KissGdt equ [ebp-4]
+KissPcr equ [ebp-8]
+KissTss equ [ebp-12]
+KissIdt equ [ebp-16]
+KissIrql equ [ebp-20]
+KissPbNumber equ [ebp-24]
+KissIdleStack equ [ebp-28]
+KissIdleThread equ [ebp-32]
+
+cPublicProc _KiSystemStartup ,1
+
+ push ebp
+ mov ebp, esp
+ sub esp, 32 ; Reserve space for local variables
+
+ mov ebx, dword ptr KissLoaderBlock
+ mov _KeLoaderBlock, ebx ; Get loader block param
+
+ movzx ecx, _KeNumberProcessors ; get number of processors
+ mov KissPbNumber, ecx
+ or ecx, ecx ; Is the the boot processor?
+ jnz @f ; no
+
+ ; P0 uses static memory for these
+ mov dword ptr [ebx].LpbThread, offset P0BootThread
+ mov dword ptr [ebx].LpbKernelStack, offset P0BootStack
+
+ push KGDT_R0_PCR ; P0 needs FS set
+ pop fs
+
+ ; Save processornumber in Prcb
+ mov byte ptr fs:PcPrcbData+PbNumber, cl
+@@:
+ mov eax, dword ptr [ebx].LpbThread
+ mov dword ptr KissIdleThread, eax
+
+ mov eax, dword ptr [ebx].LpbKernelStack
+ mov dword ptr KissIdleStack, eax
+
+ stdCall _KiInitializeMachineType
+ cmp byte ptr KissPbNumber, 0 ; if not p0, then
+ jne kiss_notp0 ; skip a bunch
+
+;
+;+++++++++++++++++++++++
+;
+; Initialize the PCR
+;
+
+ stdCall GetMachineBootPointers
+;
+; Upon return:
+; (edi) -> gdt
+; (esi) -> pcr
+; (edx) -> tss
+; (eax) -> idt
+; Now, save them in our local variables
+;
+
+
+ mov KissGdt, edi
+ mov KissPcr, esi
+ mov KissTss, edx
+ mov KissIdt, eax
+
+;
+; edit TSS to be 32bits. loader gives us a tss, but it's 16bits!
+;
+ lea ecx,[edi]+KGDT_TSS ; (ecx) -> TSS descriptor
+ mov byte ptr [ecx+5],089h ; 32bit, dpl=0, present, TSS32, not busy
+
+; KiInitializeTSS2(
+; Linear address of TSS
+; Linear address of TSS descriptor
+; );
+ stdCall _KiInitializeTSS2, <KissTss, ecx>
+
+ stdCall _KiInitializeTSS, <KissTss>
+
+ mov cx,KGDT_TSS
+ ltr cx
+
+
+;
+; set up 32bit double fault task gate so we can catch double faults.
+;
+
+ mov eax,KissIdt
+ lea ecx,[eax]+40h ; Descriptor 8
+ mov byte ptr [ecx+5],085h ; dpl=0, present, taskgate
+
+ mov word ptr [ecx+2],KGDT_DF_TSS
+
+ lea ecx,[edi]+KGDT_DF_TSS
+ mov byte ptr [ecx+5],089h ; 32bit, dpl=0, present, TSS32, not busy
+
+ mov edx,offset FLAT:_KiDoubleFaultTSS
+ mov eax,edx
+ mov [ecx+KgdtBaseLow],ax
+ shr eax,16
+ mov [ecx+KgdtBaseHi],ah
+ mov [ecx+KgdtBaseMid],al
+ mov eax, MINIMUM_TSS_SIZE
+ mov [ecx+KgdtLimitLow],ax
+
+; KiInitializeTSS(
+; address of double fault TSS
+; );
+ stdCall _KiInitializeTSS, <edx>
+
+ mov eax,cr3
+ mov [edx+TssCr3],eax
+
+if DBG
+ mov eax, offset FLAT:_KiDoubleFaultStack
+else
+; on a retail build we overload the double fault stack to overlay
+; part of the kernel's image. (we overlay the ZW thunks)
+ mov eax, offset FLAT:_ZwUnmapViewOfSection@8 - 4
+ and eax, not 3
+ push eax
+
+ sub eax, offset FLAT:_ZwAcceptConnectPort@24
+ cmp eax, 0a00h ; make sure there's enough stack
+ jnc short @f ; space available
+
+ pushad
+ stdCall _HalDisplayString, <offset FLAT:DFInternalError>
+ popad
+@@:
+ pop eax
+endif
+ mov dword ptr [edx+038h],eax
+ mov dword ptr [edx+TssEsp0],eax
+
+ mov dword ptr [edx+020h],offset FLAT:_KiTrap08
+ mov dword ptr [edx+024h],0 ; eflags
+ mov word ptr [edx+04ch],KGDT_R0_CODE ; set value for CS
+ mov word ptr [edx+058h],KGDT_R0_PCR ; set value for FS
+ mov [edx+050h],ss
+ mov word ptr [edx+048h],KGDT_R3_DATA OR RPL_MASK ; Es
+ mov word ptr [edx+054h],KGDT_R3_DATA OR RPL_MASK ; Ds
+
+;
+; set up 32bit NMI task gate so we can catch NMI faults.
+;
+
+ mov eax,KissIdt
+ lea ecx,[eax]+10h ; Descriptor 2
+ mov byte ptr [ecx+5],085h ; dpl=0, present, taskgate
+
+ mov word ptr [ecx+2],KGDT_NMI_TSS
+
+ lea ecx,[edi]+KGDT_NMI_TSS
+ mov byte ptr [ecx+5],089h ; 32bit, dpl=0, present, TSS32, not busy
+
+ mov edx,offset FLAT:_KiNMITSS
+ mov eax,edx
+ mov [ecx+KgdtBaseLow],ax
+ shr eax,16
+ mov [ecx+KgdtBaseHi],ah
+ mov [ecx+KgdtBaseMid],al
+ mov eax, MINIMUM_TSS_SIZE
+ mov [ecx+KgdtLimitLow],ax
+
+ push edx
+ stdCall _KiInitializeTSS,<edx> ; KiInitializeTSS(
+ ; address TSS
+ ; );
+
+ ; We are using the DoubleFault stack as the DoubleFault stack and the
+ ; NMI Task Gate stack
+
+
+ mov eax,cr3
+ mov [edx+TssCr3],eax
+
+ mov eax, offset FLAT:_KiDoubleFaultTSS
+ mov eax, dword ptr [eax+038h] ; get DF stack
+ mov dword ptr [edx+TssEsp0],eax ; use it for NMI stack
+ mov dword ptr [edx+038h],eax
+
+ mov dword ptr [edx+020h],offset FLAT:_KiTrap02
+ mov dword ptr [edx+024h],0 ; eflags
+ mov word ptr [edx+04ch],KGDT_R0_CODE ; set value for CS
+ mov word ptr [edx+058h],KGDT_R0_PCR ; set value for FS
+ mov [edx+050h],ss
+ mov word ptr [edx+048h],KGDT_R3_DATA OR RPL_MASK ; Es
+ mov word ptr [edx+054h],KGDT_R3_DATA OR RPL_MASK ; Ds
+
+ stdCall _KiInitializePcr, <KissPbNumber,KissPcr,KissIdt,KissGdt,KissTss,KissIdleThread>
+
+;
+; set current process pointer in current thread object
+;
+ mov edx, KissIdleThread
+ mov ecx, offset FLAT:_KiIdleProcess ; (ecx)-> idle process obj
+ mov [edx]+ThApcState+AsProcess, ecx ; set addr of thread's process
+
+
+;
+; set up PCR: Teb, Prcb pointers. The PCR:InitialStack, and various fields
+; of Prcb will be set up in _KiInitializeKernel
+;
+
+ mov dword ptr fs:PcTeb, 0 ; PCR->Teb = 0
+
+;
+; Initialize KernelDr7 and KernelDr6 to 0. This must be done before
+; the debugger is called.
+;
+
+ mov dword ptr fs:PcPrcbData+PbProcessorState+PsSpecialRegisters+SrKernelDr6,0
+ mov dword ptr fs:PcPrcbData+PbProcessorState+PsSpecialRegisters+SrKernelDr7,0
+
+;
+; Since the entries of Kernel IDT have their Selector and Extended Offset
+; fields set up in the wrong order, we need to swap them back to the order
+; which i386 recognizes.
+; This is only done by the bootup processor.
+;
+
+ stdCall _KiSwapIDT ; otherwise, do the work
+
+;
+; Switch to R3 flat selectors that we want loaded so lazy segment
+; loading will work.
+;
+ mov eax,KGDT_R3_DATA OR RPL_MASK ; Set RPL = ring 3
+ mov ds,ax
+ mov es,ax
+
+;
+; Now copy our trap handlers to replace kernel debugger's handlers.
+;
+
+ mov eax, KissIdt ; (eax)-> Idt
+ push dword ptr [eax+40h] ; save double fault's descriptor
+ push dword ptr [eax+44h]
+ push dword ptr [eax+10h] ; save nmi fault's descriptor
+ push dword ptr [eax+14h]
+
+ mov edi,KissIdt
+ mov esi,offset FLAT:_IDT
+ mov ecx,offset FLAT:_IDTLEN ; _IDTLEN is really an abs, we use
+ shr ecx,2
+
+ rep movsd
+ pop dword ptr [eax+14h] ; restore nmi fault's descriptor
+ pop dword ptr [eax+10h]
+ pop dword ptr [eax+44h] ; restore double fault's descriptor
+ pop dword ptr [eax+40h]
+
+kiss_notp0:
+ ;
+ ; A new processor can't come online while execution is frozen
+ ; Take freezelock while adding a processor to the system
+ ; NOTE: don't use SPINLOCK macro - it has debugger stuff in it
+ ;
+
+if NT_INST
+ lea eax, _KiFreezeExecutionLock
+ stdCall _KiAcquireSpinLock, <eax>
+else
+@@: test _KiFreezeExecutionLock, 1
+ jnz short @b
+
+ lock bts _KiFreezeExecutionLock, 0
+ jc short @b
+endif
+
+
+;
+; Add processor to active summary, and update BroadcastMasks
+;
+ mov ecx, dword ptr KissPbNumber ; mark this processor as active
+ mov byte ptr fs:PcNumber, cl
+ mov eax, 1
+ shl eax, cl ; our affinity bit
+ or _KeActiveProcessors, eax ; New affinity of active processors
+
+ mov fs:PcSetMember, eax
+ mov fs:PcPrcbData.PbSetMember, eax
+
+;
+; Initialize the interprocessor interrupt vector and increment ready
+; processor count to enable kernel debugger.
+;
+ stdCall _HalInitializeProcessor, <dword ptr KissPbNumber>
+
+;
+; Initialize ABIOS data structure if present.
+; Note, the KiInitializeAbios MUST be called after the KeLoaderBlock is
+; initialized.
+;
+ stdCall _KiInitializeAbios, <dword ptr KissPbNumber>
+
+ inc _KeNumberProcessors ; One more processor now active
+
+if NT_INST
+ lea eax, _KiFreezeExecutionLock
+ stdCall _KiReleaseSpinLock, <eax>
+else
+ xor eax, eax ; release the executionlock
+ mov _KiFreezeExecutionLock, eax
+endif
+
+ cmp byte ptr KissPbNumber, 0
+ jnz @f
+
+; don't stop in debugger
+ stdCall _KdInitSystem, <_KeLoaderBlock,0>
+
+if DEVL
+;
+; Give debugger an opportunity to gain control.
+;
+
+ POLL_DEBUGGER
+endif ; DEVL
+@@:
+ nop ; leave a spot for int-3 patch
+;
+; Set initial IRQL = HIGH_LEVEL for init
+;
+ mov ecx, HIGH_LEVEL
+ fstCall KfRaiseIrql
+ mov KissIrql, al
+
+;
+; If the target machine does not implement the cmpxchg8b instruction,
+; then patch the routines that use this instrucntion to simply jump
+; to the corresponding routines that use spinlocks.
+;
+ pushfd ; Save flags
+
+ cmp byte ptr KissPbNumber, 0
+ jnz cx8done ; only test on boot processor
+
+ pop ebx ; Get flags into eax
+ push ebx ; Save original flags
+
+ mov ecx, ebx
+ xor ecx, EFLAGS_ID ; flip ID bit
+ push ecx
+ popfd ; load it into flags
+ pushfd ; re-save flags
+ pop ecx ; get flags into eax
+ cmp ebx, ecx ; did bit stay flipped?
+ je short nocx8 ; No, don't try CPUID
+
+ or ebx, EFLAGS_ID
+ push ebx
+ popfd ; Make sure ID bit is set
+.586p
+ mov eax, 1 ; Get feature bits
+ cpuid ; Uses eax, ebx, ecx, edx
+.386p
+ test edx, 100h
+ jz short nocx8
+ or _KiBootFeatureBits, KF_CMPXCHG8B ; We're committed to using
+ jmp short cx8done ; this feature
+
+nocx8:
+ lea eax, @ExInterlockedCompareExchange64@16 ; get target address
+ lea ecx, @ExpInterlockedCompareExchange64@16 ; get source address
+ mov byte ptr [eax], 0e9H ; set jump opcode value
+ lea edx, [eax] + 5 ; get simulated eip value
+ sub ecx, edx ; compute displacement
+ mov [eax] + 1, ecx ; set jump displacement value
+ lea eax, @ExInterlockedPopEntrySList@8 ; get target address
+ lea ecx, @ExfInterlockedPopEntrySList@8 ; get source address
+ mov byte ptr [eax], 0e9H ; set jump opcode value
+ lea edx, [eax] + 5 ; get simulated eip value
+ sub ecx, edx ; compute displacement
+ mov [eax] + 1, ecx ; set jump displacement value
+ lea eax, @ExInterlockedPushEntrySList@12 ; get target address
+ lea ecx, @ExfInterlockedPushEntrySList@12 ; get source address
+ mov byte ptr [eax], 0e9H ; set jump opcode value
+ lea edx, [eax] + 5 ; get simulated eip value
+ sub ecx, edx ; compute displacement
+ mov [eax] + 1, ecx ; set jump displacement value
+ lea eax, _ExInterlockedExchangeAddLargeInteger@16 ; get target address
+ lea ecx, _ExInterlockedAddLargeInteger@16 ; get source address
+ mov byte ptr [eax], 0e9H ; set jump opcode value
+ lea edx, [eax] + 5 ; get simulated eip value
+ sub ecx, edx ; compute displacement
+ mov [eax] + 1, ecx ; set jump displacement value
+
+cx8done:
+ popfd
+
+;
+; Initialize ebp, esp, and argument registers for initializing the kernel.
+;
+ mov ebx, KissIdleThread
+ mov edx, KissIdleStack
+ mov eax, KissPbNumber
+ and edx, NOT 3h ; align stack to 4 byte boundary
+
+ xor ebp, ebp ; (ebp) = 0. No more stack frame
+ mov esp, edx
+ push CR0_EM+CR0_TS+CR0_MP ; make space for Cr0NpxState
+
+; arg6 - LoaderBlock
+; arg5 - processor number
+; arg4 - addr of prcb
+; arg3 - idle thread's stack
+; arg2 - addr of current thread obj
+; arg1 - addr of current process obj
+
+; initialize system data structures
+; and HAL.
+
+ stdCall _KiInitializeKernel,<offset _KiIdleProcess,ebx,edx,dword ptr fs:PcPrcb,eax,_KeLoaderBlock>
+
+
+;
+; Set "shadow" priority value for Idle thread. This will keep the Mutex
+; priority boost/drop code from dropping priority on the Idle thread, and
+; thus avoids leaving a bit set in the ActiveMatrix for the Idle thread when
+; there should never be any such bit.
+;
+
+ mov ebx,fs:PcPrcbData+PbCurrentThread ; (eax)->Thread
+ mov byte ptr [ebx]+ThPriority,LOW_REALTIME_PRIORITY ; set pri.
+
+;
+; Control is returned to the idle thread with IRQL at HIGH_LEVEL. Lower IRQL
+; to DISPATCH_LEVEL and set wait IRQL of idle thread.
+;
+
+ sti
+ mov ecx, DISPATCH_LEVEL
+ fstCall KfLowerIrql
+ mov byte ptr [ebx]+ThWaitIrql, DISPATCH_LEVEL
+
+;
+; The following code represents the idle thread for a processor. The idle
+; thread executes at IRQL DISPATCH_LEVEL and continually polls for work to
+; do. Control may be given to this loop either as a result of a return from
+; the system initialize routine or as the result of starting up another
+; processor in a multiprocessor configuration.
+;
+
+ mov ebx, PCR[PcSelfPcr] ; get address of PCR
+
+;
+; In a multiprocessor system the boot processor proceeds directly into
+; the idle loop. As other processors start executing, however, they do
+; not directly enter the idle loop and spin until all processors have
+; been started and the boot master allows them to proceed.
+;
+
+ifndef NT_UP
+
+@@: cmp _KiBarrierWait, 0 ; check if barrier set
+ jnz short @b ; if nz, barrier set
+
+endif
+
+ jmp KiIdleLoop ; enter idle loop
+
+stdENDP _KiSystemStartup
+
+INIT ends
+
+_TEXT$00 SEGMENT DWORD PUBLIC 'CODE' ; Put IdleLoop in text section
+ ASSUME DS:FLAT, ES:FLAT, SS:NOTHING, FS:NOTHING, GS:NOTHING
+
+ page ,132
+ subttl "Idle Loop"
+;++
+;
+; Routine Description:
+;
+; This routine continuously executes the idle loop and never returns.
+;
+; Arguments:
+;
+; ebx - Address of the current processor PCR.
+;
+; Return value:
+;
+; None - routine never returns.
+;
+;--
+
+ public KiIdleLoop
+KiIdleLoop proc
+
+ lea ebp, [ebx].PcPrcbData.PbDpcListHead ; set DPC listhead address
+
+if DBG
+
+ xor edi, edi ; reset poll breakin counter
+
+endif
+
+ jmp short kid20 ; Skip HalIdleProcessor on first iteration
+
+;
+; There are no entries in the DPC list and a thread has not been selected
+; for execution on this processor. Call the HAL so power managment can be
+; performed.
+;
+; N.B. The HAL is called with interrupts disabled. The HAL will return
+; with interrupts enabled.
+;
+; N.B. Use a call instruction instead of a push-jmp, as the call instruction
+; executes faster and won't invalidate the processors call-return stack
+; cache.
+;
+
+kid10: stdCall _HalProcessorIdle ;
+
+
+;
+; Give debugger an opportunity to gain control on debug systems.
+;
+; N.B. On an MP system the lowest numbered idle processor is the only
+; processor that polls for a breakin request.
+;
+
+kid20:
+
+if DBG
+ifndef NT_UP
+
+ mov eax, _KiIdleSummary ; get idle summary
+ mov ecx, [ebx].PcSetMember ; get set member
+ dec ecx ; compute right bit mask
+ and eax, ecx ; check if any lower bits set
+ jnz short CheckDpcList ; if nz, not lowest numbered
+
+endif
+
+ dec edi ; decrement poll counter
+ jg short CheckDpcList ; if g, not time to poll
+
+ POLL_DEBUGGER ; check if break in requested
+endif
+
+kid30:
+
+if DBG
+ifndef NT_UP
+
+ mov edi, 20 * 1000 ; set breakin poll interval
+
+else
+
+ mov edi, 100 ; UP idle loop has a HLT in it
+
+endif
+endif
+
+;
+; Disable interrupts and check if there is any work in the DPC list
+; of the current processor or a target processor.
+;
+
+CheckDpcList: ;
+
+;
+; N.B. The following code enables interrupts for a few cycles, then
+; disables them again for the subsequent DPC and next thread
+; checks.
+;
+
+ sti ; enable interrupts
+ nop ;
+ nop ;
+ cli ; disable interrupts
+
+;
+; Process the deferred procedure call list for the current processor.
+;
+
+ cmp ebp, [ebp].LsFlink ; check if DPC list is empty
+ je short CheckNextThread ; if eq, DPC list is empty
+ mov cl, DISPATCH_LEVEL ; set interrupt level
+ fstCall HalClearSoftwareInterrupt ; clear software interrupt
+ call KiRetireDpcList ; process the current DPC list
+
+if DBG
+
+ xor edi, edi ; clear breakin poll interval
+
+endif
+
+;
+; Check if a thread has been selected to run on the current processor.
+;
+
+CheckNextThread: ;
+ cmp dword ptr [ebx].PcPrcbData.PbNextThread, 0 ; thread selected?
+ je short kid10 ; if eq, no thread selected
+
+;
+; A thread has been selected for execution on this processor. Acquire
+; dispatcher database lock, get the thread address again (it may have
+; changed), clear the address of the next thread in the processor block,
+; and call swap context to start execution of the selected thread.
+;
+; N.B. If the dispatcher database lock cannot be obtained immediately,
+; then attempt to process another DPC rather than spinning on the
+; dispatcher database lock.
+;
+; N.B. This polls for the spinlock by first using a non-interlocked
+; instruction. This way if the lock is busy, and the code is
+; spinning, this processor won't be generating lots of locked cycles
+;
+
+ifndef NT_UP
+
+ lea eax, _KiDispatcherLock ; get address of dispatcher lock
+ TEST_SPINLOCK eax, <short CheckDpcList>
+ ACQUIRE_SPINLOCK eax, <short CheckDpcList>, NoChecking ; acquire dispatcher database lock
+
+endif
+
+;
+; Raise IRQL to synchronization level and enable interrupts.
+;
+
+
+ mov ecx, SYNCH_LEVEL ; raise IRQL to synchronization level
+ fstCall KfRaiseIrql ;
+ sti ; enable interrupts
+ mov esi, [ebx].PcPrcbData.PbNextThread ; get next thread address
+ mov edi, [ebx].PcPrcbData.PbCurrentThread ; set current thread address
+ mov dword ptr [ebx].PcPrcbData.PbNextThread, 0 ; clear next thread address
+ mov [ebx].PcPrcbData.PbCurrentThread, esi ; set current thread address
+
+ mov cl, 1 ; set APC interrupt bypass disable
+ call SwapContext ;
+ mov ecx, DISPATCH_LEVEL ; lower IRQL to dispatch level
+ fstCall KfLowerIrql ;
+
+ lea ebp, [ebx].PcPrcbData.PbDpcListHead ; set DPC listhead address
+ jmp kid30 ;
+
+KiIdleLoop endp
+
+ page ,132
+ subttl "Retire Deferred Procedure Call List"
+;++
+;
+; Routine Description:
+;
+; This routine is called to retire the specified deferred procedure
+; call list. DPC routines are called using the idle thread (current)
+; stack.
+;
+; N.B. Interrupts must be disabled and the DPC list lock held on entry
+; to this routine. Control is returned to the caller with the same
+; conditions true.
+;
+; N.B. The registers ebx and ebp are preserved across the call.
+;
+; Arguments:
+;
+; ebx - Address of the target processor PCR.
+; ebp - Address of the target DPC listhead.
+;
+; Return value:
+;
+; None.
+;
+;--
+
+ public KiRetireDpcList
+KiRetireDpcList proc
+
+ifndef NT_UP
+
+ push esi ; save register
+ lea esi, [ebx].PcPrcbData.PbDpcLock ; get DPC lock address
+
+endif
+
+rdl5: mov PCR[PcPrcbData.PbDpcRoutineActive], esp ; set DPC routine active
+
+
+;
+; Process the DPC List.
+;
+
+
+rdl10: ;
+
+ifndef NT_UP
+
+ ACQUIRE_SPINLOCK esi, rdl50, NoChecking ; acquire DPC lock
+ cmp ebp, [ebp].LsFlink ; check if DPC list is empty
+ je rdl45 ; if eq, DPC list is empty
+
+endif
+
+ mov edx, [ebp].LsFlink ; get address of next entry
+ mov ecx, [edx].LsFlink ; get address of next entry
+ mov [ebp].LsFlink, ecx ; set address of next in header
+ mov [ecx].LsBlink, ebp ; set address of previous in next
+ sub edx, DpDpcListEntry ; compute address od DPC object
+ mov ecx, [edx].DpDeferredRoutine ; get DPC routine address
+
+if DBG
+
+.fpo (5, 0, 0, 0, 0, 0)
+
+ push edi ; save register
+ push ecx ; save DPC routine address
+ push dword ptr PCR[PcPrcbData.PbInterruptCount] ; save interrupt count
+ push dword ptr PCR[PcPrcbData.PbInterruptTime] ; save interrupt time
+ push _KeTickCount ; save current tick count
+ mov edi, esp ; save current stack pointer
+
+endif
+
+.fpo (4, 0, 0, 0, 0, 0)
+
+ push [edx].DpSystemArgument2 ; second system argument
+ push [edx].DpSystemArgument1 ; first system argument
+ push [edx].DpDeferredContext ; get deferred context argument
+ push edx ; address of DPC object
+ mov dword ptr [edx]+DpLock, 0 ; clear DPC inserted state
+ dec dword ptr [ebx].PcPrcbData.PbDpcQueueDepth ; decrement depth
+
+ifndef NT_UP
+
+ RELEASE_SPINLOCK esi, NoChecking ; release DPC lock
+
+endif
+
+ sti ; enable interrupts
+ call ecx ; call DPC routine
+
+if DBG
+
+ stdCall _KeGetCurrentIrql ; get current IRQL
+ cmp al, DISPATCH_LEVEL ; check if still at dispatch level
+ jne rdl55 ; if ne, not at dispatch level
+ cmp esp, edi ; check if stack pointer is correct
+ jne rdl60 ; if ne, stack pointer is not correct
+ mov edi, [esp] ; get starting tick count
+ add edi, _KiDPCTimeout ; adjust for max dpc time allowed
+ cmp _KeTickCount, edi ; check if DPC executed too long
+ jae rdl70 ; if ae, DPC executed too long
+rdl30: add esp, 4 * 4 ; remove parameters from stack
+ pop edi ; restore register
+
+endif
+
+rdl35: cli ; disable interrupts
+ cmp ebp, [ebp].LsFlink ; check if DPC list is empty
+ jne rdl10 ; if ne, DPC list not empty
+rdl40: mov PCR[PcPrcbData.PbDpcRoutineActive], 0 ; clear DPC routine active
+ mov [ebx].PcPrcbData.PbDpcInterruptRequested, 0 ; clear DPC requested
+
+;
+; Check one last time that the DPC list is empty. This is required to
+; close a race condition with the DPC queuing code where it appears that
+; a DPC routine is active (and thus an interrupt is not requested), but
+; this code has decided the DPC list is empty and is clearing the DPC
+; active flag.
+;
+
+ cmp ebp, [ebp].LsFlink ; check if DPC list is empty
+ jne rdl5 ; if ne, DPC list not empty
+
+ifndef NT_UP
+
+ pop esi ; retore register
+
+endif
+
+ ret ; return
+
+;
+; Unlock DPC list and clear DPC active.
+;
+
+rdl45: ;
+
+ifndef NT_UP
+
+ RELEASE_SPINLOCK esi, NoChecking ; release DPC lock
+ jmp short rdl40 ;
+
+endif
+
+ifndef NT_UP
+
+rdl50: sti ; enable interrupts
+ SPIN_ON_SPINLOCK esi, <short rdl35> ; spin until lock is freee
+
+endif
+
+if DBG
+
+rdl55: stdCall _KeBugCheckEx, <IRQL_NOT_GREATER_OR_EQUAL, ebx, eax, 0, 0> ;
+
+rdl60: push dword ptr [edi+12] ; push address of DPC function
+ push offset FLAT:_MsgDpcTrashedEsp ; push message address
+ call _DbgPrint ; print debug message
+ add esp, 8 ; remove arguments from stack
+ int 3 ; break into debugger
+ mov esp, edi ; reset stack pointer
+ jmp rdl30 ;
+
+rdl70: mov edx, PCR[PcPrcbData.PbInterruptTime] ; get staring interrupt time
+ sub edx, [esp+4] ; compute time in DPC routine
+ jc rdl30 ; if c, interrupt time wrapped
+ mov ecx, PCR[PcPrcbData.PbInterruptCount] ; get starting interrupt count
+ sub ecx, [esp+8] ; compute interrupts in while in DPC
+ mov eax, [esp+12] ; get address of DPC function
+ push edx ; push interrupt time
+ push ecx ; push interrupts count
+ push eax ; push address of DPC function
+ push offset FLAT:_MsgDpcTimeout ; push message address
+ call _DbgPrint ; print debug message
+ add esp, 4 * 4 ; remove arguments from stack
+ cmp _KdDebuggerEnabled, 0 ; check if debugger enabled
+ je rdl30 ; if eq, debugger not enabled
+ call _DbgBreakPoint@0 ; break into debugger
+ jmp rdl30 ;
+
+endif
+
+KiRetireDpcList endp
+
+_TEXT$00 ends
+
+_TEXT SEGMENT DWORD PUBLIC 'CODE' ; Put IdleLoop in text section
+
+ page ,132
+ subttl "Set up 80387, or allow for emulation"
+;++
+;
+; Routine Description:
+;
+; This routine is called during kernel initialization once for each
+; processor. It sets EM+TS+MP whether we are emulating or not.
+;
+; If the 387 hardware exists, EM+TS+MP will all be cleared on the
+; first trap 07. Thereafter, EM will never be seen for this thread.
+; MP+TS will only be set when an error is detected (via IRQ 13), and
+; it will be cleared by the trap 07 that will occur on the next FP
+; instruction.
+;
+; If we're emulating, EM+TS+MP are all always set to ensure that all
+; FP instructions trap to the emulator (the trap 07 handler is edited
+; to point to the emulator, rather than KiTrap07).
+;
+; Arguments:
+;
+; None.
+;
+; Return Value:
+;
+; None.
+;
+;--
+
+cPublicProc _KiSetCR0Bits ,0
+
+ mov eax, cr0
+;
+; There are two useful bits in CR0 that we want to turn on if the processor
+; is a 486 or above. (They don't exist on the 386)
+;
+; CR0_AM - Alignment mask (so we can turn on alignment faults)
+;
+; CR0_WP - Write protect (so we get page faults if we write to a
+; write-protected page from kernel mode)
+;
+ cmp byte ptr fs:PcPrcbData.PbCpuType, 3h
+ jbe @f
+;
+; The processor is not a 386, (486 or greater) so we assume it is ok to
+; turn on these bits.
+;
+
+ or eax, CR0_WP
+
+@@:
+ mov cr0, eax
+ stdRET _KiSetCR0Bits
+
+stdENDP _KiSetCR0Bits
+
+
+ifdef DBGMP
+cPublicProc _KiPollDebugger,0
+cPublicFpo 0,3
+ push eax
+ push ecx
+ push edx
+ POLL_DEBUGGER
+ pop edx
+ pop ecx
+ pop eax
+ stdRET _KiPollDebugger
+stdENDP _KiPollDebugger
+
+endif
+
+_TEXT ends
+
+ end
diff --git a/private/ntos/ke/i386/p2w.asm b/private/ntos/ke/i386/p2w.asm
new file mode 100644
index 000000000..4f8f356dd
--- /dev/null
+++ b/private/ntos/ke/i386/p2w.asm
@@ -0,0 +1,69 @@
+ .286P
+_TEXT SEGMENT WORD PUBLIC 'CODE'
+_TEXT ENDS
+_DATA SEGMENT WORD PUBLIC 'DATA'
+_DATA ENDS
+CONST SEGMENT WORD PUBLIC 'CONST'
+CONST ENDS
+_BSS SEGMENT WORD PUBLIC 'BSS'
+_BSS ENDS
+ DGROUP GROUP _DATA, CONST, _BSS
+ ASSUME CS:_TEXT, DS:DGROUP, ES:DGROUP, SS:DGROUP
+PUBLIC _p2w
+EXTRN _printf:NEAR
+
+include callconv.inc ; calling convention macros
+
+_DATA SEGMENT
+s1 db ' equ 0',0
+s2 db '%hX%04hXH',0ah,0
+s3 db '%hXH',0ah,0
+_DATA ends
+
+_TEXT segment
+
+;
+; p2w(&ULONG which is value to print)
+;
+; if ([bx+2] != 0)
+; printf(bx+2, bx, %x, %04x)
+; else
+; printf(bx, %x)
+
+_p2w PROC NEAR
+; Line 688
+ push bp
+ mov bp, sp
+ push bx
+ push di
+ push si
+
+ push offset DGROUP:s1
+ call _printf
+ add sp,2
+
+ mov bx,[bp+4]
+ cmp word ptr [bx+2],0
+ jz p2w10
+
+ push [bx]
+ push [bx+2]
+ push offset DGROUP:s2
+ call _printf
+ add sp,6
+ jmp p2w20
+
+p2w10: push [bx]
+ push offset DGROUP:s3
+ call _printf
+ add sp,4
+
+p2w20: pop si
+ pop di
+ pop bx
+ leave
+ stdRET _p2w
+_p2w ENDP
+
+_TEXT ENDS
+END
diff --git a/private/ntos/ke/i386/procstat.asm b/private/ntos/ke/i386/procstat.asm
new file mode 100644
index 000000000..7024ee501
--- /dev/null
+++ b/private/ntos/ke/i386/procstat.asm
@@ -0,0 +1,323 @@
+ title "Processor State Save Restore"
+;++
+;
+; Copyright (c) 1989 Microsoft Corporation
+;
+; Module Name:
+;
+; procstat.asm
+;
+; Abstract:
+;
+; This module implements procedures for saving and restoring
+; processor control state, and processor run&control state.
+; These procedures support debugging of UP and MP systems.
+;
+; Author:
+;
+; Shie-Lin Tzong (shielint) 30-Aug-1990
+;
+; Environment:
+;
+; Kernel mode only.
+;
+; Revision History:
+;
+;--
+
+.386p
+ .xlist
+include ks386.inc
+include i386\kimacro.inc
+include callconv.inc
+ .list
+
+ EXTRNP _KeContextToKframes,5
+ EXTRNP _KeContextFromKframes,3
+ extrn _KeFeatureBits:DWORD
+
+ page ,132
+_TEXT SEGMENT DWORD PUBLIC 'CODE'
+ ASSUME DS:FLAT, ES:FLAT, SS:NOTHING, FS:NOTHING, GS:NOTHING
+
+ subttl "Save Processor State"
+;++
+;
+; KiSaveProcessorState(
+; PKTRAP_FRAME TrapFrame,
+; PKEXCEPTION_FRAME ExceptionFrame
+; );
+;
+; Routine Description:
+;
+; This routine saves the processor state for debugger. When the current
+; processor receives the request of IPI_FREEZE, it saves all the registers
+; in a save area in the PRCB so the debugger can get access to them.
+;
+; Arguments:
+;
+; TrapFrame (esp+4) - Pointer to machine trap frame
+;
+; ExceptionFrame (esp+8) - Pointer to exception frame
+; (IGNORED on the x86!)
+;
+; Return Value:
+;
+; None.
+;
+;--
+
+cPublicProc _KiSaveProcessorState ,2
+
+ mov eax, [esp+4] ; (eax) -> TrapFrame
+
+ mov edx, PCR[PcPrcb] ; (edx)->PrcbData
+ add edx, PbProcessorState ; (edx)->ProcessorState
+ push edx
+;
+; Copy the whole TrapFrame to our ProcessorState
+;
+
+ lea ecx, [edx].PsContextFrame
+ mov dword ptr [ecx].CsContextFlags, CONTEXT_FULL OR CONTEXT_DEBUG_REGISTERS
+
+; ecx - ContextFrame
+; 0 - ExceptionFrame == NULL
+; eax - TrapFrame
+ stdCall _KeContextFromKframes, <eax, 0, ecx>
+
+;
+; Save special registers for debugger
+;
+
+ ; TOS = PKPROCESSOR_STATE
+ call _KiSaveProcessorControlState@4
+
+ stdRET _KiSaveProcessorState
+
+stdENDP _KiSaveProcessorState
+
+
+ page ,132
+ subttl "Save Processor Control State"
+;++
+;
+; KiSaveProcessorControlState(
+; PKPROCESSOR_STATE ProcessorState
+; );
+;
+; Routine Description:
+;
+; This routine saves the control subset of the processor state.
+; (Saves the same information as KiSaveProcessorState EXCEPT that
+; data in TrapFrame/ExceptionFrame=Context record is NOT saved.)
+; Called by the debug subsystem, and KiSaveProcessorState()
+;
+; N.B. This procedure will save Dr7, and then 0 it. This prevents
+; recursive hardware trace breakpoints and allows debuggers
+; to work.
+;
+; Arguments:
+;
+; Return Value:
+;
+; None.
+;
+;--
+
+cPublicProc _KiSaveProcessorControlState ,1
+
+ mov edx, [esp+4] ; ProcessorState
+
+;
+; Save special registers for debugger
+;
+ xor ecx,ecx
+
+ mov eax, cr0
+ mov [edx].PsSpecialRegisters.SrCr0, eax
+ mov eax, cr2
+ mov [edx].PsSpecialRegisters.SrCr2, eax
+ mov eax, cr3
+ mov [edx].PsSpecialRegisters.SrCr3, eax
+
+ mov [edx].PsSpecialRegisters.SrCr4, ecx
+
+ test _KeFeatureBits, KF_CR4
+ jz short @f
+
+.586p
+ mov eax, cr4
+ mov [edx].PsSpecialRegisters.SrCr4, eax
+.486p
+
+@@:
+ mov eax,dr0
+ mov [edx].PsSpecialRegisters.SrKernelDr0,eax
+ mov eax,dr1
+ mov [edx].PsSpecialRegisters.SrKernelDr1,eax
+ mov eax,dr2
+ mov [edx].PsSpecialRegisters.SrKernelDr2,eax
+ mov eax,dr3
+ mov [edx].PsSpecialRegisters.SrKernelDr3,eax
+ mov eax,dr6
+ mov [edx].PsSpecialRegisters.SrKernelDr6,eax
+
+ mov eax,dr7
+ mov dr7,ecx
+ mov [edx].PsSpecialRegisters.SrKernelDr7,eax
+
+ sgdt fword ptr [edx].PsSpecialRegisters.SrGdtr
+ sidt fword ptr [edx].PsSpecialRegisters.SrIdtr
+
+ str word ptr [edx].PsSpecialRegisters.SrTr
+ sldt word ptr [edx].PsSpecialRegisters.SrLdtr
+
+ stdRET _KiSaveProcessorControlState
+
+stdENDP _KiSaveProcessorControlState
+
+ page ,132
+ subttl "Restore Processor State"
+;++
+;
+; KiRestoreProcessorState(
+; PKTRAP_FRAME TrapFrame,
+; PKEXCEPTION_FRAME ExceptionFrame
+; );
+;
+; Routine Description:
+;
+; This routine Restores the processor state for debugger. When the
+; control returns from debugger (UnFreezeExecution), this function
+; restores the entire processor state.
+;
+; Arguments:
+;
+; TrapFrame (esp+4) - Pointer to machine trap frame
+;
+; ExceptionFrame (esp+8) - Pointer to exception frame
+; (IGNORED on the x86!)
+;
+; Return Value:
+;
+; None.
+;
+;--
+
+cPublicProc _KiRestoreProcessorState ,2
+
+ mov eax, [esp+4] ; (eax) -> TrapFrame
+
+ mov edx, PCR[PcPrcb] ; (edx)->PrcbData
+ add edx, PbProcessorState ; (edx)->ProcessorState
+ push edx
+
+;
+; Copy the whole ContextFrame to TrapFrame
+;
+
+ lea ecx, [edx].PsContextFrame
+ mov edx, [edx].PsContextFrame.CsSegCs
+ and edx, MODE_MASK
+
+; edx - Previous mode
+; ecx - ContextFrame
+; 0 - ExceptionFrame == NULL
+; eax - TrapFrame
+ stdCall _KeContextToKframes, <eax,0,ecx,[ecx].CsContextFlags,edx>
+
+;
+; Save special registers for debugger
+;
+
+ ; TOS = KPROCESSOR_STATE
+ call _KiRestoreProcessorControlState@4
+
+ stdRET _KiRestoreProcessorState
+
+stdENDP _KiRestoreProcessorState
+
+
+ page ,132
+ subttl "Restore Processor Control State"
+;++
+;
+; KiRestoreProcessorControlState(
+; );
+;
+; Routine Description:
+;
+; This routine restores the control subset of the processor state.
+; (Restores the same information as KiRestoreProcessorState EXCEPT that
+; data in TrapFrame/ExceptionFrame=Context record is NOT restored.)
+; Called by the debug subsystem, and KiRestoreProcessorState()
+;
+; Arguments:
+;
+; Return Value:
+;
+; None.
+;
+;--
+
+cPublicProc _KiRestoreProcessorControlState,1
+
+ mov edx, [esp+4] ; (edx)->ProcessorState
+
+;
+; Restore special registers for debugger
+;
+
+ mov eax, [edx].PsSpecialRegisters.SrCr0
+ mov cr0, eax
+ mov eax, [edx].PsSpecialRegisters.SrCr2
+ mov cr2, eax
+ mov eax, [edx].PsSpecialRegisters.SrCr3
+ mov cr3, eax
+
+ test _KeFeatureBits, KF_CR4
+ jz short @f
+
+.586p
+ mov eax, [edx].PsSpecialRegisters.SrCr4
+ mov cr4, eax
+.486p
+@@:
+ mov eax, [edx].PsSpecialRegisters.SrKernelDr0
+ mov dr0, eax
+ mov eax, [edx].PsSpecialRegisters.SrKernelDr1
+ mov dr1, eax
+ mov eax, [edx].PsSpecialRegisters.SrKernelDr2
+ mov dr2, eax
+ mov eax, [edx].PsSpecialRegisters.SrKernelDr3
+ mov dr3, eax
+ mov eax, [edx].PsSpecialRegisters.SrKernelDr6
+ mov dr6, eax
+ mov eax, [edx].PsSpecialRegisters.SrKernelDr7
+ mov dr7, eax
+
+ lgdt fword ptr [edx].PsSpecialRegisters.SrGdtr
+ lidt fword ptr [edx].PsSpecialRegisters.SrIdtr
+
+;
+; Force the TSS descriptor into a non-busy state, so we don't fault
+; when we load the TR.
+;
+
+ mov eax, [edx].PsSpecialRegisters.SrGdtr+2 ; (eax)->GDT base
+ xor ecx, ecx
+ mov cx, word ptr [edx].PsSpecialRegisters.SrTr
+ add eax, 5
+ add eax, ecx ; (eax)->TSS Desc. Byte
+ and byte ptr [eax],NOT 2
+ ltr word ptr [edx].PsSpecialRegisters.SrTr
+
+ lldt word ptr [edx].PsSpecialRegisters.SrLdtr
+
+ stdRET _KiRestoreProcessorControlState
+
+stdENDP _KiRestoreProcessorControlState
+
+_TEXT ENDS
+ END
diff --git a/private/ntos/ke/i386/services.nap b/private/ntos/ke/i386/services.nap
new file mode 100644
index 000000000..1cfec241d
--- /dev/null
+++ b/private/ntos/ke/i386/services.nap
@@ -0,0 +1,123 @@
+;++
+;
+; Copyright (c) 1989 Microsoft Corporation
+;
+; Module Name:
+;
+; services.nap
+;
+; Abstract:
+;
+; This module implements the system service dispatch stub procedures.
+; It also creates a "profile" of each service by counting and
+; timing calls.
+;
+; Author:
+;
+; Shie-Lin Tzong (shielint) 6-Feb-1990
+; Russ Blake (russbl) 22-Apr-1991
+;
+; Environment:
+;
+; User or kernel mode.
+;
+; Revision History:
+;
+;--
+
+include ks386.inc
+
+.386
+STUBS_BEGIN1 macro t
+ TITLE t
+endm
+STUBS_BEGIN2 macro t
+endm
+STUBS_BEGIN3 macro t
+_TEXT SEGMENT DWORD USE32 PUBLIC 'CODE'
+endm
+STUBS_BEGIN4 macro t
+ ASSUME CS:FLAT
+endm
+STUBS_BEGIN5 macro t
+ align 4
+endm
+STUBS_BEGIN6 macro t
+endm
+STUBS_BEGIN7 macro t
+endm
+STUBS_BEGIN8 macro t
+endm
+
+STUBS_END macro t
+_TEXT ENDS
+ end
+endm
+
+SYSSTUBS_ENTRY1 macro ServiceNumber, Name
+ public _Zw&Name
+_Zw&Name proc near
+ mov eax, ServiceNumber ; (eax) = service number
+ lea edx, [esp]+4 ; (edx) -> arguments
+ INT 2Eh ; invoke system service
+ ret
+_Zw&Name endp
+endm
+
+SYSSTUBS_ENTRY2 macro ServiceNumber, Name
+endm
+SYSSTUBS_ENTRY3 macro ServiceNumber, Name
+endm
+SYSSTUBS_ENTRY4 macro ServiceNumber, Name
+endm
+SYSSTUBS_ENTRY5 macro ServiceNumber, Name
+endm
+SYSSTUBS_ENTRY6 macro ServiceNumber, Name
+endm
+SYSSTUBS_ENTRY7 macro ServiceNumber, Name
+endm
+SYSSTUBS_ENTRY8 macro ServiceNumber, Name
+endm
+
+
+USRSTUBS_ENTRY1 macro ServiceNumber, Name
+ public _Zw&Name, _Nt&Name
+_Zw&Name proc near
+_Nt&Name proc near
+
+ mov eax, ServiceNumber ; (eax) = service number
+ lea edx, [esp]+4 ; (edx) -> arguments
+
+
+ call _NapProfileDispatch ; invoke profiled system service
+
+ ret
+_Nt&Name endp
+_Zw&Name endp
+endm
+
+USRSTUBS_ENTRY2 macro ServiceNumber, Name
+endm
+USRSTUBS_ENTRY3 macro ServiceNumber, Name
+endm
+USRSTUBS_ENTRY4 macro ServiceNumber, Name
+endm
+USRSTUBS_ENTRY5 macro ServiceNumber, Name
+endm
+USRSTUBS_ENTRY6 macro ServiceNumber, Name
+endm
+USRSTUBS_ENTRY7 macro ServiceNumber, Name
+endm
+USRSTUBS_ENTRY8 macro ServiceNumber, Name
+endm
+
+ STUBS_BEGIN1 <"System Service Stub Procedures">
+ STUBS_BEGIN2 <"System Service Stub Procedures">
+ STUBS_BEGIN3 <"System Service Stub Procedures">
+ STUBS_BEGIN4 <"System Service Stub Procedures">
+ STUBS_BEGIN5 <"System Service Stub Procedures">
+ STUBS_BEGIN6 <"System Service Stub Procedures">
+ STUBS_BEGIN7 <"System Service Stub Procedures">
+ STUBS_BEGIN8 <"System Service Stub Procedures">
+
+EXTRN _NapProfileDispatch:NEAR
diff --git a/private/ntos/ke/i386/services.stb b/private/ntos/ke/i386/services.stb
new file mode 100644
index 000000000..eae2aaa9e
--- /dev/null
+++ b/private/ntos/ke/i386/services.stb
@@ -0,0 +1,131 @@
+;++
+;
+; Copyright (c) 1989 Microsoft Corporation
+;
+; Module Name:
+;
+; sysstubs.asm
+;
+; Abstract:
+;
+; This module implements the system service dispatch stub procedures.
+;
+; Author:
+;
+; Shie-Lin Tzong (shielint) 6-Feb-1990
+;
+; Environment:
+;
+; User or kernel mode.
+;
+; Revision History:
+;
+;--
+
+include ks386.inc
+include callconv.inc
+
+.386
+STUBS_BEGIN1 macro t
+ TITLE t
+endm
+STUBS_BEGIN2 macro t
+endm
+STUBS_BEGIN3 macro t
+_TEXT SEGMENT DWORD PUBLIC 'CODE'
+endm
+STUBS_BEGIN4 macro t
+endm
+STUBS_BEGIN5 macro t
+ align 4
+endm
+STUBS_BEGIN6 macro t
+endm
+STUBS_BEGIN7 macro t
+endm
+STUBS_BEGIN8 macro t
+endm
+
+STUBS_END macro t
+_TEXT ENDS
+ end
+endm
+
+SYSSTUBS_ENTRY1 macro ServiceNumber, Name, NumArgs
+cPublicProc _Zw&Name,NumArgs
+.FPO ( 0, NumArgs, 0, 0, 0, 0 )
+IFIDN <Name>, <SetHighWaitLowThread>
+ int 2Bh
+ELSE
+IFIDN <Name>, <SetLowWaitHighThread>
+ int 2Ch
+ELSE
+ mov eax, ServiceNumber ; (eax) = service number
+ lea edx, [esp]+4 ; (edx) -> arguments
+ INT 2Eh ; invoke system service
+ENDIF
+ENDIF
+ stdRET _Zw&Name
+stdENDP _Zw&Name
+endm
+
+SYSSTUBS_ENTRY2 macro ServiceNumber, Name, NumArgs
+endm
+SYSSTUBS_ENTRY3 macro ServiceNumber, Name, NumArgs
+endm
+SYSSTUBS_ENTRY4 macro ServiceNumber, Name, NumArgs
+endm
+SYSSTUBS_ENTRY5 macro ServiceNumber, Name, NumArgs
+endm
+SYSSTUBS_ENTRY6 macro ServiceNumber, Name, NumArgs
+endm
+SYSSTUBS_ENTRY7 macro ServiceNumber, Name, NumArgs
+endm
+SYSSTUBS_ENTRY8 macro ServiceNumber, Name, NumArgs
+endm
+
+
+USRSTUBS_ENTRY1 macro ServiceNumber, Name, NumArgs
+local c
+cPublicProc _Zw&Name, NumArgs
+PUBLICP _Nt&Name, NumArgs
+LABELP _Nt&Name, NumArgs
+.FPO ( 0, NumArgs, 0, 0, 0, 0 )
+IFIDN <Name>, <SetHighWaitLowThread>
+ int 2Bh
+ELSE
+IFIDN <Name>, <SetLowWaitHighThread>
+ int 2Ch
+ELSE
+ mov eax, ServiceNumber ; (eax) = service number
+ lea edx, [esp]+4 ; (edx) -> arguments
+ INT 2Eh ; invoke system service
+ENDIF
+ENDIF
+ stdRET _Zw&Name
+stdENDP _Zw&Name
+endm
+
+USRSTUBS_ENTRY2 macro ServiceNumber, Name, NumArgs
+endm
+USRSTUBS_ENTRY3 macro ServiceNumber, Name, NumArgs
+endm
+USRSTUBS_ENTRY4 macro ServiceNumber, Name, NumArgs
+endm
+USRSTUBS_ENTRY5 macro ServiceNumber, Name, NumArgs
+endm
+USRSTUBS_ENTRY6 macro ServiceNumber, Name, NumArgs
+endm
+USRSTUBS_ENTRY7 macro ServiceNumber, Name, NumArgs
+endm
+USRSTUBS_ENTRY8 macro ServiceNumber, Name, NumArgs
+endm
+
+ STUBS_BEGIN1 <"System Service Stub Procedures">
+ STUBS_BEGIN2 <"System Service Stub Procedures">
+ STUBS_BEGIN3 <"System Service Stub Procedures">
+ STUBS_BEGIN4 <"System Service Stub Procedures">
+ STUBS_BEGIN5 <"System Service Stub Procedures">
+ STUBS_BEGIN6 <"System Service Stub Procedures">
+ STUBS_BEGIN7 <"System Service Stub Procedures">
+ STUBS_BEGIN8 <"System Service Stub Procedures">
diff --git a/private/ntos/ke/i386/sources b/private/ntos/ke/i386/sources
new file mode 100644
index 000000000..d8b2f8a9b
--- /dev/null
+++ b/private/ntos/ke/i386/sources
@@ -0,0 +1,47 @@
+i386_SOURCES=..\i386\mpipia.asm \
+ ..\i386\abiosa.asm \
+ ..\i386\abiosc.c \
+ ..\i386\allproc.c \
+ ..\i386\apcuser.c \
+ ..\i386\biosa.asm \
+ ..\i386\biosc.c \
+ ..\i386\callback.c \
+ ..\i386\callout.asm \
+ ..\i386\clockint.asm \
+ ..\i386\ctxswap.asm \
+ ..\i386\cpu.asm \
+ ..\i386\cyrix.c \
+ ..\i386\dmpstate.c \
+ ..\i386\emv86.asm \
+ ..\i386\emxcptn.asm \
+ ..\i386\exceptn.c \
+ ..\i386\flush.c \
+ ..\i386\flushtb.c \
+ ..\i386\gdtsup.c \
+ ..\i386\int.asm \
+ ..\i386\intobj.c \
+ ..\i386\intsup.asm \
+ ..\i386\iopm.c \
+ ..\i386\i386init.c \
+ ..\i386\i386pcr.asm \
+ ..\i386\instemul.asm \
+ ..\i386\kernlini.c \
+ ..\i386\largepag.c \
+ ..\i386\ldtsup.c \
+ ..\i386\ldtsup2.asm \
+ ..\i386\newsysbg.asm \
+ ..\i386\misc.c \
+ ..\i386\mtrr.c \
+ ..\i386\procstat.asm \
+ ..\i386\spindbg.asm \
+ ..\i386\spinlock.asm \
+ ..\i386\spininst.asm \
+ ..\i386\sysstubs.asm \
+ ..\i386\systable.asm \
+ ..\i386\threadbg.asm \
+ ..\i386\thredini.c \
+ ..\i386\timindex.asm \
+ ..\i386\trap.asm \
+ ..\i386\trapc.c \
+ ..\i386\vdm.c \
+ ..\i386\vdmint21.c
diff --git a/private/ntos/ke/i386/spindbg.asm b/private/ntos/ke/i386/spindbg.asm
new file mode 100644
index 000000000..bfc7784b4
--- /dev/null
+++ b/private/ntos/ke/i386/spindbg.asm
@@ -0,0 +1,162 @@
+if NT_INST
+else
+ TITLE "Spin Locks"
+;++
+;
+; Copyright (c) 1989 Microsoft Corporation
+;
+; Module Name:
+;
+; spindbg.asm
+;
+; Abstract:
+;
+; Author:
+;
+; Bryan Willman (bryanwi) 13 Dec 89
+;
+; Environment:
+;
+; Kernel mode only.
+;
+; Revision History:
+;
+;--
+
+ PAGE
+
+.386p
+
+include ks386.inc
+include callconv.inc ; calling convention macros
+include i386\kimacro.inc
+
+
+if DBG
+ EXTRNP _KeBugCheck,1
+ EXTRNP _KeGetCurrentIrql,0,IMPORT
+ifdef DBGMP
+ EXTRNP _KiPollDebugger,0
+endif
+ extrn _KeTickCount:DWORD
+ extrn _KiSpinlockTimeout:DWORD
+endif
+
+
+_TEXT$00 SEGMENT DWORD PUBLIC 'CODE'
+ ASSUME DS:FLAT, ES:FLAT, SS:NOTHING, FS:NOTHING, GS:NOTHING
+
+;++
+;
+; VOID
+; Kii386SpinOnSpinLock (
+; IN PKSPIN_LOCK SpinLock
+; IN ULONG Flag
+; )
+;
+; Routine Description:
+;
+; This function is called on a debug build to spin on a spinlock.
+; It is invoked by the DEBUG version of SPIN_ON_SPINLOCK macro.
+;
+; Warning:
+;
+; Not called with C calling conventions
+; Does not destroy any register
+;
+;--
+
+cPublicProc Kii386SpinOnSpinLock,2
+
+if DBG
+cPublicFpo 2,2
+ push eax
+ push ebx
+
+ mov eax, [esp+12] ; (eax) = LockAddress
+
+ mov ebx, PCR[PcPrcbData.PbCurrentThread]
+ or ebx, 1 ; or on busy bit
+ cmp ebx, [eax] ; current thread the owner?
+ je short ssl_sameid ; Yes, go abort
+
+ssl_10:
+ mov ebx, _KeTickCount ; Current time
+ add ebx, _KiSpinlockTimeout ; wait n ticks
+
+ifdef DBGMP
+ test byte ptr [esp+16], 2 ; poll debugger while waiting?
+ jnz short ssl_30
+endif
+
+;
+; Spin while watching KeTickCount
+;
+
+ssl_20: cmp _KeTickCount, ebx ; check current time
+ jnc short ssl_timeout ; NC, too many ticks have gone by
+
+ test dword ptr [eax], 1
+ jnz short ssl_20
+
+ssl_exit:
+ pop ebx ; Spinlock is not busy, return
+ pop eax
+ stdRET Kii386SpinOnSpinLock
+
+ifdef DBGMP
+;
+; Spin while watching KeTickCount & poll debugger
+;
+
+ssl_30: cmp _KeTickCount, ebx ; check current time
+ jnc short ssl_timeout ; overflowed
+
+ stdCall _KiPollDebugger
+
+ test dword ptr [eax], 1
+ jnz short ssl_30
+
+ pop ebx ; Spinlock is not busy, return
+ pop eax
+ stdRET Kii386SpinOnSpinLock
+endif
+
+;
+; Out of line expection conditions
+;
+
+ssl_sameid:
+ test byte ptr [esp+16], 1 ; ID check enabled?
+ jz short ssl_10 ; no, continue
+
+ stdCall _KeBugCheck,<eax> ; recursed on lock, abort
+
+ssl_timeout:
+ test byte ptr [esp+16], 4 ; Timeout check enabled?
+ jz short ssl_10 ; no, continue
+
+ stdCall _KeGetCurrentIrql ; Check to see what level we're spinning at
+ cmp al, DISPATCH_LEVEL
+ mov eax, [esp+12] ; restore eax
+ jc short ssl_10 ; if < dispatch_level, don't timeout
+
+ test dword ptr [eax], 1 ; Check to see if spinlock was freed
+ jz short ssl_exit
+
+ public SpinLockSpinningForTooLong
+SpinLockSpinningForTooLong:
+
+ int 3 ; Stop here
+ jmp short ssl_10 ; re-wait
+
+else ; DBG
+ stdRET Kii386SpinOnSpinLock
+endif
+stdENDP Kii386SpinOnSpinLock,2
+
+_TEXT$00 ends
+
+endif ; NT_INST
+ end
+
diff --git a/private/ntos/ke/i386/spininst.asm b/private/ntos/ke/i386/spininst.asm
new file mode 100644
index 000000000..d364c35e5
--- /dev/null
+++ b/private/ntos/ke/i386/spininst.asm
@@ -0,0 +1,943 @@
+if NT_INST
+ TITLE "Spin Locks"
+;++
+;
+; Copyright (c) 1989 Microsoft Corporation
+;
+; Module Name:
+;
+; spininst.asm
+;
+; Abstract:
+;
+; This module implements the instrumentation versions of the routines
+; for acquiring and releasing spin locks.
+;
+; Author:
+;
+; Ken Reneris
+;
+; Environment:
+;
+; Kernel mode only.
+;
+; Revision History:
+;--
+
+ PAGE
+
+.386p
+
+include ks386.inc
+include callconv.inc ; calling convention macros
+include i386\kimacro.inc
+include mac386.inc
+
+ EXTRNP _KeRaiseIrql,2,IMPORT
+ EXTRNP _KeLowerIrql,1,IMPORT
+ EXTRNP _KeBugCheckEx,5
+
+ifdef NT_UP
+ .err SpinLock instrutmentation requires MP build
+endif
+
+s_SpinLock struc
+ SpinLock dd ? ; Back pointer to spinlock
+ InitAddr dd ? ; Address of KeInitializeSpinLock caller
+ LockValue db ? ; Actual lock varible
+ LockFlags db ? ; Various flags
+ dw ?
+ NoAcquires dd ? ; # of times acquired
+ NoCollides dd ? ; # of times busy on acquire attempt
+ TotalSpinHigh dd ? ; number spins spent waiting on this spinlock
+ TotalSpinLow dd ?
+ HighestSpin dd ? ; max spin ever waited for on this spinlock
+s_SpinLock ends
+
+LOCK_LAZYINIT equ 1h
+LOCK_NOTTRACED equ 2h
+
+
+
+_DATA SEGMENT DWORD PUBLIC 'DATA'
+
+MAXSPINLOCKS equ 1000h
+SYSTEM_ADDR equ 80000000h
+
+ public _KiNoOfSpinLocks, _KiSpinLockBogus, _KiSpinLockArray
+ public _KiSpinLockFreeList
+_KiNoOfSpinLocks dd 1 ; skip first one
+_KiSpinLockBogus dd 0
+_KiSpinLockLock dd 0
+
+_KiSpinLockArray db ((size s_SpinLock) * MAXSPINLOCKS) dup (0)
+
+_KiSpinLockFreeList dd 0
+
+_DATA ends
+
+
+
+_TEXT$00 SEGMENT DWORD PUBLIC 'CODE'
+ ASSUME DS:FLAT, ES:FLAT, SS:NOTHING, FS:NOTHING, GS:NOTHING
+
+ PAGE
+ SUBTTL "Acquire Kernel Spin Lock"
+;++
+;
+; VOID
+; KeInializeSpinLock (
+; IN PKSPIN_LOCK SpinLock,
+;
+; Routine Description:
+;
+; This function initializes a SpinLock
+;
+; Arguments:
+;
+; SpinLock (TOS+4) - Supplies a pointer to an kernel spin lock.
+;
+; Return Value:
+;
+; None.
+;
+;--
+cPublicProc _KeInitializeSpinLock ,1
+ pushfd
+ cli
+@@: lock bts _KiSpinLockLock, 0
+ jc short @b
+
+ mov eax, _KiSpinLockFreeList
+ or eax, eax
+ jz short isl10
+
+ mov ecx, [eax].InitAddr
+ mov _KiSpinLockFreeList, ecx
+ jmp short isl20
+
+isl10:
+ mov eax, _KiNoOfSpinLocks
+ cmp eax, MAXSPINLOCKS
+ jnc isl_overflow
+
+ inc _KiNoOfSpinLocks
+
+.errnz (size s_SpinLock - (8*4))
+ shl eax, 5
+ add eax, offset _KiSpinLockArray
+
+isl20:
+; (eax) = address of spinlock structure
+ mov ecx, [esp+8]
+ mov [ecx], eax
+
+ mov [eax].SpinLock, ecx
+ mov ecx, [esp+4]
+ mov [eax].InitAddr, ecx
+
+ mov _KiSpinLockLock, 0
+ popfd
+
+ stdRET _KeInitializeSpinLock
+
+isl_overflow:
+ ; Just use non-tracing locks from now on
+ mov eax, [esp+4]
+ mov dword ptr [eax], LOCK_NOTTRACED
+ popfd
+ stdRET _KeInitializeSpinLock
+
+stdENDP _KeInitializeSpinLock
+
+;++
+; VOID
+; SpinLockLazyInit (
+; IN PKSPIN_LOCK SpinLock,
+; )
+;
+; Routine Description:
+;
+; Used internaly to initialize a spinlock which is being used without
+; first being initialized (bad! bad!)
+;
+;--
+
+cPublicProc SpinLockLazyInit,1
+ push eax
+ mov eax, [esp+8] ; Get SpinLock addr
+ test dword ptr [eax], SYSTEM_ADDR
+ jnz slz_10
+
+ push ecx
+ push edx
+ inc _KiSpinLockBogus
+ stdCall _KeInitializeSpinLock, <eax>
+ pop edx
+ pop ecx
+
+ mov eax, [esp+8] ; Get SpinLock addr
+ mov eax, [eax]
+ or [eax].LockFlags, LOCK_LAZYINIT
+ pop eax
+ stdRet SpinLockLazyInit
+
+slz_10:
+ stdCall _KeBugCheckEx,<SPIN_LOCK_INIT_FAILURE,eax,0,0,0>
+
+stdENDP SpinLockLazyInit
+
+;++
+; VOID
+; SpinLockInit (VOID)
+;
+cPublicProc SpinLockInit,0
+ pushad
+ pushf
+ cli
+
+ mov ecx, MAXSPINLOCKS-1
+ mov eax, offset FLAT:_KiSpinLockArray
+ xor edx, edx
+
+@@: mov [eax].NoAcquires, edx
+ mov [eax].NoCollides, edx
+ mov [eax].TotalSpinHigh, edx
+ mov [eax].TotalSpinLow, edx
+ mov [eax].HighestSpin, edx
+
+ add eax, size s_SpinLock
+ dec ecx
+ jnz short @b
+
+ popf
+ popad
+@@: int 3
+ jmp short @b
+
+stdENDP SpinLockInit
+
+
+
+;++
+;
+; VOID
+; KeFreeSpinLock (
+; )
+;
+; Routine Description:
+; Used in instrumentation build to allow spinlocks to be
+; de-allocated if needed.
+;
+;--
+
+cPublicProc _KeFreeSpinLock,1
+ pushfd
+ cli
+@@: lock bts _KiSpinLockLock, 0
+ jc short @b
+
+ mov eax, [esp+8]
+ mov edx, [eax]
+ test edx, SYSTEM_ADDR
+ jz short @f
+
+ mov dword ptr [eax], 0
+
+;
+; Acculate old SpinLock's totals to misc bucket
+;
+ mov eax, [edx].NoAcquires
+ add _KiSpinLockArray.NoAcquires, eax
+
+ mov eax, [edx].NoCollides
+ add _KiSpinLockArray.NoCollides, eax
+
+ mov eax, [edx].TotalSpinLow
+ add _KiSpinLockArray.TotalSpinLow, eax
+ mov eax, [edx].TotalSpinHigh
+ adc _KiSpinLockArray.TotalSpinLow, eax
+
+ mov eax, [edx].HighestSpin
+ cmp _KiSpinLockArray.HighestSpin, eax
+ jnc @f
+ mov _KiSpinLockArray.HighestSpin, eax
+@@:
+ push edi
+ mov edi, edx
+ mov ecx, size s_SpinLock / 4
+ xor eax, eax
+ rep stosd
+ pop edi
+
+ mov ecx, _KiSpinLockFreeList
+ mov [edx].InitAddr, ecx
+ mov _KiSpinLockFreeList, edx
+
+@@:
+ mov _KiSpinLockLock, 0
+ popfd
+ stdRET _KeFreeSpinLock
+stdENDP _KeFreeSpinLock
+
+;++
+;
+; VOID
+; KeInializeSpinLock2 (
+; IN PKSPIN_LOCK SpinLock,
+;
+; Routine Description:
+;
+; This function initializes a non-tracing SpinLock.
+;
+; Arguments:
+;
+; SpinLock (TOS+4) - Supplies a pointer to an kernel spin lock.
+;
+; Return Value:
+;
+; None.
+;
+;--
+cPublicProc _KeInitializeSpinLock2,1
+ mov eax, [esp+4]
+ mov dword ptr [eax], LOCK_NOTTRACED
+ stdRET _KeInitializeSpinLock2
+stdENDP _KeInitializeSpinLock2,1
+
+
+ PAGE
+ SUBTTL "Acquire Kernel Spin Lock"
+;++
+;
+; VOID
+; KeAcquireSpinLock (
+; IN PKSPIN_LOCK SpinLock,
+; OUT PKIRQL OldIrql
+; )
+;
+; Routine Description:
+;
+; This function raises to DISPATCH_LEVEL and then acquires a the
+; kernel spin lock.
+;
+; Arguments:
+;
+; SpinLock (TOS+4) - Supplies a pointer to an kernel spin lock.
+; OldIrql (TOS+8) - pointer to place old irql
+;
+; Return Value:
+;
+; None.
+;
+;--
+
+align 16
+cPublicProc _KeAcquireSpinLock ,2
+ sub esp, 4 ; Make room for OldIrql
+ stdCall _KeRaiseIrql, <DISPATCH_LEVEL, esp>
+
+sl00: mov eax,[esp+8] ; (eax) -> ptr -> spinlock
+ mov eax,[eax] ; (eax) -> Spin structure
+ test eax, SYSTEM_ADDR
+ jz short sl_bogus
+
+
+ xor ecx, ecx ; Initialize spin count
+ xor edx, edx ; Initialize collide count
+
+;
+; Attempt to obtain the lock
+;
+
+sl10: lock bts [eax].LockValue, 0
+ jc short sl30 ; If lock is busy, go wait
+
+;
+; SpinLock is now owned
+;
+ inc [eax].NoAcquires ; accumulate statistic
+ add [eax].NoCollides, edx
+ lock add [eax].TotalSpinLow, ecx
+ adc [eax].TotalSpinHigh, 0
+
+ cmp [eax].HighestSpin, ecx
+ jc short sl20
+
+sl15: mov eax, [esp+12] ; pOldIrql
+ pop ecx ; OldIrql
+ mov byte ptr [eax], cl
+
+ stdRet _KeAcquireSpinLock
+
+align 4
+sl20: mov [eax].HighestSpin, ecx ; set new highest spin mark
+ jmp short sl15
+
+sl30: inc edx ; one more collide
+
+;
+; SpinLoop is kept small in order to get counts based on PcStallCunt
+;
+align 4
+sl50: inc ecx ; one more spin
+ test [eax].LockValue, 1 ; is it free?
+ jnz short sl50 ; no, loop
+
+ jmp short sl10 ; Go try again
+
+;
+; SpinLock was bogus - it's either a lock being used without being
+; initialized, or it's a lock we don't care to trace
+;
+
+sl_bogus:
+ mov eax, [esp+8]
+ test dword ptr [eax], LOCK_NOTTRACED
+ jz short sl_lazyinit
+
+sl60: lock bts dword ptr [eax], 0 ; attempt to acquire non-traced lock
+ jnc short sl15 ; if got it, return
+
+ xor ecx, ecx
+sl65: inc ecx
+ test dword ptr [eax], 1 ; wait for lock to be un-busy
+ jnz short sl65
+
+ lock add _KiSpinLockArray.TotalSpinLow, ecx
+ adc _KiSpinLockArray.TotalSpinHigh, 0
+ jmp short sl60
+
+;
+; Someone is using a lock which was not properly initialized, go do it now
+;
+
+sl_lazyinit:
+ stdCall SpinLockLazyInit,<eax>
+ jmp short sl00
+
+stdENDP _KeAcquireSpinLock
+
+
+ PAGE
+ SUBTTL "Release Kernel Spin Lock"
+;++
+;
+; VOID
+; KeReleaseSpinLock (
+; IN PKSPIN_LOCK SpinLock,
+; IN KIRQL NewIrql
+; )
+;
+; Routine Description:
+;
+; This function releases a kernel spin lock and lowers to the new irql
+;
+; Arguments:
+;
+; SpinLock (TOS+4) - Supplies a pointer to an executive spin lock.
+; NewIrql (TOS+8) - New irql value to set
+;
+; Return Value:
+;
+; None.
+;
+;--
+
+align 16
+cPublicProc _KeReleaseSpinLock ,2
+
+ mov eax,[esp+4] ; (eax) -> ptr -> spinlock
+ mov eax,[eax] ; SpinLock structure
+ test eax, SYSTEM_ADDR
+ jz short rsl_bogus
+
+ mov [eax].LockValue, 0 ; clear busy bit
+
+rsl10: pop eax ; (eax) = ret. address
+ mov [esp],eax ; set stack so we can jump directly
+ jmp _KeLowerIrql@4 ; to KeLowerIrql
+
+rsl_bogus:
+ mov eax, [esp+4]
+ test dword ptr [eax], LOCK_NOTTRACED
+ jz short rsl_lazyinit
+
+ btr dword ptr [eax], 0 ; clear lock bit on non-tracing lock
+ jmp short rsl10
+
+rsl_lazyinit: ; go initialize lock now
+ stdCall SpinLockLazyInit, <eax>
+ jmp short _KeReleaseSpinLock
+stdENDP _KeReleaseSpinLock
+
+ PAGE
+ SUBTTL "Ki Acquire Kernel Spin Lock"
+
+;++
+;
+; VOID
+; KiAcquireSpinLock (
+; IN PKSPIN_LOCK SpinLock
+; )
+;
+; Routine Description:
+;
+; This function acquires a kernel spin lock.
+;
+; N.B. This function assumes that the current IRQL is set properly.
+; It neither raises nor lowers IRQL.
+;
+; Arguments:
+;
+; SpinLock (TOS+4) - Supplies a pointer to an kernel spin lock.
+;
+; Return Value:
+;
+; None.
+;
+;--
+
+align 16
+cPublicProc _KiAcquireSpinLock ,1
+ mov eax,[esp+4] ; (eax) -> ptr -> spinlock
+ mov eax,[eax] ; (eax) -> Spin structure
+ test eax, SYSTEM_ADDR
+ jz short asl_bogus
+
+ xor ecx, ecx ; Initialize spin count
+ xor edx, edx ; Initialize collide count
+
+;
+; Attempt to obtain the lock
+;
+
+asl10: lock bts [eax].LockValue, 0
+ jc short asl40 ; If lock is busy, go wait
+
+;
+; SpinLock is owned
+;
+ inc [eax].NoAcquires ; accumulate statistics
+ add [eax].NoCollides, edx
+ lock add [eax].TotalSpinLow, ecx
+ adc [eax].TotalSpinHigh, 0
+
+ cmp [eax].HighestSpin, ecx
+ jc short asl20
+
+ stdRet _KiAcquireSpinLock
+
+align 4
+asl20: mov [eax].HighestSpin, ecx ; set new highest spin mark
+asl30: stdRet _KiAcquireSpinLock
+
+asl40: inc edx ; one more collide
+
+;
+; SpinLoop is kept small in order to get counts based on PcStallCunt
+;
+align 4
+asl50: inc ecx ; one more spin
+ test [eax].LockValue, 1 ; is it free?
+ jnz short asl50 ; no, loop
+ jmp short asl10 ; Go try again
+
+;
+; This is a non-initialized lock.
+;
+asl_bogus:
+ mov eax, [esp+4]
+ test dword ptr [eax], LOCK_NOTTRACED
+ jz asl_lazyinit
+
+asl60: lock bts dword ptr [eax], 0 ; attempt to acquire non-traced lock
+ jnc short asl30 ; if got it, return
+
+ xor ecx, ecx
+asl65: inc ecx
+ test dword ptr [eax], 1 ; wait for lock to be un-busy
+ jnz short asl65
+
+ lock add _KiSpinLockArray.TotalSpinLow, eax
+ adc _KiSpinLockArray.TotalSpinHigh, 0
+ jmp short asl60
+
+asl_lazyinit:
+ stdCall SpinLockLazyInit, <eax>
+ jmp short _KiAcquireSpinLock
+stdENDP _KiAcquireSpinLock
+
+ PAGE
+ SUBTTL "Ki Release Kernel Spin Lock"
+;++
+;
+; VOID
+; KiReleaseSpinLock (
+; IN PKSPIN_LOCK SpinLock
+; )
+;
+; Routine Description:
+;
+; This function releases a kernel spin lock.
+;
+; N.B. This function assumes that the current IRQL is set properly.
+; It neither raises nor lowers IRQL.
+;
+; Arguments:
+;
+; SpinLock (TOS+4) - Supplies a pointer to an executive spin lock.
+;
+; Return Value:
+;
+; None.
+;
+;--
+align 16
+cPublicProc _KiReleaseSpinLock ,1
+ mov eax,[esp+4] ; (eax) -> ptr -> spinlock
+ mov eax,[eax]
+ test eax, SYSTEM_ADDR
+ jz short irl_bogus
+
+ mov [eax].LockValue, 0
+ stdRET _KiReleaseSpinLock
+
+irl_bogus:
+ mov eax,[esp+4]
+ test dword ptr [eax], LOCK_NOTTRACED
+ jz short irl_lazyinit
+
+ btr dword ptr [eax], 0 ; clear busy bit on non-traced lock
+ stdRET _KiReleaseSpinLock
+
+irl_lazyinit:
+ stdCall SpinLockLazyInit, <eax>
+ stdRet _KiReleaseSpinLock
+stdENDP _KiReleaseSpinLock
+
+ PAGE
+ SUBTTL "Try to acquire Kernel Spin Lock"
+;++
+;
+; BOOLEAN
+; KeTryToAcquireSpinLock (
+; IN PKSPIN_LOCK SpinLock,
+; OUT PKIRQL OldIrql
+; )
+;
+; Routine Description:
+;
+; This function attempts acquires a kernel spin lock. If the
+; spinlock is busy, it is not acquire and FALSE is returned.
+;
+; Arguments:
+;
+; SpinLock (TOS+4) - Supplies a pointer to an kernel spin lock.
+; OldIrql (TOS+8) = Location to store old irql
+;
+; Return Value:
+; TRUE - Spinlock was acquired & irql was raise
+; FALSE - SpinLock was not acquired - irql is unchanged.
+;
+;--
+
+align dword
+cPublicProc _KeTryToAcquireSpinLock ,2
+
+;
+; This function is currently only used by the debugger, so we don't
+; keep stats on it
+;
+
+ mov eax,[esp+4] ; (eax) -> ptr -> spinlock
+ mov eax,[eax]
+ test eax, SYSTEM_ADDR
+ jz short tts_bogus
+
+
+;
+; First check the spinlock without asserting a lock
+;
+
+ test [eax].LockValue, 1
+ jnz short ttsl10
+
+;
+; Spinlock looks free raise irql & try to acquire it
+;
+
+ mov eax, [esp+8] ; (eax) -> ptr to OldIrql
+
+;
+; raise to dispatch_level
+;
+
+ stdCall _KeRaiseIrql, <DISPATCH_LEVEL, eax>
+
+ mov eax,[esp+4] ; (eax) -> ptr -> spinlock
+ mov eax,[eax]
+ lock bts [eax].LockValue, 0
+ jc short ttsl20
+
+ mov eax, 1 ; spinlock was acquired, return TRUE
+ stdRET _KeTryToAcquireSpinLock
+
+ttsl10:
+ xor eax, eax ; return FALSE
+ stdRET _KeTryToAcquireSpinLock
+
+ttsl20:
+ mov eax, [esp+8] ; spinlock was busy, restore irql
+ stdCall _KeLowerIrql, <dword ptr [eax]>
+
+ xor eax, eax ; return FALSE
+ stdRET _KeTryToAcquireSpinLock
+
+tts_bogus:
+ mov eax,[esp+4]
+ test dword ptr [eax], LOCK_NOTTRACED
+ jnz short tts_bogus2
+
+ stdCall SpinLockLazyInit, <eax>
+ jmp short _KeTryToAcquireSpinLock
+
+tts_bogus2:
+ stdCall _KeBugCheckEx,<SPIN_LOCK_INIT_FAILURE,eax,0,0,0> ; Not supported for now
+
+stdENDP _KeTryToAcquireSpinLock
+
+ PAGE
+ SUBTTL "Ki Try to acquire Kernel Spin Lock"
+;++
+;
+; BOOLEAN
+; KiTryToAcquireSpinLock (
+; IN PKSPIN_LOCK SpinLock
+; )
+;
+; Routine Description:
+;
+; This function attempts acquires a kernel spin lock. If the
+; spinlock is busy, it is not acquire and FALSE is returned.
+;
+; Arguments:
+;
+; SpinLock (TOS+4) - Supplies a pointer to an kernel spin lock.
+;
+; Return Value:
+; TRUE - Spinlock was acquired
+; FALSE - SpinLock was not acquired
+;
+;--
+align dword
+cPublicProc _KiTryToAcquireSpinLock ,1
+;
+; This function is currently only used by the debugger, so we don't
+; keep stats on it
+;
+
+ mov eax,[esp+4] ; (eax) -> ptr -> spinlock
+ mov eax,[eax]
+ test eax, SYSTEM_ADDR
+ jz short atsl_bogus
+
+
+;
+; First check the spinlock without asserting a lock
+;
+
+ test [eax].LockValue, 1
+ jnz short atsl10
+
+;
+ lock bts [eax].LockValue, 0
+ jc short atsl10
+
+atsl05:
+ mov eax, 1 ; spinlock was acquired, return TRUE
+ stdRET _KiTryToAcquireSpinLock
+
+atsl10:
+ xor eax, eax ; return FALSE
+ stdRET _KiTryToAcquireSpinLock
+
+atsl_bogus:
+ mov eax,[esp+4]
+ test dword ptr [eax], LOCK_NOTTRACED
+ jz short atsl_lazyinit
+
+ test dword ptr [eax], 1
+ jnz short atsl10
+
+ lock bts dword ptr [eax], 0
+ jnc short atsl05
+ jmp short atsl10
+
+atsl_lazyinit:
+ stdCall SpinLockLazyInit, <eax>
+ jmp short _KiTryToAcquireSpinLock
+
+stdENDP _KiTryToAcquireSpinLock
+
+
+;++
+;
+; KiInst_AcquireSpinLock
+;
+; Routine Description:
+; The NT_INST version of the macro ACQUIRE_SPINLOCK.
+; The macro thunks to this function so stats can be kept
+;
+; Arguments:
+; (eax) - SpinLock to acquire
+;
+; Return value:
+; CY - SpinLock was not acquired
+; NC - SpinLock was acquired
+;
+;--
+align dword
+cPublicProc KiInst_AcquireSpinLock, 0
+ test dword ptr [eax], SYSTEM_ADDR
+ jz short iasl_bogus
+
+ mov eax, [eax] ; Get SpinLock structure
+ lock bts [eax].LockValue, 0
+ jc short iasl_10 ; was busy, return CY
+
+ inc [eax].NoAcquires
+ mov eax, [eax].SpinLock
+ stdRET KiInst_AcquireSpinLock
+
+iasl_10:
+ inc [eax].NoCollides
+ mov eax, [eax].SpinLock
+ stdRET KiInst_AcquireSpinLock
+
+iasl_bogus:
+ test dword ptr [eax], LOCK_NOTTRACED
+ jz short iasl_lazyinit
+
+ lock bts dword ptr [eax], 0
+ stdRET KiInst_AcquireSpinLock
+
+iasl_lazyinit:
+ stdCall SpinLockLazyInit, <eax>
+ jmp short KiInst_AcquireSpinLock
+
+stdENDP KiInst_AcquireSpinLock
+
+
+;++
+;
+; KiInst_SpinOnSpinLock
+;
+; Routine Description:
+; The NT_INST version of the macro SPIN_ON_SPINLOCK.
+; The macro thunks to this function so stats can be kept
+;
+; Arguments:
+; (eax) - SpinLock to acquire
+;
+; Return value:
+; Returns when spinlock appears to be free
+;
+;--
+align dword
+cPublicProc KiInst_SpinOnSpinLock, 0
+ test dword ptr [eax], SYSTEM_ADDR
+ jz short issl_bogus
+
+ push ecx
+ mov eax, [eax] ; Get SpinLock structure
+ xor ecx, ecx ; initialize spincount
+
+align 4
+issl10: inc ecx ; one more spin
+ test [eax].LockValue, 1 ; is it free?
+ jnz short issl10 ; no, loop
+
+ lock add [eax].TotalSpinLow, ecx ; accumulate spin
+ adc [eax].TotalSpinHigh, 0
+
+ cmp [eax].HighestSpin, ecx
+ jc short issl20
+
+ mov eax, [eax].SpinLock ; restore eax
+ pop ecx
+ stdRet KiInst_SpinOnSpinLock
+
+issl20:
+ mov [eax].HighestSpin, ecx ; set new highest spin mark
+ mov eax, [eax].SpinLock ; restore eax
+ pop ecx
+ stdRet KiInst_SpinOnSpinLock
+
+issl_bogus:
+ test dword ptr [eax], LOCK_NOTTRACED
+ jz short issl_lazyinit
+
+ push ecx
+ xor ecx, ecx
+
+issl30: inc ecx
+ test dword ptr [eax], 1
+ jnz short issl30
+
+ lock add _KiSpinLockArray.TotalSpinLow, ecx
+ lock adc _KiSpinLockArray.TotalSpinHigh, 0
+ pop ecx
+
+ stdRet KiInst_SpinOnSpinLock
+
+issl_lazyinit:
+ stdCall SpinLockLazyInit, <eax>
+ stdRet KiInst_SpinOnSpinLock
+
+
+stdENDP KiInst_SpinOnSpinLock
+
+
+;++
+;
+; KiInst_ReleaseSpinLock
+;
+; Routine Description:
+; The NT_INST version of the macro ACQUIRE_SPINLOCK.
+; The macro thunks to this function so stats can be kept
+;
+; Arguments:
+; (eax) - SpinLock to acquire
+;
+; Return value:
+;
+;--
+align dword
+cPublicProc KiInst_ReleaseSpinLock, 0
+ test dword ptr [eax], SYSTEM_ADDR
+ jz short rssl_bogus
+
+ mov eax, [eax] ; Get SpinLock structure
+ mov [eax].LockValue, 0 ; Free it
+ mov eax, [eax].SpinLock ; Restore eax
+ stdRET KiInst_ReleaseSpinLock
+
+rssl_bogus:
+ test dword ptr [eax], LOCK_NOTTRACED
+ jz short rssl_lazyinit
+
+ btr dword ptr [eax], 0
+
+rssl_lazyinit:
+ stdCall SpinLockLazyInit, <eax>
+ stdRET KiInst_ReleaseSpinLock
+stdENDP KiInst_ReleaseSpinLock
+
+_TEXT$00 ends
+
+endif
+
+ end
+
+
diff --git a/private/ntos/ke/i386/spinlock.asm b/private/ntos/ke/i386/spinlock.asm
new file mode 100644
index 000000000..110a4429f
--- /dev/null
+++ b/private/ntos/ke/i386/spinlock.asm
@@ -0,0 +1,466 @@
+if NT_INST
+else
+ TITLE "Spin Locks"
+;++
+;
+; Copyright (c) 1989 Microsoft Corporation
+;
+; Module Name:
+;
+; spinlock.asm
+;
+; Abstract:
+;
+; This module implements the routines for acquiring and releasing
+; spin locks.
+;
+; Author:
+;
+; Bryan Willman (bryanwi) 13 Dec 89
+;
+; Environment:
+;
+; Kernel mode only.
+;
+; Revision History:
+;
+; Ken Reneris (kenr) 22-Jan-1991
+; Removed KeAcquireSpinLock macros, and made functions
+;--
+
+ PAGE
+
+.386p
+
+include ks386.inc
+include callconv.inc ; calling convention macros
+include i386\kimacro.inc
+include mac386.inc
+
+ EXTRNP KfRaiseIrql,1,IMPORT,FASTCALL
+ EXTRNP KfLowerIrql,1,IMPORT,FASTCALL
+ EXTRNP _KeGetCurrentIrql,0,IMPORT
+ EXTRNP _KeBugCheck,1
+
+
+_TEXT$00 SEGMENT PARA PUBLIC 'CODE'
+ ASSUME DS:FLAT, ES:FLAT, SS:NOTHING, FS:NOTHING, GS:NOTHING
+
+ PAGE
+ SUBTTL "Acquire Kernel Spin Lock"
+;++
+;
+; VOID
+; KeInializeSpinLock (
+; IN PKSPIN_LOCK SpinLock,
+;
+; Routine Description:
+;
+; This function initializes a SpinLock
+;
+; Arguments:
+;
+; SpinLock (TOS+4) - Supplies a pointer to an kernel spin lock.
+;
+; Return Value:
+;
+; None.
+;
+;--
+cPublicProc _KeInitializeSpinLock ,1
+cPublicFpo 1,0
+ mov eax, dword ptr [esp+4]
+ mov dword ptr [eax], 0
+ stdRET _KeInitializeSpinLock
+stdENDP _KeInitializeSpinLock
+
+
+
+ PAGE
+ SUBTTL "Ke Acquire Spin Lock At DPC Level"
+
+;++
+;
+; VOID
+; KefAcquireSpinLockAtDpcLevel (
+; IN PKSPIN_LOCK SpinLock
+; )
+;
+; Routine Description:
+;
+; This function acquires a kernel spin lock.
+;
+; N.B. This function assumes that the current IRQL is set properly.
+; It neither raises nor lowers IRQL.
+;
+; Arguments:
+;
+; (ecx) SpinLock - Supplies a pointer to an kernel spin lock.
+;
+; Return Value:
+;
+; None.
+;
+;--
+
+align 16
+cPublicFastCall KefAcquireSpinLockAtDpcLevel, 1
+cPublicFpo 0, 0
+if DBG
+ push ecx
+ stdCall _KeGetCurrentIrql
+ pop ecx
+
+ cmp al, DISPATCH_LEVEL
+ jne short asld50
+endif
+
+ifdef NT_UP
+ fstRET KefAcquireSpinLockAtDpcLevel
+else
+;
+; Attempt to assert the lock
+;
+
+asld10: ACQUIRE_SPINLOCK ecx,<short asld20>
+ fstRET KefAcquireSpinLockAtDpcLevel
+
+;
+; Lock is owned, spin till it looks free, then go get it again.
+;
+
+align 4
+asld20: SPIN_ON_SPINLOCK ecx,<short asld10>
+
+endif
+
+if DBG
+asld50: stdCall _KeBugCheck, <IRQL_NOT_GREATER_OR_EQUAL>
+endif
+
+fstENDP KefAcquireSpinLockAtDpcLevel
+
+
+;++
+;
+; VOID
+; KeAcquireSpinLockAtDpcLevel (
+; IN PKSPIN_LOCK SpinLock
+; )
+;
+; Routine Description:
+;
+; Thunk for standard call callers
+;
+;--
+
+cPublicProc _KeAcquireSpinLockAtDpcLevel, 1
+cPublicFpo 1,0
+
+ifndef NT_UP
+ mov ecx,[esp+4] ; SpinLock
+
+aslc10: ACQUIRE_SPINLOCK ecx,<short aslc20>
+ stdRET _KeAcquireSpinLockAtDpcLevel
+
+aslc20: SPIN_ON_SPINLOCK ecx,<short aslc10>
+endif
+ stdRET _KeAcquireSpinLockAtDpcLevel
+stdENDP _KeAcquireSpinLockAtDpcLevel
+
+
+ PAGE
+ SUBTTL "Ke Release Spin Lock From Dpc Level"
+;++
+;
+; VOID
+; KefReleaseSpinLockFromDpcLevel (
+; IN PKSPIN_LOCK SpinLock
+; )
+;
+; Routine Description:
+;
+; This function releases a kernel spin lock.
+;
+; N.B. This function assumes that the current IRQL is set properly.
+; It neither raises nor lowers IRQL.
+;
+; Arguments:
+;
+; (ecx) SpinLock - Supplies a pointer to an executive spin lock.
+;
+; Return Value:
+;
+; None.
+;
+;--
+align 16
+cPublicFastCall KefReleaseSpinLockFromDpcLevel ,1
+cPublicFpo 0,0
+ifndef NT_UP
+ RELEASE_SPINLOCK ecx
+endif
+ fstRET KefReleaseSpinLockFromDpcLevel
+
+fstENDP KefReleaseSpinLockFromDpcLevel
+
+;++
+;
+; VOID
+; KeReleaseSpinLockFromDpcLevel (
+; IN PKSPIN_LOCK SpinLock
+; )
+;
+; Routine Description:
+;
+; Thunk for standard call callers
+;
+;--
+
+cPublicProc _KeReleaseSpinLockFromDpcLevel, 1
+cPublicFpo 1,0
+ifndef NT_UP
+ mov ecx, [esp+4] ; (ecx) = SpinLock
+ RELEASE_SPINLOCK ecx
+endif
+ stdRET _KeReleaseSpinLockFromDpcLevel
+stdENDP _KeReleaseSpinLockFromDpcLevel
+
+
+
+ PAGE
+ SUBTTL "Ki Acquire Kernel Spin Lock"
+
+;++
+;
+; VOID
+; FASTCALL
+; KiAcquireSpinLock (
+; IN PKSPIN_LOCK SpinLock
+; )
+;
+; Routine Description:
+;
+; This function acquires a kernel spin lock.
+;
+; N.B. This function assumes that the current IRQL is set properly.
+; It neither raises nor lowers IRQL.
+;
+; Arguments:
+;
+; (ecx) SpinLock - Supplies a pointer to an kernel spin lock.
+;
+; Return Value:
+;
+; None.
+;
+;--
+
+align 16
+cPublicFastCall KiAcquireSpinLock ,1
+cPublicFpo 0,0
+ifndef NT_UP
+
+;
+; Attempt to assert the lock
+;
+
+asl10: ACQUIRE_SPINLOCK ecx,<short asl20>
+ fstRET KiAcquireSpinLock
+
+;
+; Lock is owned, spin till it looks free, then go get it again.
+;
+
+align 4
+asl20: SPIN_ON_SPINLOCK ecx,<short asl10>
+
+else
+ fstRET KiAcquireSpinLock
+endif
+
+fstENDP KiAcquireSpinLock
+
+ PAGE
+ SUBTTL "Ki Release Kernel Spin Lock"
+;++
+;
+; VOID
+; FASTCALL
+; KiReleaseSpinLock (
+; IN PKSPIN_LOCK SpinLock
+; )
+;
+; Routine Description:
+;
+; This function releases a kernel spin lock.
+;
+; N.B. This function assumes that the current IRQL is set properly.
+; It neither raises nor lowers IRQL.
+;
+; Arguments:
+;
+; (ecx) SpinLock - Supplies a pointer to an executive spin lock.
+;
+; Return Value:
+;
+; None.
+;
+;--
+align 16
+cPublicFastCall KiReleaseSpinLock ,1
+cPublicFpo 0,0
+ifndef NT_UP
+
+ RELEASE_SPINLOCK ecx
+
+endif
+ fstRET KiReleaseSpinLock
+
+fstENDP KiReleaseSpinLock
+
+ PAGE
+ SUBTTL "Try to acquire Kernel Spin Lock"
+
+;++
+;
+; BOOLEAN
+; KeTryToAcquireSpinLock (
+; IN PKSPIN_LOCK SpinLock,
+; OUT PKIRQL OldIrql
+; )
+;
+; Routine Description:
+;
+; This function attempts acquires a kernel spin lock. If the
+; spinlock is busy, it is not acquire and FALSE is returned.
+;
+; Arguments:
+;
+; SpinLock (TOS+4) - Supplies a pointer to an kernel spin lock.
+; OldIrql (TOS+8) = Location to store old irql
+;
+; Return Value:
+; TRUE - Spinlock was acquired & irql was raise
+; FALSE - SpinLock was not acquired - irql is unchanged.
+;
+;--
+
+align dword
+cPublicProc _KeTryToAcquireSpinLock ,2
+cPublicFpo 2,0
+
+ifdef NT_UP
+; UP Version of KeTryToAcquireSpinLock
+
+ mov ecx, DISPATCH_LEVEL
+ fstCall KfRaiseIrql
+
+ mov ecx, [esp+8] ; (ecx) -> ptr to OldIrql
+ mov [ecx], al ; save OldIrql
+
+ mov eax, 1 ; Return TRUE
+ stdRET _KeTryToAcquireSpinLock
+
+else
+; MP Version of KeTryToAcquireSpinLock
+
+ mov edx,[esp+4] ; (edx) -> spinlock
+
+;
+; First check the spinlock without asserting a lock
+;
+
+ TEST_SPINLOCK edx,<short ttsl10>
+
+;
+; Spinlock looks free raise irql & try to acquire it
+;
+
+;
+; raise to dispatch_level
+;
+
+ mov ecx, DISPATCH_LEVEL
+ fstCall KfRaiseIrql
+
+ mov edx, [esp+4] ; (edx) -> spinlock
+ mov ecx, [esp+8] ; (ecx) = Return OldIrql
+
+ ACQUIRE_SPINLOCK edx,<short ttsl20>
+
+ mov [ecx], al ; save OldIrql
+ mov eax, 1 ; spinlock was acquired, return TRUE
+
+ stdRET _KeTryToAcquireSpinLock
+
+ttsl10:
+ xor eax, eax ; return FALSE
+ stdRET _KeTryToAcquireSpinLock
+
+ttsl20:
+ mov cl, al ; (cl) = OldIrql
+ fstCall KfLowerIrql ; spinlock was busy, restore irql
+ xor eax, eax ; return FALSE
+ stdRET _KeTryToAcquireSpinLock
+endif
+
+stdENDP _KeTryToAcquireSpinLock
+
+ PAGE
+ SUBTTL "Ki Try to acquire Kernel Spin Lock"
+;++
+;
+; BOOLEAN
+; KiTryToAcquireSpinLock (
+; IN PKSPIN_LOCK SpinLock
+; )
+;
+; Routine Description:
+;
+; This function attempts acquires a kernel spin lock. If the
+; spinlock is busy, it is not acquire and FALSE is returned.
+;
+; Arguments:
+;
+; SpinLock (TOS+4) - Supplies a pointer to an kernel spin lock.
+;
+; Return Value:
+; TRUE - Spinlock was acquired
+; FALSE - SpinLock was not acquired
+;
+;--
+align dword
+cPublicProc _KiTryToAcquireSpinLock ,1
+cPublicFpo 1,0
+
+ifndef NT_UP
+ mov eax,[esp+4] ; (eax) -> spinlock
+
+;
+; First check the spinlock without asserting a lock
+;
+
+ TEST_SPINLOCK eax,<short atsl20>
+
+;
+; Spinlock looks free try to acquire it
+;
+
+ ACQUIRE_SPINLOCK eax,<short atsl20>
+endif
+ mov eax, 1 ; spinlock was acquired, return TRUE
+ stdRET _KiTryToAcquireSpinLock
+
+ifndef NT_UP
+atsl20:
+ xor eax, eax ; return FALSE
+ stdRET _KiTryToAcquireSpinLock
+endif
+stdENDP _KiTryToAcquireSpinLock
+
+
+_TEXT$00 ends
+
+endif ; NT_INST
+ end
diff --git a/private/ntos/ke/i386/table.stb b/private/ntos/ke/i386/table.stb
new file mode 100644
index 000000000..cc05775b9
--- /dev/null
+++ b/private/ntos/ke/i386/table.stb
@@ -0,0 +1,102 @@
+0 ; This is the number of in register arguments
+;++
+;
+; Copyright (c) 1989 Microsoft Corporation
+;
+; Module Name:
+;
+; systable.asm
+;
+; Abstract:
+;
+; This module implements the system service dispatch table.
+;
+; Author:
+;
+; Shie-Lin Tzong (shielint) 6-Feb-1990
+;
+; Environment:
+;
+; Kernel mode only.
+;
+; Revision History:
+;
+;--
+
+;
+; To add a system service simply add the name of the service to the below
+; table. If the system service has arguments, then immediately
+; follow the name of the serice with a comma and following that the number
+; of bytes of in memory arguments, e.g. CreateObject,40.
+;
+
+;ifdef i386
+
+.386p
+include callconv.inc
+TABLE_BEGIN1 macro t
+ TITLE t
+endm
+TABLE_BEGIN2 macro t
+_DATA SEGMENT DWORD PUBLIC 'DATA'
+ ASSUME DS:FLAT
+endm
+TABLE_BEGIN3 macro t
+ align 4
+endm
+TABLE_BEGIN4 macro t
+ public _KiServiceTable
+_KiServiceTable label dword
+endm
+TABLE_BEGIN5 macro t
+endm
+TABLE_BEGIN6 macro t
+endm
+TABLE_BEGIN7 macro t
+endm
+TABLE_BEGIN8 macro t
+endm
+
+TABLE_ENTRY macro l,bias,numargs
+ Local Bytes
+
+ Bytes = numargs*4
+
+ EXTRNP _Nt&l,&numargs
+IFDEF STD_CALL
+ ComposeInst <dd offset FLAT:>,_Nt,l,<@>,%(Bytes)
+ELSE
+ dd offset FLAT:_Nt&l
+ENDIF
+endm
+
+TABLE_END macro n
+ public _KiServiceLimit
+_KiServiceLimit dd n+1
+endm
+
+ARGTBL_BEGIN macro
+ public _KiArgumentTable
+_KiArgumentTable label dword
+endm
+
+ARGTBL_ENTRY macro e0,e1,e2,e3,e4,e5,e6,e7
+ db e0,e1,e2,e3,e4,e5,e6,e7
+endm
+
+ARGTBL_END macro
+_DATA ENDS
+ end
+endm
+
+;endif
+
+ TABLE_BEGIN1 <"System Service Dispatch Table">
+ TABLE_BEGIN2 <"System Service Dispatch Table">
+ TABLE_BEGIN3 <"System Service Dispatch Table">
+ TABLE_BEGIN4 <"System Service Dispatch Table">
+ TABLE_BEGIN5 <"System Service Dispatch Table">
+ TABLE_BEGIN6 <"System Service Dispatch Table">
+ TABLE_BEGIN7 <"System Service Dispatch Table">
+ TABLE_BEGIN8 <"System Service Dispatch Table">
+
diff --git a/private/ntos/ke/i386/threadbg.asm b/private/ntos/ke/i386/threadbg.asm
new file mode 100644
index 000000000..08631b86e
--- /dev/null
+++ b/private/ntos/ke/i386/threadbg.asm
@@ -0,0 +1,99 @@
+ title "Thread Startup"
+
+;++
+;
+; Copyright (c) 1989 Microsoft Corporation
+;
+; Module Name:
+;
+; threadbg.asm
+;
+; Abstract:
+;
+; This module implements the code necessary to startup a thread in kernel
+; mode.
+;
+; Author:
+;
+; Bryan Willman (bryanwi) 22-Feb-1990, derived from DaveC's code.
+;
+; Environment:
+;
+; Kernel mode only, IRQL APC_LEVEL.
+;
+; Revision History:
+;
+;--
+
+.386p
+ .xlist
+include ks386.inc
+include i386\kimacro.inc
+include callconv.inc
+ .list
+
+ EXTRNP KfLowerIrql,1,IMPORT, FASTCALL
+ EXTRNP _KeBugCheck,1
+ extrn _KiServiceExit2:PROC
+
+ page ,132
+ subttl "Thread Startup"
+
+_TEXT$00 SEGMENT DWORD PUBLIC 'CODE'
+ ASSUME DS:FLAT, ES:FLAT, SS:NOTHING, FS:NOTHING, GS:NOTHING
+
+;++
+;
+; Routine Description:
+;
+; This routine is called at thread startup. Its function is to call the
+; initial thread procedure. If control returns from the initial thread
+; procedure and a user mode context was established when the thread
+; was initialized, then the user mode context is restored and control
+; is transfered to user mode. Otherwise a bug check will occur.
+;
+;
+; Arguments:
+;
+; (TOS) = SystemRoutine - address of initial system routine.
+; (TOS+4) = StartRoutine - Initial thread routine.
+; (TOS+8) = StartContext - Context parm for initial thread routine.
+; (TOS+12) = UserContextFlag - 0 if no user context, !0 if there is one
+; (TOS+16) = Base of KTrapFrame if and only if there's a user context.
+;
+; Return Value:
+;
+; None.
+;
+;--
+
+cPublicProc _KiThreadStartup ,1
+
+ xor ebx,ebx ; clear registers
+ xor esi,esi ;
+ xor edi,edi ;
+ xor ebp,ebp ;
+ mov ecx, APC_LEVEL
+ fstCall KfLowerIrql ; KeLowerIrql(APC_LEVEL)
+
+ pop eax ; (eax)->SystemRoutine
+ call eax ; SystemRoutine(StartRoutine, StartContext)
+IFNDEF STD_CALL
+ add esp,8 ; Clear off args
+ENDIF
+
+ pop ecx ; (ecx) = UserContextFlag
+ or ecx, ecx
+ jz short kits10 ; No user context, go bugcheck
+
+ mov ebp,esp ; (bp) -> TrapFrame holding UserContext
+
+ jmp _KiServiceExit2
+
+kits10: stdCall _KeBugCheck, <NO_USER_MODE_CONTEXT>
+
+stdENDP _KiThreadStartup
+
+_TEXT$00 ends
+ end
+
diff --git a/private/ntos/ke/i386/thredini.c b/private/ntos/ke/i386/thredini.c
new file mode 100644
index 000000000..30dfc9f91
--- /dev/null
+++ b/private/ntos/ke/i386/thredini.c
@@ -0,0 +1,634 @@
+/*++
+
+Copyright (c) 1990 Microsoft Corporation
+
+Module Name:
+
+ thredini.c
+
+Abstract:
+
+ This module implements the machine dependent function to set the initial
+ context and data alignment handling mode for a process or thread object.
+
+Author:
+
+ David N. Cutler (davec) 31-Mar-1990
+
+Environment:
+
+ Kernel mode only.
+
+Revision History:
+
+ 3 April 90 bryan willman
+
+ This version ported to 386.
+
+--*/
+
+#include "ki.h"
+
+//
+// The following assert macros are used to check that an input object is
+// really the proper type.
+//
+
+#define ASSERT_PROCESS(E) { \
+ ASSERT((E)->Header.Type == ProcessObject); \
+}
+
+#define ASSERT_THREAD(E) { \
+ ASSERT((E)->Header.Type == ThreadObject); \
+}
+
+//
+// Our notion of alignment is different, so force use of ours
+//
+#undef ALIGN_UP
+#undef ALIGN_DOWN
+#define ALIGN_DOWN(address,amt) ((ULONG)(address) & ~(( amt ) - 1))
+#define ALIGN_UP(address,amt) (ALIGN_DOWN( (address + (amt) - 1), (amt) ))
+
+//
+// The function prototype for the special APC we use to set the
+// hardware alignment state for a thread
+//
+
+VOID
+KepSetAlignmentSpecialApc(
+ IN PKAPC Apc,
+ IN PKNORMAL_ROUTINE *NormalRoutine,
+ IN PVOID *NormalContext,
+ IN PVOID *SystemArgument1,
+ IN PVOID *SystemArgument2
+ );
+
+
+VOID
+KiInitializeContextThread (
+ IN PKTHREAD Thread,
+ IN PKSYSTEM_ROUTINE SystemRoutine,
+ IN PKSTART_ROUTINE StartRoutine OPTIONAL,
+ IN PVOID StartContext OPTIONAL,
+ IN PCONTEXT ContextFrame OPTIONAL
+ )
+
+/*++
+
+Routine Description:
+
+ This function initializes the machine dependent context of a thread object.
+
+ N.B. This function does not check the accessibility of the context record.
+ It is assumed the the caller of this routine is either prepared to
+ handle access violations or has probed and copied the context record
+ as appropriate.
+
+Arguments:
+
+ Thread - Supplies a pointer to a dispatcher object of type thread.
+
+ SystemRoutine - Supplies a pointer to the system function that is to be
+ called when the thread is first scheduled for execution.
+
+ StartRoutine - Supplies an optional pointer to a function that is to be
+ called after the system has finished initializing the thread. This
+ parameter is specified if the thread is a system thread and will
+ execute totally in kernel mode.
+
+ StartContext - Supplies an optional pointer to an arbitrary data structure
+ which will be passed to the StartRoutine as a parameter. This
+ parameter is specified if the thread is a system thread and will
+ execute totally in kernel mode.
+
+ ContextFrame - Supplies an optional pointer a context frame which contains
+ the initial user mode state of the thread. This parameter is specified
+ if the thread is a user thread and will execute in user mode. If this
+ parameter is not specified, then the Teb parameter is ignored.
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ PFLOATING_SAVE_AREA NpxFrame;
+ PKSWITCHFRAME SwitchFrame;
+ PKTRAP_FRAME TrFrame;
+ PULONG PSystemRoutine;
+ PULONG PStartRoutine;
+ PULONG PStartContext;
+ PULONG PUserContextFlag;
+ ULONG ContextFlags;
+ CONTEXT Context2;
+ PCONTEXT ContextFrame2 = NULL;
+
+ //
+ // If a context frame is specified, then initialize a trap frame and
+ // and an exception frame with the specified user mode context.
+ //
+
+ if (ARGUMENT_PRESENT(ContextFrame)) {
+
+ RtlMoveMemory(&Context2, ContextFrame, sizeof(CONTEXT));
+ ContextFrame2 = &Context2;
+ ContextFlags = CONTEXT_CONTROL;
+
+ //
+ // The 80387 save area is at the very base of the kernel stack.
+ //
+
+ NpxFrame = (PFLOATING_SAVE_AREA)(((ULONG)(Thread->InitialStack) -
+ sizeof(FLOATING_SAVE_AREA)));
+
+ //
+ // Load up an initial NPX state.
+ //
+
+ ContextFrame2->FloatSave.ControlWord = 0x27f; // like fpinit but 64bit mode
+ ContextFrame2->FloatSave.StatusWord = 0;
+ ContextFrame2->FloatSave.TagWord = 0xffff;
+ ContextFrame2->FloatSave.ErrorOffset = 0;
+ ContextFrame2->FloatSave.ErrorSelector = 0;
+ ContextFrame2->FloatSave.DataOffset = 0;
+ ContextFrame2->FloatSave.DataSelector = 0;
+
+
+ if (KeI386NpxPresent) {
+ ContextFrame2->FloatSave.Cr0NpxState = 0;
+ NpxFrame->Cr0NpxState = 0;
+ ContextFlags |= CONTEXT_FLOATING_POINT;
+
+ //
+ // Threads NPX state is not in the coprocessor.
+ //
+
+ Thread->NpxState = NPX_STATE_NOT_LOADED;
+
+ } else {
+ NpxFrame->Cr0NpxState = CR0_EM;
+
+ //
+ // Threads NPX state is not in the coprocessor.
+ // In the emulator case, do not set the CR0_EM bit as their
+ // emulators may not want exceptions on FWAIT instructions.
+ //
+
+ Thread->NpxState = NPX_STATE_NOT_LOADED & ~CR0_MP;
+ }
+
+ //
+ // Force debug registers off. They won't work anyway from an
+ // initial frame, debuggers must set a hard breakpoint in the target
+ //
+
+ ContextFrame2->Dr0 = 0;
+ ContextFrame2->Dr1 = 0;
+ ContextFrame2->Dr2 = 0;
+ ContextFrame2->Dr3 = 0;
+ ContextFrame2->Dr6 = 0;
+ ContextFrame2->Dr7 = 0;
+ ContextFrame2->ContextFlags &= ~(CONTEXT_DEBUG_REGISTERS);
+#if 0
+ //
+ // If AutoAlignment is FALSE, we want to set the Alignment Check bit
+ // in Eflags, so we will get alignment faults.
+ //
+
+ if (Thread->AutoAlignment == FALSE) {
+ ContextFrame2->EFlags |= EFLAGS_ALIGN_CHECK;
+ }
+#endif
+ //
+ // If the thread is set
+
+ TrFrame = (PKTRAP_FRAME)(((ULONG)NpxFrame - KTRAP_FRAME_LENGTH));
+
+ // Space for arguments to KiThreadStartup. Order is important,
+ // Since args are passed on stack through KiThreadStartup to
+ // PStartRoutine with PStartContext as an argument.
+
+ PUserContextFlag = (PULONG)TrFrame - 1;
+ PStartContext = PUserContextFlag - 1;
+ PStartRoutine = PStartContext - 1;
+ PSystemRoutine = PStartRoutine - 1;
+
+ SwitchFrame = (PKSWITCHFRAME)((PUCHAR)PSystemRoutine -
+ sizeof(KSWITCHFRAME));
+
+ //
+ // Copy information from the specified context frame to the trap and
+ // exception frames.
+ //
+
+ KeContextToKframes(TrFrame, NULL, ContextFrame2,
+ ContextFrame2->ContextFlags | ContextFlags,
+ UserMode);
+
+ TrFrame->HardwareSegSs |= RPL_MASK;
+ TrFrame->SegDs |= RPL_MASK;
+ TrFrame->SegEs |= RPL_MASK;
+
+#if DBG
+ TrFrame->DbgArgMark = 0xBADB0D00;
+#endif
+
+ //
+ // Tell KiThreadStartup that a user context is present.
+ //
+
+ *PUserContextFlag = 1;
+
+
+ //
+ // Initialize the kernel mode ExceptionList pointer
+ //
+
+ TrFrame->ExceptionList = EXCEPTION_CHAIN_END;
+
+ //
+ // Initialize the saved previous processor mode.
+ //
+
+ TrFrame->PreviousPreviousMode = UserMode;
+
+ //
+ // Set the previous mode in thread object to user.
+ //
+
+ Thread->PreviousMode = UserMode;
+
+
+ } else {
+
+ //
+ // Dummy floating save area. Kernel threads don't have or use
+ // the floating point - the dummy save area is make the stacks
+ // consistent.
+ //
+
+ NpxFrame = (PFLOATING_SAVE_AREA)(((ULONG)(Thread->InitialStack) -
+ sizeof(FLOATING_SAVE_AREA)));
+
+
+ //
+ // Load up an initial NPX state.
+ //
+
+ NpxFrame->ControlWord = 0x27f; // like fpinit but 64bit mode
+ NpxFrame->StatusWord = 0;
+ NpxFrame->TagWord = 0xffff;
+ NpxFrame->ErrorOffset = 0;
+ NpxFrame->ErrorSelector = 0;
+ NpxFrame->DataOffset = 0;
+ NpxFrame->DataSelector = 0;
+
+ NpxFrame->Cr0NpxState = 0;
+
+ //
+ // Threads NPX state is not in the coprocessor.
+ //
+
+ Thread->NpxState = NPX_STATE_NOT_LOADED;
+
+ //
+ // Space for arguments to KiThreadStartup.
+ // Order of fields in the switchframe is important,
+ // Since args are passed on stack through KiThreadStartup to
+ // PStartRoutine with PStartContext as an argument.
+ //
+
+ PUserContextFlag = (PULONG)((ULONG)NpxFrame) - 1;
+
+ PStartContext = PUserContextFlag - 1;
+ PStartRoutine = PStartContext - 1;
+ PSystemRoutine = PStartRoutine - 1;
+
+ SwitchFrame = (PKSWITCHFRAME)((PUCHAR)PSystemRoutine -
+ sizeof(KSWITCHFRAME));
+
+
+ //
+ // Tell KiThreadStartup that a user context is NOT present.
+ //
+
+ *PUserContextFlag = 0;
+
+
+ //
+ // Set the previous mode in thread object to kernel.
+ //
+
+ Thread->PreviousMode = KernelMode;
+ }
+
+ //
+ // Set up thread start parameters.
+ // (UserContextFlag set above)
+ //
+
+ *PStartContext = (ULONG)StartContext;
+ *PStartRoutine = (ULONG)StartRoutine;
+ *PSystemRoutine = (ULONG)SystemRoutine;
+
+
+ //
+ // Set up switch frame. Assume the thread doesn't use the 80387;
+ // if it ever does (and there is one), these flags will get reset.
+ // Each thread starts with these same flags set, regardless of
+ // whether the hardware exists or not.
+ //
+
+ SwitchFrame->RetAddr = (ULONG)KiThreadStartup;
+
+ SwitchFrame->Eflags = EFLAGS_INTERRUPT_MASK;
+
+#if 0
+ //
+ // If AutoAlignment is FALSE, we want to set the Alignment Check bit
+ // in Eflags, so we will get alignment faults.
+ //
+
+ if (Thread->AutoAlignment == FALSE) {
+ SwitchFrame->Eflags |= EFLAGS_ALIGN_CHECK;
+ }
+#endif
+
+ SwitchFrame->ExceptionList = (ULONG)(EXCEPTION_CHAIN_END);
+
+ //
+ // Set the initial kernel stack pointer.
+ //
+
+ Thread->KernelStack = (PVOID)SwitchFrame;
+ return;
+}
+
+BOOLEAN
+KeSetAutoAlignmentProcess (
+ IN PKPROCESS Process,
+ IN BOOLEAN Enable
+ )
+
+/*++
+
+Routine Description:
+
+ This function sets the data alignment handling mode for the specified
+ process and returns the previous data alignment handling mode.
+
+Arguments:
+
+ Process - Supplies a pointer to a dispatcher object of type process.
+
+ Enable - Supplies a boolean value that determines the handling of data
+ alignment exceptions for the process. A value of TRUE causes all
+ data alignment exceptions to be automatically handled by the kernel.
+ A value of FALSE causes all data alignment exceptions to be actually
+ raised as exceptions.
+
+Return Value:
+
+ A value of TRUE is returned if data alignment exceptions were
+ previously automatically handled by the kernel. Otherwise, a value
+ of FALSE is returned.
+
+--*/
+
+{
+
+ KIRQL OldIrql;
+ BOOLEAN Previous;
+
+ ASSERT_PROCESS(Process);
+
+ //
+ // Raise IRQL to dispatcher level and lock dispatcher database.
+ //
+
+ KiLockDispatcherDatabase(&OldIrql);
+
+ //
+ // Capture the previous data alignment handling mode and set the
+ // specified data alignment mode.
+ //
+
+ Previous = Process->AutoAlignment;
+ Process->AutoAlignment = Enable;
+
+ //
+ // Unlock dispatcher database, lower IRQL to its previous value, and
+ // return the previous data alignment mode.
+ //
+
+ KiUnlockDispatcherDatabase(OldIrql);
+ return Previous;
+}
+
+BOOLEAN
+KeSetAutoAlignmentThread (
+ IN PKTHREAD Thread,
+ IN BOOLEAN Enable
+ )
+
+/*++
+
+Routine Description:
+
+ This function sets the data alignment handling mode for the specified
+ thread and returns the previous data alignment handling mode.
+
+Arguments:
+
+ Thread - Supplies a pointer to a dispatcher object of type thread.
+
+ Enable - Supplies a boolean value that determines the handling of data
+ alignment exceptions for the specified thread. A value of TRUE causes
+ all data alignment exceptions to be automatically handled by the kernel.
+ A value of FALSE causes all data alignment exceptions to be actually
+ raised as exceptions.
+
+Return Value:
+
+ A value of TRUE is returned if data alignment exceptions were
+ previously automatically handled by the kernel. Otherwise, a value
+ of FALSE is returned.
+
+--*/
+
+{
+
+ BOOLEAN Previous;
+ PKAPC Apc;
+ PKEVENT Event;
+ KIRQL OldIrql;
+
+ ASSERT_THREAD(Thread);
+
+ //
+ // Raise IRQL to dispatcher level and lock dispatcher database.
+ //
+
+ KiLockDispatcherDatabase(&OldIrql);
+
+ //
+ // Capture the previous data alignment handling mode and set the
+ // specified data alignment mode.
+ //
+
+ Previous = Thread->AutoAlignment;
+ Thread->AutoAlignment = Enable;
+
+ //
+ // Unlock dispatcher database and lower IRQL to its previous value.
+ //
+
+ KiUnlockDispatcherDatabase(OldIrql);
+
+#if 0
+ Apc = ExAllocatePool(NonPagedPoolMustSucceed, sizeof(KAPC));
+ Event = ExAllocatePool(NonPagedPoolMustSucceed, sizeof(KEVENT));
+
+ KeInitializeEvent(Event, NotificationEvent, FALSE);
+
+ if ( Thread == KeGetCurrentThread() ) {
+
+ Apc->SystemArgument1 = Thread;
+ Apc->SystemArgument2 = Event;
+
+ KeRaiseIrql(APC_LEVEL, &Irql);
+ KepSetAlignmentSpecialApc( Apc, NULL, NULL,
+ &Apc->SystemArgument1,
+ &Apc->SystemArgument2 );
+ KeLowerIrql(Irql);
+ } else {
+ KeInitializeApc( Apc,
+ Thread,
+ CurrentApcEnvironment,
+ KepSetAlignmentSpecialApc,
+ NULL,
+ NULL,
+ KernelMode,
+ NULL );
+
+ if (!KeInsertQueueApc( Apc,
+ Thread,
+ Event,
+ 2 ) ) {
+ //
+ // We couldn't queue the APC, so we will not be able to change
+ // the AutoAlignment. Update the thread object so that it
+ // stays in sync with the hardware state.
+ //
+#if DBG
+ DbgPrint("KeSetAutoAlignmentThread: unable to change thread's context\n");
+#endif
+ Thread->AutoAlignment = Previous;
+ }
+
+ KeWaitForSingleObject( Event,
+ Executive,
+ KernelMode,
+ FALSE,
+ NULL );
+ }
+
+ ExFreePool(Apc);
+ ExFreePool(Event);
+#endif
+
+ return(Previous);
+}
+
+#if 0
+
+VOID
+KepSetAlignmentSpecialApc(
+ IN PKAPC Apc,
+ IN PKNORMAL_ROUTINE *NormalRoutine,
+ IN PVOID *NormalContext,
+ IN PVOID *SystemArgument1,
+ IN PVOID *SystemArgument2
+ )
+
+/*++
+
+Routine Description:
+
+ This function updates the alignment check bit of the current thread's
+ EFLAGS to reflect the AutoAlignment setting of the thread object.
+
+Arguments:
+
+ Apc - Supplies a pointer to the APC control object that caused entry
+ into this routine.
+
+ NormalRoutine - Supplies a pointer to a pointer to the normal routine
+ function that was specifed when the APC was initialized.
+
+ NormalContext - Supplies a pointer to a pointer to an arbitrary data
+ structure that was specified when the APC was initialized.
+
+ SystemArgument1 - Supplies a pointer to a PKTHREAD
+
+ SystemArgument2 - Supplies a pointer to a PKEVENT
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ PKTHREAD Thread;
+ PKEVENT Event;
+ PKTRAP_FRAME TrapFrame;
+ CONTEXT ContextFrame;
+
+ Thread = *(PKTHREAD *)SystemArgument1;
+ Event = *(PKEVENT *)SystemArgument2;
+
+ ASSERT( Thread == KeGetCurrentThread() );
+
+ //
+ // Find the trap frame on the stack, so we can get the thread context
+ //
+ TrapFrame = (PKTRAP_FRAME)((PUCHAR)Thread->InitialStack -
+ ALIGN_UP(sizeof(KTRAP_FRAME),KTRAP_FRAME_ALIGN) -
+ sizeof(FLOATING_SAVE_AREA));
+
+ ContextFrame.ContextFlags = CONTEXT_CONTROL;
+
+ KeContextFromKframes( TrapFrame,
+ NULL,
+ &ContextFrame );
+
+ //
+ // If AutoAlignment is TRUE, we want the processor to transparently fixup
+ // all alignment faults, so we clear the Alignment Check bit. If
+ // AutoAlignment is FALSE, we set the bit, so 486 processors will
+ // give us alignment faults.
+ //
+
+ if (Thread->AutoAlignment) {
+ ContextFrame.EFlags &= (~EFLAGS_ALIGN_CHECK);
+ } else {
+ ContextFrame.EFlags |= EFLAGS_ALIGN_CHECK;
+ }
+
+ //
+ // Replace the modified EFlags in the trap frame. When the thread returns
+ // to user mode, it will be running with the new alignment setting.
+ //
+
+ KeContextToKframes( TrapFrame,
+ NULL,
+ &ContextFrame,
+ CONTEXT_CONTROL,
+ KeGetPreviousMode() );
+
+ KeSetEvent(Event,0,FALSE);
+}
+#endif
diff --git a/private/ntos/ke/i386/timindex.asm b/private/ntos/ke/i386/timindex.asm
new file mode 100644
index 000000000..36aa3ed70
--- /dev/null
+++ b/private/ntos/ke/i386/timindex.asm
@@ -0,0 +1,171 @@
+ TITLE "Compute Timer Table Index"
+;++
+;
+; Copyright (c) 1993 Microsoft Corporation
+;
+; Module Name:
+;
+; timindex.asm
+;
+; Abstract:
+;
+; This module implements the code necessary to compute the timer table
+; index for a timer.
+;
+; Author:
+;
+; David N. Cutler (davec) 19-May-1993
+;
+; Environment:
+;
+; Any mode.
+;
+; Revision History:
+;
+;--
+
+.386p
+ .xlist
+include ks386.inc
+include callconv.inc ; calling convention macros
+ .list
+
+ extrn _KiTimeIncrementReciprocal:dword
+ extrn _KiTimeIncrementShiftCount:BYTE
+
+_TEXT$00 SEGMENT DWORD PUBLIC 'CODE'
+ ASSUME DS:FLAT, ES:FLAT, SS:NOTHING, FS:NOTHING, GS:NOTHING
+
+ page
+ subttl "Compute Timer Table Index"
+;++
+;
+; ULONG
+; KiComputeTimerTableIndex (
+; IN LARGE_INTEGER Interval,
+; IN LARGE_INTEGER CurrentTime,
+; IN PKTIMER Timer
+; )
+;
+; Routine Description:
+;
+; This function computes the timer table index for the specified timer
+; object and stores the due time in the timer object.
+;
+; N.B. The interval parameter is guaranteed to be negative since it is
+; expressed as relative time.
+;
+; The formula for due time calculation is:
+;
+; Due Time = Current Time - Interval
+;
+; The formula for the index calculation is:
+;
+; Index = (Due Time / Maximum time) & (Table Size - 1)
+;
+; The time increment division is performed using reciprocal multiplication.
+;
+; Arguments:
+;
+; Interval - Supplies the relative time at which the timer is to
+; expire.
+;
+; CurrentCount - Supplies the current system tick count.
+;
+; Timer - Supplies a pointer to a dispatch object of type timer.
+;
+; Return Value:
+;
+; The time table index is returned as the function value and the due
+; time is stored in the timer object.
+;
+;--
+
+LocalStack equ 20
+
+Interval equ [esp+LocalStack+4]
+CurrentTime equ [esp+LocalStack+12]
+Timer equ [esp+LocalStack+20]
+
+cPublicProc _KiComputeTimerTableIndex ,5
+ sub esp, LocalStack
+ mov [esp+16], ebx
+ mov ebx,CurrentTime ; get low current time
+ mov ecx,CurrentTime + 4 ; get high current time
+ sub ebx,Interval ; subtract low parts
+ sbb ecx,Interval + 4 ; subtract high parts and borrow
+ mov eax,Timer ; get address of timer object
+ mov [eax].TiDueTime.LiLowPart,ebx ; set low part of due time
+ mov [eax].TiDueTime.LiHighPart,ecx ; set high part of due time
+
+;
+; Compute low 32-bits of dividend times low 32-bits of divisor.
+;
+
+ mov eax,ebx ; copy low 32-bits of dividend
+ mul [_KiTimeIncrementReciprocal] ; multiply by low 32-bits of divisor
+ mov [esp+12], edx ; save high order 32-bits of product
+
+;
+; Compute low 32-bits of dividend times high 32-bits of divisor.
+;
+
+ mov eax,ebx ; copy low 32-bits of dividend
+ mul [_KiTimeIncrementReciprocal+4] ;multiply by high 32-bits of divisor
+ mov [esp+8], eax ; save full 64-bit product
+ mov [esp+4], edx ;
+
+;
+; Compute high 32-bits of dividend times low 32-bits of divisor.
+;
+
+ mov eax,ecx ; copy high 32-bits of dividend
+ mul [_KiTimeIncrementReciprocal] ; multiply by low 32-bits of divisor
+ mov [esp+0], edx ; save high 32-bits of product
+
+;
+; Compute carry out of low 64-bits of 128-bit product.
+;
+
+ xor ebx,ebx ; clear carry accumlator
+ add eax,[esp]+8 ; generate carry
+ adc ebx,0 ; accumlate carry
+ add eax,[esp]+12 ; generate carry
+ adc ebx,0 ; accumulate carry
+
+;
+; Compute high 32-bits of dividend times high 32-bits of divisor.
+;
+
+ mov eax,ecx ; copy high 32-bits of dividend
+ mul [_KiTimeIncrementReciprocal+4] ; multiply by high 32-bits of divisor
+
+;
+; Compute high 64-bits of 128-bit product.
+;
+
+ add eax,ebx ; add carry from low 64-bits
+ adc edx,0 ; propagate carry
+ add eax,[esp]+0 ; add and generate carry
+ adc edx,0 ; propagate carry
+ add eax,[esp]+4 ; add and generate carry
+ adc edx,0 ; propagate carry
+
+;
+; Right shift the result by the specified shift ocunt and mask off extra
+; bits.
+;
+
+ mov cl,[_KiTimeIncrementShiftCount] ; get shift count value
+ shrd eax,edx,cl ; extract appropriate product bits
+
+ mov ebx, [esp+16] ; restore register
+ add esp, LocalStack ; trim stack
+ and eax,(TIMER_TABLE_SIZE-1); reduce to size of table
+
+ stdRET _KicomputeTimerTableIndex
+
+stdENDP _KiComputeTimerTableIndex
+
+_TEXT$00 ends
+ end
diff --git a/private/ntos/ke/i386/trap.asm b/private/ntos/ke/i386/trap.asm
new file mode 100644
index 000000000..b47a5eda1
--- /dev/null
+++ b/private/ntos/ke/i386/trap.asm
@@ -0,0 +1,5486 @@
+ title "Trap Processing"
+;++
+;
+; Copyright (c) 1989 Microsoft Corporation
+;
+; Module Name:
+;
+; trap.asm
+;
+; Abstract:
+;
+; This module implements the code necessary to field and process i386
+; trap conditions.
+;
+; Author:
+;
+; Shie-Lin Tzong (shielint) 4-Feb-1990
+;
+; Environment:
+;
+; Kernel mode only.
+;
+; Revision History:
+;
+;--
+.386p
+ .xlist
+KERNELONLY equ 1
+include ks386.inc
+include callconv.inc ; calling convention macros
+include i386\kimacro.inc
+include mac386.inc
+include i386\mi.inc
+include ..\..\vdm\i386\vdm.inc
+include ..\..\vdm\i386\vdmtb.inc
+ .list
+
+FAST_BOP equ 1
+FAST_V86_TRAP equ 1
+
+
+ page ,132
+ extrn _KeGdiFlushUserBatch:DWORD
+ extrn _KeTickCount:DWORD
+ extrn _ExpTickCountMultiplier:DWORD
+ extrn _KiDoubleFaultTSS:dword
+ extrn _KiNMITSS:dword
+ extrn _KeServiceDescriptorTable:dword
+ extrn _KiHardwareTrigger:dword
+ extrn _KiBugCheckData:dword
+ extrn _KdpOweBreakpoint:dword
+ extrn Ki386BiosCallReturnAddress:near
+ EXTRNP _KiDeliverApc,3
+ EXTRNP KfRaiseIrql,1,IMPORT,FASTCALL
+ EXTRNP KfLowerIrql,1,IMPORT,FASTCALL
+ EXTRNP _KeGetCurrentIrql,0,IMPORT
+ EXTRNP _PsConvertToGuiThread,0
+ EXTRNP _ZwUnmapViewOfSection,2
+
+ EXTRNP _HalHandleNMI,1,IMPORT
+ EXTRNP _HalBeginSystemInterrupt,3,IMPORT
+ EXTRNP _HalEndSystemInterrupt,2,IMPORT
+ EXTRNP _KiDispatchException,5
+if DEVL
+ EXTRNP _PsWatchWorkingSet,3
+ extrn _PsWatchEnabled:byte
+endif
+ EXTRNP _MmAccessFault,3
+ EXTRNP _KeBugCheck,1
+ EXTRNP _KeBugCheckEx,5
+ EXTRNP _KeTestAlertThread,1
+ EXTRNP _KiContinue,3
+ EXTRNP _KiRaiseException,5
+ EXTRNP _Ki386DispatchOpcode,0
+ EXTRNP _Ki386DispatchOpcodeV86,0
+ EXTRNP _VdmDispatchPageFault,3
+ EXTRNP _Ki386VdmReflectException,1
+ EXTRNP _Ki386VdmSegmentNotPresent,0
+ extrn _DbgPrint:proc
+ EXTRNP _KdSetOwedBreakpoints
+ extrn _KiFreezeFlag:dword
+ EXTRNP _Ki386CheckDivideByZeroTrap,1
+ EXTRNP _Ki386CheckDelayedNpxTrap,2
+ extrn SwapContext:near
+ EXTRNP _VdmDispatchIRQ13, 1
+
+ extrn VdmDispatchBop:near
+ extrn _KeI386VdmIoplAllowed:dword
+ extrn _KeI386VirtualIntExtensions:dword
+ EXTRNP _NTFastDOSIO,2
+ EXTRNP _NtSetLdtEntries,6
+ extrn OpcodeIndex:byte
+
+; JAPAN - SUPPORT Intel CPU/Non PC/AT machine
+ extrn _VdmFixedStateLinear:dword
+
+;
+; Equates for exceptions which cause system fatal error
+;
+
+EXCEPTION_DIVIDED_BY_ZERO EQU 0
+EXCEPTION_DEBUG EQU 1
+EXCEPTION_NMI EQU 2
+EXCEPTION_INT3 EQU 3
+EXCEPTION_BOUND_CHECK EQU 5
+EXCEPTION_INVALID_OPCODE EQU 6
+EXCEPTION_NPX_NOT_AVAILABLE EQU 7
+EXCEPTION_DOUBLE_FAULT EQU 8
+EXCEPTION_NPX_OVERRUN EQU 9
+EXCEPTION_INVALID_TSS EQU 0AH
+EXCEPTION_SEGMENT_NOT_PRESENT EQU 0BH
+EXCEPTION_STACK_FAULT EQU 0CH
+EXCEPTION_GP_FAULT EQU 0DH
+EXCEPTION_RESERVED_TRAP EQU 0FH
+EXCEPTION_NPX_ERROR EQU 010H
+EXCEPTION_ALIGNMENT_CHECK EQU 011H
+
+;
+; Exception flags
+;
+
+EXCEPT_UNKNOWN_ACCESS EQU 0H
+EXCEPT_LIMIT_ACCESS EQU 10H
+
+;
+; Equates for some opcodes and instruction prefixes
+;
+
+IOPL_MASK EQU 3000H
+IOPL_SHIFT_COUNT EQU 12
+
+;
+; page fault read/write mask
+;
+
+ERR_0E_STORE EQU 2
+
+;
+; Debug register 6 (dr6) BS (single step) bit mask
+;
+
+DR6_BS_MASK EQU 4000H
+
+;
+; EFLAGS single step bit
+;
+
+EFLAGS_TF_BIT EQU 100h
+EFLAGS_OF_BIT EQU 4000H
+
+;
+; The mask of selecot's table indicator (ldt or gdt)
+;
+
+TABLE_INDICATOR_MASK EQU 4
+
+;
+; Opcode for Pop SegReg and iret instructions
+;
+
+POP_DS EQU 1FH
+POP_ES EQU 07h
+POP_FS EQU 0A10FH
+POP_GS EQU 0A90FH
+IRET_OP EQU 0CFH
+CLI_OP EQU 0FAH
+STI_OP EQU 0FBH
+PUSHF_OP EQU 9CH
+POPF_OP EQU 9DH
+INTNN_OP EQU 0CDH
+FRSTOR_ECX EQU 021DD9Bh
+FWAIT_OP EQU 09bh
+
+;
+; Force assume into place
+;
+
+_TEXT$00 SEGMENT PARA PUBLIC 'CODE'
+ ASSUME DS:NOTHING, ES:NOTHING, SS:NOTHING, FS:NOTHING, GS:NOTHING
+_TEXT$00 ENDS
+
+_DATA SEGMENT DWORD PUBLIC 'DATA'
+
+;
+; Definitions for gate descriptors
+;
+
+GATE_TYPE_386INT EQU 0E00H
+GATE_TYPE_386TRAP EQU 0F00H
+GATE_TYPE_TASK EQU 0500H
+D_GATE EQU 0
+D_PRESENT EQU 8000H
+D_DPL_3 EQU 6000H
+D_DPL_0 EQU 0
+
+;
+; Definitions for present 386 trap and interrupt gate attributes
+;
+
+D_TRAP032 EQU D_PRESENT+D_DPL_0+D_GATE+GATE_TYPE_386TRAP
+D_TRAP332 EQU D_PRESENT+D_DPL_3+D_GATE+GATE_TYPE_386TRAP
+D_INT032 EQU D_PRESENT+D_DPL_0+D_GATE+GATE_TYPE_386INT
+D_INT332 EQU D_PRESENT+D_DPL_3+D_GATE+GATE_TYPE_386INT
+D_TASK EQU D_PRESENT+D_DPL_0+D_GATE+GATE_TYPE_TASK
+
+;
+; This is the protected mode interrupt descriptor table.
+;
+
+if DBG
+;
+; NOTE - embedded enlish messages won't fly for NLS! (OK for debug code only)
+;
+
+BadInterruptMessage db 0ah,7,7,'!!! Unexpected Interrupt %02lx !!!',0ah,00
+
+KiNMIMessage db 0ah,'Non-Maskable-Interrupt (NMI) EIP = %08lx',0ah,00
+
+Ki16BitStackTrapMessage db 0ah,'Exception inside of 16bit stack',0ah,00
+endif
+
+;++
+;
+; DEFINE_SINGLE_EMPTY_VECTOR - helper for DEFINE_EMPTY_VECTORS
+;
+;--
+
+DEFINE_SINGLE_EMPTY_VECTOR macro number
+IDTEntry _KiUnexpectedInterrupt&number, D_INT032
+_TEXT$00 SEGMENT
+ public _KiUnexpectedInterrupt&number
+_KiUnexpectedInterrupt&number proc
+ push dword ptr (&number + PRIMARY_VECTOR_BASE)
+ jmp _KiUnexpectedInterruptTail
+_KiUnexpectedInterrupt&number endp
+_TEXT$00 ENDS
+
+ endm
+
+FPOFRAME macro a, b
+.FPO ( a, b, 0, 0, 0, FPO_TRAPFRAME )
+endm
+
+;++
+;
+; DEFINE_EMPTY_VECTORS emits an IDTEntry macro (and thus and IDT entry)
+; into the data segment. It then emits an unexpected interrupt target
+; with push of a constant into the code segment. Labels in the code
+; segment are defined to bracket the unexpected interrupt targets so
+; that KeConnectInterrupt can correctly test for them.
+;
+; Empty vectors will be defined from 30 to ff, which is the hardware
+; vector set.
+;
+;--
+
+NUMBER_OF_IDT_VECTOR EQU 0ffH
+
+DEFINE_EMPTY_VECTORS macro
+
+;
+; Set up
+;
+
+ empty_vector = 00H
+
+_TEXT$00 SEGMENT
+IFDEF STD_CALL
+ public _KiStartUnexpectedRange@0
+_KiStartUnexpectedRange@0 equ $
+ELSE
+ public _KiStartUnexpectedRange
+_KiStartUnexpectedRange equ $
+ENDIF
+_TEXT$00 ENDS
+
+ rept (NUMBER_OF_IDT_VECTOR - (($ - _IDT)/8)) + 1
+
+ DEFINE_SINGLE_EMPTY_VECTOR %empty_vector
+ empty_vector = empty_vector + 1
+
+ endm ;; rept
+
+_TEXT$00 SEGMENT
+IFDEF STD_CALL
+ public _KiEndUnexpectedRange@0
+_KiEndUnexpectedRange@0 equ $
+ELSE
+ public _KiEndUnexpectedRange
+_KiEndUnexpectedRange equ $
+ENDIF
+
+_TEXT$00 ENDS
+
+ endm ;; DEFINE_EMPTY_VECTORS macro
+
+IDTEntry macro name,access
+ dd offset FLAT:name
+ dw access
+ dw KGDT_R0_CODE
+ endm
+
+INIT SEGMENT DWORD PUBLIC 'CODE'
+
+;
+; The IDT table is put into the INIT code segment so the memory
+; can be reclaimed afer bootup
+;
+
+ALIGN 4
+ public _IDT, _IDTLEN, _IDTEnd
+_IDT label byte
+
+IDTEntry _KiTrap00, D_INT032 ; 0: Divide Error
+IDTEntry _KiTrap01, D_INT032 ; 1: DEBUG TRAP
+IDTEntry _KiTrap02, D_INT032 ; 2: NMI/NPX Error
+IDTEntry _KiTrap03, D_INT332 ; 3: Breakpoint
+IDTEntry _KiTrap04, D_INT332 ; 4: INTO
+IDTEntry _KiTrap05, D_INT032 ; 5: BOUND/Print Screen
+IDTEntry _KiTrap06, D_INT032 ; 6: Invalid Opcode
+IDTEntry _KiTrap07, D_INT032 ; 7: NPX Not Available
+IDTEntry _KiTrap08, D_INT032 ; 8: Double Exception
+IDTEntry _KiTrap09, D_INT032 ; 9: NPX Segment Overrun
+IDTEntry _KiTrap0A, D_INT032 ; A: Invalid TSS
+IDTEntry _KiTrap0B, D_INT032 ; B: Segment Not Present
+IDTEntry _KiTrap0C, D_INT032 ; C: Stack Fault
+IDTEntry _KiTrap0D, D_INT032 ; D: General Protection
+IDTEntry _KiTrap0E, D_INT032 ; E: Page Fault
+IDTEntry _KiTrap0F, D_INT032 ; F: Intel Reserved
+
+IDTEntry _KiTrap10, D_INT032 ;10: 486 coprocessor error
+IDTEntry _KiTrap11, D_INT032 ;11: 486 alignment
+IDTEntry _KiTrap0F, D_INT032 ;12: Intel Reserved
+IDTEntry _KiTrap0F, D_INT032 ;13: Intel Reserved
+IDTEntry _KiTrap0F, D_INT032 ;14: Intel Reserved
+IDTEntry _KiTrap0F, D_INT032 ;15: Intel Reserved
+IDTEntry _KiTrap0F, D_INT032 ;16: Intel Reserved
+IDTEntry _KiTrap0F, D_INT032 ;17: Intel Reserved
+
+IDTEntry _KiTrap0F, D_INT032 ;18: Intel Reserved
+IDTEntry _KiTrap0F, D_INT032 ;19: Intel Reserved
+IDTEntry _KiTrap0F, D_INT032 ;1A: Intel Reserved
+IDTEntry _KiTrap0F, D_INT032 ;1B: Intel Reserved
+IDTEntry _KiTrap0F, D_INT032 ;1C: Intel Reserved
+IDTEntry _KiTrap0F, D_INT032 ;1D: Intel Reserved
+IDTEntry _KiTrap0F, D_INT032 ;1E: Intel Reserved
+IDTEntry _KiTrap0F, D_INT032 ;1F: Reserved for APIC
+
+;
+; Note IDTEntry 0x21 is reserved for WOW apps.
+;
+
+ rept 2AH - (($ - _IDT)/8)
+IDTEntry 0, 0 ;invalid IDT entry
+ endm
+IDTEntry _KiGetTickCount, D_INT332 ;2A: KiGetTickCount service
+IDTEntry _KiCallbackReturn, D_INT332 ;2B: KiCallbackReturn
+IDTEntry _KiSetLowWaitHighThread, D_INT332 ;2C: KiSetLowWaitHighThread service
+IDTEntry _KiDebugService, D_INT332 ;2D: debugger calls
+IDTEntry _KiSystemService, D_INT332 ;2E: system service calls
+IDTEntry _KiTrap0F, D_INT032 ;2F: Reserved for APIC
+
+;
+; Generate per-vector unexpected interrupt entries for 30 - ff
+;
+ DEFINE_EMPTY_VECTORS
+
+_IDTLEN equ $ - _IDT
+_IDTEnd equ $
+
+INIT ends
+
+ public _KiUnexpectedEntrySize
+_KiUnexpectedEntrySize dd _KiUnexpectedInterrupt1 - _KiUnexpectedInterrupt0
+
+;
+; defines all the possible instruction prefix
+;
+
+PrefixTable label byte
+ db 0f2h ; rep prefix
+ db 0f3h ; rep ins/outs prefix
+ db 67h ; addr prefix
+ db 0f0h ; lock prefix
+ db 66h ; operand prefix
+ db 2eh ; segment override prefix:cs
+ db 3eh ; ds
+ db 26h ; es
+ db 64h ; fs
+ db 65h ; gs
+ db 36h ; ss
+
+PREFIX_REPEAT_COUNT EQU 11 ; Prefix table length
+
+;
+; defines all the possible IO privileged IO instructions
+;
+
+IOInstructionTable label byte
+; db 0fah ; cli
+; db 0fdh ; sti
+ db 0e4h, 0e5h, 0ech, 0edh ; IN
+ db 6ch, 6dh ; INS
+ db 0e6h, 0e7h, 0eeh, 0efh ; OUT
+ db 6eh, 6fh ; OUTS
+
+IO_INSTRUCTION_TABLE_LENGTH EQU 12
+
+if FAST_V86_TRAP
+
+ALIGN 4
+; V86DispatchTable - table of routines used to emulate instructions
+; in v86 mode.
+
+dtBEGIN V86DispatchTable,V86PassThrough
+ dtS VDM_INDEX_PUSHF , V86Pushf
+ dtS VDM_INDEX_POPF , V86Popf
+ dtS VDM_INDEX_INTnn , V86Intnn
+ dtS VDM_INDEX_IRET , V86Iret
+ dtS VDM_INDEX_CLI , V86Cli
+ dtS VDM_INDEX_STI , V86Sti
+dtEND MAX_VDM_INDEX
+
+endif ; FAST_V86_TRAP
+
+;
+; definition for floating status word error mask
+;
+
+FSW_INVALID_OPERATION EQU 1
+FSW_DENORMAL EQU 2
+FSW_ZERO_DIVIDE EQU 4
+FSW_OVERFLOW EQU 8
+FSW_UNDERFLOW EQU 16
+FSW_PRECISION EQU 32
+FSW_STACK_FAULT EQU 64
+FSW_CONDITION_CODE_0 EQU 100H
+FSW_CONDITION_CODE_1 EQU 200H
+FSW_CONDITION_CODE_2 EQU 400H
+FSW_CONDITION_CODE_3 EQU 4000H
+_DATA ENDS
+
+_TEXT$00 SEGMENT
+ ASSUME DS:NOTHING, ES:NOTHING, SS:FLAT, FS:NOTHING, GS:NOTHING
+
+ page , 132
+ subttl "Macro to Handle v86 trap d"
+;++
+;
+; Macro Description:
+;
+; This macro is a fast way to handle v86 bop instructions.
+; Note, all the memory write operations in this macro are done in such a
+; way that if a page fault occurs the memory will still be in a consistent
+; state.
+;
+; That is, we must process the trapped instruction in the following order:
+;
+; 1. Read and Write user memory
+; 2. Update VDM state flags
+; 3. Update trap frame
+;
+; Arguments:
+;
+; interrupts disabled
+;
+; Return Value:
+;
+;--
+
+FAST_V86_TRAP_6 MACRO
+
+local DoFastIo, a, b
+
+BOP_FOR_FASTWRITE EQU 4350C4C4H
+BOP_FOR_FASTREAD EQU 4250C4C4H
+TRAP6_IP EQU 32 ; 8 * 4
+TRAP6_CS EQU 36 ; 8 * 4 + 4
+TRAP6_FLAGS EQU 40 ; 8 * 4 + 8
+TRAP6_SP EQU 44 ; 8 * 4 + 12
+TRAP6_SS EQU 48 ; 8 * 4 + 16
+TRAP6_ES EQU 52
+TRAP6_DS EQU 56
+TRAP6_FS EQU 60
+TRAP6_GS EQU 64
+TRAP6_EAX EQU 28
+TRAP6_EDX EQU 20
+
+ pushad ;eax, ecx, edx, ebx, old esp, ebp, esi, edi
+ mov eax, KGDT_R3_DATA OR RPL_MASK
+ mov ds, ax
+ mov es, ax
+
+ifdef NT_UP
+else
+ mov eax, KGDT_R0_PCR
+ mov fs, ax
+endif
+ mov byte ptr PCR[PcVdmAlert], 6
+
+ mov ax, word ptr [esp+TRAP6_CS] ; [eax] = v86 user cs
+ shl eax, 4
+ add eax, [esp+TRAP6_IP] ; [eax] = addr of BOP
+ mov edx, [eax] ; [edx] = xxxxc4c4 bop + maj bop # + mi #
+ cmp edx, BOP_FOR_FASTREAD
+ je DoFastIo
+
+ cmp edx, BOP_FOR_FASTWRITE
+ je DoFastIo
+
+ cmp dx, 0c4c4h ; Is it a bop?
+ jne V86Trap6PassThrough ; It's an error condition
+ifdef NT_UP
+ mov eax, KGDT_R3_TEB OR RPL_MASK ; (fs)-> USER mode TEB! (Not R0 PCR)
+ shr edx, 16
+ mov fs, ax
+ mov eax, fs:[TbVdm]
+else
+ mov eax, PCR[PcTeb] ; (fs)->PCR
+ shr edx, 16
+ mov eax, [eax].TbVdm ; get pointer to VdmTib
+endif
+ and edx, 0ffh
+ mov dword ptr [eax].VtEIEvent, VdmBop
+ mov dword ptr [eax].VtEIBopNumber, edx
+ mov dword ptr [eax].VtEIInstSize, 3
+ lea eax, [eax].VtVdmContext
+
+;
+; Save V86 state to Vdm structure
+;
+ mov edx, [esp+TRAP6_EDX] ; get edx
+ mov [eax].CsEcx, ecx
+ mov [eax].CsEbx, ebx ; Save non-volatile registers
+ mov [eax].CsEsi, esi
+ mov [eax].CsEdi, edi
+ mov ecx, [esp+TRAP6_EAX] ; Get eax
+ mov [eax].CsEbp, ebp
+ mov [eax].CsEdx, edx
+ mov [eax].CsEax, ecx
+
+ mov ebx, [esp]+TRAP6_IP ; (ebx) = iser ip
+ mov ecx, [esp]+TRAP6_CS ; (ecx) = user cs
+ mov esi, [esp]+TRAP6_SP ; (esi) = user esp
+ mov edi, [esp]+TRAP6_SS ; (edi) = user ss
+ mov edx, [esp]+TRAP6_FLAGS; (edx) = user eflags
+ mov [eax].CsEip, ebx
+ mov [eax].CsSegCs, ecx
+ mov [eax].CsEsp, esi
+ mov [eax].CsSegSs, edi
+ test _KeI386VirtualIntExtensions, V86_VIRTUAL_INT_EXTENSIONS
+ jz short @f
+
+ test edx, EFLAGS_VIF
+ jnz short a
+
+ and edx, NOT EFLAGS_INTERRUPT_MASK
+ jmp short a
+
+@@: test _KeI386VdmIoplAllowed, 0ffffffffh
+ jnz short a
+
+ mov ebx, _VdmFixedStateLinear ; load ntvdm address
+ test ds:[ebx], VDM_VIRTUAL_INTERRUPTS ; check interrupt
+ jnz short a
+
+ and edx, NOT EFLAGS_INTERRUPT_MASK
+a:
+ mov [eax].CsEFlags, edx
+ mov ebx, [esp]+TRAP6_DS ; (ebx) = user ds
+ mov ecx, [esp]+TRAP6_ES ; (ecx) = user es
+ mov edx, [esp]+TRAP6_FS ; (edx) = user fs
+ mov esi, [esp]+TRAP6_GS ; (esi) = user gs
+ mov [eax].CsSegDs, ebx
+ mov [eax].CsSegEs, ecx
+ mov [eax].CsSegFs, edx
+ mov [eax].CsSegGs, esi
+
+;
+; Load Monitor context
+;
+
+ add eax, VtMonitorContext - VtVdmContext ; (eax)->monitor context
+ mov ebx, [eax].CsSegSs
+ mov esi, [eax].CsEsp
+ mov edi, [eax].CsEFlags
+ mov edx, [eax].CsSegCs
+ mov ecx, [eax].CsEip
+ mov [esp - 4], ebx ; Build Iret frame (can not single step!)
+ mov [esp - 8], esi
+ mov [esp - 12], edi
+ mov [esp - 16], edx
+ mov [esp - 20], ecx
+ mov ebx, [eax].CsEbx ; We don't need to load volatile registers.
+ mov esi, [eax].CsEsi ; because monitor uses SystemCall to return
+ mov edi, [eax].CsEdi ; back to v86. C compiler knows that
+ mov ebp, [eax].CsEbp ; SystemCall does not preserve volatile
+ ; registers.
+ ; fs, ds are set up already.
+ sub esp, 20
+
+;
+; Adjust Tss esp0 value and set return value to SUCCESS
+;
+ mov ecx, PCR[PcPrcbData+PbCurrentThread]
+ mov ecx, [ecx].thInitialStack
+ mov edx, PCR[PcTss]
+ sub ecx, NPX_FRAME_LENGTH + TsV86Gs - TsHardwareSegSs
+ xor eax, eax ; ret status = SUCCESS
+ mov [edx].TssEsp0, ecx
+ mov byte ptr PCR[PcVdmAlert], al
+ifdef NT_UP
+else
+ mov edx, KGDT_R3_TEB OR RPL_MASK
+ mov fs, dx
+endif
+ iretd
+
+DoFastIo:
+ xor eax, eax
+ mov edx, [esp]+TRAP6_EDX ; Restore edx
+ add esp, 7 * 4 ; leave eax in the TsErrCode
+ xchg [esp], eax ; Restore eax, sore a zero errcode
+ sub esp, TsErrcode ; build a trap frame
+ mov [esp].TsEbx, ebx
+ mov [esp].TsEax, eax
+ mov [esp].TsEbp, ebp
+ mov [esp].TsEsi, esi
+ mov [esp].TsEdi, edi
+ mov [esp].TsEcx, ecx
+ mov [esp].TsEdx, edx
+if DBG
+ mov [esp].TsPreviousPreviousMode, -1
+ mov [esp]+TsDbgArgMark, 0BADB0D00h
+endif
+ifdef NT_UP
+ mov ebx, KGDT_R0_PCR
+ mov fs, bx
+endif
+ mov byte ptr PCR[PcVdmAlert], 0
+ mov ebp, esp
+ cld
+ test byte ptr PCR[PcDebugActive], -1
+ jz short @f
+
+ mov ebx,dr0
+ mov esi,dr1
+ mov edi,dr2
+ mov [ebp]+TsDr0,ebx
+ mov [ebp]+TsDr1,esi
+ mov [ebp]+TsDr2,edi
+ mov ebx,dr3
+ mov esi,dr6
+ mov edi,dr7
+ mov [ebp]+TsDr3,ebx
+ mov [ebp]+TsDr6,esi
+ mov [ebp]+TsDr7,edi
+ ;
+ ; Load KernelDr* into processor
+ ;
+ mov edi,dword ptr fs:[PcPrcb]
+ mov ebx,[edi].PbProcessorState.PsSpecialRegisters.SrKernelDr0
+ mov esi,[edi].PbProcessorState.PsSpecialRegisters.SrKernelDr1
+ mov dr0,ebx
+ mov dr1,esi
+ mov ebx,[edi].PbProcessorState.PsSpecialRegisters.SrKernelDr2
+ mov esi,[edi].PbProcessorState.PsSpecialRegisters.SrKernelDr3
+ mov dr2,ebx
+ mov dr3,esi
+ mov ebx,[edi].PbProcessorState.PsSpecialRegisters.SrKernelDr6
+ mov esi,[edi].PbProcessorState.PsSpecialRegisters.SrKernelDr7
+ mov dr6,ebx
+ mov dr7,esi
+@@:
+ ; Raise Irql to APC level before enabling interrupts
+ mov ecx, APC_LEVEL
+ fstCall KfRaiseIrql
+ push eax ; Save OldIrql
+ sti
+
+ xor edx, edx
+ mov dx, word ptr [ebp].TsSegCs
+ shl edx, 4
+ xor ebx, ebx
+ add edx, [ebp].TsEip
+ mov bl, [edx+3] ; [bl] = minor BOP code
+ push ebx
+ push ebp ; (ebp)->TrapFrame
+ call _NTFastDOSIO@8
+ jmp Kt061i
+
+V86Trap6PassThrough:
+ mov byte ptr PCR[PcVdmAlert], 0
+V86Trap6Recovery:
+ popad
+ jmp Kt6SlowBop ; Fall through
+
+endm
+ page , 132
+ subttl "Macro to Handle v86 trap d"
+;++
+;
+; Macro Description:
+;
+; This macro is a fast way to handle SOME v86 mode sensitive instructions.
+; Note, all the memory write operations in this macro are done in such a
+; way that if a page fault occurs the memory will still be in a consistent
+; state.
+;
+; That is, we must process the trapped instruction in the following order:
+;
+; 1. Write user memory (prefer do read here)
+; 2. Update VDM state flags
+; 3. Update trap frame
+;
+; Arguments:
+;
+; interrupts disabled
+;
+; Return Value:
+;
+;--
+
+FAST_V86_TRAP_D MACRO
+
+local V86Exit, V86Exit_1, IntnnExit
+
+ sub esp, TsErrCode
+ mov [esp].TsEdx, edx
+ mov [esp].TsEcx, ecx
+ mov [esp].TsEbx, ebx
+ mov [esp].TsEax, eax
+
+ public _V86CriticalInstruction
+_V86CriticalInstruction:
+ mov ebx, FIXED_NTVDMSTATE_LINEAR_PC_AT
+ ; above ntvdm address may changed in KeI386VdmInitialize for PC-98
+
+ifdef NT_UP
+ mov byte ptr SS:[P0PCRADDRESS][PcVdmAlert], 0dh
+else
+ mov eax, KGDT_R0_PCR
+ mov fs, ax
+ mov byte ptr PCR[PcVdmAlert], 0dh
+endif
+ mov eax, [esp].TsSegCs ; (eax) = H/W Cs
+ shl eax,4
+ add eax,[esp].TsEip ; (eax) -> flat faulted addr
+ xor edx, edx
+ mov ecx, ss:[eax] ; (ecx) = faulted instruction
+ mov dl, cl
+ mov dl, ss:OpcodeIndex[edx] ; (edx) = opcode index
+ jmp ss:V86DispatchTable[edx * type V86DispatchTable]
+
+;
+; (ecx) = faulted instructions
+; (eax) = Flat faulted addr
+; (edx) = Opcode Index
+;
+
+ALIGN 4
+V86Pushf:
+ mov eax, ss:[ebx] ; get ntvdm address
+ mov edx, dword ptr [esp].TsEflags ; (edx) = Hardware Eflags
+ and eax,VDM_VIRTUAL_INTERRUPTS OR VDM_VIRTUAL_AC OR VDM_VIRTUAL_NT
+ or eax,EFLAGS_IOPL_MASK
+ and edx,NOT EFLAGS_INTERRUPT_MASK
+ mov [esp].TsSegDs, ecx ; save ecx
+ or eax,edx ; (ax) = client flags
+ xor ecx, ecx
+ mov cx, word ptr [esp].TsHardwareSegSs ; (edx)= hardware SS
+ xor edx, edx
+ shl ecx,4
+ mov dx, word ptr [esp].TsHardwareEsp ; (edx)= Hardware sp
+ sub edx, 2
+ mov ss:[ecx + edx],ax
+ mov ecx, [esp].TsSegDs ; restore ecx
+;
+; Usually, pushf is followed by cli. So, here we check for this case.
+; If yes, we will handle the cli to save a round trip.
+; It is very important that we first update user stack, Fixed VDM state and
+; finally hardware esp.
+;
+
+ cmp cx, (CLI_OP SHL 8) OR PUSHF_OP ; Is there a cli following pushf?
+ jnz short @f
+
+ MPLOCK and dword ptr ss:[ebx],NOT VDM_VIRTUAL_INTERRUPTS
+ inc dword ptr [esp].TsEip ; skip cli
+@@:
+ mov word ptr [esp].TsHardwareEsp, dx ; update client esp
+V86Exit:
+ inc dword ptr [esp].TsEip ; skip pushf
+V86Exit_1:
+ mov ecx, [esp].TsEcx
+ mov ebx, [esp].TsEbx
+ mov eax, [esp].TsEax
+ mov edx, [esp].TsEdx
+ add esp, TsEip
+ifdef NT_UP
+ mov byte ptr SS:[P0PCRADDRESS][PcVdmAlert], 0
+else
+ mov byte ptr PCR[PcVdmAlert], 0
+endif
+ iretd
+
+ALIGN 4
+V86Cli:
+ MPLOCK and dword ptr ss:[ebx],NOT VDM_VIRTUAL_INTERRUPTS
+ jmp short V86Exit
+
+ALIGN 4
+V86Sti:
+ test ss:[ebx], VDM_INTERRUPT_PENDING
+ ; Can we handle it in fast way?
+ jnz V86PassThrough ; if nz, no, we need to dispatch int
+
+ ;; Pentium CPU traps sti if
+ ;; 1). client's TF is ON or 2). VIP is ON
+ ;; we must set EFLAGS_VIF in this case.
+ test dword ptr _KeI386VirtualIntExtensions, V86_VIRTUAL_INT_EXTENSIONS
+ jz short v86_sti_01
+ or dword ptr [esp].TsEflags, EFLAGS_VIF
+v86_sti_01:
+ MPLOCK or dword ptr ss:[ebx], EFLAGS_INTERRUPT_MASK
+ jmp short V86Exit
+
+ALIGN 4
+V86Popf:
+ test ss:[ebx], VDM_INTERRUPT_PENDING
+ ; Can we handle it in fast way?
+ jnz V86PassThrough
+
+ xor edx, edx
+ mov dx, word ptr [esp].TsHardwareSegSs ; (edx)= hardware SS
+ xor eax, eax
+ shl edx,4
+ mov ax, word ptr [esp].TsHardwareEsp ; (ecx)= Hardware sp
+ mov edx, ss:[edx + eax] ; (edx) = Client flags
+ add ax, 2 ; (ax) = Client sp
+ and edx, 0FFFFH AND (NOT EFLAGS_IOPL_MASK)
+ MPLOCK and ss:[ebx],NOT (EFLAGS_INTERRUPT_MASK OR EFLAGS_ALIGN_CHECK OR EFLAGS_NT_MASK)
+ mov ecx, edx
+ and edx, (EFLAGS_INTERRUPT_MASK OR EFLAGS_ALIGN_CHECK OR EFLAGS_NT_MASK)
+ and ecx, NOT EFLAGS_NT_MASK
+ MPLOCK or ss:[ebx],edx
+ or ecx, (EFLAGS_INTERRUPT_MASK OR EFLAGS_V86_MASK)
+
+ ;; Pentium CPU traps popf if
+ ;; 1)client's TF is ON or 2) VIP is on and the client's IF is ON
+ ;; We have to propagate IF to VIF if virtual interrupt extension is
+ ;; enabled.
+ test dword ptr _KeI386VirtualIntExtensions, V86_VIRTUAL_INT_EXTENSIONS
+ jz v86_popf_01
+ and ecx, NOT EFLAGS_VIF ;clear it first
+ and edx, EFLAGS_INTERRUPT_MASK ;isolate and move IF to VIF
+ rol edx, 10 ;position
+.errnz (EFLAGS_INTERRUPT_MASK SHL 10) - EFLAGS_VIF
+ or ecx, edx ;propagate it!
+
+v86_popf_01:
+ mov [esp].TsEflags,ecx
+ mov [esp].TsHardwareEsp, eax ; update client esp
+ jmp V86Exit
+
+ALIGN 4
+V86Intnn:
+ shr ecx, 8
+ xor eax, eax
+ and ecx, 0FFH ; ecx is int#
+ mov ecx, ss:[ecx*4] ; (ecx) = Int nn handler
+ xor edx, edx
+ mov [esp].TsSegDs, ecx ; [esp].Ds = intnn handler
+ mov ax, word ptr [esp].TsHardwareSegSs ; (eax)= hardware SS
+ shl eax,4
+ mov dx, word ptr [esp].TsHardwareEsp ; (edx)= Hardware sp
+ sub dx, 6
+ add eax, edx ; (eax) = User stack
+ mov ecx, [esp].TsEflags
+ test ss:_KeI386VdmIoplAllowed,1
+ jnz short @f
+
+ mov edx, ss:[ebx] ; set the contents
+ and ecx, NOT EFLAGS_INTERRUPT_MASK
+ and edx,VDM_VIRTUAL_INTERRUPTS OR VDM_VIRTUAL_AC
+ or ecx, edx
+ or ecx, IOPL_MASK
+ mov word ptr ss:[eax+4], cx ; push flags
+ mov ecx, [esp].TsSegCs
+ mov edx, [esp].TsEip
+ mov word ptr ss:[eax+2], cx ; push cs
+ add dx, 2
+ MPLOCK and ss:[ebx], NOT VDM_VIRTUAL_INTERRUPTS
+ mov word ptr ss:[eax], dx ; push ip (skip int nn)
+ and [esp].TsEflags, NOT (EFLAGS_NT_MASK OR EFLAGS_TF_MASK)
+IntnnExit:
+ mov ecx, [esp].TsSegDs ; (ecx) = V86 intnn handler
+ sub word ptr [esp].TsHardwareEsp, 6
+ mov [esp].TsEip, ecx
+ shr ecx,16
+ mov [esp].TsSegCs, cx ; cs:ip on trap frame is updated
+ jmp V86Exit_1
+
+@@:
+ or ecx, IOPL_MASK
+ mov word ptr ss:[eax+4], cx ; push flags
+ mov ecx, [esp].TsSegCs
+ mov edx, [esp].TsEip
+ mov word ptr ss:[eax+2], cx ; push cs
+ add dx, 2
+ mov word ptr ss:[eax], dx ; push ip (skip int nn)
+ and [esp].TsEflags, NOT (EFLAGS_INTERRUPT_MASK OR EFLAGS_NT_MASK OR EFLAGS_TF_MASK)
+ jmp short IntnnExit
+
+ALIGN 4
+V86Iret:
+ test ss:[ebx], VDM_INTERRUPT_PENDING
+ jnz V86PassThrough
+
+ xor ecx, ecx
+ mov cx,word ptr [esp].TsHardwareSegSS
+ xor edx, edx
+ shl ecx,4
+ mov dx,word ptr [esp].TsHardwareEsp
+ add ecx,edx ; (ecx) -> User stack
+ mov dx,word ptr ss:[ecx+4] ; get flag value
+ mov ecx, ss:[ecx] ; (ecx) = ret cs:ip
+
+ and edx, NOT (EFLAGS_IOPL_MASK OR EFLAGS_NT_MASK)
+ mov eax,edx
+ or edx, (EFLAGS_V86_MASK OR EFLAGS_INTERRUPT_MASK)
+ and eax, EFLAGS_INTERRUPT_MASK
+ MPLOCK and ss:[ebx],NOT VDM_VIRTUAL_INTERRUPTS
+ MPLOCK or ss:[ebx],eax
+
+ ;; Pentium CPU traps iret if
+ ;; 1)client's TF is ON or 2) VIP is on and the client's IF is ON
+ ;; We have to propagate IF to VIF if virtual interrupt extension is
+ ;; enabled
+ test dword ptr _KeI386VirtualIntExtensions, V86_VIRTUAL_INT_EXTENSIONS
+ jz v86_iret_01
+ and edx, NOT EFLAGS_VIF ;eax must contain ONLY
+ ;INTERRUPT_MASK!!!!
+.errnz (EFLAGS_INTERRUPT_MASK SHL 10) - EFLAGS_VIF
+ rol eax, 10
+ or edx, eax
+
+v86_iret_01:
+ mov [esp].TsEFlags,edx ; update flags in trap frame
+ mov eax, ecx
+ shr ecx, 16
+ and eax, 0ffffh
+ add word ptr [esp].TsHardwareEsp, 6 ; update sp on trap frame
+ mov [esp].TsSegCs,ecx ; update cs
+ mov [esp].TsEip, eax ; update ip
+
+ ; at this point cx:ax is the addr of the ip where v86 mode
+ ; will return. Now we will check if this returning instruction
+ ; is a bop. if so we will directly dispatch the bop from here
+ ; saving a full round trip. This will be really helpful to
+ ; com apps.
+ifdef NT_UP
+ mov byte ptr SS:[P0PCRADDRESS][PcVdmAlert], 10h
+else
+ mov byte ptr PCR[PcVdmAlert], 010h
+endif
+ shl ecx, 4
+ mov ax, ss:[ecx+eax] ; Could fault
+ cmp ax, 0c4c4h
+ jne V86Exit_1
+
+ mov ecx, [esp].TsEcx
+ mov ebx, [esp].TsEbx
+ mov eax, [esp].TsEax
+ mov edx, [esp].TsEdx
+ add esp, TsEip
+if FAST_BOP eq 0
+ifdef NT_UP
+ mov byte ptr SS:[P0PCRADDRESS][PcVdmAlert], 0
+else
+ mov byte ptr PCR[PcVdmAlert], 0
+endif
+endif
+ jmp _KiTrap06
+
+V86Trap10Recovery:
+ jmp V86Exit_1
+
+;
+; If we come here, it means we hit some kind of trap while processing the
+; V86 trap in a fast way. We need to process the instruction in a normal
+; way. For this case, we simply abort the current processing and restart
+; it via reguar v86 trap processing. (This is a very rare case.)
+;
+;; public V86TrapDRecovery
+V86TrapDRecovery:
+ mov ecx, [esp].TsEcx
+ mov ebx, [esp].TsEbx
+ mov eax, [esp].TsEax
+ mov edx, [esp].TsEdx
+ add esp, TsErrCode
+ jmp KtdV86Slow
+
+;
+; If we come here, it means we can not process the trapped instruction.
+; We will build a trap frame and use regular way to process the instruction.
+; Since this could happen if interrupt is pending or the instruction trapped
+; is not in the list of instructions which we handle. We need to "continue"
+; the processing instead of abort it.
+;
+
+ALIGN 4
+V86PassThrough:
+ifdef NT_UP
+ mov byte ptr SS:[P0PCRADDRESS][PcVdmAlert], 0
+else
+ mov byte ptr PCR[PcVdmAlert], 0
+endif
+ ;
+ ; eax, ebx, ecx, edx have been saved already
+ ; Note, we don't want to destroy ecx, and edx
+ ;
+ mov [esp].TsEbp, ebp
+ mov [esp].TsEsi, esi
+ mov [esp].TsEdi, edi
+ mov ebx, KGDT_R0_PCR
+ mov esi, KGDT_R3_DATA OR RPL_MASK
+if DBG
+ mov [esp].TsPreviousPreviousMode, -1
+ mov [esp]+TsDbgArgMark, 0BADB0D00h
+endif
+ mov fs, bx
+ mov ds, esi
+ mov es, esi
+ mov ebp, esp
+ cld
+ test byte ptr PCR[PcDebugActive], -1
+ jz short @f
+
+ mov ebx,dr0
+ mov esi,dr1
+ mov edi,dr2
+ mov [ebp]+TsDr0,ebx
+ mov [ebp]+TsDr1,esi
+ mov [ebp]+TsDr2,edi
+ mov ebx,dr3
+ mov esi,dr6
+ mov edi,dr7
+ mov [ebp]+TsDr3,ebx
+ mov [ebp]+TsDr6,esi
+ mov [ebp]+TsDr7,edi
+ ;
+ ; Load KernelDr* into processor
+ ;
+ mov edi,dword ptr fs:[PcPrcb]
+ mov ebx,[edi].PbProcessorState.PsSpecialRegisters.SrKernelDr0
+ mov esi,[edi].PbProcessorState.PsSpecialRegisters.SrKernelDr1
+ mov dr0,ebx
+ mov dr1,esi
+ mov ebx,[edi].PbProcessorState.PsSpecialRegisters.SrKernelDr2
+ mov esi,[edi].PbProcessorState.PsSpecialRegisters.SrKernelDr3
+ mov dr2,ebx
+ mov dr3,esi
+ mov ebx,[edi].PbProcessorState.PsSpecialRegisters.SrKernelDr6
+ mov esi,[edi].PbProcessorState.PsSpecialRegisters.SrKernelDr7
+ mov dr6,ebx
+ mov dr7,esi
+@@:
+ jmp KtdV86Slow2
+endm
+
+ page , 132
+ subttl "Macro to dispatch user APC"
+
+;++
+;
+; Macro Description:
+;
+; This macro is called before returning to user mode. It dispatches
+; any pending user mode APCs.
+;
+; Arguments:
+;
+; TFrame - TrapFrame
+; interrupts disabled
+;
+; Return Value:
+;
+;--
+
+DISPATCH_USER_APC macro TFrame, ReturnCurrentEax
+local a, b
+ test dword ptr [TFrame]+TsEflags, EFLAGS_V86_MASK ; is previous mode v86?
+ jnz short b ; if nz, yes, go check for APC
+ test byte ptr [TFrame]+TsSegCs,MODE_MASK ; is previous mode user mode?
+ jz short a ; No, previousmode=Kernel, jump out
+b: mov ebx, PCR[PcPrcbData+PbCurrentThread]; get addr of current thread
+ mov byte ptr [ebx]+ThAlerted, 0 ; clear kernel mode alerted
+ cmp byte ptr [ebx]+ThApcState.AsUserApcPending, 0
+ je short a ; if eq, no user APC pending
+
+ mov ebx, TFrame
+ifnb <ReturnCurrentEax>
+ mov [ebx].TsEax, eax ; Store return code in trap frame
+ mov dword ptr [ebx]+TsSegFs, KGDT_R3_TEB OR RPL_MASK
+ mov dword ptr [ebx]+TsSegDs, KGDT_R3_DATA OR RPL_MASK
+ mov dword ptr [ebx]+TsSegEs, KGDT_R3_DATA OR RPL_MASK
+ mov dword ptr [ebx]+TsSegGs, 0
+endif
+
+;
+; Save previous IRQL and set new priority level
+;
+ mov ecx, APC_LEVEL
+ fstCall KfRaiseIrql
+ push eax ; Save OldIrql
+
+ sti ; Allow higher priority ints
+
+;
+; call the APC delivery routine.
+;
+; ebx - Trap frame
+; 0 - Null exception frame
+; 1 - Previous mode
+;
+; call APC deliver routine
+;
+
+ stdCall _KiDeliverApc, <1, 0, ebx>
+
+ pop ecx ; (ecx) = OldIrql
+ fstCall KfLowerIrql
+
+ifnb <ReturnCurrentEax>
+ mov eax, [ebx].TsEax ; Restore eax, just in case
+endif
+
+ cli
+ jmp short b
+
+ ALIGN 4
+a:
+endm
+
+
+if DBG
+ page ,132
+ subttl "Processing Exception occurred in a 16 bit stack"
+;++
+;
+; Routine Description:
+;
+; This routine is called after an exception being detected during
+; a 16 bit stack. The system will switch 16 stack to 32 bit
+; stack and bugcheck.
+;
+; Arguments:
+;
+; None.
+;
+; Return value:
+;
+; system stopped.
+;
+;--
+
+align dword
+ public _Ki16BitStackException
+_Ki16BitStackException proc
+
+.FPO (2, 0, 0, 0, 0, FPO_TRAPFRAME)
+
+ push ss
+ push esp
+ mov eax, esp
+ add eax, fs:PcstackLimit
+ mov esp, eax
+ mov eax, KGDT_R0_DATA
+ mov ss, ax
+
+ lea ebp, [esp+8]
+ cld
+ SET_DEBUG_DATA
+
+if DBG
+ push offset FLAT:Ki16BitStackTrapMessage
+ call _dbgPrint
+ add esp, 4
+endif
+ stdCall _KeBugCheck, <0F000FFFFh> ; Never return
+ ret
+
+_Ki16BitStackException endp
+
+endif
+
+
+ page ,132
+ subttl "System Service Call"
+;++
+;
+; Routine Description:
+;
+; This routine gains control when trap occurs via vector 2EH.
+; INT 2EH is reserved for system service calls.
+;
+; The system service is executed by locating its routine address in
+; system service dispatch table and calling the specified function.
+; On return necessary state is restored.
+;
+; Arguments:
+;
+; eax - System service number.
+; edx - Pointer to arguments
+;
+; Return Value:
+;
+; eax - System service status code.
+;
+;--
+
+if 0
+;
+; Error and exception blocks for KiSystemService
+;
+
+Kss_ExceptionHandler:
+
+;
+; WARNING: Here we directly unlink the exception handler from the
+; exception registration chain. NO unwind is performed.
+;
+
+ mov eax, [esp+4] ; (eax)-> ExceptionRecord
+ mov eax, [eax].ErExceptionCode ; (eax) = Exception code
+ mov esp, [esp+8] ; (esp)-> ExceptionList
+
+ pop eax
+ mov PCR[PcExceptionList],eax
+
+ add esp, 4
+ pop ebp
+ test dword ptr [ebp]+TsEFlags,EFLAGS_V86_MASK
+ jnz kss60 ; v86 mode => usermode
+
+ test dword ptr [ebp].TsSegCs, MODE_MASK ; if premode=kernl
+ jnz kss60 ; nz, prevmode=user, go return
+
+; raise bugcheck if prevmode=kernel
+ stdCall _KeBugCheck, <KMODE_EXCEPTION_NOT_HANDLED>
+endif
+
+;
+; The specified system service number is not within range. Attempt to
+; convert the thread to a GUI thread if the specified system service is
+; not a base service and the thread has not already been converted to a
+; GUI thread.
+;
+
+Kss_ErrorHandler:
+ cmp ecx, SERVICE_TABLE_TEST ; test if GUI service
+ jne short Kss_LimitError ; if ne, not GUI service
+ push edx ; save argument registers
+ push ebx ;
+ stdcall _PsConvertToGuiThread ; attempt to convert to GUI thread
+ or eax, eax ; check if service was successful
+ pop eax ; restore argument registers
+ pop edx ;
+ mov ebp, esp ; reset trap frame address
+ mov [esi]+ThTrapFrame, ebp ; save address of trap frame
+ jz _KiSystemServiceRepeat ; if eq, successful conversion
+
+Kss_LimitError: ;
+ mov eax, STATUS_INVALID_SYSTEM_SERVICE ; set return status
+ jmp kss70 ;
+
+ ENTER_DR_ASSIST kss_a, kss_t,NoAbiosAssist,NoV86Assist
+
+;
+; General System service entrypoint
+;
+
+align 16
+ PUBLIC _KiSystemService
+_KiSystemService proc
+
+ ENTER_SYSCALL kss_a, kss_t ; set up trap frame and save state
+
+;
+; (eax) = Service number
+; (edx) = Callers stack pointer
+; (esi) = Current thread address
+;
+; All other registers have been saved and are free.
+;
+; Check if the service number within valid range
+;
+
+_KiSystemServiceRepeat:
+ mov edi, eax ; copy system service number
+ shr edi, SERVICE_TABLE_SHIFT ; isolate service table number
+ and edi, SERVICE_TABLE_MASK ;
+ mov ecx, edi ; save service table number
+ add edi, [esi]+ThServiceTable ; compute service descriptor address
+ mov ebx, eax ; save system service number
+ and eax, SERVICE_NUMBER_MASK ; isolate service table offset
+
+;
+; If the specified system service number is not within range, then attempt
+; to convert the thread to a GUI thread and retry the service dispatch.
+;
+
+ cmp eax, [edi]+SdLimit ; check if valid service
+ jae Kss_ErrorHandler ; if ae, try to convert to GUI thread
+
+;
+; If the service is a GUI service and the GDI user batch queue is not empty,
+; then call the appropriate service to flush the user batch.
+;
+
+ cmp ecx, SERVICE_TABLE_TEST ; test if GUI service
+ jne short Kss40 ; if ne, not GUI service
+ mov ecx, PCR[PcTeb] ; get current thread TEB address
+ xor ebx, ebx ; get number of batched GDI calls
+ or ebx, [ecx]+TbGdiBatchCount ;
+ jz short Kss40 ; if z, no batched calls
+ push edx ; save address of user arguments
+ push eax ; save service number
+ call [_KeGdiFlushUserBatch] ; flush GDI user batch
+ pop eax ; restore service number
+ pop edx ; restore address of user arguments
+
+;
+; The arguments are passed on the stack. Therefore they always need to get
+; copied since additional space has been allocated on the stack for the
+; machine state frame. Note that we don't check for zero argument. Copy
+; is alway done reguardless of number of arguments. This is because zero
+; argument is very rare.
+;
+
+Kss40: inc dword ptr PCR[PcPrcbData+PbSystemCalls] ; system calls
+
+if DBG
+ mov ecx, [edi]+SdCount ; get count table address
+ jecxz Kss45 ; if zero, table not specified
+ inc dword ptr [ecx+eax*4] ; increment service count
+Kss45: push dword ptr [esi]+ThApcStateIndex ; (ebp-4)
+ push dword ptr [esi]+ThKernelApcDisable ; (ebp-8)
+
+ ;
+ ; work around errata 19 which can in some cases cause an
+ ; extra dword to be moved in the rep movsd below. In the DBG
+ ; build, this will usually case a bugcheck 1 where ebp-8 is no longer
+ ; the kernel apc disable count
+ ;
+
+ sub esp,4
+
+FPOFRAME 2, 0
+endif
+
+ mov esi, edx ; (esi)->User arguments
+ mov ebx, [edi]+SdNumber ; get argument table address
+ xor ecx, ecx
+ mov cl, byte ptr [ebx+eax] ; (ecx) = argument size
+ mov edi, [edi]+SdBase ; get service table address
+ mov ebx, [edi+eax*4] ; (ebx)-> service routine
+ sub esp, ecx ; allocate space for arguments
+ shr ecx, 2 ; (ecx) = number of argument DWORDs
+ mov edi, esp ; (es:edi)->location to receive 1st arg
+
+KiSystemServiceCopyArguments:
+ rep movsd ; copy the arguments to top of stack.
+ ; Since we usually copy more than 3
+ ; arguments. rep movsd is faster than
+ ; mov instructions.
+if DBG
+;
+; Check for user mode call into system at elevated IRQL.
+;
+
+ test byte ptr [ebp]+TsSegCs,MODE_MASK
+ jz short kss50a ; kernel mode, skip test
+ stdCall _KeGetCurrentIrql
+ or al, al ; bogus irql, go bugcheck
+ jnz kss100
+kss50a:
+endif
+
+;
+; Make actual call to system service
+;
+kssdoit:
+ call ebx ; call system service
+
+kss60:
+if DBG
+ mov ebx,PCR[PcPrcbData+PbCurrentThread] ; (ebx)-> Current Thread
+
+;
+; Check for return to user mode at elevated IRQL.
+;
+ test byte ptr [ebp]+TsSegCs,MODE_MASK
+ jz short kss50b
+ mov esi, eax
+ stdCall _KeGetCurrentIrql
+ or al, al
+ jnz kss100 ; bogus irql, go bugcheck
+ mov eax, esi
+kss50b:
+;
+; Check that APC state has not changed
+;
+ mov edx, [ebp-4]
+ cmp dl, [ebx]+ThApcStateIndex
+ jne kss120
+
+ mov edx, [ebp-8]
+ cmp dl, [ebx]+ThKernelApcDisable
+ jne kss120
+
+endif
+
+;
+; Upon return, (eax)= status code
+;
+
+ mov esp, ebp ; deallocate stack space for arguments
+
+;
+; Restore old trap frame address from the current trap frame.
+;
+
+kss70: mov ecx, PCR[PcPrcbData+PbCurrentThread] ; get current thread address
+ mov edx, [ebp].TsEdx ; restore previous trap frame address
+ mov [ecx].ThTrapFrame, edx ;
+
+;
+; System service's private version of KiExceptionExit
+; (Also used by KiDebugService)
+;
+; Check for pending APC interrupts, if found, dispatch to them
+; (saving eax in frame first).
+;
+ public _KiServiceExit
+_KiServiceExit:
+
+ cli ; disable interrupts
+ DISPATCH_USER_APC ebp, ReturnCurrentEax
+
+;
+; Exit from SystemService
+;
+ EXIT_ALL NoRestoreSegs, NoRestoreVolatile
+
+;++
+;
+; _KiServiceExit2 - same as _KiServiceExit BUT the full trap_frame
+; context is restored
+;
+;--
+ public _KiServiceExit2
+_KiServiceExit2:
+
+ cli ; disable interrupts
+ DISPATCH_USER_APC ebp
+
+;
+; Exit from SystemService
+;
+ EXIT_ALL ; RestoreAll
+
+
+
+
+if DBG
+kss100: push PCR[PcIrql] ; put bogus value on stack for dbg
+ mov byte ptr PCR[PcIrql],0 ; avoid recursive trap
+ cli
+
+ stdCall _KeBugCheck,<IRQL_GT_ZERO_AT_SYSTEM_SERVICE>
+
+kss120: stdCall _KeBugCheck,<APC_INDEX_MISMATCH>
+
+endif
+ ret
+
+_KiSystemService endp
+
+;
+; Fast path NtGetTickCount
+;
+
+align 16
+ ENTER_DR_ASSIST kitx_a, kitx_t,NoAbiosAssist
+ PUBLIC _KiGetTickCount
+_KiGetTickCount proc
+
+ cmp [esp+4], KGDT_R3_CODE OR RPL_MASK
+ jnz short @f
+
+Kgtc00:
+ mov eax,dword ptr cs:[_KeTickCount]
+ mul dword ptr cs:[_ExpTickCountMultiplier]
+ shrd eax,edx,24 ; compute resultant tick count
+
+ iretd
+@@:
+ ;
+ ; if v86 mode, we dont handle it
+ ;
+
+ test dword ptr [esp+8], EFLAGS_V86_MASK
+ jnz ktgc20
+
+ ;
+ ; if kernel mode, must be get tick count
+ ;
+
+ test [esp+4], MODE_MASK
+ jz short Kgtc00
+
+ ;
+ ; else check if the caller is USER16
+ ; if eax = ebp = 0xf0f0f0f0 it is get-tick-count
+ ; if eax = ebp = 0xf0f0f0f1 it is set-ldt-entry
+ ;
+
+ cmp eax, ebp ; if eax != ebp, not USER16
+ jne ktgc20
+
+ and eax, 0fffffff0h
+ cmp eax, 0f0f0f0f0h
+ jne ktgc20
+
+ cmp ebp, 0f0f0f0f0h ; Is it user16 gettickcount?
+ je short Kgtc00 ; if z, yes
+
+
+ cmp ebp, 0f0f0f0f1h ; If this is setldt entry
+ jne ktgc20 ; if nz, we don't know what
+ ; it is.
+
+ ;
+ ; The idea here is that user16 can call 32 bit api to
+ ; update LDT entry without going through the penalty
+ ; of DPMI. For Daytona beta.
+ ;
+
+ push 0 ; push dummy error code
+ ENTER_TRAP kitx_a, kitx_t
+ sti
+
+ xor eax, eax
+ mov ebx, [ebp+TsEbx]
+ mov ecx, [ebp+TsEcx]
+ mov edx, [ebp+TsEdx]
+ stdCall _NtSetLdtEntries <ebx, ecx, edx, eax, eax, eax>
+ mov [ebp+TsEax], eax
+ and dword ptr [ebp+TsEflags], 0FFFFFFFEH ; clear carry flag
+ cmp eax, 0 ; success?
+ je short ktgc10
+
+ or dword ptr [ebp+TsEflags], 1 ; set carry flag
+ktgc10:
+ jmp _KiExceptionExit
+
+ktgc20:
+ ;
+ ; We need to *trap* this int 2a. For exception, the eip should
+ ; point to the int 2a instruction not the instruction after it.
+ ;
+
+ sub word ptr [esp], 2
+ push 0
+ jmp _KiTrap0D
+
+_KiGetTickCount endp
+
+ page ,132
+ subttl "Return from User Mode Callback"
+;++
+;
+; NTSTATUS
+; NtCallbackReturn (
+; IN PVOID OutputBuffer OPTIONAL,
+; IN ULONG OutputLength,
+; IN NTSTATUS Status
+; )
+;
+; Routine Description:
+;
+; This function returns from a user mode callout to the kernel mode
+; caller of the user mode callback function.
+;
+; N.B. This service uses a nonstandard calling sequence.
+;
+; Arguments:
+;
+; OutputBuffer (ecx) - Supplies an optional pointer to an output buffer.
+;
+; OutputLength (edx) - Supplies the length of the output buffer.
+;
+; Status (esp + 4) - Supplies the status value returned to the caller of
+; the callback function.
+;
+; Return Value:
+;
+; If the callback return cannot be executed, then an error status is
+; returned. Otherwise, the specified callback status is returned to
+; the caller of the callback function.
+;
+; N.B. This function returns to the function that called out to user
+; mode is a callout is currently active.
+;
+;--
+
+align 16
+ PUBLIC _KiCallbackReturn
+_KiCallbackReturn proc
+
+ push fs ; save segment register
+ push ecx ; save buffer address and return status
+ push eax ;
+ mov ecx,KGDT_R0_PCR ; set PCR segment number
+ mov fs,cx ;
+ mov eax,PCR[PcPrcbData + PbCurrentThread] ; get current thread address
+ mov ecx,[eax].ThCallbackStack ; get callback stack address
+ or ecx,ecx ; check if callback active
+ jz short _KiCbExit ; if z, no callback active
+ mov edi,[esp] + 4 ; set output buffer address
+ mov esi,edx ; set output buffer length
+ mov ebp,[esp] + 0 ; set return status
+
+;
+; N.B. The following code is entered with:
+;
+; eax - The address of the current thread.
+; ecx - The callback stack address.
+; edi - The output buffer address.
+; esi - The output buffer length.
+; ebp - The callback service status.
+;
+; Restore the trap frame and callback stack addresses,
+; store the output buffer address and length, and set the service status.
+;
+
+ cld ; clear the direction flag
+ mov ebx,[ecx].CuOutBf ; get address to store output buffer
+ mov [ebx],edi ; store output buffer address
+ mov ebx,[ecx].CuOutLn ; get address to store output length
+ mov [ebx],esi ; store output buffer length
+ mov esi,PCR[PcInitialStack] ; get source NPX save area address
+ mov esp,ecx ; trim stack back to callback frame
+ pop ecx ; get previous initial stack address
+ mov [eax].ThInitialStack,ecx ; restore initial stack address
+ sub ecx,NPX_FRAME_LENGTH ; compute destination NPX save area
+ mov edx,[esi].FpControlWord ; copy NPX state to previous frame
+ mov [ecx].FpControlWord,edx ;
+ mov edx,[esi].FpStatusWord ;
+ mov [ecx].FpStatusWord,edx ;
+ mov edx,[esi].FpTagWord ;
+ mov [ecx].FpTagWord,edx ;
+ mov edx,[esi].FpCr0NpxState ;
+ mov [ecx].FpCr0NpxState,edx ;
+ mov edx,PCR[PcTss] ; get address of task switch segment
+ mov PCR[PcInitialStack],ecx ; restore stack check base address
+ sub ecx,TsV86Gs - TsHardwareSegSs ; bias for missing V86 fields
+ mov [edx].TssEsp0,ecx ; restore kernel entry stack address
+ sti ; enable interrupts
+ pop [eax].ThTrapFrame ; restore current trap frame address
+ pop [eax].ThCallbackStack ; restore callback stack address
+ mov eax,ebp ; set callback service status
+
+;
+; Restore nonvolatile registers, clean call parameters from stack, and
+; return to callback caller.
+;
+
+ pop edi ; restore nonvolatile registers
+ pop esi ;
+ pop ebx ;
+ pop ebp ;
+ pop edx ; save return address
+ add esp,8 ; remove parameters from stack
+ jmp edx ; return to callback caller
+
+;
+; Restore segment register, set systerm service status, and return.
+;
+
+_KiCbExit: ;
+ add esp, 2 * 4 ; remove saved registers from stack
+ pop fs ; restore segment register
+ mov eax,STATUS_NO_CALLBACK_ACTIVE ; set service status
+ iretd ;
+
+_KiCallbackReturn endp
+
+;
+; Fast path Nt/Zw SetLowWaitHighThread
+;
+
+ ENTER_DR_ASSIST kslwh_a, kslwh_t,NoAbiosAssist,NoV86Assist
+align 16
+ PUBLIC _KiSetLowWaitHighThread
+_KiSetLowWaitHighThread proc
+
+ ENTER_SYSCALL kslwh_a, kslwh_t ; Set up trap frame
+
+ mov eax,STATUS_NO_EVENT_PAIR ; set service status
+ mov edx,[ebp].TsEdx ; restore old trap frame address
+ mov [esi].ThTrapFrame,edx ;
+ cli ; disable interrupts
+
+ DISPATCH_USER_APC ebp, ReturnCurrentEax
+
+ EXIT_ALL NoRestoreSegs, NoRestoreVolatile
+
+_KiSetLowWaitHighThread endp
+
+ page ,132
+ subttl "Common Trap Exit"
+;++
+;
+; KiExceptionExit
+;
+; Routine Description:
+;
+; This code is transfered to at the end of the processing for
+; an exception. Its function is to restore machine state, and
+; continue thread execution. If control is returning to user mode
+; and there is a user APC pending, then control is transfered to
+; the user APC delivery routine.
+;
+; N.B. It is assumed that this code executes at IRQL zero or APC_LEVEL.
+; Therefore page faults and access violations can be taken.
+;
+; NOTE: This code is jumped to, not called.
+;
+; Arguments:
+;
+; (ebp) -> base of trap frame.
+;
+; Return Value:
+;
+; None.
+;
+;--
+align 4
+ public _KiExceptionExit
+_KiExceptionExit proc
+.FPO (0, 0, 0, 0, 0, FPO_TRAPFRAME)
+
+ cli ; disable interrupts
+ DISPATCH_USER_APC ebp
+
+;
+; Exit from Exception
+;
+
+ EXIT_ALL ,,NoPreviousMode
+
+_KiExceptionExit endp
+
+
+;++
+;
+; Kei386EoiHelper
+;
+; Routine Description:
+;
+; This code is transfered to at the end of an interrupt. (via the
+; exit_interrupt macro). It checks for user APC dispatching and
+; performs the exit_all for the interrupt.
+;
+; NOTE: This code is jumped to, not called.
+;
+; Arguments:
+;
+; (esp) -> base of trap frame.
+; interrupts are disabled
+;
+; Return Value:
+;
+; None.
+;
+;--
+align 4
+cPublicProc Kei386EoiHelper, 0
+.FPO (0, 0, 0, 0, 0, FPO_TRAPFRAME)
+ ASSERT_FS
+ DISPATCH_USER_APC esp
+ EXIT_ALL ,,NoPreviousMode
+stdENDP Kei386EoiHelper
+
+
+;++
+;
+; KiUnexpectedInterruptTail
+;
+; Routine Description:
+; This function is jumped to by an IDT entry who has no interrupt
+; handler.
+;
+; Arguments:
+;
+; (esp) - Dword, vector
+; (esp+4) - Processor generated IRet frame
+;
+;--
+
+ ENTER_DR_ASSIST kui_a, kui_t
+
+ public _KiUnexpectedInterruptTail
+_KiUnexpectedInterruptTail proc
+ ENTER_INTERRUPT kui_a, kui_t, PassDwordParm
+
+ inc dword ptr PCR[PcPrcbData+PbInterruptCount]
+
+ mov ebx, [esp] ; get vector & leave it on the stack
+ sub esp, 4 ; make space for OldIrql
+
+; esp - ptr to OldIrql
+; ebx - Vector
+; HIGH_LEVEL - Irql
+ stdCall _HalBeginSystemInterrupt, <HIGH_LEVEL,ebx,esp>
+ or eax, eax
+ jnz kui10
+
+;
+; spurious interrupt
+;
+ add esp, 8
+ EXIT_ALL ,,NoPreviousMode
+
+kui10:
+if DBG
+ push dword ptr [esp+4] ; Vector #
+ push offset FLAT:BadInterruptMessage
+ call _DbgPrint ; display unexpected interrupt message
+ add esp, 8
+endif
+;
+; end this interrupt
+;
+ INTERRUPT_EXIT
+
+_KiUnexpectedInterruptTail endp
+
+
+
+ page , 132
+ subttl "trap processing"
+
+;++
+;
+; Routine Description:
+;
+; _KiTrapxx - protected mode trap entry points
+;
+; These entry points are for internally generated exceptions,
+; such as a general protection fault. They do not handle
+; external hardware interrupts, or user software interrupts.
+;
+; Arguments:
+;
+; On entry the stack looks like:
+;
+; [ss]
+; [esp]
+; eflags
+; cs
+; eip
+; ss:sp-> [error]
+;
+; The cpu saves the previous SS:ESP, eflags, and CS:EIP on
+; the new stack if there was a privilige transition. If no
+; priviledge level transition occurred, then there is no
+; saved SS:ESP.
+;
+; Some exceptions save an error code, others do not.
+;
+; Return Value:
+;
+; None.
+;
+;--
+
+
+ page , 132
+ subttl "Macro to dispatch exception"
+
+;++
+;
+; Macro Description:
+;
+; This macro allocates exception record on stack, sets up exception
+; record using specified parameters and finally sets up arguments
+; and calls _KiDispatchException.
+;
+; Arguments:
+;
+; ExcepCode - Exception code to put into exception record
+; ExceptFlags - Exception flags to put into exception record
+; ExceptRecord - Associated exception record
+; ExceptAddress - Addr of instruction which the hardware exception occurs
+; NumParms - Number of additional parameters
+; ParameterList - the additional parameter list
+;
+; Return Value:
+;
+; None.
+;
+;--
+
+DISPATCH_EXCEPTION macro ExceptCode, ExceptFlags, ExceptRecord, ExceptAddress,\
+ NumParms, ParameterList
+ local de10, de20
+
+.FPO ( ExceptionRecordSize/4+NumParms, 0, 0, 0, 0, FPO_TRAPFRAME )
+
+; Set up exception record for raising exception
+
+?i = 0
+ sub esp, ExceptionRecordSize + NumParms * 4
+ ; allocate exception record
+ mov dword ptr [esp]+ErExceptionCode, ExceptCode
+ ; set up exception code
+ mov dword ptr [esp]+ErExceptionFlags, ExceptFlags
+ ; set exception flags
+ mov dword ptr [esp]+ErExceptionRecord, ExceptRecord
+ ; set associated exception record
+ mov dword ptr [esp]+ErExceptionAddress, ExceptAddress
+ mov dword ptr [esp]+ErNumberParameters, NumParms
+ ; set number of parameters
+ IRP z, <ParameterList>
+ mov dword ptr [esp]+(ErExceptionInformation+?i*4), z
+?i = ?i + 1
+ ENDM
+
+; set up arguments and call _KiDispatchException
+
+ mov ecx, esp ; (ecx)->exception record
+ mov eax,[ebp]+TsSegCs
+
+ test dword ptr [ebp]+TsEFlags,EFLAGS_V86_MASK
+ jz de10
+
+ mov eax,0FFFFh
+de10: and eax,MODE_MASK
+
+; 1 - first chance TRUE
+; eax - PreviousMode
+; ebp - trap frame addr
+; 0 - Null exception frame
+; ecx - exception record addr
+
+; dispatchexception as appropriate
+ stdCall _KiDispatchException, <ecx, 0, ebp, eax, 1>
+
+ mov esp, ebp ; (esp) -> trap frame
+
+ ENDM
+
+ page , 132
+ subttl "dispatch exception"
+
+;++
+;
+; CommonDispatchException
+;
+; Routine Description:
+;
+; This routine allocates exception record on stack, sets up exception
+; record using specified parameters and finally sets up arguments
+; and calls _KiDispatchException.
+;
+; NOTE:
+;
+; The purpose of this routine is to save code space. Use this routine
+; only if:
+; 1. ExceptionRecord is NULL
+; 2. ExceptionFlags is 0
+; 3. Number of parameters is less or equal than 3.
+;
+; Otherwise, you should use DISPATCH_EXCEPTION macro to set up your special
+; exception record.
+;
+; Arguments:
+;
+; (eax) = ExcepCode - Exception code to put into exception record
+; (ebx) = ExceptAddress - Addr of instruction which the hardware exception occurs
+; (ecx) = NumParms - Number of additional parameters
+; (edx) = Parameter1
+; (esi) = Parameter2
+; (edi) = Parameter3
+;
+; Return Value:
+;
+; None.
+;
+;--
+CommonDispatchException0Args:
+ xor ecx, ecx ; zero arguments
+ call CommonDispatchException
+
+CommonDispatchException1Arg0d:
+ xor edx, edx ; zero edx
+CommonDispatchException1Arg:
+ mov ecx, 1 ; one argument
+ call CommonDispatchException ; there is no return
+
+CommonDispatchException2Args0d:
+ xor edx, edx ; zero edx
+CommonDispatchException2Args:
+ mov ecx, 2 ; two arguments
+ call CommonDispatchException ; there is no return
+
+ public CommonDispatchException
+align dword
+CommonDispatchException proc
+cPublicFpo 0, ExceptionRecordLength / 4
+;
+; Set up exception record for raising exception
+;
+
+ sub esp, ExceptionRecordLength
+ ; allocate exception record
+ mov dword ptr [esp]+ErExceptionCode, eax
+ ; set up exception code
+ xor eax, eax
+ mov dword ptr [esp]+ErExceptionFlags, eax
+ ; set exception flags
+ mov dword ptr [esp]+ErExceptionRecord, eax
+ ; set associated exception record
+ mov dword ptr [esp]+ErExceptionAddress, ebx
+ mov dword ptr [esp]+ErNumberParameters, ecx
+ ; set number of parameters
+ cmp ecx, 0
+ je short de00
+
+ lea ebx, [esp + ErExceptionInformation]
+ mov [ebx], edx
+ mov [ebx+4], esi
+ mov [ebx+8], edi
+de00:
+;
+; set up arguments and call _KiDispatchException
+;
+
+ mov ecx, esp ; (ecx)->exception record
+ test dword ptr [ebp]+TsEFlags,EFLAGS_V86_MASK
+ jz short de10
+
+ mov eax,0FFFFh
+ jmp short de20
+
+de10: mov eax,[ebp]+TsSegCs
+de20: and eax,MODE_MASK
+
+; 1 - first chance TRUE
+; eax - PreviousMode
+; ebp - trap frame addr
+; 0 - Null exception frame
+; ecx - exception record addr
+
+ stdCall _KiDispatchException,<ecx, 0, ebp, eax, 1>
+
+ mov esp, ebp ; (esp) -> trap frame
+ jmp _KiExceptionExit
+
+CommonDispatchException endp
+
+ page , 132
+ subttl "Macro to verify base trap frame"
+
+;++
+;
+; Macro Description:
+;
+; This macro verifies the base trap frame is intact.
+;
+; It is possible while returing to UserMode that we take an exception.
+; Any expection which may block, such as not-present, needs to verify
+; that the base trap frame is not partially dismantled.
+;
+; Arguments:
+; The macro MUST be used directly after ENTER_TRAP macro.
+; assumses all sorts of stuff about ESP!
+;
+; Return Value:
+;
+; If the base frame was incomplete it is totally restored and the
+; return EIP of the current frame is (virtually) backed up to the
+; begining of the exit_all - the effect is that the base frame
+; will be completely exited again. (ie, the exit_all of the base
+; frame is atomic, if it's interrupted we restore it and do it over).
+;
+; None.
+;
+;--
+
+VERIFY_BASE_TRAP_FRAME macro
+ local vbfdone
+
+ mov eax, esp
+ sub eax, PCR[PcInitialStack] ; Bias out this stack
+ add eax, KTRAP_FRAME_LENGTH ; adjust for base frame
+ je short vbfdone ; if eq, then this is the base frame
+
+ cmp eax, -TsEflags ; second frame is only this big
+ jc short vbfdone ; is stack deeper then 2 frames?
+ ; yes, then done
+ ;
+ ; Stack usage is not exactly one frame, and it's not large enough
+ ; to be two complete frames; therefore, we may have a partial base
+ ; frame. (unless it's a kernel thread)
+ ;
+ ; See if this is a kernel thread - Kernel threads don't have a base
+ ; frame (and therefore don't need correcting).
+ ;
+
+ mov eax, PCR[PcTeb]
+ or eax, eax ; Any Teb?
+ jle short vbfdone ; Br if zero or kernel thread address
+
+ call KiRestoreBaseFrame
+
+ align 4
+vbfdone:
+ ENDM
+
+;++ KiRestoreBaseFrame
+;
+; Routine Description:
+;
+; Only to be used from VERIFY_BASE_TRAP_FRAME macro.
+; Makes lots of assumptions about esp & trap frames
+;
+; Arguments:
+;
+; Stack:
+; +-------------------------+
+; | |
+; | |
+; | Npx save area |
+; | |
+; | |
+; +-------------------------+
+; | (possible mvdm regs) |
+; +-------------------------+ <- fs:PcInitialStack
+; | |
+; | Partial base trap frame |
+; | |
+; | ------------+
+; +------------/ | <- Esp @ time of current frame. Location
+; | | where base trap frame is incomplete
+; | Completed 'current' |
+; | trap frame |
+; | |
+; | |
+; | |
+; | |
+; +-------------------------+ <- EBP
+; | return address (dword) |
+; +-------------------------+ <- current ESP
+; | |
+; | |
+;
+; Return:
+;
+; Stack:
+; +-------------------------+
+; | |
+; | |
+; | Npx save area |
+; | |
+; | |
+; +-------------------------+
+; | (possible mvdm regs) |
+; +-------------------------+ <- fs:PcInitialStack
+; | |
+; | Base trap frame |
+; | |
+; | |
+; | |
+; | |
+; | |
+; +-------------------------+ <- return esp & ebp
+; | |
+; | Current trap frame |
+; | | EIP set to begining of
+; | | exit_all code
+; | |
+; | |
+; | |
+; +-------------------------+ <- EBP, ESP
+; | |
+; | |
+;
+;--
+
+KiRestoreBaseFrame proc
+ pop ebx ; Get return address
+IF DBG
+ mov eax, [esp].TsEip ; EIP of trap
+ ;
+ ; This code is to handle a very specific problem of a not-present
+ ; fault during an exit_all. If it's not this problem then stop.
+ ;
+ cmp word ptr [eax], POP_GS
+ je short @f
+ cmp byte ptr [eax], POP_ES
+ je short @f
+ cmp byte ptr [eax], POP_DS
+ je short @f
+ cmp word ptr [eax], POP_FS
+ je short @f
+ cmp byte ptr [eax], IRET_OP
+ je short @f
+ int 3
+@@:
+ENDIF
+ ;
+ ; Move current trap frame out of the way to make space for
+ ; a full base trap frame
+ ;
+ mov edi, PCR[PcInitialStack]
+ sub edi, KTRAP_FRAME_LENGTH + TsEFlags + 4 ; (edi) = bottom of target
+ mov esi, esp ; (esi) = bottom of source
+ mov esp, edi ; make space before copying the data
+ mov ebp, edi ; update location of our trap frame
+ push ebx ; put return address back on stack
+
+ mov ecx, (TsEFlags+4)/4 ; # of dword to move
+ rep movsd ; Move current trap frame
+
+ ;
+ ; Part of the base frame was destoryed when the current frame was
+ ; originally pushed. Now that the current frame has been moved out of
+ ; the way restore the base frame. We know that any missing data from
+ ; the base frame was reloaded into it's corrisponding registers which
+ ; were then pushed into the current frame. So we can restore the missing
+ ; data from the current frame.
+ ;
+ mov ecx, esi ; Location of esp at time of fault
+ mov edi, PCR[PcInitialStack]
+ sub edi, KTRAP_FRAME_LENGTH ; (edi) = base trap frame
+ mov ebx, edi
+
+ sub ecx, edi ; (ecx) = # of bytes which were
+ ; removed from base frame before
+ ; trap occured
+IF DBG
+ test ecx, 3
+ jz short @f ; assume dword alignments only
+ int 3
+@@:
+ENDIF
+ mov esi, ebp ; (esi) = current frame
+ shr ecx, 2 ; copy in dwords
+ rep movsd
+ ;
+ ; The base frame is restored. Instead of backing EIP up to the
+ ; start of the interrupted EXIT_ALL, we simply move the EIP to a
+ ; well known EXIT_ALL. However, this causes a couple of problems
+ ; since this exit_all retores every register whereas the original
+ ; one may not. So:
+ ;
+ ; - When exiting from a system call, eax is normally returned by
+ ; simply not restoring it. We 'know' that the current trap frame's
+ ; EAXs is always the correct one to return. (We know this because
+ ; exit_all always restores eax (if it's going to) before any other
+ ; instruction which may cause a fault).
+ ;
+ ; - Not all enter's push the PreviousPreviousMode. Since this is
+ ; the base trap frame we know that this must be UserMode.
+ ;
+ mov eax, [ebp].TsEax ; make sure correct
+ mov [ebx].TsEax, eax ; eax is in base frame
+ mov byte ptr [ebx].TsPreviousPreviousMode, 1 ; UserMode
+
+ mov [ebp].TsEbp, ebx
+ mov [ebp].TsEip, offset _KiServiceExit2 ; ExitAll which
+
+ ; restores everything
+ ;
+ ; Since we backed up Eip we need to reset some of the kernel selector
+ ; values in case they were already restored by the attempted base frame pop
+ ;
+ mov dword ptr [ebp].TsSegDs, KGDT_R3_DATA OR RPL_MASK
+ mov dword ptr [ebp].TsSegEs, KGDT_R3_DATA OR RPL_MASK
+ mov dword ptr [ebp].TsSegFs, KGDT_R0_PCR
+
+ ;
+ ; The backed up EIP is before interrupts were disabled. Re-enable
+ ; interrupts for the current trap frame
+ ;
+ or [ebp].TsEFlags, EFLAGS_INTERRUPT_MASK
+
+ ret
+
+KiRestoreBaseFrame endp
+
+ page ,132
+ subttl "Divide error processing"
+;++
+;
+; Routine Description:
+;
+; Handle divide error fault.
+;
+; The divide error fault occurs if a DIV or IDIV instructions is
+; executed with a divisor of 0, or if the quotient is too big to
+; fit in the result operand.
+;
+; An INTEGER DIVIDED BY ZERO exception will be raised for the fault.
+; If the fault occurs in kernel mode, the system will be terminated.
+;
+; Arguments:
+;
+; At entry, the saved CS:EIP point to the faulting instruction.
+; No error code is provided with the divide error.
+;
+; Return value:
+;
+; None
+;
+;--
+ ASSUME DS:NOTHING, SS:NOTHING, ES:NOTHING
+ ENTER_DR_ASSIST kit0_a, kit0_t,NoAbiosAssist
+align dword
+ public _KiTrap00
+_KiTrap00 proc
+
+ push 0 ; push dummy error code
+ ENTER_TRAP kit0_a, kit0_t
+
+ test dword ptr [ebp]+TsEFlags,EFLAGS_V86_MASK
+ jnz Kt0040 ; trap occured in V86 mode
+
+ test byte ptr [ebp]+TsSegCs, MODE_MASK ; Is previous mode = USER
+ jz short Kt0000
+
+ cmp word ptr [ebp]+TsSegCs,KGDT_R3_CODE OR RPL_MASK
+ jne Kt0020
+;
+; Set up exception record for raising Integer_Divided_by_zero exception
+; and call _KiDispatchException
+;
+
+Kt0000:
+
+if DBG
+ test [ebp]+TsEFlags, EFLAGS_INTERRUPT_MASK ; faulted with
+ jnz short @f ; interrupts disabled?
+
+ xor eax, eax
+ mov esi, [ebp]+TsEip ; [esi] = faulting instruction
+ stdCall _KeBugCheckEx,<IRQL_NOT_LESS_OR_EQUAL,eax,-1,eax,esi>
+@@:
+endif
+
+ sti
+
+
+;
+; Flat mode
+;
+; The intel processor raises a divide by zero expcetion on DIV instruction
+; which overflows. To be compatible we other processors we want to
+; return overflows as such and not as divide by zero's. The operand
+; on the div instruction is tested to see if it's zero or not.
+;
+ stdCall _Ki386CheckDivideByZeroTrap,<ebp>
+ mov ebx, [ebp]+TsEip ; (ebx)-> faulting instruction
+ jmp CommonDispatchException0Args ; Won't return
+
+Kt0010:
+;
+; 16:16 mode
+;
+ sti
+ mov ebx, [ebp]+TsEip ; (ebx)-> faulting instruction
+ mov eax, STATUS_INTEGER_DIVIDE_BY_ZERO
+ jmp CommonDispatchException0Args ; never return
+
+Kt0020:
+; Check to see if this process is a vdm
+ mov ebx,PCR[PcPrcbData+PbCurrentThread]
+ mov ebx,[ebx]+ThApcState+AsProcess
+ test byte ptr [ebx]+PrVdmFlag,0fh ; is this a vdm process?
+ jz Kt0010
+
+Kt0040:
+ stdCall _Ki386VdmReflectException_A, <0>
+
+ or al,al
+ jz short Kt0010 ; couldn't reflect, gen exception
+ jmp _KiExceptionExit
+
+_KiTrap00 endp
+
+
+ page ,132
+ subttl "Debug Exception"
+;++
+;
+; Routine Description:
+;
+; Handle debug exception.
+;
+; The processor triggers this exception for any of the following
+; conditions:
+;
+; 1. Instruction breakpoint fault.
+; 2. Data address breakpoint trap.
+; 3. General detect fault.
+; 4. Single-step trap.
+; 5. Task-switch breadkpoint trap.
+;
+;
+; Arguments:
+;
+; At entry, the values of saved CS and EIP depend on whether the
+; exception is a fault or a trap.
+; No error code is provided with the divide error.
+;
+; Return value:
+;
+; None
+;
+;--
+ ASSUME DS:NOTHING, SS:NOTHING, ES:NOTHING
+ ENTER_DR_ASSIST kit1_a, kit1_t, NoAbiosAssist
+align dword
+ public _KiTrap01
+_KiTrap01 proc
+
+; Set up machine state frame for displaying
+
+ push 0 ; push dummy error code
+ ENTER_TRAP kit1_a, kit1_t
+
+;
+; If caller is user mode, we want interrupts back on.
+; . all relevent state has already been saved
+; . user mode code always runs with ints on
+;
+; If caller is kernel mode, we want them off!
+; . some state still in registers, must prevent races
+; . kernel mode code can run with ints off
+;
+;
+
+ test dword ptr [ebp]+TsEFlags,EFLAGS_V86_MASK
+ jnz kit01_30 ; fault occured in V86 mode => Usermode
+
+ test word ptr [ebp]+TsSegCs,MODE_MASK
+ jz kit01_10
+
+ cmp word ptr [ebp]+TsSegCs,KGDT_R3_CODE OR RPL_MASK
+ jne kit01_30
+kit01_05:
+ sti
+kit01_10:
+
+;
+; Set up exception record for raising single step exception
+; and call _KiDispatchException
+;
+
+kit01_20:
+ and dword ptr [ebp]+TsEflags, not EFLAGS_TF_BIT
+ mov ebx, [ebp]+TsEip ; (ebx)-> faulting instruction
+ mov eax, STATUS_SINGLE_STEP
+ jmp CommonDispatchException0Args ; Never return
+
+kit01_30:
+
+; Check to see if this process is a vdm
+
+ mov ebx,PCR[PcPrcbData+PbCurrentThread]
+ mov ebx,[ebx]+ThApcState+AsProcess
+ test byte ptr [ebx]+PrVdmFlag,0fh ; is this a vdm process?
+ jz kit01_05
+
+ stdCall _Ki386VdmReflectException_A, <01h>
+ test ax,0FFFFh
+ jz Kit01_20
+
+ jmp _KiExceptionExit
+
+_KiTrap01 endp
+
+ page ,132
+ subttl "Nonmaskable Interrupt"
+;++
+;
+; Routine Description:
+;
+; Handle Nonmaskable interrupt.
+;
+; An NMI is typically used to signal serious system conditions
+; such as bus time-out, memory parity error, and so on.
+;
+; Upon detection of the NMI, the system will be terminated, ie a
+; bugcheck will be raised, no matter what previous mode is.
+;
+; Arguments:
+;
+; No error code is provided with the error.
+;
+; Return value:
+;
+; None
+;
+;--
+ ASSUME DS:NOTHING, SS:NOTHING, ES:NOTHING
+; ENTER_DR_ASSIST kit2_a, kit2_t, NoAbiosAssist
+align dword
+ public _KiTrap02
+_KiTrap02 proc
+.FPO (1, 0, 0, 0, 0, 2)
+ cli
+;
+; Update the TSS pointer in the PCR to point to the NMI TSS
+; (which is what we're running on, or else we wouldn't be here)
+;
+ push dword ptr PCR[PcTss]
+
+ mov eax, PCR[PcGdt]
+ mov ch, [eax+KGDT_NMI_TSS+KgdtBaseHi]
+ mov cl, [eax+KGDT_NMI_TSS+KgdtBaseMid]
+ shl ecx, 16
+ mov cx, [eax+KGDT_NMI_TSS+KgdtBaseLow]
+ mov PCR[PcTss], ecx
+
+;
+; Clear Nested Task bit in EFLAGS
+;
+ pushfd
+ and [esp], not 04000h
+ popfd
+
+;
+; Clear the busy bit in the TSS selector
+;
+ mov ecx, PCR[PcGdt]
+ lea eax, [ecx] + KGDT_NMI_TSS
+ mov byte ptr [eax+5], 089h ; 32bit, dpl=0, present, TSS32, not busy
+
+;
+; Let the HAL have a crack at it before we crash
+;
+ stdCall _HalHandleNMI,<0>
+
+ mov eax, offset FLAT:_ZwUnmapViewOfSection@8
+ sub eax, esp
+ cmp eax, 0a00h
+ jnc short @f
+
+ ; not on a real stack, crash
+ stdCall _KeBugCheckEx,<UNEXPECTED_KERNEL_MODE_TRAP,2,0,0,0>
+@@:
+
+;
+; We're back, therefore the Hal has dealt with the NMI. (Crashing
+; is done in the Hal for this special case.)
+;
+
+ pop dword ptr PCR[PcTss] ; restore PcTss
+
+ mov ecx, PCR[PcGdt]
+ lea eax, [ecx] + KGDT_TSS
+ mov byte ptr [eax+5], 08bh ; 32bit, dpl=0, present, TSS32, *busy*
+
+ pushfd ; Set Nested Task bit in EFLAGS
+ or [esp], 04000h ; so iretd will do a tast switch
+ popfd
+
+ iretd ; Return from NMI
+ jmp short _KiTrap02 ; in case we NMI again
+
+_KiTrap02 endp
+
+ page ,132
+ subttl "DebugService Breakpoint"
+;++
+;
+; Routine Description:
+;
+; Handle INT 2d DebugService
+;
+; The trap is caused by an INT 2d. This is used instead of a
+; BREAKPOINT exception so that parameters can be passed for the
+; requested debug service. A BREAKPOINT instruction is assumed
+; to be right after the INT 2d - this allows this code to share code
+; with the breakpoint handler.
+;
+; Arguments:
+; eax - ServiceClass - which call is to be performed
+; ecx - Arg1 - generic first argument
+; edx - Arg2 - generic second argument
+;
+;--
+
+ ASSUME DS:NOTHING, SS:NOTHING, ES:NOTHING
+
+ ENTER_DR_ASSIST kids_a, kids_t, NoAbiosAssist
+align dword
+ public _KiDebugService
+_KiDebugService proc
+ push 0 ; push dummy error code
+ ENTER_TRAP kids_a, kids_t
+; sti ; *NEVER sti here*
+
+ inc dword ptr [ebp]+TsEip
+ mov eax, [ebp]+TsEax ; ServiceClass
+ mov ecx, [ebp]+TsEcx ; Arg1 (already loaded)
+ mov edx, [ebp]+TsEdx ; Arg2 (already loaded)
+ jmp KiTrap03DebugService
+
+_KiDebugService endp
+
+ page ,132
+ subttl "Single Byte INT3 Breakpoin"
+;++
+;
+; Routine Description:
+;
+; Handle INT 3 breakpoint.
+;
+; The trap is caused by a single byte INT 3 instruction. A
+; BREAKPOINT exception with additional parameter indicating
+; READ access is raised for this trap if previous mode is user.
+;
+; Arguments:
+;
+; At entry, the saved CS:EIP point to the instruction immediately
+; following the INT 3 instruction.
+; No error code is provided with the error.
+;
+; Return value:
+;
+; None
+;
+;--
+ ASSUME DS:NOTHING, SS:NOTHING, ES:NOTHING
+
+ ENTER_DR_ASSIST kit3_a, kit3_t, NoAbiosAssist
+align dword
+ public _KiTrap03
+_KiTrap03 proc
+ push 0 ; push dummy error code
+ ENTER_TRAP kit3_a, kit3_t
+
+ lock inc ds:_KiHardwareTrigger ; trip hardware analyzer
+
+ mov eax, BREAKPOINT_BREAK
+
+KiTrap03DebugService:
+;
+; If caller is user mode, we want interrupts back on.
+; . all relevent state has already been saved
+; . user mode code always runs with ints on
+;
+; If caller is kernel mode, we want them off!
+; . some state still in registers, must prevent races
+; . kernel mode code can run with ints off
+;
+;
+; Arguments:
+; eax - ServiceClass - which call is to be performed
+; ecx - Arg1 - generic first argument
+; edx - Arg2 - generic second argument
+
+ test dword ptr [ebp]+TsEFlags,EFLAGS_V86_MASK
+ jnz kit03_30 ; fault occured in V86 mode => Usermode
+
+ test word ptr [ebp]+TsSegCs,MODE_MASK
+ jz kit03_10
+
+ cmp word ptr [ebp]+TsSegCs,KGDT_R3_CODE OR RPL_MASK
+ jne kit03_30
+
+kit03_05:
+ sti
+kit03_10:
+
+
+;
+; Set up exception record and arguments for raising breakpoint exception
+;
+
+ mov esi, ecx ; ExceptionInfo 2
+ mov edi, edx ; ExceptionInfo 3
+ mov edx, eax ; ExceptionInfo 1
+
+ mov ebx, [ebp]+TsEip
+ dec ebx ; (ebx)-> int3 instruction
+ mov ecx, 3
+ mov eax, STATUS_BREAKPOINT
+ call CommonDispatchException ; Never return
+
+kit03_30:
+; Check to see if this process is a vdm
+
+ mov ebx,PCR[PcPrcbData+PbCurrentThread]
+ mov ebx,[ebx]+ThApcState+AsProcess
+ test byte ptr [ebx]+PrVdmFlag,0fh ; is this a vdm process?
+ jz kit03_05
+
+
+ stdCall _Ki386VdmReflectException_A, <03h>
+ test ax,0FFFFh
+ jz Kit03_10
+
+ jmp _KiExceptionExit
+
+_KiTrap03 endp
+
+ page ,132
+ subttl "Integer Overflow"
+;++
+;
+; Routine Description:
+;
+; Handle INTO overflow.
+;
+; The trap occurs when the processor encounters an INTO instruction
+; and the OF flag is set.
+;
+; An INTEGER_OVERFLOW exception will be raised for this fault.
+;
+; N.B. i386 will not generate fault if only OF flag is set.
+;
+; Arguments:
+;
+; At entry, the saved CS:EIP point to the instruction immediately
+; following the INTO instruction.
+; No error code is provided with the error.
+;
+; Return value:
+;
+; None
+;
+;--
+ ASSUME DS:NOTHING, SS:NOTHING, ES:NOTHING
+
+ ENTER_DR_ASSIST kit4_a, kit4_t, NoAbiosAssist
+align dword
+ public _KiTrap04
+_KiTrap04 proc
+
+ push 0 ; push dummy error code
+ ENTER_TRAP kit4_a, kit4_t
+
+ test dword ptr [ebp]+TsEFlags,EFLAGS_V86_MASK
+ jnz short Kt0430 ; in a vdm, reflect to vdm
+
+ test byte ptr [ebp]+TsSegCs,MODE_MASK
+ jz short Kt0410 ; in kernel mode, gen exception
+
+ cmp word ptr [ebp]+TsSegCs,KGDT_R3_CODE OR RPL_MASK
+ jne short Kt0420 ; maybe in a vdm
+
+; Set up exception record and arguments for raising exception
+
+Kt0410: sti
+ mov ebx, [ebp]+TsEip ; (ebx)-> instr. after INTO
+ dec ebx ; (ebx)-> INTO
+ mov eax, STATUS_INTEGER_OVERFLOW
+ jmp CommonDispatchException0Args ; Never return
+
+Kt0430:
+ stdCall _Ki386VdmReflectException_A, <04h>
+ test al,0fh
+ jz Kt0410 ; couldn't reflect, gen exception
+ jmp _KiExceptionExit
+
+Kt0420:
+; Check to see if this process is a vdm
+
+ mov ebx,PCR[PcPrcbData+PbCurrentThread]
+ mov ebx,[ebx]+ThApcState+AsProcess
+ test byte ptr [ebx]+PrVdmFlag,0fh ; is this a vdm process?
+ jz Kt0410
+ jmp Kt0430
+
+_KiTrap04 endp
+
+ page ,132
+ subttl "Bound Check fault"
+;++
+;
+; Routine Description:
+;
+; Handle bound check fault.
+;
+; The bound check fault occurs if a BOUND instruction finds that
+; the tested value is outside the specified range.
+;
+; For bound check fault, an ARRAY BOUND EXCEEDED exception will be
+; raised.
+; For kernel mode exception, it causes system to be terminated.
+;
+; Arguments:
+;
+; At entry, the saved CS:EIP point to the faulting BOUND
+; instruction.
+; No error code is provided with the error.
+;
+; Return value:
+;
+; None
+;
+;--
+ ASSUME DS:NOTHING, SS:NOTHING, ES:NOTHING
+ ENTER_DR_ASSIST kit5_a, kit5_t, NoAbiosAssist
+align dword
+ public _KiTrap05
+_KiTrap05 proc
+
+ push 0 ; push dummy error code
+ ENTER_TRAP kit5_a, kit5_t
+
+ test dword ptr [ebp]+TsEFlags,EFLAGS_V86_MASK
+ jnz Kt0530 ; fault in V86 mode
+
+ test byte ptr [ebp]+TsSegCs, MODE_MASK ; Is previous mode = USER
+ jnz short Kt0500 ; if nz, previous mode = user
+ mov eax, EXCEPTION_BOUND_CHECK ; (eax) = exception type
+ jmp _KiSystemFatalException ; go terminate the system
+
+
+kt0500: cmp word ptr [ebp]+TsSegCs,KGDT_R3_CODE OR RPL_MASK
+ jne short Kt0520 ; maybe in a vdm
+;
+; set exception record and arguments and call _KiDispatchException
+;
+Kt0510: sti
+ mov ebx, [ebp]+TsEip ; (ebx)->BOUND instruction
+ mov eax, STATUS_ARRAY_BOUNDS_EXCEEDED
+ jmp CommonDispatchException0Args ; Won't return
+
+Kt0520:
+; Check to see if this process is a vdm
+
+ mov ebx,PCR[PcPrcbData+PbCurrentThread]
+ mov ebx,[ebx]+ThApcState+AsProcess
+ test byte ptr [ebx]+PrVdmFlag,0fh ; is this a vdm process?
+ jz Kt0510
+
+Kt0530:
+
+ stdCall _Ki386VdmReflectException_A, <05h>
+ test al,0fh
+ jz Kt0510 ; couldn't reflect, gen exception
+ jmp _KiExceptionExit
+
+_KiTrap05 endp
+
+ page ,132
+ subttl "Invalid OP code"
+;++
+;
+; Routine Description:
+;
+; Handle invalid op code fault.
+;
+; The invalid opcode fault occurs if CS:EIP point to a bit pattern which
+; is not recognized as an instruction by the 386. This may happen if:
+;
+; 1. the opcode is not a valid 80386 instruction
+; 2. a register operand is specified for an instruction which requires
+; a memory operand
+; 3. the LOCK prefix is used on an instruction that cannot be locked
+;
+; If fault occurs in USER mode:
+; an Illegal_Instruction exception will be raised
+; if fault occurs in KERNEL mode:
+; system will be terminated.
+;
+; Arguments:
+;
+; At entry, the saved CS:EIP point to the first byte of the invalid
+; instruction.
+; No error code is provided with the error.
+;
+; Return value:
+;
+; None
+;
+;--
+ ASSUME DS:FLAT, SS:NOTHING, ES:NOTHING
+
+ ENTER_DR_ASSIST kit6_a, kit6_t, NoAbiosAssist,, kit6_v
+align dword
+ public _KiTrap06
+_KiTrap06 proc
+
+
+; sudeepb 08-Dec-1992 KiTrap06 is performance critical for VDMs,
+; while it hardly ever gets executed in native mode. So this whole
+; code is tuned for VDMs.
+
+ test dword ptr [esp]+8h,EFLAGS_V86_MASK
+ jz Kt060i
+
+if FAST_BOP
+
+ FAST_V86_TRAP_6
+endif
+
+Kt6SlowBop:
+ push 0 ; push dummy error code
+ ENTER_TRAPV86 kit6_a, kit6_v
+
+ ; Raise Irql to APC level before enabling interrupts
+ mov ecx, APC_LEVEL
+ fstCall KfRaiseIrql
+ push eax ; Save OldIrql
+ sti
+
+ call VdmDispatchBop
+ test al,0fh
+ jnz short Kt061i
+
+ stdCall _Ki386VdmReflectException,<6>
+ test al,0fh
+ jnz Kt061i
+ pop ecx ; (TOS) = OldIrql
+ fstCall KfLowerIrql
+ jmp Kt0635
+Kt061i:
+ pop ecx ; (TOS) = OldIrql
+ fstCall KfLowerIrql
+ cli
+ test dword ptr [ebp]+TsEFlags,EFLAGS_V86_MASK
+ jz Kt062i
+
+ EXIT_TRAPV86
+ ;
+ ; EXIT_TRAPv86 does not exit if a user mode apc has switched
+ ; the context from V86 mode to flat mode (VDM monitor context)
+ ;
+
+Kt062i:
+ jmp _KiExceptionExit
+
+Kt060i:
+ push 0 ; Push dummy error code
+ ENTER_TRAP kit6_a, kit6_t
+
+ test byte ptr [ebp]+TsSegCs, MODE_MASK ; Is previous mode = USER
+ jz short Kt0635 ; if z, kernel mode - go dispatch exception
+
+;
+; UserMode. Did the fault happen in a vdm running in protected mode?
+;
+
+ cmp word ptr [ebp]+TsSegCs, KGDT_R3_CODE OR RPL_MASK
+ jz short kt0605
+
+; Check to see if this process is a vdm
+
+ mov ebx,PCR[PcPrcbData+PbCurrentThread]
+ mov ebx,[ebx]+ThApcState+AsProcess
+ test byte ptr [ebx]+PrVdmFlag,0fh ; is this a vdm process?
+ jnz Kt0650
+
+;
+; Invalid Opcode exception could be either INVALID_LOCK_SEQUENCE or
+; ILLEGAL_INSTRUCTION.
+;
+
+kt0605: sti
+ mov eax, [ebp]+TsSegCs
+ push ds
+ mov ds, ax
+ mov esi, [ebp]+TsEip ; (ds:esi) -> address of faulting instruction
+ mov ecx, MAX_INSTRUCTION_PREFIX_LENGTH
+Kt0610:
+ lods byte ptr [esi] ; (al)= instruction byte
+ cmp al, MI_LOCK_PREFIX ; Is it a lock prefix?
+ je short Kt0640 ; Yes, raise Invalid_lock exception
+ loop short Kt0610 ; keep on looping
+
+;
+; Set up exception record for raising Illegal instruction exception
+;
+
+Kt0630: pop ds
+Kt0635: mov ebx, [ebp]+TsEip ; (ebx)-> invalid instruction
+ mov eax, STATUS_ILLEGAL_INSTRUCTION
+ jmp CommonDispatchException0Args ; Won't return
+
+;
+; Set up exception record for raising Invalid lock sequence exception
+;
+
+Kt0640: pop ds
+ mov ebx, [ebp]+TsEip ; (ebx)-> invalid instruction
+ mov eax, STATUS_INVALID_LOCK_SEQUENCE
+ jmp CommonDispatchException0Args ; Won't return
+
+Kt0650:
+
+ ; Raise Irql to APC level before enabling interrupts
+ mov ecx, APC_LEVEL
+ fstCall KfRaiseIrql
+ push eax ; SaveOldIrql
+ sti
+ call VdmDispatchBop
+ test al,0fh
+ jnz short Kt0660
+
+ stdCall _Ki386VdmReflectException,<6>
+ test al,0fh
+ jnz Kt0660
+ pop ecx ; (TOS) = OldIrql
+ fstCall KfLowerIrql
+ jmp short Kt0635
+
+Kt0660:
+ pop ecx ; (TOS) = OldIrql
+ fstCall KfLowerIrql
+ jmp _KiExceptionExit
+
+_KiTrap06 endp
+
+ page ,132
+ subttl "Coprocessor Not Avalaible"
+;++
+;
+; Routine Description:
+;
+; Handle Coprocessor not avaliable exception.
+;
+; If we are REALLY emulating the 80387, the trap 07 vector is edited
+; to point directly at the emulator's entry point. So this code is
+; only hit when an 80387 DOES exist.
+;
+; The current threads coprocessor state is loaded into the
+; coprocessor. If the coprocessor has a different threads state
+; in it (UP only) it is first saved away. The thread is then continued.
+; Note: the threads state may contian the TS bit - In this case the
+; code loops back to the top of the Trap07 handler. (which is where
+; we would end up if we let the thread return to user code anyway).
+;
+; If the threads NPX context is in the coprocessor and we hit a Trap07
+; there is an NPX error which needs to be processed. If the trap was
+; from usermode the error is dispatched. If the trap was from kernelmode
+; the error is remembered, but we clear CR0 so the kernel code can
+; continue. We can do this because the kernel mode code will restore
+; CR0 (and set TS) to signal a delayed error for this thread.
+;
+; Arguments:
+;
+; At entry, the saved CS:EIP point to the first byte of the faulting
+; instruction.
+; No error code is provided with the error.
+;
+; Return value:
+;
+; None
+;
+;--
+ ASSUME DS:NOTHING, SS:NOTHING, ES:NOTHING
+
+ ENTER_DR_ASSIST kit7_a, kit7_t, NoAbiosAssist
+align dword
+ public _KiTrap07
+_KiTrap07 proc
+
+ push 0 ; push dummy error code
+ ENTER_TRAP kit7_a, kit7_t
+Kt0700:
+ mov eax, PCR[PcPrcbData+PbCurrentThread]
+ mov ecx, PCR[PcInitialStack] ; (ecx) -> top of kernel stack
+ cli ; don't context switch
+ test dword ptr [ecx].FpCr0NpxState,CR0_EM
+ jnz Kt07140
+
+Kt0701: cmp byte ptr [eax].ThNpxState, NPX_STATE_LOADED
+ je Kt0715
+
+;
+; Trap occured and this threads NPX state is not loaded. Load it now
+; and resume the application. If someone elses state is in the coprocessor
+; (uniprocessor implementation only) then save it first.
+;
+
+ mov ebx, cr0
+ and ebx, NOT (CR0_MP+CR0_TS+CR0_EM)
+ mov cr0, ebx ; allow frstor (& fnsave) to work
+
+ifdef NT_UP
+Kt0702:
+ mov edx, PCR[PcPrcbData+PbNpxThread] ; Owner of NPX state
+ or edx, edx ; NULL?
+ jz Kt0704 ; Yes - skip save
+
+;
+; Due to an hardware errata we need to know that the coprocessor
+; doesn't generate an error condition once interrupts are disabled and
+; trying to perform an fnsave which could wait for the error condition
+; to be handled.
+;
+; The fix for this errata is that we "know" that the coprocessor is
+; being used by a different thread then the one which may have caused
+; the error condition. The round trip time to swap to a new thread
+; is longer then ANY floating point instruction. We therefore know
+; that any possible coprocessor error has already occured and been
+; handled.
+;
+ mov esi,[edx].ThInitialStack
+ sub esi, NPX_FRAME_LENGTH ; Space for NPX_FRAME
+
+ fnsave [esi] ; Save threads coprocessor state
+
+ mov byte ptr [edx].ThNpxState, NPX_STATE_NOT_LOADED
+Kt0704:
+endif
+
+;
+; Load current threads coprocessor state into the coprocessor
+;
+; (eax) - CurrentThread
+; (ecx) - CurrentThreads NPX save area
+; (ebx) - CR0
+; (ebp) - trap frame
+; Interrupts disabled
+;
+
+ mov byte ptr [eax].ThNpxState, NPX_STATE_LOADED
+ mov PCR[PcPrcbData+PbNpxThread], eax ; owner of coprocessors state
+
+;
+; frstor might generate a NPX execption if there's an error image being
+; loaded. The handler will simply set the TS bit for this context an iret.
+;
+
+Kt0704a:
+ frstor [ecx] ; reload NPX context
+ sti ; Allow interrupts & context switches
+ nop ; sti needs one cycle
+
+ cmp dword ptr [ecx].FpCr0NpxState, 0
+ jz _KiExceptionExit ; nothing to set, skip CR0 reload
+
+;
+; Note: we have to get the CR0 value again to insure that we have the
+; correct state for TS. We may have context switched since
+; the last move from CR0, and our npx state may have been moved off
+; of the npx.
+;
+ cli
+if DBG
+ test dword ptr [ecx].FpCr0NpxState, NOT (CR0_MP+CR0_EM+CR0_TS)
+ jnz short Kt07dbg1
+endif
+ mov ebx,CR0
+ or ebx, [ecx].FpCr0NpxState
+ mov cr0, ebx ; restore threads CR0 NPX state
+ sti
+ test ebx, CR0_TS ; Setting TS? (delayed error)
+ jz _KiExceptionExit ; No - continue
+
+ jmp Kt0700 ; Dispatch delayed exception
+if DBG
+Kt07dbg1: int 3
+Kt07dbg2: int 3
+Kt07dbg3: int 3
+ sti
+ jmp short $-2
+endif
+
+Kt0705:
+;
+; A Trap07 or Trap10 has occured from a ring 0 ESCAPE instruction. This
+; may occur when trying to load the coprocessors state. These
+; code paths rely on Cr0NpxState to signal a delayed error (not CR0) - we
+; set CR0_TS in Cr0NpxState to get a delayed error, and make sure CR0 CR0_TS
+; is not set so the R0 ESC instruction(s) can complete.
+;
+; (ecx) - CurrentThreads NPX save area
+; (ebp) - trap frame
+; Interrupts disabled
+;
+
+if DBG
+ mov eax, cr0 ; Did we fault because some bit in CR0
+ test eax, (CR0_TS+CR0_MP+CR0_EM)
+ jnz short Kt07dbg3
+endif
+
+ or dword ptr [ecx].FpCr0NpxState, CR0_TS ; signal a delayed error
+ mov ecx, [ebp]+TsEip
+
+ cmp ecx, Kt0704a ; Is this fault on reload a thread's context?
+ jne short Kt0716 ; No, dispatch exception
+
+ add dword ptr [ebp]+TsEip, 3 ; Skip frstor ecx instruction
+ jmp _KiExceptionExit
+
+Kt0710:
+ mov eax, PCR[PcPrcbData+PbCurrentThread]
+ mov ecx, PCR[PcInitialStack] ; (ecx) -> top of kernel stack
+
+Kt0715:
+ test dword ptr [ebp]+TsEFlags,EFLAGS_V86_MASK
+ jnz Kt07130 ; v86 mode
+
+ test byte ptr [ebp]+TsSegCs, MODE_MASK ; Is previousMode=USER?
+ jz Kt0705 ; if z, previousmode=SYSTEM
+
+ cmp word ptr [ebp]+TsSegCs,KGDT_R3_CODE OR RPL_MASK
+ jne Kt07110
+
+;
+; We are about to dispatch a floating point exception to user mode.
+; We need to check to see if the user's NPX instruction is suppose to
+; cause an exception or not.
+;
+; (ecx) - CurrentThreads NPX save area
+;
+
+Kt0716: stdCall _Ki386CheckDelayedNpxTrap,<ebp,ecx>
+ or al, al
+ jnz _KiExceptionExit ; Already handled
+
+ mov eax, PCR[PcPrcbData+PbCurrentThread]
+ mov ecx, PCR[PcInitialStack] ; (ecx) -> top of kernel stack
+
+Kt0720:
+;
+; Some type of coprocessor exception has occured for the current thread.
+;
+; (eax) - CurrentThread
+; (ecx) - CurrentThreads NPX save area
+; (ebp) - TrapFrame
+; Interrupts disabled
+;
+ mov ebx, cr0
+ and ebx, NOT (CR0_MP+CR0_EM+CR0_TS)
+ mov cr0, ebx ; Clear MP+TS+EM to do fnsave & fwait
+
+;
+; Save the faulting state so we can inspect the cause of the floating
+; point fault
+;
+ fnsave [ecx]
+ fwait ; in case fnsave hasn't finished yet
+
+if DBG
+ test dword ptr [ecx].FpCr0NpxState, NOT (CR0_MP+CR0_EM+CR0_TS)
+ jnz Kt07dbg2
+endif
+ or ebx, CR0_TS
+ or ebx,[ecx]+FpCr0NpxState ; restore this threads CR0 NPX state
+ mov cr0, ebx ; set TS so next ESC access causes trap
+
+;
+; Clear TS bit in Cr0NpxFlags in case it was set to trigger this trap.
+;
+ and dword ptr [ecx].FpCr0NpxState, NOT CR0_TS
+
+;
+; The state is no longer in the coprocessor. Clear ThNpxState and
+; re-enable interrupts to allow context switching.
+;
+ mov byte ptr [eax].ThNpxState, NPX_STATE_NOT_LOADED
+ mov dword ptr PCR[PcPrcbData+PbNpxThread], 0 ; No state in coprocessor
+ sti
+
+;
+; According to the floating error priority, we test what is the cause of
+; the NPX error and raise an appropriate exception.
+;
+
+ mov ebx, [ecx] + FpErrorOffset
+ movzx eax, word ptr [ecx] + FpControlWord
+ and eax, FSW_INVALID_OPERATION + FSW_DENORMAL + FSW_ZERO_DIVIDE + FSW_OVERFLOW + FSW_UNDERFLOW + FSW_PRECISION
+ not eax ; ax = mask of enabled exceptions
+ and eax, [ecx] + FpStatusWord
+ test eax, FSW_INVALID_OPERATION ; Is it an invalid op exception?
+ jz Kt0740 ; if z, no, go Kt0740
+ test eax, FSW_STACK_FAULT ; Is it caused by stack fault?
+ jnz short Kt0730 ; if nz, yes, go Kt0730
+
+;
+; Raise Floating reserved operand exception
+;
+
+ mov eax, STATUS_FLOAT_INVALID_OPERATION
+ jmp CommonDispatchException1Arg0d ; Won't return
+
+Kt0730:
+;
+; Raise Access Violation exception for stack overflow/underflow
+;
+
+ mov esi, [ecx] + FpDataOffset ; (esi) = operand addr
+ mov eax, STATUS_FLOAT_STACK_CHECK
+ jmp CommonDispatchException2Args0d ; Won't return
+
+
+Kt0740:
+
+; Check for floating zero divide exception
+
+ test eax, FSW_ZERO_DIVIDE ; Is it a zero divide error?
+ jz short Kt0750 ; if z, no, go Kt0750
+
+; Raise Floating divided by zero exception
+
+ mov eax, STATUS_FLOAT_DIVIDE_BY_ZERO
+ jmp CommonDispatchException1Arg0d ; Won't return
+
+Kt0750:
+
+; Check for denormal error
+
+ test eax, FSW_DENORMAL ; Is it a denormal error?
+ jz short Kt0760 ; if z, no, go Kt0760
+
+; Raise floating reserved operand exception
+
+ mov eax, STATUS_FLOAT_INVALID_OPERATION
+ jmp CommonDispatchException1Arg0d ; Won't return
+
+Kt0760:
+
+; Check for floating overflow error
+
+ test eax, FSW_OVERFLOW ; Is it an overflow error?
+ jz short Kt0770 ; if z, no, go Kt0770
+
+; Raise floating overflow exception
+
+ mov eax, STATUS_FLOAT_OVERFLOW
+ jmp CommonDispatchException1Arg0d ; Won't return
+
+Kt0770:
+
+; Check for floating underflow error
+
+ test eax, FSW_UNDERFLOW ; Is it a underflow error?
+ jz short Kt0780 ; if z, no, go Kt0780
+
+; Raise floating underflow exception
+
+ mov eax, STATUS_FLOAT_UNDERFLOW
+ jmp CommonDispatchException1Arg0d ; Won't return
+
+Kt0780:
+
+; Check for precision (IEEE inexact) error
+
+ test eax, FSW_PRECISION ; Is it a precision error
+ jz short Kt07100 ; if z, no, go Kt07100
+
+ mov eax, STATUS_FLOAT_INEXACT_RESULT
+ jmp CommonDispatchException1Arg0d ; Won't return
+
+Kt07100:
+
+; If status word does not indicate error, then something is wrong...
+
+ sti
+; stop the system
+ stdCall _KeBugCheck, <TRAP_CAUSE_UNKNOWN>
+
+Kt07110:
+; Check to see if this process is a vdm
+
+ mov ebx,PCR[PcPrcbData+PbCurrentThread]
+ mov ebx,[ebx]+ThApcState+AsProcess
+ test byte ptr [ebx]+PrVdmFlag,0fh ; is this a vdm process?
+ jz Kt0720 ; no, dispatch exception
+
+Kt07130:
+; Turn off TS
+ mov ebx,CR0
+ and ebx,NOT CR0_TS
+ mov CR0,ebx
+ and dword ptr [ecx]+FpCr0NpxState,NOT CR0_TS
+
+
+; Reflect the exception to the vdm, the VdmHandler enables interrupts
+
+ ; Raise Irql to APC level before enabling interrupts
+ mov ecx, APC_LEVEL
+ fstCall KfRaiseIrql
+ push eax ; Save OldIrql
+ sti
+
+ stdCall _VdmDispatchIRQ13, <ebp> ; ebp - Trapframe
+ test al,0fh
+ jnz Kt07135
+ pop ecx ; (TOS) = OldIrql
+ fstCall KfLowerIrql
+ jmp Kt0720 ; could not reflect, gen exception
+
+Kt07135:
+ pop ecx ; (TOS) = OldIrql
+ fstCall KfLowerIrql
+ jmp _KiExceptionExit
+
+Kt07140:
+
+;
+; Insure that this is not an NPX instruction in the kernel. (If
+; an app, such as C7, sets the EM bit after executing NPX instructions,
+; the fsave in SwapContext will catch an NPX exception
+;
+ cmp [ebp].TsSegCS, word ptr KGDT_R0_CODE
+ je Kt0701
+
+;
+; Check to see if it really is a VDM
+;
+ mov ebx,PCR[PcPrcbData+PbCurrentThread]
+ mov ebx,[ebx]+ThApcState+AsProcess
+ test byte ptr [ebx]+PrVdmFlag,0fh ; is this a vdm process?
+ jz Kt07100
+
+; A vdm is emulating NPX instructions on a machine with an NPX.
+
+ stdCall _Ki386VdmReflectException_A, <07h>
+ test al,0fh
+ jnz _KiExceptionExit
+
+ mov ebx, [ebp]+TsEip ; (ebx)->faulting instruction
+ mov eax, STATUS_ACCESS_VIOLATION
+ mov esi, -1
+ jmp CommonDispatchException2Args0d ; Won't return
+
+_KiTrap07 endp
+
+
+ page ,132
+ subttl "Double Fault"
+;++
+;
+; Routine Description:
+;
+; Handle double exception fault.
+;
+; Normally, when the processor detects an exception while trying to
+; invoke the handler for a prior exception, the two exception can be
+; handled serially. If, however, the processor cannot handle them
+; serially, it signals the double-fault exception instead.
+;
+; If double exception is detected, no matter previous mode is USER
+; or kernel, a bugcheck will be raised and the system will be terminated.
+;
+; Arguments:
+;
+; error code, which is always zero, is pushed on stack.
+;
+; Return value:
+;
+; None
+;
+;--
+ ASSUME DS:NOTHING, SS:NOTHING, ES:NOTHING
+align dword
+ public _KiTrap08
+_KiTrap08 proc
+.FPO (0, 0, 0, 0, 0, 2)
+
+ cli
+;
+; Update the TSS pointer in the PCR to point to the double-fault TSS
+; (which is what we're running on, or else we wouldn't be here)
+;
+ mov eax, PCR[PcGdt]
+ mov ch, [eax+KGDT_DF_TSS+KgdtBaseHi]
+ mov cl, [eax+KGDT_DF_TSS+KgdtBaseMid]
+ shl ecx, 16
+ mov cx, [eax+KGDT_DF_TSS+KgdtBaseLow]
+ mov PCR[PcTss], ecx
+
+;
+; Clear the busy bit in the TSS selector
+;
+ mov ecx, PCR[PcGdt]
+ lea eax, [ecx] + KGDT_DF_TSS
+ mov byte ptr [eax+5], 089h ; 32bit, dpl=0, present, TSS32, not busy
+
+;
+; Clear Nested Task bit in EFLAGS
+;
+ pushfd
+ and [esp], not 04000h
+ popfd
+
+;
+; The original machine context is in original task's TSS
+;
+@@: stdCall _KeBugCheckEx,<UNEXPECTED_KERNEL_MODE_TRAP,8,0,0,0>
+ jmp short @b ; do not remove - for debugger
+
+_KiTrap08 endp
+
+ page ,132
+ subttl "Coprocessor Segment Overrun"
+;++
+;
+; Routine Description:
+;
+; Handle Coprocessor Segment Overrun exception.
+;
+; This exception only occurs on the 80286 (it's a trap 0d on the 80386),
+; so choke if we get here.
+;
+; Arguments:
+;
+; At entry, the saved CS:EIP point to the aborted instruction.
+; No error code is provided with the error.
+;
+; Return value:
+;
+; None
+;
+;--
+ ASSUME DS:NOTHING, SS:NOTHING, ES:NOTHING
+
+ ENTER_DR_ASSIST kit9_a, kit9_t, NoAbiosAssist
+align dword
+ public _KiTrap09
+_KiTrap09 proc
+
+ push 0 ; push dummy error code
+ ENTER_TRAP kit9_a, kit9_t
+ sti
+
+ mov eax, EXCEPTION_NPX_OVERRUN ; (eax) = exception type
+ jmp _KiSystemFatalException ; go terminate the system
+
+_KiTrap09 endp
+
+ page ,132
+ subttl "Invalid TSS exception"
+;++
+;
+; Routine Description:
+;
+; Handle Invalid TSS fault.
+;
+; This exception occurs if a segment exception other than the
+; not-present exception is detected when loading a selector
+; from the TSS.
+;
+; If the exception is caused as a result of the kernel, device
+; drivers, or user incorrectly setting the NT bit in the flags
+; while the back-link selector in the TSS is invalid and the
+; IRET instruction being executed, in this case, this routine
+; will clear the NT bit in the trap frame and restart the iret
+; instruction. For other causes of the fault, the user process
+; will be terminated if previous mode is user and the system
+; will stop if the exception occurs in kernel mode. No exception
+; is raised.
+;
+; Arguments:
+;
+; At entry, the saved CS:EIP point to the faulting instruction or
+; the first instruction of the task if the fault occurs as part of
+; a task switch.
+; Error code containing the segment causing the exception is provided.
+;
+; NT386 does not use TSS for context switching. So, the invalid tss
+; fault should NEVER occur. If it does, something is wrong with
+; the kernel. We simply shutdown the system.
+;
+; Return value:
+;
+; None
+;
+;--
+ ASSUME DS:NOTHING, SS:NOTHING, ES:NOTHING
+
+ ENTER_DR_ASSIST kita_a, kita_t, NoAbiosAssist
+align dword
+ public _KiTrap0A
+_KiTrap0A proc
+
+ ENTER_TRAP kita_a, kita_t
+
+ ; We can not enable interrupt here. If we came here because DOS/WOW
+ ; iret with NT bit set, it is possible that vdm will swap the trap frame
+ ; with their monitor context. If this happen before we check the NT bit
+ ; we will bugcheck.
+
+; sti
+
+;
+; If the trap occur in USER mode and is caused by iret instruction with
+; OF bit set, we simply clear the OF bit and restart the iret.
+; Any other causes of Invalid TSS cause system to be shutdown.
+;
+
+ test dword ptr [ebp]+TsEFlags, EFLAGS_V86_MASK
+ jnz short Kt0a10
+
+ test byte ptr [ebp]+TsSegCs, MODE_MASK ; Is previous mode = USER
+ jz short Kt0a20
+
+Kt0a10:
+ test dword ptr [ebp]+TsEFlags, EFLAGS_OF_BIT
+ sti
+ jz short Kt0a20
+
+ and dword ptr [ebp]+TsEFlags, NOT EFLAGS_OF_BIT
+ jmp _KiExceptionExit ; restart the instruction
+
+Kt0a20:
+ mov eax, EXCEPTION_INVALID_TSS ; (eax) = trap type
+ jmp _KiSystemFatalException ; go terminate the system
+
+_KiTrap0A endp
+
+ page ,132
+ subttl "Segment Not Present"
+;++
+;
+; Routine Description:
+;
+; Handle Segment Not Present fault.
+;
+; This exception occurs when the processor finds the P bit 0
+; when accessing an otherwise valid descriptor that is not to
+; be loaded in SS register.
+;
+; The only place the fault can occur (in kernel mode) is Trap/Exception
+; exit code. Otherwise, this exception causes system to be terminated.
+; NT386 uses flat mode, the segment not present fault in Kernel mode
+; indicates system malfunction.
+;
+; Arguments:
+;
+; At entry, the saved CS:EIP point to the faulting instruction or
+; the first instruction of the task if the fault occurs as part of
+; a task switch.
+; Error code containing the segment causing the exception is provided.
+;
+; Return value:
+;
+; None
+;
+;--
+ ASSUME DS:NOTHING, SS:NOTHING, ES:NOTHING
+
+ ENTER_DR_ASSIST kitb_a, kitb_t, NoAbiosAssist
+
+align dword
+ public _KiTrap0B
+_KiTrap0B proc
+
+; Set up machine state frame for displaying
+
+ ENTER_TRAP kitb_a, kitb_t
+
+;
+; Did the trap occur in a VDM?
+;
+
+ test byte ptr [ebp]+TsSegCs, MODE_MASK ; Is previous mode = USER
+ jz Kt0b30
+
+ cmp word ptr [ebp]+TsSegCs,KGDT_R3_CODE OR RPL_MASK
+ je Kt0b20
+
+Kt0b10:
+
+; Check to see if this process is a vdm
+
+ mov ebx,PCR[PcPrcbData+PbCurrentThread]
+ mov ebx,[ebx]+ThApcState+AsProcess
+ test byte ptr [ebx]+PrVdmFlag,0fh ; is this a vdm process?
+ jz short Kt0b20
+
+ ; Raise Irql to APC level before enabling interrupts
+ mov ecx, APC_LEVEL
+ fstCall KfRaiseIrql
+ push eax ; Save OldIrql
+ sti
+
+ stdCall _Ki386VdmSegmentNotPresent
+ test eax, 0ffffh
+ jz short Kt0b15
+
+ pop ecx ; (TOS) = OldIrql
+ fstCall KfLowerIrql
+ jmp _KiExceptionExit
+
+Kt0b15:
+ pop ecx ; (TOS) = OldIrql
+ fstCall KfLowerIrql
+
+Kt0b20: sti
+ mov ebx, [ebp]+TsEip ; (ebx)->faulting instruction
+ mov esi, [ebp]+TsErrCode
+ and esi, 0FFFFh
+ or esi, RPL_MASK
+ mov eax, STATUS_ACCESS_VIOLATION
+ jmp CommonDispatchException2Args0d ; Won't return
+
+Kt0b30:
+;
+; Check if the exception is caused by pop SegmentRegister.
+; We need to deal with the case that user puts a NP selector in fs, ds, cs
+; or es through kernel debugger. (kernel will trap while popping segment
+; registers in trap exit code.)
+; Note: We assume if the faulted instruction is pop segreg. It MUST be
+; in trap exit code. So there MUST be a valid trap frame for the trap exit.
+;
+
+ mov eax, [ebp]+TsEip ; (eax)->faulted Instruction
+ mov eax, [eax] ; (eax)= opcode of faulted instruction
+ mov edx, [ebp]+TsEbp ; (edx)->previous trap exit trapframe
+
+ add edx, TsSegDs ; [edx] = prev trapframe + TsSegDs
+ cmp al, POP_DS ; Is it pop ds instruction?
+ jz Kt0b90 ; if z, yes, go Kt0b90
+
+ add edx, TsSegEs - TsSegDs ; [edx] = prev trapframe + TsSegEs
+ cmp al, POP_ES ; Is it pop es instruction?
+ jz Kt0b90 ; if z, yes, go Kt0b90
+
+ add edx, TsSegFs - TsSegEs ; [edx] = prev trapframe + TsSegFs
+ cmp ax, POP_FS ; Is it pop fs (2-byte) instruction?
+ jz Kt0b90 ; If z, yes, go Kt0b90
+
+ add edx, TsSegGs - TsSegFs ; [edx] = prev trapframe + TsSegGs
+ cmp ax, POP_GS ; Is it pop gs (2-byte) instruction?
+ jz Kt0b90 ; If z, yes, go Kt0b90
+
+;
+; The exception is not caused by pop instruction. We still need to check
+; if it is caused by iret (to user mode.) Because user may have a NP
+; cs and we will trap at iret in trap exit code.
+;
+
+ sti
+ cmp al, IRET_OP ; Is it an iret instruction?
+ jne Kt0b199 ; if ne, not iret, go bugcheck
+
+ lea edx, [ebp]+TsHardwareEsp ; (edx)->trapped iret frame
+ test dword ptr [edx]+4, RPL_MASK ; Check CS of iret addr
+ ; Does the iret have ring transition?
+ jz Kt0b199 ; if z, it's a real fault
+
+;
+; we trapped at iret while returning back to user mode. We will dispatch
+; the exception back to user program.
+;
+
+ mov ecx, (KTRAP_FRAME_LENGTH - 12) / 4
+ lea edx, [ebp]+TsErrCode
+Kt0b40:
+ mov eax, [edx]
+ mov [edx+12], eax
+ sub edx, 4
+ loop Kt0b40
+
+ add esp, 12 ; adjust esp and ebp
+ add ebp, 12
+ jmp Kt0b10
+
+; mov ebx, [ebp]+TsEip ; (ebx)->faulting instruction
+; xor edx, edx
+; mov esi, [ebp]+TsErrCode
+; or esi, RPL_MASK
+; and esi, 0FFFFh
+; mov ecx, 2
+; mov eax, STATUS_ACCESS_VIOLATION
+; call CommonDispatchException ; WOn't return
+
+;
+; The faulted instruction is pop seg
+;
+
+Kt0b90:
+ mov dword ptr [edx], 0 ; set the segment reg to 0 such that
+ ; we will trap in user mode.
+ EXIT_ALL NoRestoreSegs,,NoPreviousMode ; RETURN
+
+Kt0b199:
+ mov eax, EXCEPTION_SEGMENT_NOT_PRESENT ; (eax) = exception type
+ jmp _KiSystemFatalException ; terminate the system
+
+_KiTrap0B endp
+
+ page ,132
+ subttl "Stack segment fault"
+;++
+;
+; Routine Description:
+;
+; Handle Stack Segment fault.
+;
+; This exception occurs when the processor detects certain problem
+; with the segment addressed by the SS segment register:
+;
+; 1. A limit violation in the segment addressed by the SS (error
+; code = 0)
+; 2. A limit vioalation in the inner stack during an interlevel
+; call or interrupt (error code = selector for the inner stack)
+; 3. If the descriptor to be loaded into SS has its present bit 0
+; (error code = selector for the not-present segment)
+;
+; The exception should never occurs in kernel mode except when we
+; perform the iret back to user mode.
+;
+; Arguments:
+;
+; At entry, the saved CS:EIP point to the faulting instruction or
+; the first instruction of the task if the fault occurs as part of
+; a task switch.
+; Error code (whose value depends on detected condition) is provided.
+;
+; Return value:
+;
+; None
+;
+;--
+ ASSUME DS:NOTHING, SS:NOTHING, ES:NOTHING
+
+ ENTER_DR_ASSIST kitc_a, kitc_t, NoAbiosAssist
+align dword
+ public _KiTrap0C
+_KiTrap0C proc
+
+ ENTER_TRAP kitc_a, kitc_t
+
+ test dword ptr [ebp]+TsEFlags,EFLAGS_V86_MASK
+ jnz Kt0c30
+
+ test byte ptr [ebp]+TsSegCs, MODE_MASK ; Is previous mode = USER
+ jz Kt0c10
+
+ cmp word ptr [ebp]+TsSegCs, KGDT_R3_CODE OR RPL_MASK
+ jne Kt0c20 ; maybe in a vdm
+
+Kt0c00: sti
+ mov ebx, [ebp]+TsEip ; (ebx)->faulting instruction
+ mov edx, EXCEPT_LIMIT_ACCESS; assume it is limit violation
+ mov esi, [ebp]+TsHardwareEsp; (ecx) = User Stack pointer
+ cmp word ptr [ebp]+TsErrCode, 0 ; Is errorcode = 0?
+ jz short kt0c05 ; if z, yes, go dispatch exception
+
+ mov esi, [ebp]+TsErrCode ; Otherwise, set SS segment value
+ ; to be the address causing the fault
+ mov edx, EXCEPT_UNKNOWN_ACCESS
+ or esi, RPL_MASK
+ and esi, 0FFFFh
+kt0c05: mov eax, STATUS_ACCESS_VIOLATION
+ jmp CommonDispatchException2Args ; Won't return
+
+kt0c10:
+ sti
+;
+; Check if the exception is caused by kernel mode iret to user code.
+; We need to deal with the case that user puts a bogus value in ss
+; through SetContext call. (kernel will trap while iret to user code
+; in trap exit code.)
+; Note: We assume if the faulted instruction is iret. It MUST be in
+; trap/exception exit code. So there MUST be a valid trap frame for
+; the trap exit.
+;
+
+ mov eax, [ebp]+TsEip ; (eax)->faulted Instruction
+ mov eax, [eax] ; (eax)= opcode of faulted instruction
+
+;
+; Check if the exception is caused by iret (to user mode.)
+; Because user may have a NOT PRESENT ss and we will trap at iret
+; in trap exit code. (If user put a bogus/not valid SS in trap frame, we
+; will catch it in trap 0D handler.
+;
+
+ cmp al, IRET_OP ; Is it an iret instruction?
+ jne Kt0c15 ; if ne, not iret, go bugcheck
+
+ lea edx, [ebp]+TsHardwareEsp ; (edx)->trapped iret frame
+ test dword ptr [edx]+4, RPL_MASK ; Check CS of iret addr
+ ; Does the iret have ring transition?
+ jz Kt0c15 ; if z, no SS involed, it's a real fault
+
+;
+; we trapped at iret while returning back to user mode. We will dispatch
+; the exception back to user program.
+;
+
+ mov ecx, (KTRAP_FRAME_LENGTH - 12) / 4
+ lea edx, [ebp]+TsErrCode
+@@:
+ mov eax, [edx]
+ mov [edx+12], eax
+ sub edx, 4
+ loop @b
+
+ add esp, 12 ; adjust esp and ebp
+ add ebp, 12
+
+ ;
+ ; Now, we have user mode trap frame set up
+ ;
+
+ mov ebx, [ebp]+TsEip ; (ebx)->faulting instruction
+ mov edx, EXCEPT_LIMIT_ACCESS; assume it is limit violation
+ mov esi, [ebp]+TsHardwareEsp; (ecx) = User Stack pointer
+ cmp word ptr [ebp]+TsErrCode, 0 ; Is errorcode = 0?
+ jz short @f ; if z, yes, go dispatch exception
+
+ mov esi, [ebp]+TsErrCode ; Otherwise, set SS segment value
+ ; to be the address causing the fault
+ and esi, 0FFFFh
+ mov edx, EXCEPT_UNKNOWN_ACCESS
+ or esi, RPL_MASK
+@@:
+ mov eax, STATUS_ACCESS_VIOLATION
+ jmp CommonDispatchException2Args ; Won't return
+
+Kt0c15:
+ mov eax, EXCEPTION_STACK_FAULT ; (eax) = trap type
+ jmp _KiSystemFatalException
+
+Kt0c20:
+; Check to see if this process is a vdm
+
+ mov ebx,PCR[PcPrcbData+PbCurrentThread]
+ mov ebx,[ebx]+ThApcState+AsProcess
+ test byte ptr [ebx]+PrVdmFlag,0fh ; is this a vdm process?
+ jz Kt0c00
+
+Kt0c30:
+ stdCall _Ki386VdmReflectException_A,<0ch>
+ test al,0fh
+ jz Kt0c00
+ jmp _KiExceptionExit
+
+_KiTrap0C endp
+
+
+ page ,132
+ subttl "General Protection Fault"
+;++
+;
+; Routine Description:
+;
+; Handle General protection fault.
+;
+; First, check to see if the fault occured in kernel mode with
+; incorrect selector values. If so, this is a lazy segment load.
+; Correct the selector values and restart the instruction. Otherwise,
+; parse out various kinds of faults and report as exceptions.
+;
+; All protection violations that do not cause another exception
+; cause a general exception. If the exception indicates a violation
+; of the protection model by an application program executing a
+; previleged instruction or I/O reference, a PRIVILEGED INSTRUCTION
+; exception will be raised. All other causes of general protection
+; fault cause a ACCESS VIOLATION exception to be raised.
+;
+; If previous mode = Kernel;
+; the system will be terminated (assuming not lazy segment load)
+; Else previous mode = USER
+; the process will be terminated if the exception was not caused
+; by privileged instruction.
+;
+; Arguments:
+;
+; At entry, the saved CS:EIP point to the faulting instruction or
+; the first instruction of the task if the fault occurs as part of
+; a task switch.
+; Error code (whose value depends on detected condition) is provided.
+;
+; Return value:
+;
+; None
+;
+;--
+ ASSUME DS:FLAT, SS:NOTHING, ES:FLAT
+
+
+;
+; Error and exception blocks for KiTrap0d
+;
+
+Ktd_ExceptionHandler:
+
+;
+; WARNING: Here we directly unlink the exception handler from the
+; exception registration chain. NO unwind is performed.
+;
+
+ mov esp, [esp+8] ; (esp)-> ExceptionList
+ pop PCR[PcExceptionList]
+ add esp, 4 ; pop out except handler
+ pop ebp ; (ebp)-> trap frame
+
+ test dword ptr [ebp].TsSegCs, MODE_MASK ; if premode=kernl
+ jnz Kt0d103 ; nz, prevmode=user, go return
+
+; raise bugcheck if prevmode=kernel
+ stdCall _KeBugCheck, <KMODE_EXCEPTION_NOT_HANDLED>
+
+ ENTER_DR_ASSIST kitd_a, kitd_t, NoAbiosAssist,, kitd_v
+align dword
+ public _KiTrap0D
+_KiTrap0D proc
+
+
+
+;
+; Did the trap occur in a VDM in V86 mode? Trap0d is not critical from
+; performance point of view to native NT, but its super critical to
+; VDMs. So here we are doing every thing to make v86 mode most efficient.
+;
+ test dword ptr [esp]+0ch,EFLAGS_V86_MASK
+ jz Ktdi
+
+
+if FAST_V86_TRAP
+
+ FAST_V86_TRAP_D
+endif
+
+
+KtdV86Slow:
+ ENTER_TRAPV86 kitd_a, kitd_v
+
+KtdV86Slow2:
+
+ ; Raise Irql to APC level, before enabling interrupts
+ mov ecx, APC_LEVEL
+ fstCall KfRaiseIrql
+ push eax ; Save OldIrql
+ sti
+
+ stdCall _Ki386DispatchOpcodeV86
+KtdV86Exit:
+ test al,0FFh
+ jnz short Ktdi2
+
+ stdCall _Ki386VdmReflectException,<0dh>
+ test al,0fh
+ jnz short Ktdi2
+ pop ecx ; (TOS) = OldIrql
+ fstCall KfLowerIrql
+ jmp Kt0d105
+Ktdi2:
+ pop ecx ; (TOS) = OldIrql
+ fstCall KfLowerIrql
+ cli
+ test dword ptr [ebp]+TsEFlags,EFLAGS_V86_MASK
+ jz Ktdi3
+
+ EXIT_TRAPV86
+ ;
+ ; EXIT_TRAPv86 does not exit if a user mode apc has switched
+ ; the context from V86 mode to flat mode (VDM monitor context)
+ ;
+
+Ktdi3:
+ jmp _KiExceptionExit
+
+Ktdi:
+ ENTER_TRAP kitd_a, kitd_t
+
+;
+; DO NOT TURN INTERRUPTS ON! If we're doing a lazy segment load,
+; could be in an ISR or other code that needs ints off!
+;
+
+;
+; Is this just a lazy segment load? First make sure the exception occurred
+; in kernel mode.
+;
+
+ test dword ptr [ebp]+TsSegCs,MODE_MASK
+ jnz Kt0d02 ; not kernel mode, go process normally
+
+;
+; Before handling kernel mode trap0d, we need to do some checks to make
+; sure the kernel mode code is the one to blame.
+;
+
+if FAST_BOP or FAST_V86_TRAP
+
+ cmp byte ptr PCR[PcVdmAlert], 0 ; See Kt0eVdmAlert
+ jne Kt0eVdmAlert
+endif
+
+;
+; Check if the exception is caused by the handler trying to examine offending
+; instruction. If yes, we raise exception to user mode program. This occurs
+; when user cs is bogus. Note if cs is valid and eip is bogus, the exception
+; will be caught by page fault and out Ktd_ExceptionHandler will be invoked.
+; Both cases, the exception is dispatched back to user mode.
+;
+
+ mov eax, [ebp]+TsEip
+ cmp eax, offset FLAT:Kt0d03
+ jbe short Kt0d000
+ cmp eax, offset FLAT:Kt0d60
+ jae short Kt0d000
+
+ sti
+ mov ebp, [ebp]+TsEbp ; remove the current trap frame
+ mov esp, ebp ; set ebp, esp to previous trap frame
+ jmp Kt0d105 ; and dispatch exception to user mode.
+
+;
+; Check if the exception is caused by pop SegmentRegister.
+; We need to deal with the case that user puts a bogus value in fs, ds,
+; or es through kernel debugger. (kernel will trap while popping segment
+; registers in trap exit code.)
+; Note: We assume if the faulted instruction is pop segreg. It MUST be
+; in trap exit code. So there MUST be a valid trap frame for the trap exit.
+;
+
+Kt0d000:
+ mov eax, [ebp]+TsEip ; (eax)->faulted Instruction
+ mov eax, [eax] ; (eax)= opcode of faulted instruction
+ mov edx, [ebp]+TsEbp ; (edx)->previous trap exit trapframe
+
+ add edx, TsSegDs ; [edx] = prev trapframe + TsSegDs
+ cmp al, POP_DS ; Is it pop ds instruction?
+ jz Kt0d005 ; if z, yes, go Kt0d005
+
+ add edx, TsSegEs - TsSegDs ; [edx] = prev trapframe + TsSegEs
+ cmp al, POP_ES ; Is it pop es instruction?
+ jz Kt0d005 ; if z, yes, go Kt0d005
+
+ add edx, TsSegFs - TsSegEs ; [edx] = prev trapframe + TsSegFs
+ cmp ax, POP_FS ; Is it pop fs (2-byte) instruction?
+ jz Kt0d005 ; If z, yes, go Kt0d005
+
+ add edx, TsSegGs - TsSegFs ; [edx] = prev trapframe + TsSegGs
+ cmp ax, POP_GS ; Is it pop gs (2-byte) instruction?
+ jz Kt0d005 ; If z, yes, go Kt0d005
+
+;
+; The exception is not caused by pop instruction. We still need to check
+; if it is caused by iret (to user mode.) Because user may have a bogus
+; ss and we will trap at iret in trap exit code.
+;
+
+ cmp al, IRET_OP ; Is it an iret instruction?
+ jne Kt0d002 ; if ne, not iret, go check lazy load
+
+ lea edx, [ebp]+TsHardwareEsp ; (edx)->trapped iret frame
+ mov ax, [ebp]+TsErrCode ; (ax) = Error Code
+ and ax, NOT RPL_MASK ; No need to do this ...
+ mov cx, word ptr [edx]+4 ; [cx] = cs selector
+ and cx, NOT RPL_MASK
+ cmp cx, ax ; is it faulted in CS?
+ jne short Kt0d0008 ; No
+
+;
+; Check if this is the code which we use to return to Ki386CallBios
+; (see biosa.asm):
+; cs should be KGDT_R0_CODE OR RPL_MASK
+; eip should be Ki386BiosCallReturnAddress
+; esi should be the esp of function Ki386SetUpAndExitToV86Code
+; (edx) -> trapped iret frame
+;
+
+ mov eax, OFFSET FLAT:Ki386BiosCallReturnAddress
+ cmp eax, [edx] ; [edx]= trapped eip
+ ; Is eip what we're expecting?
+ jne short Kt0d0005 ; No, continue
+
+ mov eax, [edx]+4 ; (eax) = trapped cs
+ cmp ax, KGDT_R0_CODE OR RPL_MASK ; Is Cs what we're exptecting?
+ jne short Kt0d0005 ; No
+
+; add edx, 5 * 4 + NPX_FRAME_LENGTH + (TsV86Gs - TsHardwareSegSs)
+; cmp [ebp]+TsEsi, edx ; esi should be the esp of
+ ; Ki386SetupAndExitToV86Code
+ mov edx, [ebp].TsEsi
+; jne short Kt0d0005
+ mov esp, edx
+ jmp Ki386BiosCallReturnAddress ; with interrupts off
+
+Kt0d0005:
+;
+; Since the CS is bogus, we can not tell if we are going back
+; to user mode...
+;
+
+ mov ebx,PCR[PcPrcbData+PbCurrentThread] ; if previous mode is
+ test byte ptr [ebx]+ThPreviousMode, 0ffh ; kernel, we bugcheck
+ jz Kt0d02
+
+ or word ptr [edx]+4, RPL_MASK
+Kt0d0008:
+ test dword ptr [edx]+4, RPL_MASK ; Check CS of iret addr
+ ; Does the iret have ring transition?
+ jz Kt0d02 ; if z, no SS involed, it's a real fault
+
+ sti
+
+;
+; we trapped at iret while returning back to user mode. We will dispatch
+; the exception back to user program.
+;
+
+ mov ecx, (KTRAP_FRAME_LENGTH - 12) / 4
+ lea edx, [ebp]+TsErrCode
+Kt0d001:
+ mov eax, [edx]
+ mov [edx+12], eax
+ sub edx, 4
+ loop Kt0d001
+
+ add esp, 12 ; adjust esp and ebp
+ add ebp, 12
+ mov ebx, [ebp]+TsEip ; (ebx)->faulting instruction
+ mov esi, [ebp]+TsErrCode
+ and esi, 0FFFFh
+ mov eax, STATUS_ACCESS_VIOLATION
+ jmp CommonDispatchException2Args0d ; Won't return
+
+;
+; Kernel mode, first opcode byte is 0f, check for rdmsr or wrmsr instruction
+;
+
+Kt0d001a:
+ shr eax, 8
+ cmp al, 30h
+ je short Kt0d001b
+ cmp al, 32h
+ jne short Kt0d002a
+
+Kt0d001b:
+ mov ebx, [ebp]+TsEip ; (ebx)->faulting instruction
+ mov eax, STATUS_ACCESS_VIOLATION
+ jmp CommonDispatchException0Args ; Won't return
+
+;
+; The Exception is not caused by pop instruction. Check to see
+; if the instruction is a rdmsr or wrmsr
+;
+Kt0d002:
+ cmp al, 0fh
+ je short Kt0d001a
+
+;
+; We now check if DS and ES contain correct value. If not, this is lazy
+; segment load, we simply set them to valid selector.
+;
+
+Kt0d002a:
+ cmp word ptr [ebp]+TsSegDs, KGDT_R3_DATA OR RPL_MASK
+ je short Kt0d003
+
+ mov dword ptr [ebp]+TsSegDs, KGDT_R3_DATA OR RPL_MASK
+ jmp short Kt0d01
+
+Kt0d003:
+ cmp word ptr [ebp]+TsSegEs, KGDT_R3_DATA OR RPL_MASK
+ je Kt0d02 ; Real fault, go process it
+
+ mov dword ptr [ebp]+TsSegEs, KGDT_R3_DATA OR RPL_MASK
+ jmp short Kt0d01
+
+;
+; The faulted instruction is pop seg
+;
+
+Kt0d005:
+ xor eax, eax
+ mov dword ptr [edx], eax ; set the segment reg to 0 such that
+ ; we will trap in user mode.
+Kt0d01:
+ EXIT_ALL NoRestoreSegs,,NoPreviousMode ; RETURN
+
+;
+; Caller is not kernel mode, or DS and ES are OK. Therefore this
+; is a real fault rather than a lazy segment load. Process as such.
+; Since this is not a lazy segment load is now safe to turn interrupts on.
+;
+Kt0d02: mov eax, EXCEPTION_GP_FAULT ; (eax) = trap type
+ test byte ptr [ebp]+TsSegCs, MODE_MASK ; Is prevmode=User?
+ jz _KiSystemFatalException ; If z, prevmode=kernel, stop...
+
+
+; preload pointer to process
+ mov ebx,PCR[PcPrcbData+PbCurrentThread]
+ mov ebx,[ebx]+ThApcState+AsProcess
+
+; flat or protect mode ?
+ cmp word ptr [ebp]+TsSegCs, KGDT_R3_CODE OR RPL_MASK
+ jz kt0d0201
+
+;
+; if vdm running in protected mode, handle instruction
+;
+ test byte ptr [ebx]+PrVdmFlag,0fh ; is this a vdm process?
+ jnz Kt0d110
+
+ sti
+ test word ptr [ebp]+TsErrCode, 0 ; if errcode<>0, raise access
+ ; violation exception
+ jnz Kt0d105 ; if nz, raise access violation
+ jmp short Kt0d03
+
+
+;
+; if vdm running in flat mode, handle pop fs,gs by setting to Zero
+;
+kt0d0202:
+ add dword ptr [ebp].TsEip, 2
+ jmp Kt0d005
+
+kt0d0201:
+ test byte ptr [ebx]+PrVdmFlag,0fh ; is this a vdm process?
+ jz short Kt0d03
+
+ mov eax, [ebp]+TsEip ; (eax)->faulted Instruction
+ mov eax, [eax] ; (eax)= opcode of faulted instruction
+ mov edx, ebp ; (edx)-> trap frame
+
+ add edx, TsSegFs ; [edx] = prev trapframe + TsSegFs
+ cmp ax, POP_FS ; Is it pop fs (2-byte) instruction?
+ jz short kt0d0202
+
+ add edx, TsSegGs - TsSegFs ; [edx] = prev trapframe + TsSegGs
+ cmp ax, POP_GS ; Is it pop gs (2-byte) instruction?
+ jz short kt0d0202
+
+;
+; we need to determine if the trap0d was caused by privileged instruction.
+; First, we need to skip all the instruction prefix bytes
+;
+
+Kt0d03: sti
+ push ds
+
+;
+; First we need to set up an exception handler to handle the case that
+; we fault while reading user mode instruction.
+;
+
+ push ebp ; pass trapframe to handler
+ push offset FLAT:ktd_ExceptionHandler
+ ; set up exception registration record
+ push PCR[PcExceptionList]
+ mov PCR[PcExceptionList], esp
+
+ mov esi, [ebp]+TsEip ; (esi) -> flat address of faulting instruction
+ mov ax, [ebp]+TsSegCs
+ mov ds, ax
+ mov ecx, MAX_INSTRUCTION_LENGTH
+Kt0d05: push ecx ; save ecx for loop count
+ lods byte ptr [esi] ; (al)= instruction byte
+ mov ecx, PREFIX_REPEAT_COUNT
+ mov edi, offset FLAT:PrefixTable ; (ES:EDI)->prefix table
+ repnz scasb ; search for matching (al)
+ pop ecx ; restore loop count
+ jnz short Kt0d10 ; (al) not a prefix byte, go kt0d10
+ loop short Kt0d05 ; go check for prefix byte again
+ pop PCR[PcExceptionList]
+ add esp, 8 ; clear stack
+ jmp Kt0630 ; exceed max instruction length,
+ ; raise ILLEGALINSTRUCTION exception
+
+;
+; (al) = first opcode which is NOT prefix byte
+; (ds:esi)= points to the first opcode which is not prefix byte + 1
+; We need to check if it is one of the privileged instructions
+;
+
+Kt0d10: cmp al, MI_HLT ; Is it a HLT instruction?
+ je Kt0d80 ; if e, yes, go kt0d80
+
+ cmp al, MI_TWO_BYTE ; Is it a two-byte instruction?
+ jne short Kt0d50 ; if ne, no, go check for IO inst.
+
+ lods byte ptr [esi] ; (al)= next instruction byte
+ cmp al, MI_LTR_LLDT ; Is it a LTR or LLDT ?
+ jne short Kt0d20 ; if ne, no, go kt0d20
+
+ lods byte ptr [esi] ; (al)= ModRM byte of instruction
+ and al, MI_MODRM_MASK ; (al)= bit 3-5 of ModRM byte
+ cmp al, MI_LLDT_MASK ; Is it a LLDT instruction?
+ je Kt0d80 ; if e, yes, go Kt0d80
+
+ cmp al, MI_LTR_MASK ; Is it a LTR instruction?
+ je Kt0d80 ; if e, yes, go Kt0d80
+
+ jmp Kt0d100 ; if ne, go raise access vioalation
+
+Kt0d20: cmp al, MI_LGDT_LIDT_LMSW ; Is it one of these instructions?
+ jne short Kt0d30 ; if ne, no, go check special mov inst.
+
+ lods byte ptr [esi] ; (al)= ModRM byte of instruction
+ and al, MI_MODRM_MASK ; (al)= bit 3-5 of ModRM byte
+ cmp al, MI_LGDT_MASK ; Is it a LGDT instruction?
+ je short Kt0d80 ; if e, yes, go Kt0d80
+
+ cmp al, MI_LIDT_MASK ; Is it a LIDT instruction?
+ je short Kt0d80 ; if e, yes, go Kt0d80
+
+ cmp al, MI_LMSW_MASK ; Is it a LMSW instruction?
+ je short Kt0d80 ; if e, yes, go Kt0d80
+
+ jmp Kt0d100 ; else, raise access violation except
+
+Kt0d30: and al, MI_SPECIAL_MOV_MASK ; Is it a special mov instruction?
+ jnz kt0d80 ; if nz, yes, go raise priv instr
+ ; (Even though the regular mov may
+ ; have the special_mov_mask bit set,
+ ; they are NOT 2 byte opcode instr.)
+ jmp Kt0d100 ; else, no, raise access violation
+
+;
+; Now, we need to check if the trap 0d was caused by IO privileged instruct.
+; (al) = first opcode which is NOT prefix byte
+; Also note, if we come here, the instruction has 1 byte opcode (still need to
+; check REP case.)
+;
+
+Kt0d50: mov ebx, [ebp]+TsEflags ; (ebx) = client's eflags
+ and ebx, IOPL_MASK ;
+ shr ebx, IOPL_SHIFT_COUNT ; (ebx) = client's IOPL
+ mov ecx, [ebp]+TsSegCs
+ and ecx, RPL_MASK ; RPL_MASK NOT MODE_MASK!!!
+ ; (ecx) = CPL, 1/2 of computation of
+ ; whether IOPL applies.
+
+ cmp ebx,ecx ; compare IOPL with CPL of caller
+ jge short Kt0d100 ; if ge, not IO privileged,
+ ; go raise access violation
+
+Kt0d60: cmp al, CLI_OP ; Is it a CLI instruction
+ je short Kt0d80 ; if e, yes. Report it.
+
+ cmp al, STI_OP ; Is it a STI?
+ je short Kt0d80 ; if e, yes, report it.
+
+ mov ecx, IO_INSTRUCTION_TABLE_LENGTH
+ mov edi, offset FLAT:IOInstructionTable
+ repnz scasb ; Is it a IO instruction?
+ jnz short Kt0d100 ; if nz, not io instrct.
+
+;
+; We know the instr is an IO instr without IOPL. But, this doesn't mean
+; this is a privileged instruction exception. We need to make sure the
+; IO port access is not granted in the bit map
+;
+
+
+ mov edi, fs:PcSelfPcr ; (edi)->Pcr
+ mov esi, [edi]+PcGdt ; (esi)->Gdt addr
+ add esi, KGDT_TSS
+ movzx ebx, word ptr [esi] ; (ebx) = Tss limit
+
+ mov edx, [ebp].TsEdx ; [edx] = port addr
+ mov ecx, edx
+ and ecx, 07 ; [ecx] = Bit position
+ shr edx, 3 ; [edx] = offset to the IoMap
+
+ mov edi, [edi]+PcTss ; (edi)->TSS
+ movzx eax, word ptr [edi + TssIoMapBase] ; [eax] = Iomap offset
+ add edx, eax
+ cmp edx, ebx ; is the offset addr beyond tss limit?
+ ja short Kt0d80 ; yes, no I/O priv.
+
+ add edi, edx ; (edi)-> byte correspons to the port addr
+ mov edx, 1
+ shl edx, cl
+ test dword ptr [edi], edx ; Is the bit of the port disabled?
+ jz short Kt0d100 ; if z, no, then it is access violation
+
+Kt0d80:
+ pop PCR[PcExceptionList]
+ add esp, 8 ; clear stack
+ pop ds
+ mov ebx, [ebp]+TsEip ; (ebx)->faulting instruction
+ mov eax, STATUS_PRIVILEGED_INSTRUCTION
+ jmp CommonDispatchException0Args ; Won't return
+
+;
+; NOTE All the GP fault (except the ones we can
+; easily detect now) will cause access violation exception
+; AND, all the access violation will be raised with additional
+; parameters set to "read" and "virtual address which caused
+; the violation = unknown (-1)"
+;
+
+Kt0d100:
+ pop PCR[PcExceptionList]
+ add esp, 8 ; clear stack
+Kt0d103:
+ pop ds
+Kt0d105:
+ mov ebx, [ebp]+TsEip ; (ebx)->faulting instruction
+ mov esi, -1
+ mov eax, STATUS_ACCESS_VIOLATION
+ jmp CommonDispatchException2Args0d ; Won't return
+
+Kt0d110:
+ ; Raise Irql to APC level, before enabling interrupts
+ mov ecx, APC_LEVEL
+ fstCall KfRaiseIrql
+ push eax ; Save OldIrql
+ sti
+
+ stdCall _Ki386DispatchOpcode
+ test eax,0FFFFh
+ jnz short Kt0d120
+
+ stdCall _Ki386VdmReflectException,<0dh>
+ test al,0fh
+ jnz short Kt0d120
+ pop ecx ; (TOS) = OldIrql
+ fstCall KfLowerIrql
+ jmp short Kt0d105
+
+Kt0d120:
+ pop ecx ; (TOS) = OldIrql
+ fstCall KfLowerIrql
+ jmp _KiExceptionExit
+
+_KiTrap0D endp
+
+ page ,132
+ subttl "Page fault processing"
+;++
+;
+; Routine Description:
+;
+; Handle page fault.
+;
+; The page fault occurs if paging is enable and any one of the
+; conditions is true:
+;
+; 1. page not present
+; 2. the faulting procedure does not have sufficient privilege to
+; access the indicated page.
+;
+; For case 1, the referenced page will be loaded to memory and
+; execution continues.
+; For case 2, registered exception handler will be invoked with
+; appropriate error code (in most cases STATUS_ACCESS_VIOLATION)
+;
+; N.B. It is assume that no page fault is allowed during task
+; switches.
+;
+; N.B. INTERRUPTS MUST REMAIN OFF UNTIL AFTER CR2 IS CAPTURED.
+;
+; Arguments:
+;
+; Error code left on stack.
+; CR2 contains faulting address.
+; Interrupts are turned off at entry by use of an interrupt gate.
+;
+; Return value:
+;
+; None
+;
+;--
+
+ ASSUME DS:NOTHING, SS:NOTHING, ES:NOTHING
+ ENTER_DR_ASSIST kite_a, kite_t, NoAbiosAssist
+align dword
+ public _KiTrap0E
+_KiTrap0E proc
+
+ ENTER_TRAP kite_a, kite_t
+
+if FAST_V86_TRAP or FAST_BOP
+
+ cmp byte ptr PCR[PcVdmAlert], 0
+ jne Kt0eVdmAlert
+endif
+
+ VERIFY_BASE_TRAP_FRAME
+
+ mov edi,cr2
+ sti
+
+
+ test [ebp]+TsEFlags, EFLAGS_INTERRUPT_MASK ; faulted with
+ jz Kt0e12b ; interrupts disabled?
+Kt0e01:
+
+;
+; call _MmAccessFault to page in the not present page. If the cause
+; of the fault is 2, _MmAccessFault will return approriate error code
+;
+
+ sub esp, 12
+ mov eax,[ebp]+TsSegCs
+ and eax,MODE_MASK ; (eax) = arg3: PreviousMode
+ mov [esp+8], eax
+ mov [esp+4], edi
+ mov eax, [ebp]+TsErrCode ; (eax)= error code
+ and eax, ERR_0E_STORE ; (eax)= 0 if fault caused by read
+ ; = 2 if fault caused by write
+ shr eax, 1 ; (eax) = 0 if read fault, 1 if write fault
+ mov [esp+0], eax ; arg3: load/store indicator
+
+ call _MmAccessFault@12
+
+if DEVL
+ cmp _PsWatchEnabled,0
+ jz short xxktskip
+ mov ebx, [ebp]+TsEip
+ stdCall _PsWatchWorkingSet,<eax, ebx, edi>
+xxktskip:
+endif
+
+ or eax, eax ; sucessful?
+ jge Kt0e10 ; yes, go exit
+
+ mov ecx,PCR[PcGdt]
+
+ ; Form Ldt Base
+ movzx ebx,byte ptr [ecx + KGDT_LDT].KgdtBaseHi
+ shl ebx,8
+ or bl,byte ptr [ecx + KGDT_LDT].KgdtBaseMid
+ shl ebx,16
+ or bx,word ptr [ecx + KGDT_LDT].KgdtBaseLow
+ or ebx,ebx ; check for zero
+ jz short Kt0e05 ; no ldt
+
+ cmp edi,ebx
+ jb short Kt0e05 ; address not in LDT
+
+ ; Form Ldt limit
+ movzx edx,byte ptr [ecx + KGDT_LDT].KgdtLimitHi
+ and edx,000000FFh
+ shl edx,16
+ or dx,word ptr [ecx + KGDT_LDT].KgdtLimitLow
+ add ebx,edx
+ cmp edi,ebx
+ jae short Kt0e05 ; too high to be an ldt address
+
+ sldt cx ; Verify this process has an LDT
+ test ecx, 0ffffh ; Check CX
+ jz short Kt0e05 ; If ZY, no LDT
+
+ mov eax, [ebp]+TsErrCode ; (eax)= error code
+ and eax, ERR_0E_STORE ; (eax)= 0 if fault caused by read
+ ; = 2 if fault caused by write
+ shr eax, 1 ; (eax) = 0 if read fault, 1 if write fault
+ ; call page fault handler
+ stdCall _MmAccessFault, <eax, edi, 0>
+
+ or eax, eax ; sucessful?
+ jge Kt0e10 ; if z, yes, go exit
+
+ mov ebx,[ebp]+TsSegCs ; restore previous mode
+ and ebx,MODE_MASK
+ mov [esp + 8],ebx
+
+Kt0e05:
+;
+; Did the fault occur in KiSystemService while copying arguments from
+; user stack to kernel stack?
+;
+
+ mov ecx, offset FLAT:KiSystemServiceCopyArguments
+ cmp [ebp].TsEip, ecx
+ jne short @f
+
+ mov ecx, [ebp].TsEbp ; (eax)->TrapFrame of SysService
+ test [ecx].TsSegCs, MODE_MASK
+ jz short @f ; caller of SysService is k mode, we
+ ; will let it bugchecked.
+ mov [ebp].TsEip, offset FLAT:kss60
+ mov eax, STATUS_ACCESS_VIOLATION
+ mov [ebp].TsEax, eax
+ jmp _KiExceptionExit
+@@:
+
+ mov ecx, [ebp]+TsErrCode ; (ecx) = error code
+ and ecx, ERR_0E_STORE ; (ecx) = 0 if fault caused by read
+ ; 2 if fault caused by write
+ shr ecx,1 ; (ecx) = load/store indicator
+;
+; Did the fault occur in a VDM?
+;
+ test dword ptr [ebp]+TsEFlags,EFLAGS_V86_MASK
+ jnz Kt0e7
+
+;
+; Did the fault occur in a VDM while running in protected mode?
+;
+
+ mov esi,PCR[PcPrcbData+PbCurrentThread]
+ mov esi,[esi]+ThApcState+AsProcess
+ test byte ptr [esi]+PrVdmFlag,0fh ; is this a vdm process?
+ jz short Kt0e9 ; z -> not vdm
+
+ test dword ptr [ebp]+TsSegCs, MODE_MASK
+ jz short kt0e8
+
+ cmp word ptr [ebp]+TsSegCs, KGDT_R3_CODE OR RPL_MASK
+ jz kt0e9 ; z -> not vdm
+
+Kt0e7: mov esi, eax
+ stdCall _VdmDispatchPageFault, <ebp,ecx,edi>
+ test al,0fh ; returns TRUE, if success
+ jnz Kt0e11 ; Exit,No need to call the debugger
+
+ mov eax, esi
+ jmp short Kt0e9
+
+Kt0e8:
+;
+; Did the fault occur in our kernel VDM support code?
+; At this point, we know:
+; . Current process is VDM process
+; . this is a unresolvable pagefault
+; . the fault occurred in kernel mode.
+;
+
+;
+; First make sure this is not pagefault at irql > 1
+; which should be bugchecked.
+;
+
+ cmp eax, STATUS_IN_PAGE_ERROR or 10000000h
+ je Kt0e12
+
+ cmp word ptr [ebp]+TsSegCs, KGDT_R0_CODE ; Did fault occur in kernel?
+ jnz short Kt0e9 ; if nz, no, not ours
+
+ cmp PCR[PcExceptionList], EXCEPTION_CHAIN_END
+ jnz short Kt0e9 ; there is at least a handler to
+ ; handle this exception
+
+ mov ebp, PCR[PcInitialStack]
+ xor ecx, ecx ; set to fault-at-read
+ sub ebp, KTRAP_FRAME_LENGTH
+ mov esp, ebp ; clear stack (ebp)=(esp)->User trap frame
+ mov esi, [ebp]+TsEip ; (esi)-> faulting instruction
+ jmp Kt0e9b ; go dispatching the exception to user
+
+Kt0e9:
+; Set up exception record and arguments and call _KiDispatchException
+ mov esi, [ebp]+TsEip ; (esi)-> faulting instruction
+
+ cmp eax, STATUS_ACCESS_VIOLATION ; dispatch access violation or
+ je short Kt0e9b ; or in_page_error?
+
+ cmp eax, STATUS_GUARD_PAGE_VIOLATION
+ je short Kt0e9b
+
+ cmp eax, STATUS_STACK_OVERFLOW
+ je short Kt0e9b
+
+
+;
+; test to see if davec's reserved status code bit is set. If so, then bugchecka
+;
+
+ cmp eax, STATUS_IN_PAGE_ERROR or 10000000h
+ je Kt0e12 ; bugchecka
+
+;
+; (ecx) = ExceptionInfo 1
+; (edi) = ExceptionInfo 2
+; (eax) = ExceptionInfo 3
+; (esi) -> Exception Addr
+;
+
+ mov edx, ecx
+ mov ebx, esi
+ mov esi, edi
+ mov ecx, 3
+ mov edi, eax
+ mov eax, STATUS_IN_PAGE_ERROR
+ call CommonDispatchException ; Won't return
+
+Kt0e9b:
+ mov ebx, esi
+ mov edx, ecx
+ mov esi, edi
+ jmp CommonDispatchException2Args ; Won't return
+
+.FPO ( 0, 0, 0, 0, 0, FPO_TRAPFRAME )
+
+Kt0e10:
+
+if DEVL
+ mov esp,ebp ; (esp) -> trap frame
+ test _KdpOweBreakpoint, 1 ; do we have any owed breakpoints?
+ jz _KiExceptionExit ; No, all done
+
+ stdCall _KdSetOwedBreakpoints ; notify the debugger
+endif
+
+Kt0e11: mov esp,ebp ; (esp) -> trap frame
+ jmp _KiExceptionExit ; join common code
+
+
+Kt0e12:
+ stdCall _KeGetCurrentIrql ; (eax) = OldIrql
+Kt0e12a:
+ lock inc ds:_KiHardwareTrigger ; trip hardware analyzer
+
+;
+; bugcheck a, addr, irql, load/store, pc
+;
+ mov ecx, [ebp]+TsErrCode ; (ecx)= error code
+ and ecx, ERR_0E_STORE ; (ecx)= 0 if fault caused by read
+ shr ecx, 1 ; (ecx) = 0 if read fault, 1 if write fault
+
+ mov esi, [ebp]+TsEip ; [esi] = faulting instruction
+
+ stdCall _KeBugCheckEx,<IRQL_NOT_LESS_OR_EQUAL,edi,eax,ecx,esi>
+
+Kt0e12b:
+ ; In V86 mode with iopl allowed it is OK to handle
+ ; a page fault with interrupts disabled
+
+ test dword ptr [ebp]+TsEFlags, EFLAGS_V86_MASK
+ jz short Kt0e12c
+
+ cmp _KeI386VdmIoplAllowed, 0
+ jnz Kt0e01
+
+Kt0e12c:
+ cmp _KiFreezeFlag,0 ; during boot we can take
+ jnz Kt0e01 ; 'transistion failts' on the
+ ; debugger before it's been locked
+
+ cmp _KiBugCheckData, 0 ; If crashed, handle trap in
+ jnz Kt0e01 ; normal manner
+
+
+ mov eax, 0ffh ; OldIrql = -1
+ jmp short Kt0e12a
+
+if FAST_BOP or FAST_V86_TRAP
+
+Kt0eVdmAlert:
+ ; If a page fault occured while we are in VDM alert mode (processing
+ ; v86 trap without building trap frame), we will restore all the
+ ; registers and return to its recovery routine which is stored in
+ ; the TsSegGs of original trap frame.
+ ;
+ mov eax, PCR[PcVdmAlert]
+ mov byte ptr PCR[PcVdmAlert], 0
+
+IF FAST_V86_TRAP
+ cmp al, 0Dh
+ jne short @f
+
+ mov eax, offset FLAT:V86TrapDRecovery
+ jmp short KtvaExit
+
+@@:
+ cmp al, 10h
+ jne short @f
+
+ mov eax, offset FLAT:V86Trap10Recovery
+ jmp short KtvaExit
+
+@@:
+ENDIF ; FAST_V86_TRAP
+
+IF FAST_BOP
+ cmp al, 6
+ jne short @f
+
+ mov eax, offset FLAT:V86Trap6Recovery
+ jmp short KtvaExit
+
+@@:
+ENDIF ; FAST_BOP
+
+ mov eax, [ebp].TsEip
+KtvaExit:
+ mov [ebp].TsEip, eax
+ mov esp,ebp ; (esp) -> trap frame
+ jmp _KiExceptionExit ; join common code
+
+if DBG
+@@: int 3
+endif
+ENDIF ; FAST_BOP OR FAST_V86_TRAP
+_KiTrap0E endp
+
+ page ,132
+ subttl "Trap0F -- Intel Reserved"
+;++
+;
+; Routine Description:
+;
+; The trap 0F should never occur. If, however, the exception occurs in
+; USER mode, the current process will be terminated. If the exception
+; occurs in KERNEL mode, a bugcheck will be raised. NO registered
+; handler, if any, will be inviked to handle the exception.
+;
+; Arguments:
+;
+; None
+; Return value:
+;
+; None
+;
+;--
+ ASSUME DS:NOTHING, SS:NOTHING, ES:NOTHING
+
+ ENTER_DR_ASSIST kitf_a, kitf_t, NoAbiosAssist
+align dword
+ public _KiTrap0F
+_KiTrap0F proc
+
+ push 0 ; push dummy error code
+ ENTER_TRAP kitf_a, kitf_t
+ sti
+
+ mov eax, EXCEPTION_RESERVED_TRAP ; (eax) = trap type
+ jmp _KiSystemFatalException ; go terminate the system
+
+_KiTrap0F endp
+
+
+ page ,132
+ subttl "Coprocessor Error"
+
+;++
+;
+; Routine Description:
+;
+; Handle Coprocessor Error.
+;
+; This exception is used on 486 or above only. For i386, it uses
+; IRQ 13 instead.
+;
+; Arguments:
+;
+; At entry, the saved CS:EIP point to the aborted instruction.
+; No error code is provided with the error.
+;
+; Return value:
+;
+; None
+;
+;--
+ ASSUME DS:NOTHING, SS:NOTHING, ES:NOTHING
+
+ ENTER_DR_ASSIST kit10_a, kit10_t, NoAbiosAssist
+
+align dword
+ public _KiTrap10
+_KiTrap10 proc
+
+ push 0 ; push dummy error code
+ ENTER_TRAP kit10_a, kit10_t
+
+ mov eax, PCR[PcPrcbData+PbNpxThread] ; Correct context for fault?
+ cmp eax, PCR[PcPrcbData+PbCurrentThread]
+ je Kt0710 ; Yes - go try to dispatch it
+
+;
+; We are in the wrong NPX context and can not dispatch the exception right now.
+; Set up the target thread for a delay exception.
+;
+; Note: we don't think this is a possible case, but just to be safe...
+;
+ mov eax, [eax].ThInitialStack
+ sub eax, NPX_FRAME_LENGTH ; Space for NPX_FRAME
+ or dword ptr [eax].FpCr0NpxState, CR0_TS ; Set for delayed error
+
+ jmp _KiExceptionExit
+
+_KiTrap10 endp
+
+ page ,132
+ subttl "Alignment fault"
+;++
+;
+; Routine Description:
+;
+; Handle alignment faults.
+;
+; This exception occurs when an unaligned data access is made by a thread
+; with alignment checking turned on.
+;
+; This exception will only occur on 486 machines. The 386 will not do
+; any alignment checking. Only threads which have the appropriate bit
+; set in EFLAGS will generate alignment faults.
+;
+; The exception will never occur in kernel mode. (hardware limitation)
+;
+; Arguments:
+;
+; At entry, the saved CS:EIP point to the faulting instruction.
+; Error code is provided.
+;
+; Return value:
+;
+; None
+;
+;--
+ ASSUME DS:NOTHING, SS:NOTHING, ES:NOTHING
+
+ ENTER_DR_ASSIST kit11_a, kit11_t, NoAbiosAssist
+align dword
+ public _KiTrap11
+_KiTrap11 proc
+
+ ENTER_TRAP kit11_a, kit11_t
+ sti
+
+ test dword ptr [ebp]+TsEFlags,EFLAGS_V86_MASK
+ jnz Kt11_01 ; v86 mode => usermode
+
+ test byte ptr [ebp]+TsSegCs, MODE_MASK ; Is previous mode = USER
+ jz Kt11_10
+
+;
+; Check to make sure that the AutoAlignment state of this thread is FALSE.
+; If not, this fault occurred because the thread messed with its own EFLAGS.
+; In order to "fixup" this fault, we just clear the ALIGN_CHECK bit in the
+; EFLAGS and restart the instruction. Exceptions will only be generated if
+; AutoAlignment is FALSE.
+;
+Kt11_01:
+ mov ebx,PCR[PcPrcbData+PbCurrentThread] ; (ebx)-> Current Thread
+ test byte ptr [ebx].ThAutoAlignment, -1
+ jz kt11_00
+;
+; This fault was generated even though the thread had AutoAlignment set to
+; TRUE. So we fix it up by setting the correct state in his EFLAGS and
+; restarting the instruction.
+;
+ and dword ptr [ebp]+TsEflags, NOT EFLAGS_ALIGN_CHECK
+ jmp _KiExceptionExit
+
+kt11_00:
+ mov ebx, [ebp]+TsEip ; (ebx)->faulting instruction
+ mov edx, EXCEPT_LIMIT_ACCESS; assume it is limit violation
+ mov esi, [ebp]+TsHardwareEsp; (esi) = User Stack pointer
+ cmp word ptr [ebp]+TsErrCode, 0 ; Is errorcode = 0?
+ jz short kt11_05 ; if z, yes, go dispatch exception
+
+ mov edx, EXCEPT_UNKNOWN_ACCESS
+kt11_05:
+ mov eax, STATUS_DATATYPE_MISALIGNMENT
+ jmp CommonDispatchException2Args ; Won't return
+
+kt11_10:
+;
+; We should never be here, since the 486 will not generate alignment faults
+; in kernel mode.
+;
+ mov eax, EXCEPTION_ALIGNMENT_CHECK ; (eax) = trap type
+ jmp _KiSystemFatalException
+
+_KiTrap11 endp
+
+ page ,132
+ subttl "Coprocessor Error Handler"
+;++
+;
+; Routine Description:
+;
+; When the 80387 detects an error, it raises its error line. This
+; was supposed to be routed directly to the 386 to cause a trap 16
+; (which would actually occur when the 386 encountered the next FP
+; instruction).
+;
+; However, the ISA design routes the error line to IRQ13 on the
+; slave 8259. So an interrupt will be generated whenever the 387
+; discovers an error. Unfortunately, we could be executing system
+; code at the time, in which case we can't dispatch the exception.
+;
+; So we force emulation of the intended behaviour. This interrupt
+; handler merely sets TS and Cr0NpxState TS and dismisses the interrupt.
+; Then, on the next user FP instruction, a trap 07 will be generated, and
+; the exception can be dispatched then.
+;
+; Note that we don't have to clear the FP exeception here,
+; since that will be done in the trap 07 handler. The 386 will
+; generate the trap 07 before the 387 gets a chance to raise another
+; error interrupt. We'll want to save the 387 state in the trap 07
+; handler WITH the error information.
+;
+; Note the caller must clear the 387 error latch. (this is done in
+; the hal).
+;
+; Arguments:
+;
+; None
+;
+; Return value:
+;
+; None
+;
+;--
+ ASSUME DS:FLAT, SS:NOTHING, ES:NOTHING
+
+align dword
+cPublicProc _KiCoprocessorError ,0
+
+;
+; Set TS in Cr0NpxState - the next time this thread runs an ESC
+; instruction the error will be dispatched. We also need to set TS
+; in CR0 in case the owner of the NPX is currently running.
+;
+; Bit must be set in FpCr0NpxState before CR0.
+;
+ mov eax, PCR[PcPrcbData+PbNpxThread]
+ mov eax, [eax].ThInitialStack
+ sub eax, NPX_FRAME_LENGTH ; Space for NPX_FRAME
+ or dword ptr [eax].FpCr0NpxState, CR0_TS
+
+ mov eax, cr0
+ or eax, CR0_TS
+ mov cr0, eax
+ stdRET _KiCoprocessorError
+
+stdENDP _KiCoprocessorError
+
+
+;++
+;
+; VOID
+; KiFlushNPXState (
+; VOID
+; )
+;
+; Routine Description:
+;
+; When a threads NPX context is requested (most likely by a debugger)
+; this function is called to flush the threads NPX context out of the
+; compressor if required.
+;
+; Note: the kernel debugger does not invoke this function. (which
+; is good since it may have interrupts off).
+;
+; Arguments:
+;
+; None
+;
+; Return value:
+;
+; None
+;
+;--
+ ASSUME DS:FLAT, SS:NOTHING, ES:NOTHING
+align dword
+cPublicProc _KiFlushNPXState ,0
+
+ mov eax, PCR[PcPrcbData+PbCurrentThread]
+ cmp byte ptr [eax].ThNpxState, NPX_STATE_LOADED
+ jne short fnpx90
+
+ pushfd
+ mov ecx, PCR[PcInitialStack] ; (ecx) -> top of kernel stack
+
+; this was only needed for 388/387 support
+; fwait ; Get any errors now
+
+ cli ; don't context switch
+ cmp byte ptr [eax].ThNpxState, NPX_STATE_LOADED
+ jne short fnpx70 ; still in coprocessor?
+
+ fnsave [ecx] ; NPX state to save area
+ fwait ; Make sure data is in save area
+ mov byte ptr [eax].ThNpxState, NPX_STATE_NOT_LOADED
+ mov dword ptr PCR[PcPrcbData+PbNpxThread], 0 ; clear owner
+ mov eax, cr0
+ or eax, CR0_TS
+if DBG
+ test dword ptr [ecx].FpCr0NpxState, NOT (CR0_MP+CR0_EM+CR0_TS)
+ jz @f
+ int 3
+@@:
+endif
+ or eax, [ecx].FpCr0NpxState
+ mov cr0, eax
+
+fnpx70: popfd ; enable interrupts
+fnpx90: stdRET _KiFlushNPXState
+
+stdENDP _KiFlushNPXState
+
+
+;++
+;
+; VOID
+; KiSetHardwareTrigger (
+; VOID
+; )
+;
+; Routine Description:
+;
+; This function sets KiHardwareTrigger such that an analyzer can sniff
+; for this access. It needs to occur with a lock cycle such that
+; the processor won't speculatively read this value. Interlocked
+; functions can't be used as in a UP build they do not use a
+; lock prefix.
+;
+; Arguments:
+;
+; None
+;
+; Return value:
+;
+; None
+;
+;--
+ ASSUME DS:FLAT, SS:NOTHING, ES:NOTHING
+cPublicProc _KiSetHardwareTrigger,0
+ lock inc ds:_KiHardwareTrigger ; trip hardware analyzer
+ stdRet _KiSetHardwareTrigger
+stdENDP _KiSetHardwareTrigger
+
+
+ page ,132
+ subttl "Processing System Fatal Exceptions"
+;++
+;
+; Routine Description:
+;
+; This routine processes the system fatal exceptions.
+; The machine state and trap type will be displayed and
+; System will be stopped.
+;
+; Arguments:
+;
+; (eax) = Trap type
+; (ebp) -> machine state frame
+;
+; Return value:
+;
+; system stopped.
+;
+;--
+ assume ds:nothing, es:nothing, ss:nothing, fs:nothing, gs:nothing
+
+align dword
+ public _KiSystemFatalException
+_KiSystemFatalException proc
+.FPO (0, 0, 0, 0, 0, FPO_TRAPFRAME)
+
+ stdCall _KeBugCheckEx,<UNEXPECTED_KERNEL_MODE_TRAP, eax, 0, 0, 0>
+ ret
+
+_KiSystemFatalException endp
+
+ page
+ subttl "Continue Execution System Service"
+;++
+;
+; NTSTATUS
+; NtContinue (
+; IN PCONTEXT ContextRecord,
+; IN BOOLEAN TestAlert
+; )
+;
+; Routine Description:
+;
+; This routine is called as a system service to continue execution after
+; an exception has occurred. Its function is to transfer information from
+; the specified context record into the trap frame that was built when the
+; system service was executed, and then exit the system as if an exception
+; had occurred.
+;
+; WARNING - Do not call this routine directly, always call it as
+; ZwContinue!!! This is required because it needs the
+; trapframe built by KiSystemService.
+;
+; Arguments:
+;
+; KTrapFrame (ebp+0: after setup) -> base of KTrapFrame
+;
+; ContextRecord (ebp+8: after setup) = Supplies a pointer to a context rec.
+;
+; TestAlert (esp+12: after setup) = Supplies a boolean value that specifies
+; whether alert should be tested for the previous processor mode.
+;
+; Return Value:
+;
+; Normally there is no return from this routine. However, if the specified
+; context record is misaligned or is not accessible, then the appropriate
+; status code is returned.
+;
+;--
+
+NcTrapFrame equ [ebp + 0]
+NcContextRecord equ [ebp + 8]
+NcTestAlert equ [ebp + 12]
+
+align dword
+cPublicProc _NtContinue ,2
+
+ push ebp
+
+;
+; Restore old trap frame address since this service exits directly rather
+; than returning.
+;
+
+ mov ebx, PCR[PcPrcbData+PbCurrentThread] ; get current thread address
+ mov edx, [ebp].TsEdx ; restore old trap frame address
+ mov [ebx].ThTrapFrame, edx ;
+
+;
+; Call KiContinue to load ContextRecord into TrapFrame. On x86 TrapFrame
+; is an atomic entity, so we don't need to allocate any other space here.
+;
+; KiContinue(NcContextRecord, 0, NcTrapFrame)
+;
+
+ mov ebp,esp
+ mov eax, NcTrapFrame
+ mov ecx, NcContextRecord
+ stdCall _KiContinue, <ecx, 0, eax>
+ or eax,eax ; return value 0?
+ jnz short Nc20 ; KiContinue failed, go report error
+
+;
+; Check to determine if alert should be tested for the previous processor mode.
+;
+
+ cmp byte ptr NcTestAlert,0 ; Check test alert flag
+ je short Nc10 ; if z, don't test alert, go Nc10
+ mov al,byte ptr [ebx]+ThPreviousMode ; No need to xor eax, eax.
+ stdCall _KeTestAlertThread, <eax> ; test alert for current thread
+
+Nc10: pop ebp ; (ebp) -> TrapFrame
+ mov esp,ebp ; (esp) = (ebp) -> trapframe
+ jmp _KiServiceExit2 ; common exit
+
+Nc20: pop ebp ; (ebp) -> TrapFrame
+ mov esp,ebp ; (esp) = (ebp) -> trapframe
+ jmp _KiServiceExit ; common exit
+
+stdENDP _NtContinue
+
+ page
+ subttl "Raise Exception System Service"
+;++
+;
+; NTSTATUS
+; NtRaiseException (
+; IN PEXCEPTION_RECORD ExceptionRecord,
+; IN PCONTEXT ContextRecord,
+; IN BOOLEAN FirstChance
+; )
+;
+; Routine Description:
+;
+; This routine is called as a system service to raise an exception. Its
+; function is to transfer information from the specified context record
+; into the trap frame that was built when the system service was executed.
+; The exception may be raised as a first or second chance exception.
+;
+; WARNING - Do not call this routine directly, always call it as
+; ZwRaiseException!!! This is required because it needs the
+; trapframe built by KiSystemService.
+;
+; NOTE - KiSystemService will terminate the ExceptionList, which is
+; not what we want for this case, so we will fish it out of
+; the trap frame and restore it.
+;
+; Arguments:
+;
+; TrapFrame (ebp+0: before setup) -> System trap frame for this call
+;
+; ExceptionRecord (ebp+8: after setup) -> An exception record.
+;
+; ContextRecord (ebp+12: after setup) -> Points to a context record.
+;
+; FirstChance (epb+16: after setup) -> Supplies a boolean value that
+; specifies whether the exception is to be raised as a first (TRUE)
+; or second chance (FALSE) exception.
+;
+; Return Value:
+;
+; None.
+;--
+align dword
+cPublicProc _NtRaiseException ,3
+
+ push ebp
+
+;
+; Restore old trap frame address since this service exits directly rather
+; than returning.
+;
+
+ mov ebx, PCR[PcPrcbData+PbCurrentThread] ; get current thread address
+ mov edx, [ebp].TsEdx ; restore old trap frame address
+ mov [ebx].ThTrapFrame, edx ;
+
+;
+; Put back the ExceptionList so the exception can be properly
+; dispatched.
+;
+
+ mov ebp,esp ; [ebp+0] -> TrapFrame
+ mov ebx, [ebp+0] ; (ebx)->TrapFrame
+ mov edx, [ebp+16] ; (edx) = First chance indicator
+ mov eax, [ebx]+TsExceptionList ; Old exception list
+ mov ecx, [ebp+12] ; (ecx)->ContextRecord
+ mov PCR[PcExceptionList],eax
+ mov eax, [ebp+8] ; (eax)->ExceptionRecord
+
+;
+; KiRaiseException(ExceptionRecord, ContextRecord, ExceptionFrame,
+; TrapFrame, FirstChance)
+;
+
+ stdCall _KiRaiseException,<eax, ecx, 0, ebx, edx>
+
+ pop ebp
+ mov esp,ebp ; (esp) = (ebp) -> trap frame
+
+;
+; If the exception was handled, then the trap frame has been edited to
+; reflect new state, and we'll simply exit the system service to get
+; the effect of a continue.
+;
+; If the exception was not handled, we'll return to our caller, who
+; will raise a new exception.
+;
+ jmp _KiServiceExit2
+
+stdENDP _NtRaiseException
+
+
+
+ page
+ subttl "Reflect Exception to a Vdm"
+;++
+;
+; Routine Description:
+; Local stub which reflects an exception to a VDM using
+; Ki386VdmReflectException,
+;
+; Arguments:
+;
+; ebp -> Trap frame
+; ss:esp + 4 = trap number
+;
+; Returns
+; ret value from Ki386VdmReflectException
+; interrupts are disabled uppon return
+;
+cPublicProc _Ki386VdmReflectException_A, 1
+
+ sub esp, 4*2
+
+ mov ecx, APC_LEVEL
+ fstCall KfRaiseIrql
+
+ sti
+ mov [esp+4], eax ; Save OldIrql
+ mov eax, [esp+12] ; Pick up trap number
+ mov [esp+0], eax
+
+ call _Ki386VdmReflectException@4 ; pops one dword
+
+ mov ecx, [esp+0] ; (ecx) = OldIrql
+ mov [esp+0], eax ; Save return code
+
+ fstCall KfLowerIrql
+
+ pop eax ; pops second dword
+ ret 4
+
+stdENDP _Ki386VdmReflectException_A
+
+
+_TEXT$00 ends
+ end
diff --git a/private/ntos/ke/i386/trapc.c b/private/ntos/ke/i386/trapc.c
new file mode 100644
index 000000000..0cd804e4e
--- /dev/null
+++ b/private/ntos/ke/i386/trapc.c
@@ -0,0 +1,545 @@
+/*++
+
+Copyright (c) 1989 Microsoft Corporation
+
+Module Name:
+
+ trapc.c
+
+Abstract:
+
+ This module contains some trap handling code written in C.
+ Only by the kernel.
+
+Author:
+
+ Ken Reneris 6-9-93
+
+Revision History:
+
+--*/
+
+#include "ki.h"
+
+NTSTATUS
+Ki386CheckDivideByZeroTrap (
+ IN PKTRAP_FRAME UserFrame
+ );
+
+
+#ifdef ALLOC_PRAGMA
+#pragma alloc_text(PAGE, Ki386CheckDivideByZeroTrap)
+#endif
+
+
+#define REG(field) ((ULONG)(&((KTRAP_FRAME *)0)->field))
+#define GETREG(frame,reg) ((PULONG) (((ULONG) frame)+reg))[0]
+
+typedef struct {
+ UCHAR RmDisplaceOnly; // RM of displacment only, no base reg
+ UCHAR RmSib; // RM of SIB
+ UCHAR RmDisplace; // bit mask of RMs which have a displacement
+ UCHAR Disp; // sizeof displacement (in bytes)
+} KMOD, *PKMOD;
+
+static UCHAR RM32[] = {
+ /* 000 */ REG(Eax),
+ /* 001 */ REG(Ecx),
+ /* 010 */ REG(Edx),
+ /* 011 */ REG(Ebx),
+ /* 100 */ REG(HardwareEsp),
+ /* 101 */ REG(Ebp), // SIB
+ /* 110 */ REG(Esi),
+ /* 111 */ REG(Edi)
+};
+
+static UCHAR RM8[] = {
+ /* 000 */ REG(Eax), // al
+ /* 001 */ REG(Ecx), // cl
+ /* 010 */ REG(Edx), // dl
+ /* 011 */ REG(Ebx), // bl
+ /* 100 */ REG(Eax) + 1, // ah
+ /* 101 */ REG(Ecx) + 1, // ch
+ /* 110 */ REG(Edx) + 1, // dh
+ /* 111 */ REG(Ebx) + 1 // bh
+};
+
+static KMOD MOD32[] = {
+ /* 00 */ 5, 4, 0x20, 4,
+ /* 01 */ 0xff, 4, 0xff, 1,
+ /* 10 */ 0xff, 4, 0xff, 4,
+ /* 11 */ 0xff, 0xff, 0x00, 0
+} ;
+
+static struct {
+ UCHAR Opcode1, Opcode2; // instruction opcode
+ UCHAR ModRm, type; // if 2nd part of opcode is encoded in ModRm
+} NoWaitNpxInstructions[] = {
+ /* FNINIT */ 0xDB, 0xE3, 0, 1,
+ /* FNCLEX */ 0xDB, 0xE2, 0, 1,
+ /* FNSTENV */ 0xD9, 0x06, 1, 1,
+ /* FNSAVE */ 0xDD, 0x06, 1, 1,
+ /* FNSTCW */ 0xD9, 0x07, 1, 2,
+ /* FNSTSW */ 0xDD, 0x07, 1, 3,
+ /* FNSTSW AX*/ 0xDF, 0xE0, 0, 4,
+ 0x00, 0x00, 0, 1
+};
+
+
+NTSTATUS
+Ki386CheckDivideByZeroTrap (
+ IN PKTRAP_FRAME UserFrame
+ )
+/*++
+
+Routine Description:
+
+ This function gains control when the x86 processor generates a
+ divide by zero trap. The x86 design generates such a trap on
+ divide by zero and on division overflows. In order to determine
+ which expection code to dispatch, the divisor of the "div" or "idiv"
+ instruction needs to be inspected.
+
+Arguments:
+
+ UserFrame - Trap frame of the divide by zero trap
+
+Return Value:
+
+ exception code dispatch
+
+--*/
+{
+ ULONG operandsize, operandmask, i, accum;
+ PUCHAR istream, pRM;
+ UCHAR ibyte, rm;
+ PKMOD Mod;
+ BOOLEAN fPrefix;
+ NTSTATUS status;
+
+ status = STATUS_INTEGER_DIVIDE_BY_ZERO;
+ try {
+
+ //
+ // read instruction prefixes
+ //
+
+ fPrefix = TRUE;
+ pRM = RM32;
+ operandsize = 4;
+ operandmask = 0xffffffff;
+ istream = (PUCHAR) UserFrame->Eip;
+ while (fPrefix) {
+ ibyte = ProbeAndReadUchar(istream);
+ istream++;
+ switch (ibyte) {
+ case 0x2e: // cs override
+ case 0x36: // ss override
+ case 0x3e: // ds override
+ case 0x26: // es override
+ case 0x64: // fs override
+ case 0x65: // gs override
+ case 0xF3: // rep
+ case 0xF2: // rep
+ case 0xF0: // lock
+ break;
+
+ case 0x66:
+ // 16 bit operand override
+ operandsize = 2;
+ operandmask = 0xffff;
+ break;
+
+ case 0x67:
+ // 16 bit address size override
+ // this is some non-flat code
+ goto try_exit;
+
+ default:
+ fPrefix = FALSE;
+ break;
+ }
+ }
+
+ //
+ // Check instruction opcode
+ //
+
+ if (ibyte != 0xf7 && ibyte != 0xf6) {
+ // this is not a DIV or IDIV opcode
+ goto try_exit;
+ }
+
+ if (ibyte == 0xf6) {
+ // this is a byte div or idiv
+ operandsize = 1;
+ operandmask = 0xff;
+ }
+
+ //
+ // Get Mod R/M
+ //
+
+ ibyte = ProbeAndReadUchar (istream);
+ istream++;
+ Mod = MOD32 + (ibyte >> 6);
+ rm = ibyte & 7;
+
+ //
+ // put register values into accum
+ //
+
+ if (operandsize == 1 && (ibyte & 0xc0) == 0xc0) {
+ pRM = RM8;
+ }
+
+ accum = 0;
+ if (rm != Mod->RmDisplaceOnly) {
+ if (rm == Mod->RmSib) {
+ // get SIB
+ ibyte = ProbeAndReadUchar(istream);
+ istream++;
+ i = (ibyte >> 3) & 7;
+ if (i != 4) {
+ accum = GETREG(UserFrame, RM32[i]);
+ accum = accum << (ibyte >> 6); // apply scaler
+ }
+ i = ibyte & 7;
+ accum = accum + GETREG(UserFrame, RM32[i]);
+ } else {
+ // get register's value
+ accum = GETREG(UserFrame, pRM[rm]);
+ }
+ }
+
+ //
+ // apply displacement to accum
+ //
+
+ if (Mod->RmDisplace & (1 << rm)) {
+ if (Mod->Disp == 4) {
+ i = ProbeAndReadUlong ((PULONG) istream);
+ } else {
+ ibyte = ProbeAndReadChar (istream);
+ i = (signed long) ((signed char) ibyte); // sign extend
+ }
+ accum += i;
+ }
+
+ //
+ // if this is an effective address, go get the data value
+ //
+
+ if (Mod->Disp) {
+ switch (operandsize) {
+ case 1: accum = ProbeAndReadUchar((PUCHAR) accum); break;
+ case 2: accum = ProbeAndReadUshort((PUSHORT) accum); break;
+ case 4: accum = ProbeAndReadUlong((PULONG) accum); break;
+ }
+ }
+
+ //
+ // accum now contains the instruction operand, see if the
+ // operand was really a zero
+ //
+
+ if (accum & operandmask) {
+ // operand was non-zero, must be an overflow
+ status = STATUS_INTEGER_OVERFLOW;
+ }
+
+try_exit: ;
+ } except (EXCEPTION_EXECUTE_HANDLER) {
+ // do nothing...
+ }
+
+ return status;
+}
+
+UCHAR
+KiNextIStreamByte (
+ IN PKTRAP_FRAME UserFrame,
+ IN PUCHAR *istream
+ )
+/*++
+
+Routine Description:
+
+ Reads the next byte from the istream pointed to by the UserFrame, and
+ advances the EIP.
+
+
+ Note: this function works for 32 bit code only
+
+--*/
+{
+ UCHAR ibyte;
+
+ if (UserFrame->SegCs == KGDT_R0_CODE) {
+ ibyte = **istream;
+ } else {
+ ibyte = ProbeAndReadUchar (*istream);
+ }
+
+ *istream += 1;
+ return ibyte;
+}
+
+
+
+
+BOOLEAN
+Ki386CheckDelayedNpxTrap (
+ IN PKTRAP_FRAME UserFrame,
+ IN PFLOATING_SAVE_AREA NpxFrame
+ )
+/*++
+
+Routine Description:
+
+ This function gains control from the Trap07 handler. It examines
+ the user mode instruction to see if it's a NoWait NPX instruction.
+ Such instructions do not generate floating point exceptions - this
+ check needs to be done due to the way 80386/80387 systems are
+ implemented. Such machines will generate a floating point exception
+ interrupt when the kernel performs an FRSTOR to reload the thread's
+ NPX context. If the thread's next instruction is a NoWait style
+ instruction, then we clear the exception or emulate the instruction.
+
+ AND... due to a different 80386/80387 "feature" the kernel needs
+ to use FWAIT at times which can causes 80487's to generate delayed
+ exceptions that can lead to the same problem described above.
+
+Arguments:
+
+ UserFrame - Trap frame of the exception
+ NpxFrame - Thread's NpxFrame (WARNING: does not have NpxState)
+
+ Interrupts are disabled
+
+Return Value:
+
+ FALSE - Dispatch NPX exception to user mode
+ TRUE - Exception handled, continue
+
+--*/
+{
+ EXCEPTION_RECORD ExceptionRecord;
+ UCHAR ibyte1, ibyte2, inmodrm, status;
+ USHORT StatusWord, ControlWord, UsersWord;
+ PUCHAR istream;
+ BOOLEAN fPrefix;
+ UCHAR rm;
+ PKMOD Mod;
+ ULONG accum, i;
+
+ status = 0;
+ try {
+
+ //
+ // read instruction prefixes
+ //
+
+ fPrefix = TRUE;
+ istream = (PUCHAR) UserFrame->Eip;
+ while (fPrefix) {
+ ibyte1 = KiNextIStreamByte (UserFrame, &istream);
+ switch (ibyte1) {
+ case 0x2e: // cs override
+ case 0x36: // ss override
+ case 0x3e: // ds override
+ case 0x26: // es override
+ case 0x64: // fs override
+ case 0x65: // gs override
+ break;
+
+ default:
+ fPrefix = FALSE;
+ break;
+ }
+ }
+
+ //
+ // Check for coprocessor NoWait NPX instruction
+ //
+
+ ibyte2 = KiNextIStreamByte (UserFrame, &istream);
+ inmodrm = (ibyte2 >> 3) & 0x7;
+
+ for (i=0; NoWaitNpxInstructions[i].Opcode1; i++) {
+ if (NoWaitNpxInstructions[i].Opcode1 == ibyte1) {
+
+ //
+ // first opcode byte matched - check second part of opcode
+ //
+
+ if (NoWaitNpxInstructions[i].ModRm) {
+ if (NoWaitNpxInstructions[i].Opcode2 == inmodrm) {
+ // This is a no-wait NPX instruction
+ status = NoWaitNpxInstructions[i].type;
+ break;
+ }
+
+ } else {
+ if (NoWaitNpxInstructions[i].Opcode2 == ibyte2) {
+ // This is a no-wait NPX instruction
+ status = NoWaitNpxInstructions[i].type;
+ break;
+ }
+ }
+ }
+ }
+
+ } except (EXCEPTION_EXECUTE_HANDLER) {
+ // do nothing...
+ }
+
+ if (status == 0) {
+ //
+ // Dispatch coprocessor exception to user mode
+ //
+
+ return FALSE;
+ }
+
+ if (status == 1) {
+ //
+ // Ignore pending exception, user mode instruction does not trap
+ // on pending execptions and it will clear/mask the pending exceptions
+ //
+
+ _asm {
+ mov eax, cr0
+ and eax, NOT (CR0_MP+CR0_EM+CR0_TS)
+ mov cr0, eax
+ }
+
+ NpxFrame->Cr0NpxState &= ~CR0_TS;
+ return TRUE;
+ }
+
+ //
+ // This is either FNSTSW or FNSTCW. Both of these instructions get
+ // a value from the coprocessor without effecting the pending exception
+ // state. To do this we emulate the instructions.
+ //
+
+ //
+ // Read the coprocessors Status & Control word state, then re-enable
+ // interrupts. (it's safe to context switch after that point)
+ //
+
+ _asm {
+ mov eax, cr0
+ mov ecx, eax
+ and eax, NOT (CR0_MP+CR0_EM+CR0_TS)
+ mov cr0, eax
+
+ fnstsw StatusWord
+ fnstcw ControlWord
+
+ mov cr0, ecx
+ sti
+ }
+
+ if (status == 4) {
+ //
+ // Emulate FNSTSW AX
+ //
+
+ UserFrame->Eip = (ULONG)istream;
+ UserFrame->Eax = (UserFrame->Eax & 0xFFFF0000) | StatusWord;
+ return TRUE;
+ }
+
+ if (status == 2) {
+ UsersWord = ControlWord;
+ } else {
+ UsersWord = StatusWord;
+ }
+
+ try {
+
+ //
+ // (PERFNOTE: the operand decode code should really share code with
+ // KiCheckDivideByZeroTrap, but this is a late change therefore the
+ // code was copied to keep the impact of the change localized)
+ //
+
+ //
+ // decode Mod/RM byte
+ //
+
+ Mod = MOD32 + (ibyte2 >> 6);
+ rm = ibyte2 & 7;
+
+ //
+ // Decode the instruction's word pointer into accum
+ //
+
+ accum = 0;
+ if (rm != Mod->RmDisplaceOnly) {
+ if (rm == Mod->RmSib) {
+ // get SIB
+ ibyte1 = KiNextIStreamByte (UserFrame, &istream);
+ i = (ibyte1 >> 3) & 7;
+ if (i != 4) {
+ accum = GETREG(UserFrame, RM32[i]);
+ accum = accum << (ibyte1 >> 6); // apply scaler
+ }
+ i = ibyte1 & 7;
+ accum = accum + GETREG(UserFrame, RM32[i]);
+ } else {
+ // get register's value
+ accum = GETREG(UserFrame, RM32[rm]);
+ }
+ }
+
+ //
+ // apply displacement to accum
+ //
+
+ if (Mod->RmDisplace & (1 << rm)) {
+ if (Mod->Disp == 4) {
+ i = KiNextIStreamByte (UserFrame, &istream);
+ (KiNextIStreamByte (UserFrame, &istream) << 8) |
+ (KiNextIStreamByte (UserFrame, &istream) << 16) |
+ (KiNextIStreamByte (UserFrame, &istream) << 24);
+ } else {
+ ibyte1 = KiNextIStreamByte (UserFrame, &istream);
+ i = (signed long) ((signed char) ibyte1); // sign extend
+ }
+ accum += i;
+ }
+
+ //
+ // Set the word pointer
+ //
+
+ if (UserFrame->SegCs == KGDT_R0_CODE) {
+ *((PUSHORT) accum) = UsersWord;
+ } else {
+ ProbeAndWriteUshort ((PUSHORT) accum, UsersWord);
+ }
+ UserFrame->Eip = (ULONG)istream;
+
+ } except (KiCopyInformation(&ExceptionRecord,
+ (GetExceptionInformation())->ExceptionRecord)) {
+ //
+ // Faulted addressing user's memory.
+ // Set the address of the exception to the current program address
+ // and raise the exception by calling the exception dispatcher.
+ //
+
+ ExceptionRecord.ExceptionAddress = (PVOID)(UserFrame->Eip);
+ KiDispatchException(
+ &ExceptionRecord,
+ NULL, // ExceptionFrame
+ UserFrame,
+ UserMode,
+ TRUE
+ );
+ }
+
+ return TRUE;
+}
diff --git a/private/ntos/ke/i386/vdm.c b/private/ntos/ke/i386/vdm.c
new file mode 100644
index 000000000..92498d399
--- /dev/null
+++ b/private/ntos/ke/i386/vdm.c
@@ -0,0 +1,1641 @@
+/*++
+
+Copyright (c) 1990 Microsoft Corporation
+
+Module Name:
+
+ VDM.C
+
+Abstract:
+
+ This module contains support routines for the x86 monitor for
+ running Dos applications in V86 mode.
+
+Author:
+
+ Dave Hastings (daveh) 20 Mar 1991
+
+Environment:
+
+ The code in this module is all x86 specific.
+
+Notes:
+
+ In its current implementation, this code is less robust than it needs
+ to be. This will be fixed. Specifically, parameter verification needs
+ to be done. (daveh 7/15/91)
+
+ Support for 32 bit segements (2/2/92)
+Revision History:
+
+ 20-Mar-1991 daveh
+ created
+--*/
+
+#include "ki.h"
+#pragma hdrstop
+#include "vdmntos.h"
+
+#define VDM_IO_TEST 0
+
+#if VDM_IO_TEST
+VOID
+TestIoHandlerStuff(
+ VOID
+ );
+#endif
+
+BOOLEAN
+Ki386GetSelectorParameters(
+ IN USHORT Selector,
+ OUT PULONG Flags,
+ OUT PULONG Base,
+ OUT PULONG Limit
+ );
+
+
+BOOLEAN
+Ki386VdmDispatchIo(
+ IN ULONG PortNumber,
+ IN ULONG Size,
+ IN BOOLEAN Read,
+ IN UCHAR InstructionSize,
+ IN PKTRAP_FRAME TrapFrame
+ );
+
+BOOLEAN
+Ki386VdmDispatchStringIo(
+ IN ULONG PortNumber,
+ IN ULONG Size,
+ IN BOOLEAN Rep,
+ IN BOOLEAN Read,
+ IN ULONG Count,
+ IN ULONG Address,
+ IN UCHAR InstructionSize,
+ IN PKTRAP_FRAME TrapFrame
+ );
+
+
+BOOLEAN
+VdmDispatchIoToHandler(
+ IN PVDM_IO_HANDLER VdmIoHandler,
+ IN ULONG Context,
+ IN ULONG PortNumber,
+ IN ULONG Size,
+ IN BOOLEAN Read,
+ IN OUT PULONG Data
+ );
+
+BOOLEAN
+VdmDispatchUnalignedIoToHandler(
+ IN PVDM_IO_HANDLER VdmIoHandler,
+ IN ULONG Context,
+ IN ULONG PortNumber,
+ IN ULONG Size,
+ IN BOOLEAN Read,
+ IN OUT PULONG Data
+ );
+
+BOOLEAN
+VdmDispatchStringIoToHandler(
+ IN PVDM_IO_HANDLER VdmIoHandler,
+ IN ULONG Context,
+ IN ULONG PortNumber,
+ IN ULONG Size,
+ IN ULONG Count,
+ IN BOOLEAN Read,
+ IN ULONG Data
+ );
+
+BOOLEAN
+VdmCallStringIoHandler(
+ IN PVDM_IO_HANDLER VdmIoHandler,
+ IN PVOID StringIoRoutine,
+ IN ULONG Context,
+ IN ULONG PortNumber,
+ IN ULONG Size,
+ IN ULONG Count,
+ IN BOOLEAN Read,
+ IN ULONG Data
+ );
+
+BOOLEAN
+VdmConvertToLinearAddress(
+ IN ULONG SegmentedAddress,
+ IN PVOID *LinearAddress
+ );
+
+VOID
+KeI386VdmInitialize(
+ VOID
+ );
+
+ULONG
+Ki386VdmEnablePentiumExtentions(
+ ULONG
+ );
+
+#ifdef ALLOC_PRAGMA
+#pragma alloc_text(PAGE, Ki386GetSelectorParameters)
+#pragma alloc_text(PAGE, Ki386VdmDispatchIo)
+#pragma alloc_text(PAGE, Ki386VdmDispatchStringIo)
+#pragma alloc_text(PAGE, VdmDispatchIoToHandler)
+#pragma alloc_text(PAGE, VdmDispatchUnalignedIoToHandler)
+#pragma alloc_text(PAGE, VdmDispatchStringIoToHandler)
+#pragma alloc_text(PAGE, VdmCallStringIoHandler)
+#pragma alloc_text(PAGE, VdmConvertToLinearAddress)
+#pragma alloc_text(INIT, KeI386VdmInitialize)
+#endif
+
+KMUTEX VdmStringIoMutex;
+ULONG VdmFixedStateLinear;
+
+ULONG KeI386EFlagsAndMaskV86 = EFLAGS_USER_SANITIZE;
+ULONG KeI386EFlagsOrMaskV86 = EFLAGS_INTERRUPT_MASK;
+BOOLEAN KeI386VdmIoplAllowed = FALSE;
+ULONG KeI386VirtualIntExtensions = 0;
+
+
+BOOLEAN
+Ki386GetSelectorParameters(
+ IN USHORT Selector,
+ OUT PULONG Flags,
+ OUT PULONG Base,
+ OUT PULONG Limit
+ )
+
+/*++
+
+Routine Description:
+
+ This routine gets information about a selector in the ldt, and
+ returns it to the caller.
+
+Arguments:
+
+ IN USHORT Selector -- selector number for selector to return info for
+ OUT PULONG Flags -- flags indicating the type of the selector.
+ OUT PULONG Base -- base linear address of the selector
+ OUT PULONG Limit -- limit of the selector.
+
+Return Value:
+
+ return-value - True if the selector is in the LDT, and present.
+ False otherwise.
+Note:
+
+ This routine should probably be somewhere else. There are a number
+ of issues to clear up with respect to selectors and the kernel, and
+ after they have been cleared up, this code will be moved to its
+ correct place
+
+--*/
+
+{
+
+ PLDT_ENTRY Ldt,OldLdt;
+ ULONG LdtLimit,OldLdtLimit,RetryCount = 0;
+ PKPROCESS Process;
+ BOOLEAN ReturnValue;
+
+ *Flags = 0;
+
+ if ((Selector & (SELECTOR_TABLE_INDEX | DPL_USER))
+ != (SELECTOR_TABLE_INDEX | DPL_USER)) {
+ return FALSE;
+ }
+
+
+ Process = KeGetCurrentThread()->ApcState.Process;
+ Ldt = (PLDT_ENTRY)((Process->LdtDescriptor.BaseLow) |
+ (Process->LdtDescriptor.HighWord.Bytes.BaseMid << 16) |
+ (Process->LdtDescriptor.HighWord.Bytes.BaseHi << 24));
+
+ LdtLimit = ((Process->LdtDescriptor.LimitLow) |
+ (Process->LdtDescriptor.HighWord.Bits.LimitHi << 16));
+
+ Selector &= ~(SELECTOR_TABLE_INDEX | DPL_USER);
+
+ //
+ // Under normal circumstances, we will only execute the following loop
+ // once. If there is a bug in the user mode wow code however, the LDT
+ // may change while we execute the following code. We don't want to take
+ // the Ldt mutex, because that is expensive.
+ //
+
+ do {
+
+ RetryCount++;
+
+ if (((ULONG)Selector >= LdtLimit) || (!Ldt)) {
+ return FALSE;
+ }
+
+ try {
+
+ if (!Ldt[Selector/sizeof(LDT_ENTRY)].HighWord.Bits.Pres) {
+ *Flags = SEL_TYPE_NP;
+ ReturnValue = FALSE;
+ } else {
+
+ *Base = (Ldt[Selector/sizeof(LDT_ENTRY)].BaseLow |
+ (Ldt[Selector/sizeof(LDT_ENTRY)].HighWord.Bytes.BaseMid << 16) |
+ (Ldt[Selector/sizeof(LDT_ENTRY)].HighWord.Bytes.BaseHi << 24));
+
+ *Limit = (Ldt[Selector/sizeof(LDT_ENTRY)].LimitLow |
+ (Ldt[Selector/sizeof(LDT_ENTRY)].HighWord.Bits.LimitHi << 16));
+
+ *Flags = 0;
+
+ if ((Ldt[Selector/sizeof(LDT_ENTRY)].HighWord.Bits.Type & 0x18) == 0x18) {
+ *Flags |= SEL_TYPE_EXECUTE;
+
+ if (Ldt[Selector/sizeof(LDT_ENTRY)].HighWord.Bits.Type & 0x02) {
+ *Flags |= SEL_TYPE_READ;
+ }
+ } else {
+ *Flags |= SEL_TYPE_READ;
+ if (Ldt[Selector/sizeof(LDT_ENTRY)].HighWord.Bits.Type & 0x02) {
+ *Flags |= SEL_TYPE_WRITE;
+ }
+ if (Ldt[Selector/sizeof(LDT_ENTRY)].HighWord.Bits.Type & 0x04) {
+ *Flags |= SEL_TYPE_ED;
+ }
+ }
+
+ if (Ldt[Selector/sizeof(LDT_ENTRY)].HighWord.Bits.Default_Big) {
+ *Flags |= SEL_TYPE_BIG;
+ }
+
+ if (Ldt[Selector/sizeof(LDT_ENTRY)].HighWord.Bits.Granularity) {
+ *Flags |= SEL_TYPE_2GIG;
+ }
+ }
+ ReturnValue = TRUE;
+ } except(EXCEPTION_EXECUTE_HANDLER) {
+ // Don't do anything here. We took the fault because the
+ // Ldt moved. We will get an answer the next time around
+ }
+
+ //
+ // If we can't get an answer in 10 tries, we never will
+ //
+ if ((RetryCount > 10)) {
+ ReturnValue = FALSE;
+ }
+
+ if (ReturnValue == FALSE) {
+ break;
+ }
+
+ OldLdt = Ldt;
+ OldLdtLimit = LdtLimit;
+
+ Ldt = (PLDT_ENTRY)((Process->LdtDescriptor.BaseLow) |
+ (Process->LdtDescriptor.HighWord.Bytes.BaseMid << 16) |
+ (Process->LdtDescriptor.HighWord.Bytes.BaseHi << 24));
+
+ LdtLimit = ((Process->LdtDescriptor.LimitLow) |
+ (Process->LdtDescriptor.HighWord.Bits.LimitHi << 16));
+
+ } while ((Ldt != OldLdt) || (LdtLimit != OldLdtLimit));
+
+ return ReturnValue;
+}
+
+BOOLEAN
+Ki386VdmDispatchIo(
+ IN ULONG PortNumber,
+ IN ULONG Size,
+ IN BOOLEAN Read,
+ IN UCHAR InstructionSize,
+ IN PKTRAP_FRAME TrapFrame
+ )
+/*++
+
+Routine Description:
+
+ This routine sets up the Event info for an IO event, and causes the
+ event to be reflected to the Monitor.
+
+ It is assumed that interrupts are enabled upon entry, and Irql is
+ at APC level.
+
+Arguments:
+
+ PortNumber -- Supplies the port number the IO was done to
+ Size -- Supplies the size of the IO operation.
+ Read -- Indicates whether the IO operation was a read or a write.
+ InstructionSize -- Supplies the size of the IO instruction in bytes.
+
+Return Value:
+
+ True if the io instruction will be reflected to User mode.
+
+--*/
+{
+ PVDM_TIB VdmTib;
+ EXCEPTION_RECORD ExceptionRecord;
+ VDM_IO_HANDLER VdmIoHandler;
+ ULONG Result;
+ BOOLEAN Success = FALSE;
+ ULONG Context;
+
+ Success = Ps386GetVdmIoHandler(
+ PsGetCurrentProcess(),
+ PortNumber & ~0x3,
+ &VdmIoHandler,
+ &Context
+ );
+
+ if (Success) {
+ Result = TrapFrame->Eax;
+ // if port is not aligned, perform unaligned IO
+ // else do the io the easy way
+ if (PortNumber % Size) {
+ Success = VdmDispatchUnalignedIoToHandler(
+ &VdmIoHandler,
+ Context,
+ PortNumber,
+ Size,
+ Read,
+ &Result
+ );
+ } else {
+ Success = VdmDispatchIoToHandler(
+ &VdmIoHandler,
+ Context,
+ PortNumber,
+ Size,
+ Read,
+ &Result
+ );
+ }
+ }
+
+ if (Success) {
+ if (Read) {
+ switch (Size) {
+ case 4:
+ TrapFrame->Eax = Result;
+ break;
+ case 2:
+ *(PUSHORT)(&TrapFrame->Eax) = (USHORT)Result;
+ break;
+ case 1:
+ *(PUCHAR)(&TrapFrame->Eax) = (UCHAR)Result;
+ break;
+ }
+ }
+ TrapFrame->Eip += (ULONG) InstructionSize;
+ return TRUE;
+ } else {
+ try {
+ VdmTib = NtCurrentTeb()->Vdm;
+ VdmTib->EventInfo.InstructionSize = (ULONG) InstructionSize;
+ VdmTib->EventInfo.Event = VdmIO;
+ VdmTib->EventInfo.IoInfo.PortNumber = (USHORT)PortNumber;
+ VdmTib->EventInfo.IoInfo.Size = (USHORT)Size;
+ VdmTib->EventInfo.IoInfo.Read = Read;
+ } except(EXCEPTION_EXECUTE_HANDLER) {
+ ExceptionRecord.ExceptionCode = STATUS_ACCESS_VIOLATION;
+ ExceptionRecord.ExceptionFlags = 0;
+ ExceptionRecord.NumberParameters = 0;
+ ExRaiseException(&ExceptionRecord);
+ return FALSE;
+ }
+ }
+
+ VdmEndExecution(TrapFrame, VdmTib);
+
+ return TRUE;
+
+}
+
+
+BOOLEAN
+Ki386VdmDispatchStringIo(
+ IN ULONG PortNumber,
+ IN ULONG Size,
+ IN BOOLEAN Rep,
+ IN BOOLEAN Read,
+ IN ULONG Count,
+ IN ULONG Address,
+ IN UCHAR InstructionSize,
+ IN PKTRAP_FRAME TrapFrame
+ )
+/*++
+
+Routine Description:
+
+ This routine sets up the Event info for a string IO event, and causes the
+ event to be reflected to the Monitor.
+
+ It is assumed that interrupts are enabled upon entry, and Irql is
+ at APC level.
+
+Arguments:
+
+ PortNumber -- Supplies the port number the IO was done to
+ Size -- Supplies the size of the IO operation.
+ Read -- Indicates whether the IO operation was a read or a write.
+ Count -- indicates the number of IO operations of Size size
+ Address -- Indicates address for string io
+ InstructionSize -- Supplies the size of the IO instruction in bytes.
+
+
+Return Value:
+
+ True if the io instruction will be reflected to User mode.
+
+
+
+--*/
+{
+ PVDM_TIB VdmTib;
+ EXCEPTION_RECORD ExceptionRecord;
+ BOOLEAN Success = FALSE;
+ VDM_IO_HANDLER VdmIoHandler;
+ ULONG Context;
+
+ Success = Ps386GetVdmIoHandler(
+ PsGetCurrentProcess(),
+ PortNumber & ~0x3,
+ &VdmIoHandler,
+ &Context
+ );
+
+
+ if (Success) {
+ Success = VdmDispatchStringIoToHandler(
+ &VdmIoHandler,
+ Context,
+ PortNumber,
+ Size,
+ Count,
+ Read,
+ Address
+ );
+ }
+
+ if (Success) {
+ PUSHORT pIndexRegister;
+ USHORT Index;
+
+ // WARNING no 32 bit address support
+
+ pIndexRegister = Read ? (PUSHORT)&TrapFrame->Edi
+ : (PUSHORT)&TrapFrame->Esi;
+
+ if (TrapFrame->EFlags & EFLAGS_DF_MASK) {
+ Index = *pIndexRegister - (USHORT)(Count * Size);
+ }
+ else {
+ Index = *pIndexRegister + (USHORT)(Count * Size);
+ }
+
+ *pIndexRegister = Index;
+
+ if (Rep) {
+ (USHORT)TrapFrame->Ecx = 0;
+ }
+
+ TrapFrame->Eip += (ULONG) InstructionSize;
+ return TRUE;
+ }
+
+ try {
+ VdmTib = NtCurrentTeb()->Vdm;
+ VdmTib->EventInfo.InstructionSize = (ULONG) InstructionSize;
+ VdmTib->EventInfo.Event = VdmStringIO;
+ VdmTib->EventInfo.StringIoInfo.PortNumber = (USHORT)PortNumber;
+ VdmTib->EventInfo.StringIoInfo.Size = (USHORT)Size;
+ VdmTib->EventInfo.StringIoInfo.Rep = Rep;
+ VdmTib->EventInfo.StringIoInfo.Read = Read;
+ VdmTib->EventInfo.StringIoInfo.Count = Count;
+ VdmTib->EventInfo.StringIoInfo.Address = Address;
+ } except(EXCEPTION_EXECUTE_HANDLER) {
+ ExceptionRecord.ExceptionCode = STATUS_ACCESS_VIOLATION;
+ ExceptionRecord.ExceptionFlags = 0;
+ ExceptionRecord.NumberParameters = 0;
+ ExRaiseException(&ExceptionRecord);
+ return FALSE;
+ }
+
+
+ VdmEndExecution(TrapFrame, VdmTib);
+
+ return TRUE;
+}
+
+
+BOOLEAN
+VdmDispatchIoToHandler(
+ IN PVDM_IO_HANDLER VdmIoHandler,
+ IN ULONG Context,
+ IN ULONG PortNumber,
+ IN ULONG Size,
+ IN BOOLEAN Read,
+ IN OUT PULONG Data
+ )
+/*++
+
+Routine Description:
+
+ This routine calls the handler for the IO. If there is not a handler
+ of the proper size, it will call this function for 2 io's to the next
+ smaller size. If the size was a byte, and there was no handler, FALSE
+ is returned.
+
+Arguments:
+
+ VdmIoHandler -- Supplies a pointer to the handler table
+ Context -- Supplies 32 bits of data set when the port was trapped
+ PortNumber -- Supplies the port number the IO was done to
+ Size -- Supplies the size of the IO operation.
+ Read -- Indicates whether the IO operation was a read or a write.
+ Result -- Supplies a pointer to the location to put the result
+
+Return Value:
+
+ True if one or more handlers were called to take care of the IO.
+ False if no handler was called to take care of the IO.
+
+--*/
+{
+ NTSTATUS Status;
+ BOOLEAN Success1, Success2;
+ USHORT FnIndex;
+ UCHAR AccessType;
+
+ // Insure that Io is aligned
+ ASSERT((!(PortNumber % Size)));
+
+ if (Read) {
+ FnIndex = 0;
+ AccessType = EMULATOR_READ_ACCESS;
+ } else {
+ FnIndex = 1;
+ AccessType = EMULATOR_WRITE_ACCESS;
+ }
+
+ switch (Size) {
+ case 1:
+ if (VdmIoHandler->IoFunctions[FnIndex].UcharIo[PortNumber % 4]) {
+ Status = (*(VdmIoHandler->IoFunctions[FnIndex].UcharIo[PortNumber % 4]))(
+ Context,
+ PortNumber,
+ AccessType,
+ (PUCHAR)Data
+ );
+ if (NT_SUCCESS(Status)) {
+ return TRUE;
+ }
+ }
+ // No handler for this port
+ return FALSE;
+
+ case 2:
+ if (VdmIoHandler->IoFunctions[FnIndex].UshortIo[PortNumber % 2]) {
+ Status = (*(VdmIoHandler->IoFunctions[FnIndex].UshortIo[PortNumber % 2]))(
+ Context,
+ PortNumber,
+ AccessType,
+ (PUSHORT)Data
+ );
+ if (NT_SUCCESS(Status)) {
+ return TRUE;
+ }
+ } else {
+ // Dispatch to the two uchar handlers for this ushort port
+ Success1 = VdmDispatchIoToHandler(
+ VdmIoHandler,
+ Context,
+ PortNumber,
+ Size /2,
+ Read,
+ Data
+ );
+
+ Success2 = VdmDispatchIoToHandler(
+ VdmIoHandler,
+ Context,
+ PortNumber + 1,
+ Size / 2,
+ Read,
+ (PULONG)((PUCHAR)Data + 1)
+ );
+
+ return (Success1 || Success2);
+
+ }
+ return FALSE;
+
+ case 4:
+ if (VdmIoHandler->IoFunctions[FnIndex].UlongIo) {
+ Status = (*(VdmIoHandler->IoFunctions[FnIndex].UlongIo))(
+ Context,
+ PortNumber,
+ AccessType,
+ Data
+ );
+ if (NT_SUCCESS(Status)) {
+ return TRUE;
+ }
+ } else {
+ // Dispatch to the two ushort handlers for this port
+ Success1 = VdmDispatchIoToHandler(
+ VdmIoHandler,
+ Context,
+ PortNumber,
+ Size /2,
+ Read,
+ Data);
+ Success2 = VdmDispatchIoToHandler(
+ VdmIoHandler,
+ Context,
+ PortNumber + 2,
+ Size / 2,
+ Read,
+ (PULONG)((PUSHORT)Data + 1)
+ );
+
+ return (Success1 || Success2);
+ }
+ return FALSE;
+ }
+}
+
+BOOLEAN
+VdmDispatchUnalignedIoToHandler(
+ IN PVDM_IO_HANDLER VdmIoHandler,
+ IN ULONG Context,
+ IN ULONG PortNumber,
+ IN ULONG Size,
+ IN BOOLEAN Read,
+ IN OUT PULONG Data
+ )
+/*++
+
+Routine Description:
+
+ This routine converts the unaligned IO to the necessary number of aligned
+ IOs to smaller ports.
+
+Arguments:
+
+ VdmIoHandler -- Supplies a pointer to the handler table
+ Context -- Supplies 32 bits of data set when the port was trapped
+ PortNumber -- Supplies the port number the IO was done to
+ Size -- Supplies the size of the IO operation.
+ Read -- Indicates whether the IO operation was a read or a write.
+ Result -- Supplies a pointer to the location to put the result
+
+Return Value:
+
+ True if one or more handlers were called to take care of the IO.
+ False if no handler was called to take care of the IO.
+
+--*/
+{
+ ULONG Offset;
+ BOOLEAN Success;
+
+ ASSERT((Size > 1));
+ ASSERT((PortNumber % Size));
+
+ Offset = 0;
+
+ //
+ // The possible unaligned io situations are as follows.
+ //
+ // 1. Uchar aligned Ulong io
+ // We have to dispatch a uchar io, a ushort io, and a uchar io
+ //
+ // 2. Ushort aligned Ulong Io
+ // We have to dispatch a ushort io, and a ushort io
+ //
+ // 3. Uchar aligned Ushort Io
+ // We have to dispatch a uchar io and a uchar io
+ //
+
+ // if the port is uchar aligned
+ if ((PortNumber % Size) & 1) {
+ Success = VdmDispatchIoToHandler(
+ VdmIoHandler,
+ Context,
+ PortNumber,
+ 1,
+ Read,
+ Data
+ );
+ Offset += 1;
+ // else it is ushort aligned (and therefore must be a ulong port)
+ } else {
+ Success = VdmDispatchIoToHandler(
+ VdmIoHandler,
+ Context,
+ PortNumber,
+ 2,
+ Read,
+ Data
+ );
+ Offset += 2;
+ }
+
+ // if it is a ulong port, we know we have a ushort IO to dispatch
+ if (Size == 4) {
+ Success |= VdmDispatchIoToHandler(
+ VdmIoHandler,
+ Context,
+ PortNumber + Offset,
+ 2,
+ Read,
+ (PULONG)((PUCHAR)Data + Offset)
+ );
+ Offset += 2;
+ }
+
+ // If we haven't dispatched the entire port, dispatch the final uchar
+ if (Offset != 4) {
+ Success |= VdmDispatchIoToHandler(
+ VdmIoHandler,
+ Context,
+ PortNumber + Offset,
+ 1,
+ Read,
+ (PULONG)((PUCHAR)Data + Offset)
+ );
+ }
+
+ return Success;
+}
+
+BOOLEAN
+VdmDispatchStringIoToHandler(
+ IN PVDM_IO_HANDLER VdmIoHandler,
+ IN ULONG Context,
+ IN ULONG PortNumber,
+ IN ULONG Size,
+ IN ULONG Count,
+ IN BOOLEAN Read,
+ IN ULONG Data
+ )
+/*++
+
+Routine Description:
+
+ This routine calls the handler for the IO. If there is not a handler
+ of the proper size, or the io is not aligned, it will simulate the io
+ to the normal io handlers.
+
+Arguments:
+
+ VdmIoHandler -- Supplies a pointer to the handler table
+ Context -- Supplies 32 bits of data set when the port was trapped
+ PortNumber -- Supplies the port number the IO was done to
+ Size -- Supplies the size of the IO operation.
+ Count -- Supplies the number of IO operations.
+ Read -- Indicates whether the IO operation was a read or a write.
+ Data -- Supplies a segmented address at which to put the result.
+
+Return Value:
+
+ True if one or more handlers were called to take care of the IO.
+ False if no handler was called to take care of the IO.
+
+--*/
+{
+ BOOLEAN Success = FALSE;
+ USHORT FnIndex;
+ NTSTATUS Status;
+
+ if (Read) {
+ FnIndex = 0;
+ } else {
+ FnIndex = 1;
+ }
+
+ Status = KeWaitForSingleObject(
+ &VdmStringIoMutex,
+ Executive,
+ KernelMode,
+ FALSE,
+ NULL
+ );
+
+ if (!NT_SUCCESS(Status)) {
+ return FALSE;
+ }
+
+ switch (Size) {
+ case 1:
+ Success = VdmCallStringIoHandler(
+ VdmIoHandler,
+ (PVOID)VdmIoHandler->IoFunctions[FnIndex].UcharStringIo[PortNumber % 4],
+ Context,
+ PortNumber,
+ Size,
+ Count,
+ Read,
+ Data
+ );
+ case 2:
+ Success = VdmCallStringIoHandler(
+ VdmIoHandler,
+ (PVOID)VdmIoHandler->IoFunctions[FnIndex].UshortStringIo[PortNumber % 2],
+ Context,
+ PortNumber,
+ Size,
+ Count,
+ Read,
+ Data
+ );
+ case 4:
+ Success = VdmCallStringIoHandler(
+ VdmIoHandler,
+ (PVOID)VdmIoHandler->IoFunctions[FnIndex].UlongStringIo,
+ Context,
+ PortNumber,
+ Size,
+ Count,
+ Read,
+ Data
+ );
+ }
+ KeReleaseMutex(&VdmStringIoMutex, FALSE);
+ return Success;
+}
+
+#define STRINGIO_BUFFER_SIZE 1024
+UCHAR VdmStringIoBuffer[STRINGIO_BUFFER_SIZE];
+
+BOOLEAN
+VdmCallStringIoHandler(
+ IN PVDM_IO_HANDLER VdmIoHandler,
+ IN PVOID StringIoRoutine,
+ IN ULONG Context,
+ IN ULONG PortNumber,
+ IN ULONG Size,
+ IN ULONG Count,
+ IN BOOLEAN Read,
+ IN ULONG Data
+ )
+/*++
+
+Routine Description:
+
+ This routine actually performs the call to string io routine. It takes
+ care of buffering the user data in kernel space so that the device driver
+ does not have to. If there is not a string io function, or the io is
+ misaligned, it will be simulated as a series of normal io operations
+
+Arguments:
+
+ StringIoRoutine -- Supplies a pointer to the string Io routine
+ Context -- Supplies 32 bits of data set when the port was trapped
+ PortNumber -- Supplies the number of the port to perform Io to
+ Size -- Supplies the size of the io operations
+ Count -- Supplies the number of Io operations in the string.
+ Read -- Indicates a read operation
+ Data -- Supplies a pointer to the user buffer to perform the io on.
+
+Returns
+
+ TRUE if a handler was called
+ FALSE if not.
+
+--*/
+{
+ ULONG TotalBytes,BytesDone,BytesToDo,LoopCount,NumberIo;
+ PUCHAR CurrentDataPtr;
+ UCHAR AccessType;
+ EXCEPTION_RECORD ExceptionRecord;
+ NTSTATUS Status;
+ BOOLEAN Success;
+
+ Success = VdmConvertToLinearAddress(
+ Data,
+ &CurrentDataPtr
+ );
+
+ if (!Success) {
+ ExceptionRecord.ExceptionCode = STATUS_ACCESS_VIOLATION;
+ ExceptionRecord.ExceptionFlags = 0;
+ ExceptionRecord.NumberParameters = 0;
+ ExRaiseException(&ExceptionRecord);
+ // Cause kernel exit, rather than Io reflection
+ return TRUE;
+ }
+
+
+ TotalBytes = Count * Size;
+ BytesDone = 0;
+
+ if (PortNumber % Size) {
+ StringIoRoutine = NULL;
+ }
+
+ if (Read) {
+ AccessType = EMULATOR_READ_ACCESS;
+ } else {
+ AccessType = EMULATOR_WRITE_ACCESS;
+ }
+
+
+ // Set up try out here to avoid overhead in loop
+ try {
+ while (BytesDone < TotalBytes) {
+ if ((BytesDone + STRINGIO_BUFFER_SIZE) > TotalBytes) {
+ BytesToDo = TotalBytes - BytesDone;
+ } else {
+ BytesToDo = STRINGIO_BUFFER_SIZE;
+ }
+
+ ASSERT((!(BytesToDo % Size)));
+
+ if (!Read) {
+ RtlMoveMemory(VdmStringIoBuffer, CurrentDataPtr, BytesToDo);
+ }
+
+ NumberIo = BytesToDo / Size;
+
+ if (StringIoRoutine) {
+ // in order to avoid having 3 separate calls, one for each size
+ // we simply cast the parameters appropriately for the
+ // byte routine.
+
+ Status = (*((PDRIVER_IO_PORT_UCHAR_STRING)StringIoRoutine))(
+ Context,
+ PortNumber,
+ AccessType,
+ VdmStringIoBuffer,
+ NumberIo
+ );
+
+ if (NT_SUCCESS(Status)) {
+ Success |= TRUE;
+ }
+ } else {
+ if (PortNumber % Size) {
+ for (LoopCount = 0; LoopCount < NumberIo; LoopCount++ ) {
+ Success |= VdmDispatchUnalignedIoToHandler(
+ VdmIoHandler,
+ Context,
+ PortNumber,
+ Size,
+ Read,
+ (PULONG)(VdmStringIoBuffer + LoopCount * Size)
+ );
+ }
+ } else {
+ for (LoopCount = 0; LoopCount < NumberIo; LoopCount++ ) {
+ Success |= VdmDispatchIoToHandler(
+ VdmIoHandler,
+ Context,
+ PortNumber,
+ Size,
+ Read,
+ (PULONG)(VdmStringIoBuffer + LoopCount * Size)
+ );
+ }
+
+ }
+ }
+
+ if (Read) {
+ RtlMoveMemory(CurrentDataPtr, VdmStringIoBuffer, BytesToDo);
+ }
+
+ BytesDone += BytesToDo;
+ CurrentDataPtr += BytesToDo;
+ }
+ } except(EXCEPTION_EXECUTE_HANDLER) {
+ ExceptionRecord.ExceptionCode = GetExceptionCode();
+ ExceptionRecord.ExceptionFlags = 0;
+ ExceptionRecord.NumberParameters = 0;
+ ExRaiseException(&ExceptionRecord);
+ // Cause kernel exit, rather than Io reflection
+ Success = TRUE;
+ }
+ return Success;
+
+}
+
+BOOLEAN
+VdmConvertToLinearAddress(
+ IN ULONG SegmentedAddress,
+ OUT PVOID *LinearAddress
+ )
+/*++
+
+Routine Description:
+
+ This routine converts the specified segmented address into a linear
+ address, based on processor mode in user mode.
+
+Arguments:
+
+ SegmentedAddress -- Supplies the segmented address to convert.
+ LinearAddress -- Supplies a pointer to the destination for the
+ coresponding linear address
+
+Return Value:
+
+ True if the address was converted.
+ False otherwise
+
+Note:
+
+ A linear address of 0 is a valid return
+--*/
+{
+ PKTHREAD Thread;
+ PKTRAP_FRAME TrapFrame;
+ BOOLEAN Success;
+ ULONG Base, Limit, Flags;
+
+ Thread = KeGetCurrentThread();
+ TrapFrame = VdmGetTrapFrame(Thread);
+
+ if (TrapFrame->EFlags & EFLAGS_V86_MASK) {
+ *LinearAddress = (PVOID)(((SegmentedAddress & 0xFFFF0000) >> 12) +
+ (SegmentedAddress & 0xFFFF));
+ Success = TRUE;
+ } else {
+ Success = Ki386GetSelectorParameters(
+ (USHORT)((SegmentedAddress & 0xFFFF0000) >> 12),
+ &Flags,
+ &Base,
+ &Limit
+ );
+ if (Success) {
+ *LinearAddress = (PVOID)(Base + (SegmentedAddress & 0xFFFF));
+ }
+ }
+ return Success;
+}
+
+VOID
+KeI386VdmInitialize(
+ VOID
+ )
+/*++
+
+Routine Description:
+
+ This routine initializes the vdm stuff
+
+Arguments:
+
+ None
+
+Return Value:
+
+ None
+--*/
+{
+ NTSTATUS Status;
+ OBJECT_ATTRIBUTES ObjectAttributes;
+ HANDLE RegistryHandle = NULL;
+ UNICODE_STRING WorkString;
+ UCHAR KeyInformation[sizeof(KEY_VALUE_BASIC_INFORMATION) + 30];
+ ULONG ResultLength;
+
+ extern UCHAR V86CriticalInstruction;
+
+ KeInitializeMutex( &VdmStringIoMutex, MUTEX_LEVEL_VDM_IO );
+
+ //
+ // Set up and open KeyPath to wow key
+ //
+
+ RtlInitUnicodeString(
+ &WorkString,
+ L"\\REGISTRY\\MACHINE\\SYSTEM\\CurrentControlSet\\Control\\Wow"
+ );
+
+ InitializeObjectAttributes(
+ &ObjectAttributes,
+ &WorkString,
+ OBJ_CASE_INSENSITIVE,
+ (HANDLE)NULL,
+ NULL
+ );
+
+ Status = ZwOpenKey(
+ &RegistryHandle,
+ KEY_READ,
+ &ObjectAttributes
+ );
+
+ //
+ // If there is no Wow key, don't allow Vdms to run
+ //
+ if (!NT_SUCCESS(Status)) {
+ return;
+ }
+
+ //
+ // Set up for using virtual interrupt extensions if they are available
+ //
+
+ //
+ // Get the Pentium Feature disable value.
+ // If this value is present, don't enable vme stuff.
+ //
+ RtlInitUnicodeString(
+ &WorkString,
+ L"DisableVme"
+ );
+
+ Status = ZwQueryValueKey(
+ RegistryHandle,
+ &WorkString,
+ KeyValueBasicInformation,
+ &KeyInformation,
+ sizeof(KEY_VALUE_BASIC_INFORMATION) + 30,
+ &ResultLength
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ //
+ // If we have the extensions, set the appropriate bits
+ // in cr4
+ //
+ if (KeFeatureBits & KF_V86_VIS) {
+ KiIpiGenericCall(
+ Ki386VdmEnablePentiumExtentions,
+ TRUE
+ );
+ KeI386VirtualIntExtensions = V86_VIRTUAL_INT_EXTENSIONS;
+ }
+ }
+
+ //
+ // If we have V86 mode int extensions, we don't want to run with
+ // IOPL in v86 mode
+ //
+ if (!KeI386VirtualIntExtensions & V86_VIRTUAL_INT_EXTENSIONS) {
+ //
+ // Read registry to determine if Vdms will run with IOPL in v86 mode
+ //
+
+ //
+ // Get the VdmIOPL value.
+ //
+ RtlInitUnicodeString(
+ &WorkString,
+ L"VdmIOPL"
+ );
+
+ Status = ZwQueryValueKey(
+ RegistryHandle,
+ &WorkString,
+ KeyValueBasicInformation,
+ &KeyInformation,
+ sizeof(KEY_VALUE_BASIC_INFORMATION) + 30,
+ &ResultLength
+ );
+
+ //
+ // If the value exists, let Vdms run with IOPL in V86 mode
+ //
+ if (NT_SUCCESS(Status)) {
+ //
+ // KeEflagsAndMaskV86 and KeEflagsOrMaskV86 are used
+ // in SANITIZE_FLAGS, and the Vdm code to make sure the
+ // values in EFlags for v86 mode trap frames are acceptable
+ //
+ KeI386EFlagsAndMaskV86 = EFLAGS_USER_SANITIZE | EFLAGS_INTERRUPT_MASK;
+ KeI386EFlagsOrMaskV86 = EFLAGS_IOPL_MASK;
+
+ //
+ // KeVdmIoplAllowed is used by the Vdm code to determine if
+ // the virtual interrupt flag is in EFlags, or 40:xx
+ //
+ KeI386VdmIoplAllowed = TRUE;
+
+ }
+ }
+
+ ZwClose(RegistryHandle);
+
+ //
+ // Initialize the address of the Vdm communications area based on
+ // machine type because of non-AT Japanese PCs. Note that we only
+ // have to change the op-code for PC-98 machines as the default is
+ // the PC/AT value.
+ //
+
+ if (KeI386MachineType & MACHINE_TYPE_PC_9800_COMPATIBLE) {
+
+ //
+ // Set NTVDM state liner for PC-9800 Series
+ //
+
+ VdmFixedStateLinear = FIXED_NTVDMSTATE_LINEAR_PC_98;
+
+ *(PULONG)(&V86CriticalInstruction + 1) = VdmFixedStateLinear;
+
+ } else {
+
+ //
+ // We are running on an normal PC/AT or a Fujitsu FMR comaptible.
+ //
+
+ VdmFixedStateLinear = FIXED_NTVDMSTATE_LINEAR_PC_AT;
+ }
+}
+
+
+BOOLEAN
+Ke386VdmInsertQueueApc (
+ IN PKAPC Apc,
+ IN PKTHREAD Thread,
+ IN KPROCESSOR_MODE ApcMode,
+ IN PKKERNEL_ROUTINE KernelRoutine,
+ IN PKRUNDOWN_ROUTINE RundownRoutine OPTIONAL,
+ IN PKNORMAL_ROUTINE NormalRoutine OPTIONAL,
+ IN PVOID NormalContext OPTIONAL,
+ IN PVOID SystemArgument1 OPTIONAL,
+ IN PVOID SystemArgument2 OPTIONAL,
+ IN KPRIORITY Increment
+ )
+
+/*++
+
+Routine Description:
+
+ This function initializes, and queues a vdm type of APC to the
+ specified thread.
+
+
+ A Vdm type of APC:
+ - OriginalApcEnvironment
+ - will only be queued to one thread at a time
+ - if UserMode Fires on the next system exit. A UserMode apc should
+ not be queued if the current vdm context is not application mode.
+
+Arguments:
+
+ Apc - Supplies a pointer to a control object of type APC.
+
+ Thread - Supplies a pointer to a dispatcher object of type thread.
+
+ ApcMode - Supplies the processor mode user\kernel of the Apc
+
+ KernelRoutine - Supplies a pointer to a function that is to be
+ executed at IRQL APC_LEVEL in kernel mode.
+
+ RundownRoutine - Supplies an optional pointer to a function that is to be
+ called if the APC is in a thread's APC queue when the thread terminates.
+
+ NormalRoutine - Supplies an optional pointer to a function that is
+ to be executed at IRQL 0 in the specified processor mode. If this
+ parameter is not specified, then the ProcessorMode and NormalContext
+ parameters are ignored.
+
+ NormalContext - Supplies a pointer to an arbitrary data structure which is
+ to be passed to the function specified by the NormalRoutine parameter.
+
+ SystemArgument1, SystemArgument2 - Supply a set of two arguments that
+ contain untyped data provided by the executive.
+
+ Increment - Supplies the priority increment that is to be applied if
+ queuing the APC causes a thread wait to be satisfied.
+
+
+Return Value:
+
+ If APC queuing is disabled, then a value of FALSE is returned.
+ Otherwise a value of TRUE is returned.
+
+
+--*/
+
+{
+
+ PKAPC_STATE ApcState;
+ PKTHREAD ApcThread;
+ KIRQL OldIrql;
+ BOOLEAN Inserted;
+
+ //
+ // Raise IRQL to dispatcher level and lock dispatcher database.
+ //
+
+ KiLockDispatcherDatabase(&OldIrql);
+
+ //
+ // If the apc object not initialized, then initialize it and acquire
+ // the target thread APC queue lock.
+ //
+
+ if (Apc->Type != ApcObject) {
+ Apc->Type = ApcObject;
+ Apc->Size = sizeof(KAPC);
+ Apc->ApcStateIndex = OriginalApcEnvironment;
+ } else {
+
+ //
+ // Acquire the APC thread APC queue lock.
+ //
+ // If the APC is inserted in the corresponding APC queue, and the
+ // APC thread is not the same thread as the target thread, then
+ // the APC is removed from its current queue, the APC pending state
+ // is updated, the APC thread APC queue lock is released, and the
+ // target thread APC queue lock is acquired. Otherwise, the APC
+ // thread and the target thread are same thread and the APC is already
+ // queued to the correct thread.
+ //
+ // If the APC is not inserted in an APC queue, then release the
+ // APC thread APC queue lock and acquire the target thread APC queue
+ // lock.
+ //
+
+ ApcThread = Apc->Thread;
+ if (ApcThread) {
+ KiAcquireSpinLock(&ApcThread->ApcQueueLock);
+ if (Apc->Inserted) {
+ if (ApcThread == Apc->Thread && Apc->Thread != Thread) {
+ Apc->Inserted = FALSE;
+ RemoveEntryList(&Apc->ApcListEntry);
+ ApcState = Apc->Thread->ApcStatePointer[Apc->ApcStateIndex];
+ if (IsListEmpty(&ApcState->ApcListHead[Apc->ApcMode]) != FALSE) {
+ if (Apc->ApcMode == KernelMode) {
+ ApcState->KernelApcPending = FALSE;
+
+ } else {
+ ApcState->UserApcPending = FALSE;
+ }
+ }
+
+ } else {
+ KiReleaseSpinLock(&ApcThread->ApcQueueLock);
+ KiUnlockDispatcherDatabase(OldIrql);
+ return TRUE;
+ }
+ }
+
+ KiReleaseSpinLock(&ApcThread->ApcQueueLock);
+ }
+ }
+
+
+ KiAcquireSpinLock(&Thread->ApcQueueLock);
+
+ Apc->ApcMode = ApcMode;
+ Apc->Thread = Thread;
+ Apc->KernelRoutine = KernelRoutine;
+ Apc->RundownRoutine = RundownRoutine;
+ Apc->NormalRoutine = NormalRoutine;
+ Apc->SystemArgument1 = SystemArgument1;
+ Apc->SystemArgument2 = SystemArgument2;
+ Apc->NormalContext = NormalContext;
+
+ //
+ // Unlock the target thread APC queue.
+ //
+
+ KiReleaseSpinLock(&Thread->ApcQueueLock);
+
+ //
+ // If APC queuing is enable, then attempt to queue the APC object.
+ //
+
+ if (Thread->ApcQueueable && KiInsertQueueApc(Apc, Increment)) {
+ Inserted = TRUE;
+
+ //
+ // If UserMode:
+ // For vdm a UserMode Apc is only queued by a kernel mode
+ // apc which is on the current thread for the target thread.
+ // Force UserApcPending for User mode apcstate, so that
+ // the apc will fire when this thread exits the kernel.
+ //
+
+ if (ApcMode == UserMode) {
+ KiBoostPriorityThread(Thread, Increment);
+ Thread->ApcState.UserApcPending = TRUE;
+ }
+
+ } else {
+ Inserted = FALSE;
+ }
+
+ //
+ // Unlock the dispatcher database, lower IRQL to its previous value, and
+ // return whether the APC object was inserted.
+ //
+
+ KiUnlockDispatcherDatabase(OldIrql);
+ return Inserted;
+}
+
+
+VOID
+Ke386VdmClearApcObject(
+ IN PKAPC Apc
+ )
+/*++
+
+Routine Description:
+
+ Clears a VDM APC object, synchronously with Ke386VdmInsertQueueApc, and
+ is expected to be called by one of the vdm kernel apc routine or the
+ rundown routine.
+
+
+Arguments:
+
+ Apc - Supplies a pointer to a control object of type APC.
+
+
+Return Value:
+
+ void
+
+--*/
+{
+
+ KIRQL OldIrql;
+
+ //
+ // Take Dispatcher database lock, to sync with Ke386VDMInsertQueueApc
+ //
+
+ KiLockDispatcherDatabase(&OldIrql);
+ Apc->Thread = NULL;
+ KiUnlockDispatcherDatabase(OldIrql);
+
+}
+
+
+
+
+
+
+
+//
+// END of ACTIVE CODE
+//
+
+
+
+
+
+
+
+#if VDM_IO_TEST
+NTSTATUS
+TestIoByteRoutine(
+ IN ULONG Port,
+ IN UCHAR AccessMode,
+ IN OUT PUCHAR Data
+ )
+{
+ if (AccessMode & EMULATOR_READ_ACCESS) {
+ *Data = Port - 400;
+ }
+
+ return STATUS_SUCCESS;
+}
+
+NTSTATUS
+TestIoWordReadRoutine(
+ IN ULONG Port,
+ IN UCHAR AccessMode,
+ IN OUT PUSHORT Data
+ )
+{
+ if (AccessMode & EMULATOR_READ_ACCESS) {
+ *Data = Port - 200;
+ }
+
+ return STATUS_SUCCESS;
+}
+
+NTSTATUS
+TestIoWordWriteRoutine(
+ IN ULONG Port,
+ IN UCHAR AccessMode,
+ IN OUT PUSHORT Data
+ )
+{
+ DbgPrint("Word Write routine port # %lx, %x\n",Port,*Data);
+
+ return STATUS_SUCCESS;
+}
+
+NTSTATUS
+TestIoDwordRoutine(
+ IN ULONG Port,
+ IN USHORT AccessMode,
+ IN OUT PULONG Data
+ )
+{
+ if (AccessMode & EMULATOR_READ_ACCESS) {
+ *Data = Port;
+ }
+
+ return STATUS_SUCCESS;
+}
+
+NTSTATUS
+TestIoStringRoutine(
+ IN ULONG Port,
+ IN USHORT AccessMode,
+ IN OUT PSHORT Data,
+ IN ULONG Count
+ )
+{
+ ULONG i;
+
+ if (AccessMode & EMULATOR_READ_ACCESS) {
+ for (i = 0;i < Count ;i++ ) {
+ Data[i] = i;
+ }
+ } else {
+ DbgPrint("String Port Called for write port #%lx,",Port);
+ for (i = 0;i < Count ;i++ ) {
+ DbgPrint("%x\n",Data[i]);
+ }
+ }
+
+ return STATUS_SUCCESS;
+}
+
+PROCESS_IO_PORT_HANDLER_INFORMATION IoPortHandler;
+EMULATOR_ACCESS_ENTRY Entry[4];
+BOOLEAN Connect = TRUE, Disconnect = FALSE;
+
+VOID
+TestIoHandlerStuff(
+ VOID
+ )
+{
+ NTSTATUS Status;
+
+ IoPortHandler.Install = TRUE;
+ IoPortHandler.NumEntries = 5L;
+ IoPortHandler.EmulatorAccessEntries = Entry;
+
+ Entry[0].BasePort = 0x400;
+ Entry[0].NumConsecutivePorts = 0x30;
+ Entry[0].AccessType = Uchar;
+ Entry[0].AccessMode = EMULATOR_READ_ACCESS | EMULATOR_WRITE_ACCESS;
+ Entry[0].StringSupport = FALSE;
+ Entry[0].Routine = TestIoByteRoutine;
+
+ Entry[1].BasePort = 0x400;
+ Entry[1].NumConsecutivePorts = 0x18;
+ Entry[1].AccessType = Ushort;
+ Entry[1].AccessMode = EMULATOR_READ_ACCESS | EMULATOR_WRITE_ACCESS;
+ Entry[1].StringSupport = FALSE;
+ Entry[1].Routine = TestIoWordReadRoutine;
+
+ Entry[2].BasePort = 0x400;
+ Entry[2].NumConsecutivePorts = 0xc;
+ Entry[2].AccessType = Ulong;
+ Entry[2].AccessMode = EMULATOR_READ_ACCESS | EMULATOR_WRITE_ACCESS;
+ Entry[2].StringSupport = FALSE;
+ Entry[2].Routine = TestIoDwordRoutine;
+
+ Entry[3].BasePort = 0x400;
+ Entry[3].NumConsecutivePorts = 0x18;
+ Entry[3].AccessType = Ushort;
+ Entry[3].AccessMode = EMULATOR_READ_ACCESS | EMULATOR_WRITE_ACCESS;
+ Entry[3].StringSupport = TRUE;
+ Entry[3].Routine = TestIoStringRoutine;
+
+ if (Connect) {
+ Status = ZwSetInformationProcess(
+ NtCurrentProcess(),
+ ProcessIoPortHandlers,
+ &IoPortHandler,
+ sizeof(PROCESS_IO_PORT_HANDLER_INFORMATION)
+ ) ;
+ if (!NT_SUCCESS(Status)) {
+ DbgBreakPoint();
+ }
+ Connect = FALSE;
+ }
+
+ IoPortHandler.Install = FALSE;
+ if (Disconnect) {
+ Status = ZwSetInformationProcess(
+ NtCurrentProcess(),
+ ProcessIoPortHandlers,
+ &IoPortHandler,
+ sizeof(PROCESS_IO_PORT_HANDLER_INFORMATION)
+ );
+ if (!NT_SUCCESS(Status)) {
+ DbgBreakPoint();
+ }
+ Disconnect = FALSE;
+ }
+}
+#endif
+
diff --git a/private/ntos/ke/i386/vdmint21.c b/private/ntos/ke/i386/vdmint21.c
new file mode 100644
index 000000000..dbc32c356
--- /dev/null
+++ b/private/ntos/ke/i386/vdmint21.c
@@ -0,0 +1,228 @@
+/*++
+
+Copyright (c) 1991 Microsoft Corporation
+
+Module Name:
+
+ vdmint21.c
+
+Abstract:
+
+ This module implements interfaces that support manipulation of i386
+ int 21 entry of IDT. These entry points only exist on i386 machines.
+
+Author:
+
+ Shie-Lin Tzong (shielint) 26-Dec-1993
+
+Environment:
+
+ Kernel mode only.
+
+Revision History:
+
+--*/
+
+#include "ki.h"
+#pragma hdrstop
+#include "vdmntos.h"
+
+#define IDT_ACCESS_DPL_USER 0x6000
+#define IDT_ACCESS_TYPE_386_TRAP 0xF00
+#define IDT_ACCESS_TYPE_286_TRAP 0x700
+#define IDT_ACCESS_PRESENT 0x8000
+#define LDT_MASK 4
+
+//
+// External Reference
+//
+
+BOOLEAN
+Ki386GetSelectorParameters(
+ IN USHORT Selector,
+ OUT PULONG Flags,
+ OUT PULONG Base,
+ OUT PULONG Limit
+ );
+
+//
+// Define forward referenced function prototypes.
+//
+
+VOID
+Ki386LoadTargetInt21Entry (
+ IN PKIPI_CONTEXT SignalDone,
+ IN PVOID Parameter1,
+ IN PVOID Parameter2,
+ IN PVOID Parameter3
+ );
+
+#define KiLoadInt21Entry() \
+ KeGetPcr()->IDT[0x21] = PsGetCurrentProcess()->Pcb.Int21Descriptor
+
+NTSTATUS
+Ke386SetVdmInterruptHandler (
+ PKPROCESS Process,
+ ULONG Interrupt,
+ USHORT Selector,
+ ULONG Offset,
+ BOOLEAN Gate32
+ )
+
+/*++
+
+Routine Description:
+
+ The specified (software) interrupt entry of IDT will be updated to
+ point to the specified handler. For all threads which belong to the
+ specified process, their execution processors will be notified to
+ make the same change.
+
+ This function only exists on i386 and i386 compatible processors.
+
+ No checking is done on the validity of the interrupt handler.
+
+Arguments:
+
+ Process - Pointer to KPROCESS object describing the process for
+ which the int 21 entry is to be set.
+
+ Interrupt - The software interrupt vector which will be updated.
+
+ Selector, offset - Specified the address of the new handler.
+
+ Gate32 - True if the gate should be 32 bit, false otherwise
+
+Return Value:
+
+ NTSTATUS.
+
+--*/
+
+{
+
+ KIRQL OldIrql;
+ BOOLEAN LocalProcessor;
+ KAFFINITY TargetProcessors;
+ PKPRCB Prcb;
+ KIDTENTRY IdtDescriptor;
+ ULONG Flags, Base, Limit;
+
+ //
+ // Check the validity of the request
+ // 1. Currently, we support int21 redirection only
+ // 2. The specified interrupt handler must be in user space.
+ //
+
+ if (Interrupt != 0x21 || Offset >= (ULONG)MM_HIGHEST_USER_ADDRESS ||
+ !Ki386GetSelectorParameters(Selector, &Flags, &Base, &Limit) ){
+ return(STATUS_INVALID_PARAMETER);
+ }
+
+ //
+ // Initialize the contents of the IDT entry
+ //
+
+ IdtDescriptor.Offset = (USHORT)Offset;
+ IdtDescriptor.Selector = Selector | RPL_MASK | LDT_MASK;
+ IdtDescriptor.ExtendedOffset = (USHORT)(Offset >> 16);
+ IdtDescriptor.Access = IDT_ACCESS_DPL_USER | IDT_ACCESS_PRESENT;
+ if (Gate32) {
+ IdtDescriptor.Access |= IDT_ACCESS_TYPE_386_TRAP;
+
+ } else {
+ IdtDescriptor.Access |= IDT_ACCESS_TYPE_286_TRAP;
+ }
+
+ //
+ // Acquire the context swap lock so a context switch will not occur.
+ //
+
+ KiLockContextSwap(&OldIrql);
+
+ //
+ // Set the Ldt fields in the process object
+ //
+
+ Process->Int21Descriptor = IdtDescriptor;
+
+ //
+ // Tell all processors active for this process to reload their LDTs
+ //
+
+#if !defined(NT_UP)
+
+ Prcb = KeGetCurrentPrcb();
+ TargetProcessors = Process->ActiveProcessors & ~Prcb->SetMember;
+ if (TargetProcessors != 0) {
+ KiIpiSendPacket(TargetProcessors,
+ Ki386LoadTargetInt21Entry,
+ NULL,
+ NULL,
+ NULL);
+ }
+
+#endif
+
+ KiLoadInt21Entry();
+
+#if !defined(NT_UP)
+
+ //
+ // Wait until all of the target processors have finished reloading
+ // their LDT.
+ //
+
+ if (TargetProcessors != 0) {
+ KiIpiStallOnPacketTargets();
+ }
+
+#endif
+
+ //
+ // Restore IRQL and unlock the context swap lock.
+ //
+
+ KiUnlockContextSwap(OldIrql);
+ return STATUS_SUCCESS;
+}
+
+#if !defined(NT_UP)
+
+
+VOID
+Ki386LoadTargetInt21Entry (
+ IN PKIPI_CONTEXT PacketContext,
+ IN PVOID Parameter1,
+ IN PVOID Parameter2,
+ IN PVOID Parameter3
+ )
+/*++
+
+Routine Description:
+
+ Reload local Ldt register and clear signal bit in TargetProcessor mask
+
+Arguments:
+
+ Argument - pointer to a ipi packet structure.
+ ReadyFlag - Pointer to flag to be set once LDTR has been reloaded
+
+Return Value:
+
+ none.
+
+--*/
+
+{
+
+ //
+ // Set the int 21 entry of IDT from currently active process object
+ //
+
+ KiLoadInt21Entry();
+ KiIpiSignalPacketDone(PacketContext);
+ return;
+}
+
+#endif
diff --git a/private/ntos/ke/i386/vdmp.h b/private/ntos/ke/i386/vdmp.h
new file mode 100644
index 000000000..e9211422b
--- /dev/null
+++ b/private/ntos/ke/i386/vdmp.h
@@ -0,0 +1,73 @@
+#define VDM_APP_MODE 0x00000001L
+#define VDM_INTERRUPT_PENDING 0x00000002L
+#define VDM_STATE_CHANGE 0x00000004L
+#define VDM_VIRTUAL_INTERRUPTS 0x00000200L
+#define VDM_PE_MASK 0x80000000L
+
+typedef enum _VdmEventClass {
+ VdmIO,
+ VdmStringIO,
+ VdmMemAccess,
+ VdmIntAck,
+ VdmBop,
+ VdmError,
+ VdmIrq13
+} VDMEVENTCLASS, *PVDMEVENTCLASS;
+
+typedef struct _VdmIoInfo {
+ USHORT PortNumber;
+ USHORT Size;
+ BOOLEAN Read;
+} VDMIOINFO, *PVDMIOINFO;
+
+typedef struct _VdmStringIoInfo {
+ USHORT PortNumber;
+ USHORT Size;
+ BOOLEAN Read;
+ ULONG Count;
+ ULONG Address;
+} VDMSTRINGIOINFO, *PVDMSTRINGIOINFO;
+
+typedef ULONG VDMBOPINFO;
+typedef NTSTATUS VDMERRORINFO;
+
+typedef struct _VdmEventInfo {
+ ULONG Size;
+ VDMEVENTCLASS Event;
+ ULONG InstructionSize;
+ union {
+ VDMIOINFO IoInfo;
+ VDMSTRINGIOINFO StringIoInfo;
+ VDMBOPINFO BopNumber;
+ VDMERRORINFO ErrorStatus;
+ };
+} VDMEVENTINFO, *PVDMEVENTINFO;
+
+typedef struct _Vdm_InterruptHandler {
+ USHORT CsSelector;
+ ULONG Eip;
+ USHORT SsSelector;
+ ULONG Esp;
+} VDM_INTERRUPTHANDLER, *PVDM_INTERRUPTHANDLER;
+
+typedef struct _Vdm_Tib {
+ ULONG Size;
+ ULONG Flags;
+ VDM_INTERRUPTHANDLER VdmInterruptHandlers[255];
+ CONTEXT MonitorContext;
+ CONTEXT VdmContext;
+ VDMEVENTINFO EventInfo;
+} VDM_TIB, *PVDM_TIB;
+
+NTSTATUS
+NtStartVdmExecution(
+ );
+
+// Flags that don't belong here
+
+#define SEL_TYPE_READ 0x00000001
+#define SEL_TYPE_WRITE 0x00000002
+#define SEL_TYPE_EXECUTE 0x00000004
+#define SEL_TYPE_BIG 0x00000008
+#define SEL_TYPE_ED 0x00000010
+#define SEL_TYPE_2GIG 0x00000020