/*++

Copyright (c) Microsoft Corporation. All rights reserved. 

You may only use this code if you agree to the terms of the Windows Research Kernel Source Code License agreement (see License.txt).
If you do not agree to the terms, do not use the code.


Module Name:

    psvdm.c

Abstract:

    This module contains code for the io port handler support

--*/

#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();

    //
    // Ensure that this call was made from KernelMode
    //
    if (KeGetPreviousMode () != KernelMode) {
        return STATUS_INVALID_PARAMETER;    // this info type invalid in usermode
    }
    //
    // Ensure 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 ensure 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 ensure 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 = ExAllocatePoolWithQuotaTag (PagedPool,
                                                       sizeof(VDM_IO_HANDLER),
                                                       'HVsP');

        } 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 = ExAllocatePoolWithQuotaTag (NonPagedPool,
                                                              sizeof(VDM_IO_LISTHEAD),
                                                              'LVsP');

            } 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 ExInitializeResourceLite (&VdmIoListCreationResource);
}

