/*++

Copyright (c) 1990  Microsoft Corporation

Module Name:

    psldt.c

Abstract:

    This module contains code for the io port handler support

Author:

    Dave Hastings (daveh) 26 Jan 1991

Revision History:

--*/

#include "psp.h"


#if DBG
#define ASSERTEQUAL(value1, value2, string)     \
        if ((ULONG)value1 != (ULONG)value2) {   \
            DbgPrint string ;                   \
        }

#define ASSERTEQUALBREAK(value1, value2, string)\
        if ((ULONG)value1 != (ULONG)value2) {   \
            DbgPrint string ;                   \
            DbgBreakPoint();                    \
        }
#else

#define ASSERTEQUAL(value1, value2, string)
#define ASSERTEQUALBREAK(value1, value2, string)

#endif


//
// Internal functions
//

NTSTATUS
Psp386InstallIoHandler(
    IN PEPROCESS Process,
    IN PEMULATOR_ACCESS_ENTRY EmulatorAccessEntry,
    IN ULONG PortNumber,
    IN ULONG Context
    );

NTSTATUS
Psp386RemoveIoHandler(
    IN PEPROCESS Process,
    IN PEMULATOR_ACCESS_ENTRY EmulatorAccessEntry,
    IN ULONG PortNumber
    );

NTSTATUS
Psp386InsertVdmIoHandlerBlock(
    IN PEPROCESS Process,
    IN PVDM_IO_HANDLER VdmIoHandler
    );

PVDM_IO_HANDLER
Psp386GetVdmIoHandler(
    IN PEPROCESS Process,
    IN ULONG PortNumber
    );

NTSTATUS
Psp386CreateVdmIoListHead(
    IN PEPROCESS Process
    );


#ifdef ALLOC_PRAGMA
#pragma alloc_text(INIT,PspVdmInitialize)
#pragma alloc_text(PAGE,PspSetProcessIoHandlers)
#pragma alloc_text(PAGE,Ps386GetVdmIoHandler)
#pragma alloc_text(PAGE,Psp386RemoveIoHandler)
#pragma alloc_text(PAGE,Psp386InstallIoHandler)
#pragma alloc_text(PAGE,Psp386CreateVdmIoListHead)
#pragma alloc_text(PAGE,Psp386InsertVdmIoHandlerBlock)
#pragma alloc_text(PAGE,Psp386GetVdmIoHandler)
#pragma alloc_text(PAGE,PspDeleteVdmObjects)
#endif


//
//  Resource to synchronize access to IoHandler list
//
ERESOURCE VdmIoListCreationResource;




NTSTATUS
PspSetProcessIoHandlers(
    IN PEPROCESS Process,
    IN PVOID IoHandlerInformation,
    IN ULONG IoHandlerLength
    )
