windows-nt/Source/XPSP1/NT/base/mvdm/vdmredir/vrinit.c

751 lines
17 KiB
C
Raw Permalink Normal View History

2020-09-26 03:20:57 -05:00
/*++
Copyright (c) 1991 Microsoft Corporation
Module Name:
vrinit.c
Abstract:
Contains Vdm Redir (Vr) 32-bit side initialization and uninitialization
routines
Contents:
VrInitialized
VrInitialize
VrUninitialize
VrRaiseInterrupt
VrDismissInterrupt
VrQueueCompletionHandler
VrHandleAsyncCompletion
VrCheckPmNetbiosAnr
VrEoiAndDismissInterrupt
VrSuspendHook
VrResumeHook
Author:
Richard L Firth (rfirth) 13-Sep-1991
Environment:
32-bit flat address space
Revision History:
13-Sep-1991 RFirth
Created
--*/
#include <nt.h>
#include <ntrtl.h> // ASSERT, DbgPrint
#include <nturtl.h>
#include <windows.h>
#include <softpc.h> // x86 virtual machine definitions
#include <vrdlctab.h>
#include <vdmredir.h> // common Vdm Redir stuff
#include <vrinit.h>
#include <nb30.h>
#include <netb.h>
#include <dlcapi.h>
#include <vrdefld.h>
#include "vrdlc.h"
#include "vrdebug.h"
#define BOOL // kludge for mips build
#include <insignia.h> // Required for ica.h
#include <xt.h> // Required for ica.h
#include <ica.h>
#include <vrica.h> // call_ica_hw_interrupt
//
// external functions
//
extern BOOL VDDInstallUserHook(HANDLE, FARPROC, FARPROC, FARPROC, FARPROC);
//
// prototypes
//
VOID
VrSuspendHook(
VOID
);
VOID
VrResumeHook(
VOID
);
//
// data
//
static BOOLEAN IsVrInitialized = FALSE; // set when TSR loaded
extern DWORD VrPeekNamedPipeTickCount;
extern CRITICAL_SECTION VrNmpRequestQueueCritSec;
extern CRITICAL_SECTION VrNamedPipeCancelCritSec;
//
// Async Event Disposition. The following critical sections, queue and counter
// plus the routines VrRaiseInterrupt, VrDismissInterrupt,
// VrQueueCompletionHandler and VrHandleAsyncCompletion comprise the async
// event disposition processing.
//
// We employ these to dispose of the asynchronous event completions in the order
// they occur. Also we keep calls to call_ica_hw_interrupt serialized: the reason
// for this is that the ICA is not guaranteed to generate an interrupt. Rather
// than blast the ICA with interrupt requests, we generate one only when we
// know we have completed the previous disposition
//
CRITICAL_SECTION AsyncEventQueueCritSec;
VR_ASYNC_DISPOSITION* AsyncEventQueueHead = NULL;
VR_ASYNC_DISPOSITION* AsyncEventQueueTail = NULL;
CRITICAL_SECTION QueuedInterruptCritSec;
LONG QueuedInterrupts = -1;
LONG FrozenInterrupts = 0;
//
// FrozenVdmContext - TRUE if the 16-bit context has been suspended. When this
// happens, we need to queue any hardware interrupt requests until the 16-bit
// context has been resumed
//
BOOLEAN FrozenVdmContext = FALSE;
//
// routines
//
BOOLEAN
VrInitialized(
VOID
)
/*++
Routine Description:
Returns whether the VdmRedir support has been initialized yet (ie redir.exe
TSR loaded in DOS emulation memory). Principally here because VdmRedir is
now a DLL loaded at run-time via LoadLibrary
Arguments:
None.
Return Value:
BOOLEAN
TRUE VdmRedir support is active
FALSE VdmRedir support inactive
--*/
{
return IsVrInitialized;
}
BOOLEAN
VrInitialize(
VOID
)
/*++
Routine Description:
Performs 32-bit side initialization when the redir TSR is loaded
Arguments:
None. ES:BX in VDM context is place to return computer name, CX is size
of buffer in VDM context for computer name
Return Value:
None.
--*/
{
LPBYTE lpVdmVrInitialized;
#if DBG
DIAGNOSTIC_INFO info;
VrDebugInit();
DIAGNOSTIC_ENTRY("VrInitialize", DG_NONE, &info);
#endif
//
// if we are already initialized return TRUE. Not sure if this should
// really happen?
//
if (IsVrInitialized) {
return TRUE;
}
//
// register our hooks
//
if (!VDDInstallUserHook(GetModuleHandle("VDMREDIR"),
(FARPROC)NULL, // 16-bit process create hook
(FARPROC)NULL, // 16-bit process terminate hook
(FARPROC)VrSuspendHook,
(FARPROC)VrResumeHook
)) {
return FALSE;
}
//
// do the rest of the initialization - none of this can fail
//
InitializeCriticalSection(&VrNmpRequestQueueCritSec);
InitializeCriticalSection(&AsyncEventQueueCritSec);
InitializeCriticalSection(&QueuedInterruptCritSec);
InitializeCriticalSection(&VrNamedPipeCancelCritSec);
VrNetbios5cInitialize();
VrDlcInitialize();
IsVrInitialized = TRUE;
//
// deferred loading: we need to let the VDM redir know that the 32-bit
// support is loaded. Set the VrInitialized flag in the VDM Redir at
// the known address
//
lpVdmVrInitialized = LPBYTE_FROM_WORDS(getCS(), (DWORD)(&(((VDM_LOAD_INFO*)0)->VrInitialized)));
*lpVdmVrInitialized = 1;
//
// VrPeekNamedPipe idle processing
//
VrPeekNamedPipeTickCount = GetTickCount();
setCF(0); // no carry == successful initialization
//
// inform 32-bit caller of successful initialization
//
return TRUE;
}
VOID
VrUninitialize(
VOID
)
/*++
Routine Description:
Performs 32-bit side uninitialization when the redir TSR is removed
Arguments:
None.
Return Value:
None.
--*/
{
IF_DEBUG(DLL) {
DPUT("VrUninitialize\n");
}
if (IsVrInitialized) {
DeleteCriticalSection(&VrNmpRequestQueueCritSec);
DeleteCriticalSection(&AsyncEventQueueCritSec);
DeleteCriticalSection(&QueuedInterruptCritSec);
DeleteCriticalSection(&VrNamedPipeCancelCritSec);
}
IsVrInitialized = FALSE;
setCF(0); // no carry == successful uninitialization
}
VOID
VrRaiseInterrupt(
VOID
)
/*++
Routine Description:
Generates a simulated hardware interrupt by calling the ICA routine. Access
to the ICA is serialized here: we maintain a count. If the count goes from
-1 to 0, we call the ICA function to generate the interrupt in the VDM. Any
other value just queues the interrupt by incrementing the counter. When the
corresponding VrDismissInterrupt call is made, a queued interrupt will be
generated. This stops us losing simulated h/w interrupts to the VDM
Arguments:
None.
Return Value:
None.
--*/
{
EnterCriticalSection(&QueuedInterruptCritSec);
++QueuedInterrupts;
if (QueuedInterrupts == 0) {
if (!FrozenVdmContext) {
IF_DEBUG(CRITICAL) {
CRITDUMP(("*** VrRaiseInterrupt: Interrupting VDM ***\n"));
}
IF_DEBUG(HW_INTERRUPTS) {
DBGPRINT("VrRaiseInterrupt: interrupting VDM\n");
}
call_ica_hw_interrupt(NETWORK_ICA, NETWORK_LINE, 1);
} else {
IF_DEBUG(HW_INTERRUPTS) {
DBGPRINT("*** VrRaiseInterrupt: VDM is Frozen, not interrupting ***\n");
}
}
}
IF_DEBUG(CRITICAL) {
CRITDUMP(("*** VrRaiseInterrupt (%d) ***\n", QueuedInterrupts));
}
IF_DEBUG(HW_INTERRUPTS) {
DBGPRINT("*** VrRaiseInterrupt (%d) ***\n", QueuedInterrupts);
}
LeaveCriticalSection(&QueuedInterruptCritSec);
}
VOID
VrDismissInterrupt(
VOID
)
/*++
Routine Description:
Companion routine to VrRaiseInterrupt: this function is called when the
async event which called VrRaiseInterrupt has been disposed. If other calls
to VrRaiseInterrupt have been made in the interim then QueuedInterrupts will
be >0. In this case we re-issue the call to call_ica_hw_interrupt() which
will generate an new simulated h/w interrupt in the VDM.
Note: This routine is called from the individual disposition routine, not
from the disposition dispatch routine (VrHandleAsyncCompletion)
Arguments:
None.
Return Value:
None.
--*/
{
EnterCriticalSection(&QueuedInterruptCritSec);
if (!FrozenVdmContext) {
--QueuedInterrupts;
if (QueuedInterrupts >= 0) {
IF_DEBUG(CRITICAL) {
CRITDUMP(("*** VrDismissInterrupt: interrupting VDM ***\n"));
}
IF_DEBUG(HW_INTERRUPTS) {
DBGPRINT("VrDismissInterrupt: interrupting VDM\n");
}
call_ica_hw_interrupt(NETWORK_ICA, NETWORK_LINE, 1);
}
} else {
IF_DEBUG(HW_INTERRUPTS) {
DBGPRINT("*** VrDismissInterrupt: VDM is Frozen??? ***\n");
}
}
IF_DEBUG(CRITICAL) {
CRITDUMP(("*** VrDismissInterrupt (%d) ***\n", QueuedInterrupts));
}
IF_DEBUG(HW_INTERRUPTS) {
DBGPRINT("*** VrDismissInterrupt (%d) ***\n", QueuedInterrupts);
}
LeaveCriticalSection(&QueuedInterruptCritSec);
}
VOID
VrQueueCompletionHandler(
IN VOID (*AsyncDispositionRoutine)(VOID)
)
/*++
Routine Description:
Adds an async event disposition packet to the queue of pending completions
(event waiting to be fully completed by the VDM async event ISR/BOP). We
keep these in a singly-linked queue so that we avoid giving priority to one
completion handler while polling
Arguments:
AsyncDispositionRoutine - address of routine which will dispose of the
async completion event
Return Value:
None.
--*/
{
VR_ASYNC_DISPOSITION* pDisposition;
pDisposition = (VR_ASYNC_DISPOSITION*)LocalAlloc(LMEM_FIXED,
sizeof(VR_ASYNC_DISPOSITION)
);
if (pDisposition == NULL) {
IF_DEBUG(CRITICAL) {
CRITDUMP(("*** VrQueueCompletionHandler: ERROR: Failed to alloc Q packet ***\n"));
}
IF_DEBUG(HW_INTERRUPTS) {
DBGPRINT("!!! VrQueueCompletionHandler: failed to allocate memory\n");
}
return;
}
EnterCriticalSection(&AsyncEventQueueCritSec);
pDisposition->Next = NULL;
pDisposition->AsyncDispositionRoutine = AsyncDispositionRoutine;
if (AsyncEventQueueHead == NULL) {
AsyncEventQueueHead = pDisposition;
} else {
AsyncEventQueueTail->Next = pDisposition;
}
AsyncEventQueueTail = pDisposition;
LeaveCriticalSection(&AsyncEventQueueCritSec);
IF_DEBUG(CRITICAL) {
CRITDUMP(("VrQueueCompletionHandler: Handler %08x queued @ %08x\n",
AsyncDispositionRoutine,
pDisposition
));
}
IF_DEBUG(HW_INTERRUPTS) {
DBGPRINT("VrQueueCompletionHandler: Handler %08x queued @ %08x\n",
AsyncDispositionRoutine,
pDisposition
);
}
}
VOID
VrHandleAsyncCompletion(
VOID
)
/*++
Routine Description:
Called by VrDispatch for the async completion event BOP. Dequeues the
disposition packet from the head of the queue and calls the disposition
routine
Arguments:
None.
Return Value:
None.
--*/
{
VR_ASYNC_DISPOSITION* pDisposition;
VOID (*AsyncDispositionRoutine)(VOID);
EnterCriticalSection(&AsyncEventQueueCritSec);
pDisposition = AsyncEventQueueHead;
AsyncDispositionRoutine = pDisposition->AsyncDispositionRoutine;
AsyncEventQueueHead = pDisposition->Next;
IF_DEBUG(CRITICAL) {
CRITDUMP(("VrHandleAsyncCompletion: Handler %08x dequeued @ %08x\n",
AsyncDispositionRoutine,
pDisposition
));
}
IF_DEBUG(HW_INTERRUPTS) {
DBGPRINT("VrHandleAsyncCompletion: freeing @ %08x && calling handler %08x\n",
pDisposition,
AsyncDispositionRoutine
);
}
LocalFree((HLOCAL)pDisposition);
LeaveCriticalSection(&AsyncEventQueueCritSec);
AsyncDispositionRoutine();
}
VOID
VrCheckPmNetbiosAnr(
VOID
)
/*++
Routine Description:
If the disposition routine queued at the head of the disposition list is
VrNetbios5cInterrupt, indicating that the next asynchronous event to be
completed is an async Netbios call, then set the 16-bit Zero Flag to TRUE
if the NCB originated in 16-bit protect mode
Assumes:
1. This function is called after the corresponding interrupt has been
delivered
2. There is something on the AsyncEventQueue
Arguments:
None.
Return Value:
None.
ZF in 16-bit context flags word:
TRUE - the next thing to complete is not an NCB, OR, it originated
in 16-bit real-mode
FALSE - the next event to be disposed of IS an async Netbios request,
the NCB for which originated in 16-bit protect mode
--*/
{
BOOLEAN result;
EnterCriticalSection(&AsyncEventQueueCritSec);
if (AsyncEventQueueHead->AsyncDispositionRoutine == VrNetbios5cInterrupt) {
result = IsPmNcbAtQueueHead();
} else {
result = FALSE;
}
IF_DEBUG(HW_INTERRUPTS) {
DBGPRINT("VrCheckPmNetbiosAnr: %s\n", result ? "TRUE" : "FALSE");
}
//
// set ZF: TRUE means event at head of list not PM NCB completion, or no
// NCB completion event on list
//
setZF(!result);
LeaveCriticalSection(&AsyncEventQueueCritSec);
}
VOID
VrEoiAndDismissInterrupt(
VOID
)
/*++
Routine Description:
Performs an EOI then calls VrDismissInterrupt which checks for pending
interrupt requests.
Called when we handle the simulated h/w interrupt entirely in protect mode
(original call was from a WOW app). In this case, the p-m interrupt handler
doesn't perform out a0,20; out 20,20 (non-specific EOI to PIC 0 & PIC 1),
but calls this handler which gets SoftPc to handle the EOIs to the virtual
PICs. This is quicker because we don't take any restricted-opcode faults
(the out instruction in real mode causes a GPF because the code doesn't
have enough privilege to execute I/O instructions. SoftPc takes a look and
sees that the code is trying to talk to the PIC. It then performs the
necessary mangling of the PIC state)
Arguments:
None.
Return Value:
None.
--*/
{
int line;
extern VOID SoftPcEoi(int, int*);
IF_DEBUG(HW_INTERRUPTS) {
DBGPRINT("VrEoiAndDismissInterrupt\n");
}
#ifndef NEC_98
line = -1;
SoftPcEoi(1, &line); // non-specific EOI to slave PIC
#endif
line = -1;
SoftPcEoi(0, &line); // non-specific EOI to master PIC
VrDismissInterrupt();
}
VOID
VrSuspendHook(
VOID
)
/*++
Routine Description:
This is the hook called by NTVDM.EXE for the VDD handle owned by VDMREDIR.DLL.
The hook is called when NTVDM is about to execute a 32-bit process and it
suspends the 16-bit context
Within the queued interrupt critical section we note that the 16-bit context
has been frozen and snapshot the outstanding interrupt request count
N.B. We don't expect that this function will be called again before an
intervening call to VrResumeHook
Arguments:
None.
Return Value:
None.
--*/
{
EnterCriticalSection(&QueuedInterruptCritSec);
FrozenVdmContext = TRUE;
FrozenInterrupts = QueuedInterrupts;
IF_DEBUG(HW_INTERRUPTS) {
DBGPRINT("VrSuspendHook - FrozenInterrupts = %d\n", FrozenInterrupts);
}
LeaveCriticalSection(&QueuedInterruptCritSec);
}
VOID
VrResumeHook(
VOID
)
/*++
Routine Description:
This hook is called when NTVDM resumes the 16-bit context after executing a
32-bit process
Within the queued interrupt critical section we note that the 16-bit context
has been resumed and we compare the current queued interrupt request count
with the snapshot value we took when the context was suspended. If during the
intervening period, interrupt requests have been made, we call
VrDismissInterrupt to generate the next interrupt
N.B. We don't expect that this function will be called again before an
intervening call to VrSuspendHook
Arguments:
None.
Return Value:
None.
--*/
{
EnterCriticalSection(&QueuedInterruptCritSec);
IF_DEBUG(HW_INTERRUPTS) {
DBGPRINT("VrResumeHook - FrozenInterrupts = %d QueuedInterrupts = %d\n",
FrozenInterrupts,
QueuedInterrupts
);
}
FrozenVdmContext = FALSE;
if (QueuedInterrupts > FrozenInterrupts) {
//
// interrupts were queued while the 16-bit context was suspended. If
// the QueuedInterrupts count was -1 when we took the snapshot then
// we must interrupt the VDM. The count has already been updated to
// account for the interrupt, but none was delivered. Do it here
//
// if (FrozenInterrupts == -1) {
IF_DEBUG(HW_INTERRUPTS) {
DBGPRINT("*** VrResumeHook: interrupting VDM ***\n");
}
call_ica_hw_interrupt(NETWORK_ICA, NETWORK_LINE, 1);
// }
}
LeaveCriticalSection(&QueuedInterruptCritSec);
}