You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
521 lines
14 KiB
521 lines
14 KiB
/*++
|
|
|
|
Copyright (c) 1991 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
vdmentry.c
|
|
|
|
Abstract:
|
|
|
|
This function dispatches to the vdm services
|
|
|
|
Author:
|
|
|
|
Dave Hastings (daveh) 6-Apr-1992
|
|
|
|
Notes:
|
|
|
|
This module will be fleshed out when the great vdm code consolidation
|
|
occurs, sometime soon after the functionality is done.
|
|
|
|
Revision History:
|
|
|
|
24-Sep-1993 Jonle: reoptimize dispatcher to suit the number of services
|
|
add QueueInterrupt service
|
|
|
|
--*/
|
|
|
|
#include "vdmp.h"
|
|
#include <ntvdmp.h>
|
|
|
|
#define VDM_DEFAULT_PM_CLI_TIMEOUT 0xf
|
|
ULONG VdmpMaxPMCliTime;
|
|
|
|
BOOLEAN
|
|
VdmpIsVdmProcess(
|
|
VOID
|
|
);
|
|
|
|
NTSTATUS
|
|
VdmpQueryVdmProcess (
|
|
PVDM_QUERY_VDM_PROCESS_DATA VdmProcessData
|
|
);
|
|
|
|
#ifdef ALLOC_PRAGMA
|
|
#pragma alloc_text(PAGE, VdmpIsVdmProcess)
|
|
#pragma alloc_text(PAGE, VdmpQueryVdmProcess)
|
|
#pragma alloc_text(PAGE, NtVdmControl)
|
|
#endif
|
|
|
|
#if DBG
|
|
ULONG VdmInjectFailures;
|
|
#endif
|
|
|
|
BOOLEAN
|
|
VdmpIsVdmProcess(
|
|
VOID
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function verifies the caller is a VDM process.
|
|
|
|
Arguments:
|
|
|
|
None.
|
|
|
|
Return Value:
|
|
|
|
True if the caller is a VDM process.
|
|
False if not
|
|
--*/
|
|
|
|
{
|
|
PEPROCESS Process;
|
|
PVDM_TIB VdmTib;
|
|
NTSTATUS Status;
|
|
|
|
PAGED_CODE();
|
|
|
|
Process = PsGetCurrentProcess();
|
|
|
|
if (Process->VdmObjects == NULL) {
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// Make sure the current thread has valid vdmtib.
|
|
//
|
|
|
|
Status = VdmpGetVdmTib(&VdmTib);
|
|
if (!NT_SUCCESS(Status)) {
|
|
return(FALSE);
|
|
}
|
|
|
|
//
|
|
// More checking here ...
|
|
//
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
NTSTATUS
|
|
NtVdmControl(
|
|
IN VDMSERVICECLASS Service,
|
|
IN OUT PVOID ServiceData
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
386 specific routine which dispatches to the appropriate function
|
|
based on service number.
|
|
|
|
Arguments:
|
|
|
|
Service -- Specifies what service is to be performed
|
|
ServiceData -- Supplies a pointer to service specific data
|
|
|
|
Return Value:
|
|
|
|
if invalid service number: STATUS_INVALID_PARAMETER_1
|
|
else see individual services.
|
|
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS Status;
|
|
PVDM_PROCESS_OBJECTS pVdmObjects;
|
|
VDM_INITIALIZE_DATA CapturedVdmInitializeData;
|
|
PVDM_TIB VdmTib;
|
|
|
|
PAGED_CODE();
|
|
|
|
//
|
|
// Allow any process to call this API to check if a process handle specifies
|
|
// and NTVDM process.
|
|
//
|
|
if (Service == VdmQueryVdmProcess) {
|
|
return VdmpQueryVdmProcess((PVDM_QUERY_VDM_PROCESS_DATA)ServiceData);
|
|
}
|
|
|
|
//
|
|
// Make sure current process is VDMAllowed
|
|
//
|
|
|
|
if (!(PsGetCurrentProcess()->Flags & PS_PROCESS_FLAGS_VDM_ALLOWED)) {
|
|
return STATUS_ACCESS_DENIED;
|
|
}
|
|
|
|
//
|
|
// Make sure the caller is ntvdm. Except ...
|
|
// VdmInitialize - the vdm state is not fully initialized to
|
|
// perform the check
|
|
//
|
|
|
|
if ((Service != VdmInitialize) &&
|
|
(PsGetCurrentProcess()->VdmObjects == NULL)) {
|
|
|
|
return STATUS_ACCESS_DENIED;
|
|
}
|
|
|
|
//
|
|
// Some services required a valid VdmTib
|
|
//
|
|
|
|
Status = VdmpGetVdmTib(&VdmTib);
|
|
if (!NT_SUCCESS(Status)) {
|
|
VdmTib = NULL;
|
|
}
|
|
|
|
try {
|
|
|
|
//
|
|
// Dispatch in descending order of frequency
|
|
//
|
|
if (Service == VdmStartExecution && VdmTib) {
|
|
Status = VdmpStartExecution();
|
|
} else if (Service == VdmQueueInterrupt) {
|
|
Status = VdmpQueueInterrupt(ServiceData);
|
|
} else if (Service == VdmDelayInterrupt) {
|
|
Status = VdmpDelayInterrupt(ServiceData);
|
|
} else if (Service == VdmQueryDir && VdmTib) {
|
|
Status = VdmQueryDirectoryFile(ServiceData);
|
|
} else if (Service == VdmInitialize) {
|
|
VdmpMaxPMCliTime = VDM_DEFAULT_PM_CLI_TIMEOUT;
|
|
ProbeForRead(ServiceData, sizeof(VDM_INITIALIZE_DATA), 1);
|
|
RtlCopyMemory (&CapturedVdmInitializeData, ServiceData, sizeof (VDM_INITIALIZE_DATA));
|
|
Status = VdmpInitialize(&CapturedVdmInitializeData);
|
|
} else if (Service == VdmFeatures) {
|
|
//
|
|
// Verify that we were passed a valid user address
|
|
//
|
|
ProbeForWriteBoolean((PBOOLEAN)ServiceData);
|
|
|
|
//
|
|
// Return the appropriate feature bits to notify
|
|
// ntvdm which modes (if any) fast IF emulation is
|
|
// available for
|
|
//
|
|
|
|
*((PULONG)ServiceData) = KeI386VirtualIntExtensions &
|
|
~PM_VIRTUAL_INT_EXTENSIONS;
|
|
Status = STATUS_SUCCESS;
|
|
|
|
} else if (Service == VdmSetInt21Handler && VdmTib) {
|
|
ProbeForRead(ServiceData, sizeof(VDMSET_INT21_HANDLER_DATA), 1);
|
|
|
|
Status = Ke386SetVdmInterruptHandler(
|
|
KeGetCurrentThread()->ApcState.Process,
|
|
0x21L,
|
|
(USHORT)(((PVDMSET_INT21_HANDLER_DATA)ServiceData)->Selector),
|
|
((PVDMSET_INT21_HANDLER_DATA)ServiceData)->Offset,
|
|
((PVDMSET_INT21_HANDLER_DATA)ServiceData)->Gate32
|
|
);
|
|
|
|
} else if (Service == VdmPrinterDirectIoOpen && VdmTib) {
|
|
Status = VdmpPrinterDirectIoOpen(ServiceData);
|
|
} else if (Service == VdmPrinterDirectIoClose && VdmTib) {
|
|
Status = VdmpPrinterDirectIoClose(ServiceData);
|
|
} else if (Service == VdmPrinterInitialize && VdmTib) {
|
|
Status = VdmpPrinterInitialize(ServiceData);
|
|
} else if (Service == VdmSetLdtEntries && VdmTib) {
|
|
ProbeForRead(ServiceData, sizeof(VDMSET_LDT_ENTRIES_DATA), 1);
|
|
|
|
Status = PsSetLdtEntries(
|
|
((PVDMSET_LDT_ENTRIES_DATA)ServiceData)->Selector0,
|
|
((PVDMSET_LDT_ENTRIES_DATA)ServiceData)->Entry0Low,
|
|
((PVDMSET_LDT_ENTRIES_DATA)ServiceData)->Entry0Hi,
|
|
((PVDMSET_LDT_ENTRIES_DATA)ServiceData)->Selector1,
|
|
((PVDMSET_LDT_ENTRIES_DATA)ServiceData)->Entry1Low,
|
|
((PVDMSET_LDT_ENTRIES_DATA)ServiceData)->Entry1Hi
|
|
);
|
|
} else if (Service == VdmSetProcessLdtInfo && VdmTib) {
|
|
PPROCESS_LDT_INFORMATION ldtInfo;
|
|
ULONG length;
|
|
|
|
ProbeForRead(ServiceData, sizeof(VDMSET_PROCESS_LDT_INFO_DATA), 1);
|
|
|
|
ldtInfo = ((PVDMSET_PROCESS_LDT_INFO_DATA)ServiceData)->LdtInformation;
|
|
length = ((PVDMSET_PROCESS_LDT_INFO_DATA)ServiceData)->LdtInformationLength;
|
|
|
|
ProbeForRead(ldtInfo, length, 1);
|
|
Status = PsSetProcessLdtInfo(ldtInfo, length);
|
|
} else if (Service == VdmAdlibEmulation && VdmTib) {
|
|
//
|
|
// Ntvdm calls here to do adlib emulation under the following conditions:
|
|
// ADLIB_DIRECT_IO - only If a FM synth device is opened for exclusive access.
|
|
// ADLIB_KERNEL_EMULATION - otherwise.
|
|
// Note ADLIB_USER_EMULATION is defaulted. It is basically used by external
|
|
// ADLIB/SB vdds.
|
|
//
|
|
ProbeForRead(ServiceData, sizeof(VDM_ADLIB_DATA), 1);
|
|
pVdmObjects = PsGetCurrentProcess()->VdmObjects;
|
|
|
|
if (((PVDM_ADLIB_DATA)ServiceData)->Action == ADLIB_DIRECT_IO) {
|
|
Status = STATUS_ACCESS_DENIED;
|
|
} else {
|
|
pVdmObjects->AdlibAction = ((PVDM_ADLIB_DATA)ServiceData)->Action;
|
|
pVdmObjects->AdlibPhysPortStart = ((PVDM_ADLIB_DATA)ServiceData)->PhysicalPortStart;
|
|
pVdmObjects->AdlibPhysPortEnd = ((PVDM_ADLIB_DATA)ServiceData)->PhysicalPortEnd;
|
|
pVdmObjects->AdlibVirtPortStart = ((PVDM_ADLIB_DATA)ServiceData)->VirtualPortStart;
|
|
pVdmObjects->AdlibVirtPortEnd = ((PVDM_ADLIB_DATA)ServiceData)->VirtualPortEnd;
|
|
pVdmObjects->AdlibIndexRegister = 0;
|
|
pVdmObjects->AdlibStatus = 0x6; // OPL2 emulation
|
|
Status = STATUS_SUCCESS;
|
|
}
|
|
} else if (Service == VdmPMCliControl) {
|
|
pVdmObjects = PsGetCurrentProcess()->VdmObjects;
|
|
ProbeForRead(ServiceData, sizeof(VDM_PM_CLI_DATA), 1);
|
|
|
|
Status = STATUS_SUCCESS;
|
|
switch (((PVDM_PM_CLI_DATA)ServiceData)->Control) {
|
|
case PM_CLI_CONTROL_DISABLE:
|
|
pVdmObjects->VdmControl &= ~PM_CLI_CONTROL;
|
|
break;
|
|
case PM_CLI_CONTROL_ENABLE:
|
|
pVdmObjects->VdmControl |= PM_CLI_CONTROL;
|
|
if ((*FIXED_NTVDMSTATE_LINEAR_PC_AT & VDM_VIRTUAL_INTERRUPTS) == 0) {
|
|
VdmSetPMCliTimeStamp(TRUE);
|
|
}
|
|
break;
|
|
case PM_CLI_CONTROL_CHECK:
|
|
VdmCheckPMCliTimeStamp();
|
|
break;
|
|
case PM_CLI_CONTROL_SET:
|
|
VdmSetPMCliTimeStamp(FALSE);
|
|
break;
|
|
case PM_CLI_CONTROL_CLEAR:
|
|
VdmClearPMCliTimeStamp();
|
|
break;
|
|
default:
|
|
Status = STATUS_INVALID_PARAMETER_1;
|
|
}
|
|
} else {
|
|
Status = STATUS_INVALID_PARAMETER_1;
|
|
}
|
|
} except (EXCEPTION_EXECUTE_HANDLER) {
|
|
Status = GetExceptionCode();
|
|
}
|
|
#if DBG
|
|
if (PsGetCurrentProcess()->VdmObjects != NULL) {
|
|
if (VdmInjectFailures != 0) {
|
|
PS_SET_BITS (&PsGetCurrentProcess()->Flags,
|
|
PS_PROCESS_INJECT_INPAGE_ERRORS);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
ASSERT(KeGetCurrentIrql () == PASSIVE_LEVEL);
|
|
return Status;
|
|
|
|
}
|
|
|
|
VOID
|
|
VdmCheckPMCliTimeStamp (
|
|
VOID
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine checks if interrupts are disabled for too long by protected
|
|
mode apps. If ints are disabled for over predefined limit, they will be
|
|
reenabled such that ntvdm will be able to dispatch pending interrupts.
|
|
|
|
Note, V86 mode should NOT call this function.
|
|
|
|
Arguments:
|
|
|
|
None.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
|
|
--*/
|
|
{
|
|
PVDM_PROCESS_OBJECTS pVdmObjects;
|
|
PKPROCESS process = (PKPROCESS)PsGetCurrentProcess();
|
|
NTSTATUS status;
|
|
PVDM_TIB vdmTib;
|
|
|
|
pVdmObjects = ((PEPROCESS)process)->VdmObjects;
|
|
if (pVdmObjects->VdmControl & PM_CLI_CONTROL &&
|
|
pVdmObjects->PMCliTimeStamp != 0) {
|
|
if (((process->UserTime + 1)- pVdmObjects->PMCliTimeStamp) >= VdmpMaxPMCliTime) {
|
|
pVdmObjects->PMCliTimeStamp = 0;
|
|
try {
|
|
|
|
*FIXED_NTVDMSTATE_LINEAR_PC_AT |= VDM_VIRTUAL_INTERRUPTS;
|
|
status = VdmpGetVdmTib(&vdmTib);
|
|
if (NT_SUCCESS(status)) {
|
|
vdmTib->VdmContext.EFlags |= EFLAGS_INTERRUPT_MASK;
|
|
}
|
|
} except(EXCEPTION_EXECUTE_HANDLER) {
|
|
status = GetExceptionCode();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
VOID
|
|
VdmSetPMCliTimeStamp (
|
|
BOOLEAN Reset
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine checks if interrupts are disabled for too long by protected
|
|
mode apps. If ints are disabled for over predefined limit, they will be
|
|
reenabled such that ntvdm will be able to dispatch pending interrupts.
|
|
|
|
Note, V86 mode should NOT call this function.
|
|
|
|
Arguments:
|
|
|
|
Reset - a Bool value to indicate should we re-set the count if it is not zero
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
|
|
--*/
|
|
{
|
|
PVDM_PROCESS_OBJECTS pVdmObjects;
|
|
PKPROCESS process = (PKPROCESS)PsGetCurrentProcess();
|
|
|
|
pVdmObjects = ((PEPROCESS)process)->VdmObjects;
|
|
if (pVdmObjects->VdmControl & PM_CLI_CONTROL) {
|
|
if (Reset || pVdmObjects->PMCliTimeStamp == 0) {
|
|
pVdmObjects->PMCliTimeStamp = process->UserTime + 1;
|
|
}
|
|
}
|
|
}
|
|
|
|
VOID
|
|
VdmClearPMCliTimeStamp (
|
|
VOID
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine checks if interrupts are disabled for too long by protected
|
|
mode apps. If ints are disabled for over predefined limit, they will be
|
|
reenabled such that ntvdm will be able to dispatch pending interrupts.
|
|
|
|
Note, V86 mode should NOT call this function.
|
|
|
|
Arguments:
|
|
|
|
None.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
|
|
--*/
|
|
{
|
|
PVDM_PROCESS_OBJECTS pVdmObjects;
|
|
|
|
pVdmObjects = PsGetCurrentProcess()->VdmObjects;
|
|
if (pVdmObjects->VdmControl & PM_CLI_CONTROL) {
|
|
pVdmObjects->PMCliTimeStamp = 0;
|
|
}
|
|
}
|
|
|
|
NTSTATUS
|
|
VdmpQueryVdmProcess (
|
|
PVDM_QUERY_VDM_PROCESS_DATA QueryVdmProcessData
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine checks if the process handle specifies a ntvdm process.
|
|
If the specified process has VDM_ALLOW bit set and it has VdmObject kernel
|
|
mode structure allocated, then it is a VDM process.
|
|
|
|
Arguments:
|
|
|
|
QueryVdmProcessData - supplies a pointer to a user mode VDM_QUERY_VDM_PROCESS_DATA
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS
|
|
The QueryVdmProcessData is filled in only when return status is STATUS_SUCCESS
|
|
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS st = STATUS_SUCCESS;
|
|
HANDLE processHandle;
|
|
PEPROCESS process;
|
|
BOOLEAN flag;
|
|
KPROCESSOR_MODE PreviousMode;
|
|
|
|
PreviousMode = KeGetPreviousMode();
|
|
if (PreviousMode != KernelMode) {
|
|
try {
|
|
|
|
//
|
|
// Verify that we were passed a valid user address
|
|
//
|
|
ProbeForRead(&(QueryVdmProcessData->ProcessHandle), sizeof(HANDLE), sizeof(UCHAR));
|
|
|
|
processHandle = QueryVdmProcessData->ProcessHandle;
|
|
|
|
} except (EXCEPTION_EXECUTE_HANDLER) {
|
|
return GetExceptionCode();
|
|
}
|
|
} else {
|
|
processHandle = QueryVdmProcessData->ProcessHandle;
|
|
}
|
|
st = ObReferenceObjectByHandle(
|
|
processHandle,
|
|
PROCESS_QUERY_INFORMATION,
|
|
PsProcessType,
|
|
KeGetPreviousMode(),
|
|
(PVOID *)&process,
|
|
NULL
|
|
);
|
|
|
|
if ( !NT_SUCCESS(st) ) {
|
|
return st;
|
|
}
|
|
|
|
if (process->Flags & PS_PROCESS_FLAGS_VDM_ALLOWED && process->VdmObjects) {
|
|
flag = TRUE;
|
|
} else {
|
|
flag = FALSE;
|
|
}
|
|
|
|
ObDereferenceObject(process);
|
|
|
|
try {
|
|
|
|
//
|
|
// Verify the user address is writable.
|
|
//
|
|
|
|
ProbeForWrite(&(QueryVdmProcessData->IsVdmProcess), sizeof(BOOLEAN), sizeof(UCHAR));
|
|
QueryVdmProcessData->IsVdmProcess = flag;
|
|
|
|
} except (EXCEPTION_EXECUTE_HANDLER) {
|
|
st = GetExceptionCode();
|
|
}
|
|
|
|
return st;
|
|
}
|