/*++

Routine Description:

    This routine installs a device driver IO handling routine for the
    specified process.  If an io operation is detected in a vdm on a port
    that has a device driver IO handling routine, that routine will be called.

Arguments:

    Process -- Supplies a pointer to the process for which Io port handlers
        are to be installed
    IoHandlerInformation -- Supplies a pointer to the information about the
        io port handlers
    IoHandlerLength -- Supplies the length of the IoHandlerInformation
        structure.

Return Value:



--*/
{
    PPROCESS_IO_PORT_HANDLER_INFORMATION IoHandlerInfo;
    NTSTATUS Status;
    PEMULATOR_ACCESS_ENTRY EmulatorAccess;
    ULONG EmulatorEntryNumber, NumberPorts;
    ULONG PortSize;
    PAGED_CODE();

    //
    // Insure that this call was made from KernelMode
    //
    if (KeGetPreviousMode() != KernelMode) {
        return STATUS_INVALID_PARAMETER;    // this info type invalid in usermode
    }
    //
    // Insure that the data passed is long enough
    //
    if (IoHandlerLength < (ULONG)sizeof( PROCESS_IO_PORT_HANDLER_INFORMATION)) {
        return STATUS_INFO_LENGTH_MISMATCH;
    }
    IoHandlerInfo = IoHandlerInformation;

    //
    // For each of the entries in the array that describes the handlers,
    // determine what size of port the specified handler is being installed
    // for, and then iterate over each individual port.
    //
    for (EmulatorEntryNumber = 0, EmulatorAccess =
            IoHandlerInfo->EmulatorAccessEntries;
        EmulatorEntryNumber < IoHandlerInfo->NumEntries;
        EmulatorEntryNumber++, EmulatorAccess++) {

            switch (EmulatorAccess->AccessType) {
            case Uchar:
                PortSize = 1;
                break;
            case Ushort:
                PortSize = 2;
                break;
            case Ulong:
            default:
                PortSize = 4;
            }

            for (NumberPorts = 0;
                NumberPorts < EmulatorAccess->NumConsecutivePorts;
                NumberPorts++) {
                    if (IoHandlerInfo->Install) {
                        Status = Psp386InstallIoHandler(
                            Process,
                            EmulatorAccess,
                            EmulatorAccess->BasePort + NumberPorts * PortSize,
                            IoHandlerInfo->Context
                            );
                        if (NT_SUCCESS(Status)) {
                        }
                    } else {
                        Status = Psp386RemoveIoHandler(
                            Process,
                            EmulatorAccess,
                            EmulatorAccess->BasePort + NumberPorts * PortSize
                            );
                    }
                    if (!NT_SUCCESS(Status)) {
                        goto exitloop;
                    }
            }
    }
    Status = STATUS_SUCCESS;
exitloop:
    return Status;

}


VOID
PspDeleteVdmObjects(
    IN PEPROCESS Process
    )
/*++

Routine Description:

    Frees the VdmObjects structure and the Frees the IoHandler list

Arguments:

    Process -- Supplies a pointer to the process

Return Value:

    None
--*/
{
    SIZE_T PoolQuota;
    PVDM_PROCESS_OBJECTS pVdmObjects;
    PVDM_IO_HANDLER p1, p2;
    PVDM_IO_LISTHEAD p3;
    PLIST_ENTRY  Next;
    PDELAYINTIRQ pDelayIntIrq;

    pVdmObjects = Process->VdmObjects;

    if (pVdmObjects == NULL)  {
        return;
    }

    //
    // First Free any port handler entries for this process,
    //
    p1 = NULL;
    p3 = pVdmObjects->VdmIoListHead;

    if (p3) {
        p2 = p3->VdmIoHandlerList;

        while (p2) {
            p1 = p2;
            p2 = p1->Next;
            ExFreePool( p1 );
        }

        ExDeleteResourceLite(&p3->VdmIoResource);

        ExFreePool( p3 );
        pVdmObjects->VdmIoListHead = NULL;
    }

    if (pVdmObjects->pIcaUserData) {
        PsReturnProcessPagedPoolQuota (Process,
                                       sizeof(VDMICAUSERDATA));

        ExFreePool(pVdmObjects->pIcaUserData);
    }

    //
    // Free up the DelayedIntList, spinlock protection is not needed because
    // object referencing on the process is being used instead.  Meaning there
    // can be no outstanding timers because the process object reference
    // count would have to be nonzero.
    //

    PoolQuota = 0;

    Next = pVdmObjects->DelayIntListHead.Flink;

    while (Next != &pVdmObjects->DelayIntListHead) {
        pDelayIntIrq = CONTAINING_RECORD(Next, DELAYINTIRQ, DelayIntListEntry);
        Next = Next->Flink;
        RemoveEntryList (&pDelayIntIrq->DelayIntListEntry);
        ExFreePool (pDelayIntIrq);
        PoolQuota += sizeof(DELAYINTIRQ);
    }

    if (PoolQuota != 0) {
        PsReturnProcessNonPagedPoolQuota(Process, PoolQuota);
    }

    PsReturnProcessNonPagedPoolQuota (Process, sizeof(VDM_PROCESS_OBJECTS));

    ExFreePool (pVdmObjects);

    Process->VdmObjects = NULL;
}



NTSTATUS
Psp386RemoveIoHandler(
    IN PEPROCESS Process,
    IN PEMULATOR_ACCESS_ENTRY EmulatorAccessEntry,
    IN ULONG PortNumber
    )
