diff options
Diffstat (limited to 'private/ntos/ke/i386')
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 |