1805 lines
46 KiB
C
1805 lines
46 KiB
C
/*++
|
|
|
|
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 segments (2/2/92)
|
|
|
|
Revision History:
|
|
|
|
20-Mar-1991 daveh
|
|
created
|
|
|
|
--*/
|
|
|
|
#include "ki.h"
|
|
#pragma hdrstop
|
|
#include "vdmntos.h"
|
|
#include "..\..\vdm\i386\vdmp.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
|
|
);
|
|
|
|
VOID
|
|
Ki386AdlibEmulation(
|
|
IN ULONG PortNumber,
|
|
IN ULONG Size,
|
|
IN BOOLEAN Read,
|
|
IN UCHAR InstructionSize,
|
|
IN PKTRAP_FRAME TrapFrame
|
|
);
|
|
|
|
VOID
|
|
Ki386AdlibDirectIo (
|
|
IN ULONG PortNumber,
|
|
IN ULONG Size,
|
|
IN BOOLEAN Read,
|
|
IN UCHAR InstructionSize,
|
|
IN PKTRAP_FRAME TrapFrame
|
|
);
|
|
|
|
#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(PAGE, Ki386AdlibEmulation)
|
|
#pragma alloc_text(PAGE, Ki386AdlibDirectIo)
|
|
#pragma alloc_text(INIT, KeI386VdmInitialize)
|
|
|
|
KMUTEX VdmStringIoMutex;
|
|
|
|
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 = TRUE;
|
|
|
|
*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;
|
|
}
|
|
}
|
|
} except(EXCEPTION_EXECUTE_HANDLER) {
|
|
Ldt = 0;
|
|
LdtLimit = 0;
|
|
}
|
|
|
|
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)) && (RetryCount <= 10));
|
|
|
|
//
|
|
// If we can't get an answer in 10 tries, we never will
|
|
//
|
|
if ((RetryCount > 10)) {
|
|
ReturnValue = FALSE;
|
|
}
|
|
|
|
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;
|
|
PVDM_PROCESS_OBJECTS pVdmObjects;
|
|
|
|
//
|
|
// First check if this port needs special handling
|
|
//
|
|
|
|
if (Size == 1) {
|
|
pVdmObjects = PsGetCurrentProcess()->VdmObjects;
|
|
if (pVdmObjects &&
|
|
(pVdmObjects->AdlibAction == ADLIB_DIRECT_IO ||
|
|
pVdmObjects->AdlibAction == ADLIB_KERNEL_EMULATION)) {
|
|
|
|
if ((PortNumber >= pVdmObjects->AdlibPhysPortStart &&
|
|
PortNumber <= pVdmObjects->AdlibPhysPortEnd) ||
|
|
(PortNumber >= pVdmObjects->AdlibVirtPortStart &&
|
|
PortNumber <= pVdmObjects->AdlibVirtPortEnd)) {
|
|
if (pVdmObjects->AdlibAction == ADLIB_DIRECT_IO) {
|
|
|
|
//
|
|
// Convert virtual ports to physical porrs otherwise
|
|
// we don't know where we write to.
|
|
//
|
|
|
|
if (PortNumber >= pVdmObjects->AdlibVirtPortStart &&
|
|
PortNumber <= pVdmObjects->AdlibVirtPortEnd) {
|
|
PortNumber = PortNumber - pVdmObjects->AdlibVirtPortStart +
|
|
pVdmObjects->AdlibPhysPortStart;
|
|
}
|
|
Ki386AdlibDirectIo (PortNumber,
|
|
Size,
|
|
Read,
|
|
InstructionSize,
|
|
TrapFrame);
|
|
} else {
|
|
Ki386AdlibEmulation(PortNumber,
|
|
Size,
|
|
Read,
|
|
InstructionSize,
|
|
TrapFrame);
|
|
}
|
|
TrapFrame->Eip += InstructionSize;
|
|
return TRUE;
|
|
}
|
|
}
|
|
}
|
|
|
|
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 {
|
|
if (!NT_SUCCESS (VdmpGetVdmTib(&VdmTib))) {
|
|
ExceptionRecord.ExceptionCode = STATUS_ACCESS_VIOLATION;
|
|
ExceptionRecord.ExceptionFlags = 0;
|
|
ExceptionRecord.NumberParameters = 0;
|
|
ExRaiseException(&ExceptionRecord);
|
|
return FALSE;
|
|
}
|
|
try {
|
|
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;
|
|
}
|
|
|
|
if (!NT_SUCCESS (VdmpGetVdmTib(&VdmTib))) {
|
|
ExceptionRecord.ExceptionCode = STATUS_ACCESS_VIOLATION;
|
|
ExceptionRecord.ExceptionFlags = 0;
|
|
ExceptionRecord.NumberParameters = 0;
|
|
ExRaiseException(&ExceptionRecord);
|
|
return FALSE;
|
|
}
|
|
|
|
try {
|
|
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;
|
|
}
|
|
|
|
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
|
|
);
|
|
break;
|
|
|
|
case 2:
|
|
Success = VdmCallStringIoHandler(
|
|
VdmIoHandler,
|
|
(PVOID)VdmIoHandler->IoFunctions[FnIndex].UshortStringIo[PortNumber % 2],
|
|
Context,
|
|
PortNumber,
|
|
Size,
|
|
Count,
|
|
Read,
|
|
Data
|
|
);
|
|
break;
|
|
|
|
case 4:
|
|
Success = VdmCallStringIoHandler(
|
|
VdmIoHandler,
|
|
(PVOID)VdmIoHandler->IoFunctions[FnIndex].UlongStringIo,
|
|
Context,
|
|
PortNumber,
|
|
Size,
|
|
Count,
|
|
Read,
|
|
Data
|
|
);
|
|
break;
|
|
|
|
}
|
|
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) {
|
|
RtlCopyMemory(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) {
|
|
RtlCopyMemory(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) >> 16),
|
|
&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;
|
|
|
|
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);
|
|
}
|
|
|
|
BOOLEAN
|
|
KeVdmInsertQueueApc (
|
|
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 KPRIORITY Increment
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function initializes and queues a vdm type of APC to the specified
|
|
target 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.
|
|
|
|
N.B. The delay interrupt lock must be held when this routine is called
|
|
to ensure that no other processor attempts to queue or requeue the
|
|
same APC.
|
|
|
|
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.
|
|
|
|
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;
|
|
KLOCK_QUEUE_HANDLE LockHandle;
|
|
BOOLEAN Inserted;
|
|
|
|
//
|
|
// 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, raise IRQL to SYNCH_LEVEL,
|
|
// and lock the dispatcher database.
|
|
//
|
|
// 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) {
|
|
KeAcquireInStackQueuedSpinLockRaiseToSynch(&ApcThread->ApcQueueLock,
|
|
&LockHandle);
|
|
|
|
KiLockDispatcherDatabaseAtSynchLevel();
|
|
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 {
|
|
KiUnlockDispatcherDatabaseFromSynchLevel();
|
|
KeReleaseInStackQueuedSpinLock(&LockHandle);
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
KiUnlockDispatcherDatabaseFromSynchLevel();
|
|
KeReleaseInStackQueuedSpinLock(&LockHandle);
|
|
}
|
|
}
|
|
|
|
Apc->ApcMode = ApcMode;
|
|
Apc->Thread = Thread;
|
|
Apc->KernelRoutine = KernelRoutine;
|
|
Apc->RundownRoutine = RundownRoutine;
|
|
Apc->NormalRoutine = NormalRoutine;
|
|
Apc->SystemArgument1 = NULL;
|
|
Apc->SystemArgument2 = NULL;
|
|
Apc->NormalContext = NormalContext;
|
|
|
|
//
|
|
// Raise IRQL to SYNCH_LEVEL, acquire the thread APC queue lock, and lock
|
|
// the dispatcher database.
|
|
//
|
|
|
|
KeAcquireInStackQueuedSpinLockRaiseToSynch(&Thread->ApcQueueLock, &LockHandle);
|
|
KiLockDispatcherDatabaseAtSynchLevel();
|
|
|
|
//
|
|
// 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 from SYNCH_LEVEL, unlock the thread APC
|
|
// queue lock and lower IRQL to its previous value, and return whether the
|
|
// APC object was inserted.
|
|
//
|
|
|
|
KiUnlockDispatcherDatabaseFromSynchLevel();
|
|
KeReleaseInStackQueuedSpinLock(&LockHandle);
|
|
return Inserted;
|
|
}
|
|
|
|
#define AD_MASK 0x04 // adlib register used to control opl2
|
|
|
|
VOID
|
|
Ki386AdlibEmulation(
|
|
IN ULONG PortNumber,
|
|
IN ULONG Size,
|
|
IN BOOLEAN Read,
|
|
IN UCHAR InstructionSize,
|
|
IN PKTRAP_FRAME TrapFrame
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine performs kernel mode adlib emulation.
|
|
|
|
Note, here we only do SB2.0 adlib emulation. That means the only IO ports
|
|
that we emulatated are 0x388, 0x389 and 0x2x8 and 0x2x9.
|
|
|
|
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:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
PVDM_PROCESS_OBJECTS pVdmObjects = PsGetCurrentProcess()->VdmObjects;
|
|
PUCHAR pData = (PUCHAR)&TrapFrame->Eax;
|
|
|
|
if (Read) {
|
|
|
|
//
|
|
// Must be read status
|
|
//
|
|
|
|
*pData = (UCHAR)pVdmObjects->AdlibStatus;
|
|
} else {
|
|
|
|
//
|
|
// Could be write adlib index register or write actual data
|
|
//
|
|
|
|
if ((PortNumber & 0xf) == 0x8) {
|
|
|
|
//
|
|
// It's adlib register select
|
|
//
|
|
|
|
pVdmObjects->AdlibIndexRegister = (USHORT)*pData;
|
|
|
|
} else {
|
|
|
|
//
|
|
// It's adlib data write. We don't actually write any data out.
|
|
// But we will emulate the status change.
|
|
//
|
|
|
|
UCHAR data = *pData;
|
|
|
|
if ((pVdmObjects->AdlibIndexRegister >= 0xB0 &&
|
|
pVdmObjects->AdlibIndexRegister <= 0xBD) ||
|
|
pVdmObjects->AdlibIndexRegister == AD_MASK) {
|
|
|
|
if (pVdmObjects->AdlibIndexRegister == AD_MASK) {
|
|
// Look for RST and starting timers
|
|
if (data & 0x80) {
|
|
pVdmObjects->AdlibStatus = 0x00; // reset both timers
|
|
}
|
|
}
|
|
|
|
//
|
|
// We ignore starting of timers if their interrupt
|
|
// flag is set because the timer status will have to
|
|
// be set again to make the status for this timer change
|
|
//
|
|
|
|
if ((data & 1) && !(pVdmObjects->AdlibStatus & 0x40)) {
|
|
|
|
//
|
|
// simulate immediate expiry of timer1
|
|
//
|
|
|
|
pVdmObjects->AdlibStatus |= 0xC0;
|
|
}
|
|
|
|
if ((data & 2) && !(pVdmObjects->AdlibStatus & 0x20)) {
|
|
|
|
//
|
|
// simulate immediate expiry of timer2
|
|
//
|
|
|
|
pVdmObjects->AdlibStatus |= 0xA0;
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
}
|
|
}
|
|
|
|
VOID
|
|
Ki386AdlibDirectIo (
|
|
IN ULONG PortNumber,
|
|
IN ULONG Size,
|
|
IN BOOLEAN Read,
|
|
IN UCHAR InstructionSize,
|
|
IN PKTRAP_FRAME TrapFrame
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine performs direct IO for user mode program.
|
|
|
|
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:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
PUCHAR pData = (PUCHAR)&TrapFrame->Eax;
|
|
|
|
if (Read) {
|
|
*pData = READ_PORT_UCHAR((PUCHAR)PortNumber);
|
|
} else {
|
|
WRITE_PORT_UCHAR((PUCHAR)PortNumber, *pData);
|
|
}
|
|
}
|
|
|
|
//
|
|
// 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
|