/*++

Routine Description:

    This routine remove a handler for a port.  On debug version, it will
    print a message if there is no handler.

Arguments:

    Process -- Supplies a pointer to the process
    EmulatorAccess -- Supplies a pointer to the information about the
        io port handler
    PortNumber -- Supplies the port number to remove the handler from.

Return Value:

--*/
{
    PVDM_PROCESS_OBJECTS pVdmObjects = Process->VdmObjects;
    PVDM_IO_HANDLER VdmIoHandler;
    KIRQL OldIrql;
    PAGED_CODE();

    //
    // Ensure we have a vdm process which is initialized
    // correctly for VdmIoHandlers
    //
    if (!pVdmObjects) {
#if DBG
        DbgPrint("Psp386RemoveIoHandler: uninitialized VdmObjects\n");
#endif
        return STATUS_UNSUCCESSFUL;
    }


    //
    // If the list does not have a head, then there are no handlers to
    // remove.
    //
    if (!pVdmObjects->VdmIoListHead) {
#if DBG
        DbgPrint("Psp386RemoveIoHandler : attempt to remove non-existent hdlr\n");
#endif
        return STATUS_SUCCESS;
    }

    //
    // Lock the list, so we can insure a correct update.
    //
    KeRaiseIrql(APC_LEVEL, &OldIrql);
    ExAcquireResourceExclusiveLite(&pVdmObjects->VdmIoListHead->VdmIoResource,TRUE);

    VdmIoHandler = Psp386GetVdmIoHandler(
        Process,
        PortNumber & ~0x3
        );

    if (!VdmIoHandler) {
#if DBG
        DbgPrint("Psp386RemoveIoHandler : attempt to remove non-existent hdlr\n");
#endif
        ExReleaseResourceLite(&pVdmObjects->VdmIoListHead->VdmIoResource);
        KeLowerIrql(OldIrql);
        return STATUS_SUCCESS;
    }

    ASSERTEQUALBREAK(
        VdmIoHandler->PortNumber,
        (PortNumber & ~0x3),
        ("Psp386RemoveIoHandler : Bad pointer returned from GetVdmIoHandler\n")
        );

    if (EmulatorAccessEntry->AccessMode & EMULATOR_READ_ACCESS) {
        switch (EmulatorAccessEntry->AccessType) {
        case Uchar:
            if (EmulatorAccessEntry->StringSupport) {
                ASSERTEQUAL(
                    EmulatorAccessEntry->Routine,
                    VdmIoHandler->IoFunctions[0].UcharStringIo[PortNumber % 4],
                    ("Psp386RemoveIoHandler : UcharString fns don't match\n")
                    );
                VdmIoHandler->IoFunctions[0].UcharStringIo[PortNumber % 4] = NULL;
            } else {
                ASSERTEQUAL(
                    EmulatorAccessEntry->Routine,
                    VdmIoHandler->IoFunctions[0].UcharIo[PortNumber % 4],
                    ("Psp386RemoveIoHandler : Uchar fns don't match\n")
                    );
                VdmIoHandler->IoFunctions[0].UcharIo[PortNumber % 4] = NULL;
            }
            break;
        case Ushort:
            if (EmulatorAccessEntry->StringSupport) {
                ASSERTEQUAL(
                    EmulatorAccessEntry->Routine,
                    VdmIoHandler->IoFunctions[0].UshortStringIo[(PortNumber & 2) >> 1],
                    ("Psp386RemoveIoHandler : UshortString fns don't match\n")
                    );
                VdmIoHandler->IoFunctions[0].UshortStringIo[(PortNumber & 2) >> 1] = NULL;
            } else {
                ASSERTEQUAL(
                    EmulatorAccessEntry->Routine,
                    VdmIoHandler->IoFunctions[0].UshortIo[(PortNumber & 2) >> 1],
                    ("Psp386RemoveIoHandler : Ushort fns don't match\n")
                    );
                VdmIoHandler->IoFunctions[0].UshortIo[(PortNumber & 2) >> 1] = NULL;
            }
            break;
        case Ulong:
            if (EmulatorAccessEntry->StringSupport) {
                ASSERTEQUAL(
                    EmulatorAccessEntry->Routine,
                    VdmIoHandler->IoFunctions[0].UlongStringIo,
                    ("Psp386RemoveIoHandler : UlongString fns don't match\n")
                    );
                VdmIoHandler->IoFunctions[0].UlongStringIo = NULL;
            } else {
                ASSERTEQUAL(
                    EmulatorAccessEntry->Routine,
                    VdmIoHandler->IoFunctions[0].UlongIo,
                    ("Psp386RemoveIoHandler : Ulong fns don't match\n")
                    );
                VdmIoHandler->IoFunctions[0].UlongIo = NULL;
            }
            break;
        }
    }

    if (EmulatorAccessEntry->AccessMode & EMULATOR_WRITE_ACCESS) {
        switch (EmulatorAccessEntry->AccessType) {
        case Uchar:
            if (EmulatorAccessEntry->StringSupport) {
                ASSERTEQUAL(
                    EmulatorAccessEntry->Routine,
                    VdmIoHandler->IoFunctions[1].UcharStringIo[PortNumber % 4],
                    ("Psp386RemoveIoHandler : UcharString fns don't match\n")
                    );
                VdmIoHandler->IoFunctions[1].UcharStringIo[PortNumber % 4] = NULL;
            } else {
                ASSERTEQUAL(
                    EmulatorAccessEntry->Routine,
                    VdmIoHandler->IoFunctions[1].UcharIo[PortNumber % 4],
                    ("Psp386RemoveIoHandler : Uchar fns don't match\n")
                    );
                VdmIoHandler->IoFunctions[1].UcharIo[PortNumber % 4] = NULL;
            }
            break;
        case Ushort:
            if (EmulatorAccessEntry->StringSupport) {
                ASSERTEQUAL(
                    EmulatorAccessEntry->Routine,
                    VdmIoHandler->IoFunctions[1].UshortStringIo[(PortNumber & 2) >> 1],
                    ("Psp386RemoveIoHandler : UshortString fns don't match\n")
                    );
                VdmIoHandler->IoFunctions[1].UshortStringIo[(PortNumber & 2) >> 1] = NULL;
            } else {
                ASSERTEQUAL(
                    EmulatorAccessEntry->Routine,
                    VdmIoHandler->IoFunctions[1].UshortIo[(PortNumber & 2) >> 1],
                    ("Psp386RemoveIoHandler : Ushort fns don't match\n")
                    );
                VdmIoHandler->IoFunctions[1].UshortIo[(PortNumber & 2) >> 1] = NULL;
            }
            break;
        case Ulong:
            if (EmulatorAccessEntry->StringSupport) {
                ASSERTEQUAL(
                    EmulatorAccessEntry->Routine,
                    VdmIoHandler->IoFunctions[1].UlongStringIo,
                    ("Psp386RemoveIoHandler : UlongString fns don't match\n")
                    );
                VdmIoHandler->IoFunctions[1].UlongStringIo = NULL;
            } else {
                ASSERTEQUAL(
                    EmulatorAccessEntry->Routine,
                    VdmIoHandler->IoFunctions[1].UlongIo,
                    ("Psp386RemoveIoHandler : Ulong fns don't match\n")
                    );
                VdmIoHandler->IoFunctions[1].UlongIo = NULL;
            }
            break;
        }
    }

    ExReleaseResourceLite(&pVdmObjects->VdmIoListHead->VdmIoResource);
    KeLowerIrql(OldIrql);

    return STATUS_SUCCESS;

}

NTSTATUS
Psp386InstallIoHandler(
    IN PEPROCESS Process,
    IN PEMULATOR_ACCESS_ENTRY EmulatorAccessEntry,
    IN ULONG PortNumber,
    IN ULONG Context
    )
/*++

Routine Description:

    This routine install a handler for a port.  On debug version, it will
    print a message if there is already a handler.

Arguments:

    Process -- Supplies a pointer to the process
    EmulatorAccess -- Supplies a pointer to the information about the
        io port handler
    PortNumber -- Supplies the port number to install the handler for.

Return Value:

--*/
{
    PVDM_PROCESS_OBJECTS pVdmObjects = Process->VdmObjects;
    PVDM_IO_HANDLER VdmIoHandler;
    NTSTATUS Status;
    KIRQL    OldIrql;
    PAGED_CODE();


    //
    // Ensure we have a vdm process which is initialized
    // correctly for VdmIoHandlers
    //
    if (!pVdmObjects) {
#if DBG
        DbgPrint("Psp386InstallIoHandler: uninitialized VdmObjects\n");
#endif
        return STATUS_UNSUCCESSFUL;
    }


    Status = STATUS_SUCCESS;

    //
    // If this is the first handler to be installed, create the list head,
    // and initialize the resource lock.
    //
    if (!pVdmObjects->VdmIoListHead) {
        Status = Psp386CreateVdmIoListHead(
            Process
            );

        if (!NT_SUCCESS(Status)) {
            return Status;
        }
    }

    //
    // Lock the list to insure correct update.
    //
    KeRaiseIrql(APC_LEVEL, &OldIrql);
    ExAcquireResourceExclusiveLite(&pVdmObjects->VdmIoListHead->VdmIoResource,TRUE);

    //
    // Update Context
    //

    pVdmObjects->VdmIoListHead->Context = Context;

    VdmIoHandler = Psp386GetVdmIoHandler(
        Process,
        PortNumber & ~0x3
        );

    // If there isn't already a node for this block of ports,
    // attempt to allocate a new one.
    //
    if (!VdmIoHandler) {
        try {

            VdmIoHandler = ExAllocatePoolWithQuota(
                                PagedPool,
                                sizeof(VDM_IO_HANDLER)
                                );

        } except(EXCEPTION_EXECUTE_HANDLER) {
            Status = GetExceptionCode();
            if (VdmIoHandler) {
                ExFreePool(VdmIoHandler);
            }
        }

        if (!NT_SUCCESS(Status)) {
            ExReleaseResourceLite(&pVdmObjects->VdmIoListHead->VdmIoResource);
            KeLowerIrql(OldIrql);
            return Status;
        }

        RtlZeroMemory(VdmIoHandler, sizeof(VDM_IO_HANDLER));
        VdmIoHandler->PortNumber = PortNumber & ~0x3;

        Status = Psp386InsertVdmIoHandlerBlock(
            Process,
            VdmIoHandler
            );

        if (!NT_SUCCESS(Status)) {
            ExReleaseResourceLite(&pVdmObjects->VdmIoListHead->VdmIoResource);
            KeLowerIrql(OldIrql);
            return Status;
        }
    }

    ASSERTEQUALBREAK(
        VdmIoHandler->PortNumber,
        (PortNumber & ~0x3),
        ("Psp386InstallIoHandler : Bad pointer returned from GetVdmIoHandler\n")
        );

    if (EmulatorAccessEntry->AccessMode & EMULATOR_READ_ACCESS) {
        switch (EmulatorAccessEntry->AccessType) {
        case Uchar:
            if (EmulatorAccessEntry->StringSupport) {
                ASSERTEQUALBREAK(
                    NULL,
                    VdmIoHandler->IoFunctions[0].UcharStringIo[PortNumber % 4],
                    ("Psp386InstallIoHandler : UcharString fns don't match\n")
                    );
                VdmIoHandler->IoFunctions[0].UcharStringIo[PortNumber % 4] =
                    (PDRIVER_IO_PORT_UCHAR_STRING)EmulatorAccessEntry->Routine;
            } else {
                ASSERTEQUALBREAK(
                    NULL,
                    VdmIoHandler->IoFunctions[0].UcharIo[PortNumber % 4],
                    ("Psp386InstallIoHandler : Uchar fns don't match\n")
                    );
                VdmIoHandler->IoFunctions[0].UcharIo[PortNumber % 4] =
                    (PDRIVER_IO_PORT_UCHAR)EmulatorAccessEntry->Routine;
            }
            break;
        case Ushort:
            if (EmulatorAccessEntry->StringSupport) {
                ASSERTEQUALBREAK(
                    NULL,
                    VdmIoHandler->IoFunctions[0].UshortStringIo[(PortNumber & 2) >> 1],
                    ("Psp386InstallIoHandler : UshortString fns don't match\n")
                    );
                VdmIoHandler->IoFunctions[0].UshortStringIo[(PortNumber & 2) >> 1] =
                    (PDRIVER_IO_PORT_USHORT_STRING)EmulatorAccessEntry->Routine;
            } else {
                ASSERTEQUALBREAK(
                    NULL,
                    VdmIoHandler->IoFunctions[0].UshortIo[(PortNumber & 2) >> 1],
                    ("Psp386InstallIoHandler : Ushort fns don't match\n")
                    );
                VdmIoHandler->IoFunctions[0].UshortIo[(PortNumber & 2) >> 1] =
                    (PDRIVER_IO_PORT_USHORT)EmulatorAccessEntry->Routine;
            }
            break;
        case Ulong:
            if (EmulatorAccessEntry->StringSupport) {
                ASSERTEQUALBREAK(
                    NULL,
                    VdmIoHandler->IoFunctions[0].UlongStringIo,
                    ("Psp386InstallIoHandler : UlongString fns don't match\n")
                    );
                VdmIoHandler->IoFunctions[0].UlongStringIo =
                    (PDRIVER_IO_PORT_ULONG_STRING)EmulatorAccessEntry->Routine;
            } else {
                ASSERTEQUALBREAK(
                    NULL,
                    VdmIoHandler->IoFunctions[0].UlongIo,
                    ("Psp386InstallIoHandler : Ulong fns don't match\n")
                    );
                VdmIoHandler->IoFunctions[0].UlongIo =
                    (PDRIVER_IO_PORT_ULONG)EmulatorAccessEntry->Routine;
            }
            break;
        }
    }

    if (EmulatorAccessEntry->AccessMode & EMULATOR_WRITE_ACCESS) {
        switch (EmulatorAccessEntry->AccessType) {
        case Uchar:
            if (EmulatorAccessEntry->StringSupport) {
                ASSERTEQUALBREAK(
                    NULL,
                    VdmIoHandler->IoFunctions[1].UcharStringIo[PortNumber % 4],
                    ("Psp386InstallIoHandler : UcharString fns don't match\n")
                    );
                VdmIoHandler->IoFunctions[1].UcharStringIo[PortNumber % 4] =
                    (PDRIVER_IO_PORT_UCHAR_STRING)EmulatorAccessEntry->Routine;
            } else {
                ASSERTEQUALBREAK(
                    NULL,
                    VdmIoHandler->IoFunctions[1].UcharIo[PortNumber % 4],
                    ("Psp386InstallIoHandler : Uchar fns don't match\n")
                    );
                VdmIoHandler->IoFunctions[1].UcharIo[PortNumber % 4] =
                    (PDRIVER_IO_PORT_UCHAR)EmulatorAccessEntry->Routine;
            }
            break;
        case Ushort:
            if (EmulatorAccessEntry->StringSupport) {
                ASSERTEQUALBREAK(
                    NULL,
                    VdmIoHandler->IoFunctions[1].UshortStringIo[(PortNumber & 2) >> 1],
                    ("Psp386InstallIoHandler : UshortString fns don't match\n")
                    );
                VdmIoHandler->IoFunctions[1].UshortStringIo[(PortNumber & 2) >> 1] =
                    (PDRIVER_IO_PORT_USHORT_STRING)EmulatorAccessEntry->Routine;
            } else {
                ASSERTEQUALBREAK(
                    NULL,
                    VdmIoHandler->IoFunctions[1].UshortIo[(PortNumber & 2) >> 1],
                    ("Psp386InstallIoHandler : Ushort fns don't match\n")
                    );
                VdmIoHandler->IoFunctions[1].UshortIo[(PortNumber & 2) >> 1] =
                    (PDRIVER_IO_PORT_USHORT)EmulatorAccessEntry->Routine;
            }
            break;
        case Ulong:
            if (EmulatorAccessEntry->StringSupport) {
                ASSERTEQUALBREAK(
                    NULL,
                    VdmIoHandler->IoFunctions[1].UlongStringIo,
                    ("Psp386InstallIoHandler : UlongString fns don't match\n")
                    );
                VdmIoHandler->IoFunctions[1].UlongStringIo =
                    (PDRIVER_IO_PORT_ULONG_STRING)EmulatorAccessEntry->Routine;
            } else {
                ASSERTEQUALBREAK(
                    NULL,
                    VdmIoHandler->IoFunctions[1].UlongIo,
                    ("Psp386InstallIoHandler : Ulong fns don't match\n")
                    );
                VdmIoHandler->IoFunctions[1].UlongIo =
                    (PDRIVER_IO_PORT_ULONG)EmulatorAccessEntry->Routine;
            }
        }
    }

    ExReleaseResourceLite(&pVdmObjects->VdmIoListHead->VdmIoResource);
    KeLowerIrql(OldIrql);
    return STATUS_SUCCESS;

}



NTSTATUS
Psp386CreateVdmIoListHead(
    IN PEPROCESS Process
    )
/*++

Routine Description:

    This routine creates the head node of the Io handler list.  This node
    contains the spin lock that protects the list.  This routine also
    initializes the spin lock.

Arguments:

    Process -- Supplies a pointer to the process

Return Value:

Notes:

--*/
{
    PVDM_PROCESS_OBJECTS pVdmObjects = Process->VdmObjects;
    NTSTATUS Status;
    PVDM_IO_LISTHEAD HandlerListHead=NULL;
    KIRQL    OldIrql;
    PAGED_CODE();

    Status = STATUS_SUCCESS;

    // if there isn't yet a head, grab the resource lock and create one
    if (pVdmObjects->VdmIoListHead == NULL) {
        KeRaiseIrql(APC_LEVEL, &OldIrql);
        ExAcquireResourceExclusiveLite(&VdmIoListCreationResource, TRUE);

        // if no head was created while we grabbed the spin lock
        if (pVdmObjects->VdmIoListHead == NULL) {

            try {
                // allocate space for the list head
                // and charge the quota for it

                HandlerListHead = ExAllocatePoolWithQuota(
                                       NonPagedPool,
                                       sizeof(VDM_IO_LISTHEAD)
                                       );

                } except(EXCEPTION_EXECUTE_HANDLER) {
                    Status = GetExceptionCode();
                    if (HandlerListHead) {
                        ExFreePool(HandlerListHead);
                    }
                }

            if ((!NT_SUCCESS(Status) || !HandlerListHead)) {
                ExReleaseResourceLite(&VdmIoListCreationResource);
                KeLowerIrql(OldIrql);

                return (Status == STATUS_SUCCESS ?
                    STATUS_INSUFFICIENT_RESOURCES :
                    Status);

            }

            ExInitializeResourceLite(&HandlerListHead->VdmIoResource);

            HandlerListHead->VdmIoHandlerList = NULL;

            //
            // Attach the list head to the process
            // and attach the handler to the list.
            // Since this was a new list

            pVdmObjects->VdmIoListHead = HandlerListHead;

            ExReleaseResourceLite(&VdmIoListCreationResource);
            KeLowerIrql(OldIrql);


        }
    }
    return STATUS_SUCCESS;
}

NTSTATUS
Psp386InsertVdmIoHandlerBlock(
    IN PEPROCESS Process,
    IN PVDM_IO_HANDLER VdmIoHandler
    )
/*++

Routine Description:

    This routine inserts a new VdmIoHandler block into the process's io
    handler list.

Arguments:

    Process -- Supplies a pointer to the process
    VdmIoHandler -- Supplies a pointer to the block to insert.

Return Value:

--*/
{
    PVDM_PROCESS_OBJECTS pVdmObjects = Process->VdmObjects;
    PVDM_IO_HANDLER HandlerList, p;
    PVDM_IO_LISTHEAD HandlerListHead;
    PAGED_CODE();


    HandlerListHead = pVdmObjects->VdmIoListHead;
    HandlerList = HandlerListHead->VdmIoHandlerList;
    p = NULL;
    while ((HandlerList != NULL) &&
        (HandlerList->PortNumber < VdmIoHandler->PortNumber)) {
#if DBG
            if (HandlerList->PortNumber == VdmIoHandler->PortNumber) {
                DbgPrint("Ps386InsertVdmIoHandlerBlock : handler list corrupt\n");
            }
#endif
            p = HandlerList;
            HandlerList = HandlerList->Next;
    }

    if (p == NULL) { // Beginning of list
        VdmIoHandler->Next = HandlerListHead->VdmIoHandlerList;
        HandlerListHead->VdmIoHandlerList = VdmIoHandler;
    } else if (HandlerList == NULL) { // End of list
        p->Next = VdmIoHandler;
        VdmIoHandler->Next = NULL;
    } else { // Middle of list
        VdmIoHandler->Next = HandlerList;
        p->Next = VdmIoHandler;
    }

    return STATUS_SUCCESS;
}

BOOLEAN
Ps386GetVdmIoHandler(
    IN PEPROCESS Process,
    IN ULONG PortNumber,
    OUT PVDM_IO_HANDLER VdmIoHandler,
    OUT PULONG Context
    )
/*++

Routine Description:

    This routine finds the VdmIoHandler block for the specified port.

Arguments:

    Process -- Supplies a pointer to the process
    PortNumber -- Supplies the port number
    VdmIoHandler -- Supplies a pointer to the destination for the lookup

Returns:

    True -- A handler structure was found and copied
    False -- A handler structure was not found


--*/
{
    PVDM_PROCESS_OBJECTS pVdmObjects = Process->VdmObjects;
    PVDM_IO_HANDLER p;
    BOOLEAN Success;
    KIRQL   OldIrql;
    PAGED_CODE();

    if (pVdmObjects == NULL) {
        return FALSE;
    }

    if (PortNumber % 4) {
#if DBG
        DbgPrint(
            "Ps386GetVdmIoHandler : Invalid Port Number %lx\n",
            PortNumber
            );
#endif
        return FALSE;
    }

    if (!pVdmObjects->VdmIoListHead) {
        return FALSE;
    }


    KeRaiseIrql(APC_LEVEL, &OldIrql);
    ExAcquireResourceExclusiveLite(&pVdmObjects->VdmIoListHead->VdmIoResource,TRUE);

    p = Psp386GetVdmIoHandler(
        Process,
        PortNumber
        );

    if (p) {
        *VdmIoHandler = *p;
        *Context = pVdmObjects->VdmIoListHead->Context;
        Success = TRUE;
    } else {
        Success = FALSE;
    }
    ExReleaseResourceLite(&pVdmObjects->VdmIoListHead->VdmIoResource);
    KeLowerIrql(OldIrql);

    return Success;
}


PVDM_IO_HANDLER
Psp386GetVdmIoHandler(
    IN PEPROCESS Process,
    IN ULONG PortNumber
    )
/*++

Routine Description:

    This routine finds the VdmIoHandler block for the specified port.

Arguments:

    Process -- Supplies a pointer to the process
    PortNumber -- Supplies the port number

Returns:

    NULL  if no handler found
    non-NULL if handler found

--*/
{
    PVDM_PROCESS_OBJECTS pVdmObjects = Process->VdmObjects;
    PVDM_IO_HANDLER p;
    PAGED_CODE();

    if (PortNumber % 4) {
#if DBG
        DbgPrint(
            "Ps386GetVdmIoHandler : Invalid Port Number %lx\n",
            PortNumber
            );
#endif
        return NULL;
    }

    p = pVdmObjects->VdmIoListHead->VdmIoHandlerList;
    while ((p) && (p->PortNumber != PortNumber)) {
        p = p->Next;
    }

    return p;

}

NTSTATUS
PspVdmInitialize(
    )

/*++

Routine Description:

    This routine initializes the process based Vdm support for x86.

Arguments:

    None

Return Value:

    TBS
--*/
{
    return ExInitializeResourceLite(&VdmIoListCreationResource);
}

