/*++

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:

    harderr.c

Abstract:

    This module implements NT Hard Error APIs

--*/

#include "exp.h"

NTSTATUS
ExpRaiseHardError (
    IN NTSTATUS ErrorStatus,
    IN ULONG NumberOfParameters,
    IN ULONG UnicodeStringParameterMask,
    IN PULONG_PTR Parameters,
    IN ULONG ValidResponseOptions,
    OUT PULONG Response
    );

VOID
ExpSystemErrorHandler (
    IN NTSTATUS ErrorStatus,
    IN ULONG NumberOfParameters,
    IN ULONG UnicodeStringParameterMask,
    IN PULONG_PTR Parameters,
    IN BOOLEAN CallShutdown
    );

#if defined(_AMD64_)

VOID
ExpSystemErrorHandler2 (
    IN NTSTATUS ErrorStatus,
    IN ULONG NumberOfParameters,
    IN ULONG UnicodeStringParameterMask,
    IN PULONG_PTR Parameters,
    IN BOOLEAN CallShutdown
    );

#endif

#pragma alloc_text(PAGE, NtRaiseHardError)
#pragma alloc_text(PAGE, NtSetDefaultHardErrorPort)
#pragma alloc_text(PAGE, ExRaiseHardError)
#pragma alloc_text(PAGE, ExpRaiseHardError)
#pragma alloc_text(PAGELK, ExpSystemErrorHandler)

#define HARDERROR_MSG_OVERHEAD (sizeof(HARDERROR_MSG) - sizeof(PORT_MESSAGE))
#define HARDERROR_API_MSG_LENGTH \
            sizeof(HARDERROR_MSG)<<16 | (HARDERROR_MSG_OVERHEAD)

PEPROCESS ExpDefaultErrorPortProcess;

#define STARTING 0
#define STARTED 1
#define SHUTDOWN 2

BOOLEAN ExReadyForErrors = FALSE;

ULONG HardErrorState = STARTING;
BOOLEAN ExpTooLateForErrors = FALSE;
HANDLE ExpDefaultErrorPort;

extern PVOID PsSystemDllDllBase;

#ifdef _X86_
#pragma optimize("y", off)      // RtlCaptureContext needs EBP to be correct
#endif

#if defined(_AMD64_)

VOID
ExpSystemErrorHandler2 (
    IN NTSTATUS ErrorStatus,
    IN ULONG NumberOfParameters,
    IN ULONG UnicodeStringParameterMask,
    IN PULONG_PTR Parameters,
    IN BOOLEAN CallShutdown
    )

#else

VOID
ExpSystemErrorHandler (
    IN NTSTATUS ErrorStatus,
    IN ULONG NumberOfParameters,
    IN ULONG UnicodeStringParameterMask,
    IN PULONG_PTR Parameters,
    IN BOOLEAN CallShutdown
    )

#endif

{
    ULONG Counter;
    ANSI_STRING AnsiString;
    NTSTATUS Status;
    ULONG_PTR ParameterVector[MAXIMUM_HARDERROR_PARAMETERS];
    CHAR DefaultFormatBuffer[32];
    CHAR ExpSystemErrorBuffer[256];
    PMESSAGE_RESOURCE_ENTRY MessageEntry;
    PSZ ErrorCaption;
    CHAR const* ErrorFormatString;
    ANSI_STRING Astr;
    UNICODE_STRING Ustr;
    OEM_STRING Ostr;
    PSZ OemCaption;
    PSZ OemMessage;
    static char const* UnknownHardError = "Unknown Hard Error";

    PAGED_CODE();

#if !defined(_AMD64_)

    //
    // This handler is called whenever a hard error occurs before the
    // default handler has been installed.
    //
    // This is done regardless of whether or not the process has chosen
    // default hard error processing.
    //

    //
    // Capture the callers context as closely as possible into the debugger's
    // processor state area of the Prcb
    //
    // N.B. There may be some prologue code that shuffles registers such that
    //      they get destroyed.
    //
    // This code is here only for crash dumps.
    //

    RtlCaptureContext (&KeGetCurrentPrcb()->ProcessorState.ContextFrame);
    KiSaveProcessorControlState (&KeGetCurrentPrcb()->ProcessorState);

#endif

    DefaultFormatBuffer[0] = '\0';
    RtlZeroMemory (ParameterVector, sizeof(ParameterVector));

    RtlCopyMemory (ParameterVector, Parameters, NumberOfParameters * sizeof (ULONG_PTR));

    for (Counter = 0; Counter < NumberOfParameters; Counter += 1) {

        if (UnicodeStringParameterMask & 1 << Counter) {

            strcat(DefaultFormatBuffer," %s");

            Status = RtlUnicodeStringToAnsiString (&AnsiString,
                                                   (PUNICODE_STRING)Parameters[Counter],
                                                   TRUE);
            if (NT_SUCCESS (Status)) {

                ParameterVector[Counter] = (ULONG_PTR)AnsiString.Buffer;
            } else {
                ParameterVector[Counter] = (ULONG_PTR)L"???";
            }
        }
        else {
            strcat(DefaultFormatBuffer," %x");
        }
    }

    strcat(DefaultFormatBuffer,"\n");

    ErrorFormatString = (char const *)DefaultFormatBuffer;
    ErrorCaption = (PSZ) UnknownHardError;

    if (PsSystemDllDllBase != NULL) {

        try {

            //
            // If we are on a DBCS code page, we have to use ENGLISH resource
            // instead of default resource because HalDisplayString() can only
            // display ASCII characters on the blue screen.
            //

            Status = RtlFindMessage (PsSystemDllDllBase,
                                     11,
                                     NlsMbCodePageTag ?
                                     MAKELANGID(LANG_ENGLISH,SUBLANG_ENGLISH_US) :
                                     0,
                                     ErrorStatus,
                                     &MessageEntry);

            if (!NT_SUCCESS(Status)) {
                ErrorCaption = (PSZ) UnknownHardError;
                ErrorFormatString = (char const *)UnknownHardError;
            }
            else {
                if (MessageEntry->Flags & MESSAGE_RESOURCE_UNICODE) {

                    //
                    // Message resource is Unicode.  Convert to ANSI.
                    //

                    RtlInitUnicodeString (&Ustr, (PCWSTR)MessageEntry->Text);
                    Astr.Length = (USHORT) RtlUnicodeStringToAnsiSize (&Ustr);

                    ErrorCaption = ExAllocatePoolWithTag (NonPagedPool,
                                                          Astr.Length+16,
                                                          ' rrE');

                    if (ErrorCaption != NULL) {
                        Astr.MaximumLength = Astr.Length + 16;
                        Astr.Buffer = ErrorCaption;
                        Status = RtlUnicodeStringToAnsiString(&Astr, &Ustr, FALSE);
                        if (!NT_SUCCESS(Status)) {
                            ExFreePool(ErrorCaption);
                            ErrorCaption = (PSZ) UnknownHardError;
                            ErrorFormatString = (char const *)UnknownHardError;
                        }
                    }
                    else {
                        ErrorCaption = (PSZ) UnknownHardError;
                        ErrorFormatString = (char const *) UnknownHardError;
                    }
                }
                else {
                    ErrorCaption = ExAllocatePoolWithTag(NonPagedPool,
                                    strlen((PCHAR)MessageEntry->Text)+16,
                                    ' rrE');

                    if (ErrorCaption != NULL) {
                        strcpy(ErrorCaption,(PCHAR)MessageEntry->Text);
                    }
                    else {
                        ErrorFormatString = (char const *)UnknownHardError;
                        ErrorCaption = (PSZ) UnknownHardError;
                    }
                }

                if (ErrorCaption != UnknownHardError) {

                    //
                    // It's assumed the Error String from the message table
                    // is in the format:
                    //
                    // {ErrorCaption}\r\n\0ErrorFormatString\0.
                    //
                    // Parse out the caption.
                    //

                    ErrorFormatString = ErrorCaption;
                    Counter = (ULONG) strlen(ErrorCaption);

                    while (Counter && *ErrorFormatString >= ' ') {
                        ErrorFormatString += 1;
                        Counter -= 1;
                    }

                    *(char*)ErrorFormatString++ = '\0';
                    Counter -= 1;

                    while (Counter && *ErrorFormatString && *ErrorFormatString <= ' ') {
                        ErrorFormatString += 1;
                        Counter -= 1;
                    }
                }

                if (!Counter) {
                    // Bad Format String.
                    ErrorFormatString = (char const *)"";
                }
            }
        }
        except (EXCEPTION_EXECUTE_HANDLER) {
            ErrorFormatString = (char const *)UnknownHardError;
            ErrorCaption = (PSZ) UnknownHardError;
        }
    }

    try {
        _snprintf (ExpSystemErrorBuffer,
                   sizeof (ExpSystemErrorBuffer),
                   "\nSTOP: %lx %s\n",
                   ErrorStatus,
                   ErrorCaption);
    }
    except(EXCEPTION_EXECUTE_HANDLER) {
        _snprintf (ExpSystemErrorBuffer,
                   sizeof (ExpSystemErrorBuffer),
                   "\nHardError %lx\n",
                   ErrorStatus);
    }

    ASSERT(ExPageLockHandle);
    MmLockPageableSectionByHandle(ExPageLockHandle);

    //
    // Take the caption and convert it to OEM.
    //

    OemCaption = (PSZ) UnknownHardError;
    OemMessage = (PSZ) UnknownHardError;

    RtlInitAnsiString (&Astr, ExpSystemErrorBuffer);

    Status = RtlAnsiStringToUnicodeString (&Ustr, &Astr, TRUE);

    if (!NT_SUCCESS(Status)) {
        goto punt1;
    }

    //
    // Allocate the OEM string out of nonpaged pool so that bugcheck
    // can read it.
    //

    Ostr.Length = (USHORT)RtlUnicodeStringToOemSize(&Ustr);
    Ostr.MaximumLength = Ostr.Length;
    Ostr.Buffer = ExAllocatePoolWithTag(NonPagedPool, Ostr.Length, ' rrE');
    OemCaption = Ostr.Buffer;

    if (Ostr.Buffer != NULL) {
        Status = RtlUnicodeStringToOemString (&Ostr, &Ustr, FALSE);
        if (!NT_SUCCESS(Status)) {
            goto punt1;
        }
    }

    //
    // Can't do much of anything after calling HalDisplayString...
    //

punt1:

    try {
        _snprintf (ExpSystemErrorBuffer, sizeof (ExpSystemErrorBuffer),
                   (const char *)ErrorFormatString,
                   ParameterVector[0],
                   ParameterVector[1],
                   ParameterVector[2],
                   ParameterVector[3]);
    }
    except(EXCEPTION_EXECUTE_HANDLER) {
        _snprintf (ExpSystemErrorBuffer, sizeof (ExpSystemErrorBuffer),
                   "Exception Processing Message %lx Parameters %lx %lx %lx %lx",
                   ErrorStatus,
                   ParameterVector[0],
                   ParameterVector[1],
                   ParameterVector[2],
                   ParameterVector[3]);
    }


    RtlInitAnsiString (&Astr, ExpSystemErrorBuffer);
    Status = RtlAnsiStringToUnicodeString (&Ustr, &Astr, TRUE);

    if (!NT_SUCCESS(Status)) {
        goto punt2;
    }

    //
    // Allocate the OEM string out of nonpaged pool so that bugcheck
    // can read it.
    //

    Ostr.Length = (USHORT) RtlUnicodeStringToOemSize (&Ustr);
    Ostr.MaximumLength = Ostr.Length;

    Ostr.Buffer = ExAllocatePoolWithTag (NonPagedPool, Ostr.Length, ' rrE');

    OemMessage = Ostr.Buffer;

    if (Ostr.Buffer) {

        Status = RtlUnicodeStringToOemString (&Ostr, &Ustr, FALSE);

        if (!NT_SUCCESS(Status)) {
            goto punt2;
        }
    }

punt2:

    ASSERT (sizeof(PVOID) == sizeof(ULONG_PTR));
    ASSERT (sizeof(ULONG) == sizeof(NTSTATUS));

    //
    // We don't come back from here.
    //

    if (CallShutdown) {

        PoShutdownBugCheck (TRUE,
                            FATAL_UNHANDLED_HARD_ERROR,
                            (ULONG)ErrorStatus,
                            (ULONG_PTR)&(ParameterVector[0]),
                            (ULONG_PTR)OemCaption,
                            (ULONG_PTR)OemMessage);

    }
    else {

        KeBugCheckEx (FATAL_UNHANDLED_HARD_ERROR,
                      (ULONG)ErrorStatus,
                      (ULONG_PTR)&(ParameterVector[0]),
                      (ULONG_PTR)OemCaption,
                      (ULONG_PTR)OemMessage);
    }
}

#ifdef _X86_
#pragma optimize("", on)
#endif

NTSTATUS
ExpRaiseHardError (
    IN NTSTATUS ErrorStatus,
    IN ULONG NumberOfParameters,
    IN ULONG UnicodeStringParameterMask,
    IN PULONG_PTR Parameters,
    IN ULONG ValidResponseOptions,
    OUT PULONG Response
    )
{
    PTEB Teb;
    PETHREAD Thread;
    PEPROCESS Process;
    ULONG_PTR MessageBuffer[PORT_TOTAL_MAXIMUM_MESSAGE_LENGTH/sizeof(ULONG_PTR)];
    PHARDERROR_MSG m;
    NTSTATUS Status;
    HANDLE ErrorPort;
    KPROCESSOR_MODE PreviousMode;
    BOOLEAN DoingShutdown;

    PAGED_CODE();

    m = (PHARDERROR_MSG)&MessageBuffer[0];
    PreviousMode = KeGetPreviousMode();

    DoingShutdown = FALSE;

    if (ValidResponseOptions == OptionShutdownSystem) {

        //
        // Check to see if the caller has the privilege to make this call.
        //

        if (!SeSinglePrivilegeCheck (SeShutdownPrivilege, PreviousMode)) {
            return STATUS_PRIVILEGE_NOT_HELD;
        }

        ExReadyForErrors = FALSE;
        HardErrorState = SHUTDOWN;
        DoingShutdown = TRUE;
    }

    Thread = PsGetCurrentThread();
    Process = PsGetCurrentProcess();

    //
    // If the default handler is not installed, then
    // call the fatal hard error handler if the error
    // status is error
    //
    // Let GDI override this since it does not want to crash the machine
    // when a bad driver was loaded via MmLoadSystemImage.
    //

    if ((Thread->CrossThreadFlags & PS_CROSS_THREAD_FLAGS_HARD_ERRORS_DISABLED) == 0) {

        if (NT_ERROR(ErrorStatus) && (HardErrorState == STARTING || DoingShutdown)) {

            ExpSystemErrorHandler (
                ErrorStatus,
                NumberOfParameters,
                UnicodeStringParameterMask,
                Parameters,
                (BOOLEAN)((PreviousMode != KernelMode) ? TRUE : FALSE));
        }
    }

    //
    // If the process has an error port, then if it wants default
    // handling, use its port. If it disabled default handling, then
    // return the error to the caller. If the process does not
    // have a port, then use the registered default handler.
    //

    ErrorPort = NULL;

    if (Process->ExceptionPort) {
        if (Process->DefaultHardErrorProcessing & 1) {
            ErrorPort = Process->ExceptionPort;
        } else {

            //
            // If error processing is disabled, check the error override
            // status.
            //

            if (ErrorStatus & HARDERROR_OVERRIDE_ERRORMODE) {
                ErrorPort = Process->ExceptionPort;
            }
        }
    } else {
        if (Process->DefaultHardErrorProcessing & 1) {
            ErrorPort = ExpDefaultErrorPort;
        } else {

            //
            // If error processing is disabled, check the error override
            // status.
            //

            if (ErrorStatus & HARDERROR_OVERRIDE_ERRORMODE) {
                ErrorPort = ExpDefaultErrorPort;
            }
        }
    }

    if ((Thread->CrossThreadFlags&PS_CROSS_THREAD_FLAGS_HARD_ERRORS_DISABLED) != 0) {
        ErrorPort = NULL;
    }

    if ((ErrorPort != NULL) && (!IS_SYSTEM_THREAD(Thread))) {
        Teb = (PTEB)PsGetCurrentThread()->Tcb.Teb;
        try {
            if (Teb->HardErrorMode & RTL_ERRORMODE_FAILCRITICALERRORS) {
                ErrorPort = NULL;
            }
        } except (EXCEPTION_EXECUTE_HANDLER) {
            ;
        }
    }

    if (ErrorPort == NULL) {
        *Response = (ULONG)ResponseReturnToCaller;
        return STATUS_SUCCESS;
    }

    if (Process == ExpDefaultErrorPortProcess) {
        if (NT_ERROR(ErrorStatus)) {
            ExpSystemErrorHandler (ErrorStatus,
                                   NumberOfParameters,
                                   UnicodeStringParameterMask,
                                   Parameters,
                                   (BOOLEAN)((PreviousMode != KernelMode) ? TRUE : FALSE));
        }
        *Response = (ULONG)ResponseReturnToCaller;
        Status = STATUS_SUCCESS;
        return Status;
    }

    m->h.u1.Length = HARDERROR_API_MSG_LENGTH;
    m->h.u2.ZeroInit = LPC_ERROR_EVENT;
    m->Status = ErrorStatus & ~HARDERROR_OVERRIDE_ERRORMODE;
    m->ValidResponseOptions = ValidResponseOptions;
    m->UnicodeStringParameterMask = UnicodeStringParameterMask;
    m->NumberOfParameters = NumberOfParameters;

    if (Parameters != NULL) {
        try {
            RtlCopyMemory (&m->Parameters,
                           Parameters,
                           sizeof(ULONG_PTR)*NumberOfParameters);
        } except (EXCEPTION_EXECUTE_HANDLER) {
        }
    }

    KeQuerySystemTime(&m->ErrorTime);

    Status = LpcRequestWaitReplyPortEx (ErrorPort,
                                        (PPORT_MESSAGE) m,
                                        (PPORT_MESSAGE) m);

    if (NT_SUCCESS(Status)) {
        switch (m->Response) {
            case ResponseReturnToCaller :
            case ResponseNotHandled :
            case ResponseAbort :
            case ResponseCancel :
            case ResponseIgnore :
            case ResponseNo :
            case ResponseOk :
            case ResponseRetry :
            case ResponseYes :
            case ResponseTryAgain :
            case ResponseContinue :
                break;
            default:
                m->Response = (ULONG)ResponseReturnToCaller;
                break;
        }
        *Response = m->Response;
    }

    return Status;
}

NTSTATUS
NtRaiseHardError (
    __in NTSTATUS ErrorStatus,
    __in ULONG NumberOfParameters,
    __in ULONG UnicodeStringParameterMask,
    __in_ecount(NumberOfParameters) PULONG_PTR Parameters,
    __in ULONG ValidResponseOptions,
    __out PULONG Response
    )
{
    NTSTATUS Status;
    ULONG_PTR CapturedParameters[MAXIMUM_HARDERROR_PARAMETERS];
    KPROCESSOR_MODE PreviousMode;
    ULONG LocalResponse;
    UNICODE_STRING CapturedString;
    ULONG Counter;

    PAGED_CODE();

    if (NumberOfParameters > MAXIMUM_HARDERROR_PARAMETERS) {
        return STATUS_INVALID_PARAMETER_2;
    }

    if (ARGUMENT_PRESENT(Parameters) && NumberOfParameters == 0) {
        return STATUS_INVALID_PARAMETER_2;
    }

    PreviousMode = KeGetPreviousMode();
    if (PreviousMode != KernelMode) {
        switch (ValidResponseOptions) {
            case OptionAbortRetryIgnore :
            case OptionOk :
            case OptionOkCancel :
            case OptionRetryCancel :
            case OptionYesNo :
            case OptionYesNoCancel :
            case OptionShutdownSystem :
            case OptionOkNoWait :
            case OptionCancelTryContinue:
                break;
            default :
                return STATUS_INVALID_PARAMETER_4;
        }

        try {
            ProbeForWriteUlong(Response);

            if (ARGUMENT_PRESENT(Parameters)) {
                ProbeForRead (Parameters,
                              sizeof(ULONG_PTR)*NumberOfParameters,
                              sizeof(ULONG_PTR));

                RtlCopyMemory (CapturedParameters,
                               Parameters,
                               sizeof(ULONG_PTR)*NumberOfParameters);

                //
                // Probe all strings.
                //

                if (UnicodeStringParameterMask) {

                    for (Counter = 0;Counter < NumberOfParameters; Counter += 1) {

                        //
                        // if there is a string in this position,
                        // then probe and capture the string
                        //

                        if (UnicodeStringParameterMask & (1<<Counter)) {

                            ProbeForReadSmallStructure ((PVOID)CapturedParameters[Counter],
                                                        sizeof(UNICODE_STRING),
                                                        sizeof(ULONG_PTR));

                            RtlCopyMemory (&CapturedString,
                                           (PVOID)CapturedParameters[Counter],
                                           sizeof(UNICODE_STRING));

                            //
                            // Now probe the string
                            //

                            ProbeForRead (CapturedString.Buffer,
                                          CapturedString.MaximumLength,
                                          sizeof(UCHAR));
                        }
                    }
                }
            }
        }
        except(EXCEPTION_EXECUTE_HANDLER) {
            return GetExceptionCode();
        }

        if ((ErrorStatus == STATUS_SYSTEM_IMAGE_BAD_SIGNATURE) &&
            (KdDebuggerEnabled)) {

            if ((NumberOfParameters != 0) && (ARGUMENT_PRESENT(Parameters))) {
                DbgPrint("****************************************************************\n");
                DbgPrint("* The system detected a bad signature on file %wZ\n",(PUNICODE_STRING)CapturedParameters[0]);
                DbgPrint("****************************************************************\n");
            }
            return STATUS_SUCCESS;
        }

        //
        // Call ExpRaiseHardError. All parameters are probed and everything
        // should be user-mode.
        // ExRaiseHardError will squirt all strings into user-mode
        // without any probing
        //

        Status = ExpRaiseHardError (ErrorStatus,
                                    NumberOfParameters,
                                    UnicodeStringParameterMask,
                                    CapturedParameters,
                                    ValidResponseOptions,
                                    &LocalResponse);

        try {
            *Response = LocalResponse;
        }
        except (EXCEPTION_EXECUTE_HANDLER) {
            NOTHING;
        }
    }
    else {
        Status = ExRaiseHardError (ErrorStatus,
                                   NumberOfParameters,
                                   UnicodeStringParameterMask,
                                   Parameters,
                                   ValidResponseOptions,
                                   &LocalResponse);

        *Response = LocalResponse;
    }

    return Status;
}

NTSTATUS
ExRaiseHardError (
    __in NTSTATUS ErrorStatus,
    __in ULONG NumberOfParameters,
    __in ULONG UnicodeStringParameterMask,
    __in_ecount(NumberOfParameters) PULONG_PTR Parameters,
    __in ULONG ValidResponseOptions,
    __out PULONG Response
    )
{
    NTSTATUS Status;
    PULONG_PTR ParameterBlock;
    PULONG_PTR UserModeParameterBase;
    PUNICODE_STRING UserModeStringsBase;
    PUCHAR UserModeStringDataBase;
    UNICODE_STRING CapturedStrings[MAXIMUM_HARDERROR_PARAMETERS];
    ULONG LocalResponse;
    ULONG Counter;
    SIZE_T UserModeSize;

    PAGED_CODE();

    //
    // If we are in the process of shutting down the system, do not allow
    // hard errors.
    //

    if (ExpTooLateForErrors) {

        *Response = ResponseNotHandled;

        return STATUS_SUCCESS;
    }

    ParameterBlock = NULL;

    //
    // If the parameters contain strings, we need to capture
    // the strings and the string descriptors and push them into
    // user-mode.
    //

    if (ARGUMENT_PRESENT(Parameters)) {
        if (UnicodeStringParameterMask) {

            //
            // We have strings - push them into usermode.
            //

            UserModeSize = (sizeof(ULONG_PTR)+sizeof(UNICODE_STRING))*MAXIMUM_HARDERROR_PARAMETERS;
            UserModeSize += sizeof(UNICODE_STRING);

            for (Counter = 0; Counter < NumberOfParameters; Counter += 1) {

                //
                // If there is a string in this position,
                // then probe and capture the string.
                //

                if (UnicodeStringParameterMask & 1<<Counter) {

                    RtlCopyMemory (&CapturedStrings[Counter],
                                   (PVOID)Parameters[Counter],
                                   sizeof(UNICODE_STRING));

                    UserModeSize += CapturedStrings[Counter].MaximumLength;
                }
            }

            //
            // Now we have the user-mode size all figured out.
            // Allocate some memory and point to it with the
            // parameter block. Then go through and copy all
            // of the parameters, string descriptors, and
            // string data into the memory.
            //

            Status = ZwAllocateVirtualMemory (NtCurrentProcess(),
                                              (PVOID *)&ParameterBlock,
                                              0,
                                              &UserModeSize,
                                              MEM_COMMIT,
                                              PAGE_READWRITE);

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

            UserModeParameterBase = ParameterBlock;
            UserModeStringsBase = (PUNICODE_STRING)((PUCHAR)ParameterBlock + sizeof(ULONG_PTR)*MAXIMUM_HARDERROR_PARAMETERS);
            UserModeStringDataBase = (PUCHAR)UserModeStringsBase + sizeof(UNICODE_STRING)*MAXIMUM_HARDERROR_PARAMETERS;
            try {

                for (Counter = 0; Counter < NumberOfParameters; Counter += 1) {

                    //
                    // Copy parameters to user-mode portion of the address space.
                    //

                    if (UnicodeStringParameterMask & 1<<Counter) {

                        //
                        // Fix the parameter to point at the string descriptor slot
                        // in the user-mode buffer.
                        //

                        UserModeParameterBase[Counter] = (ULONG_PTR)&UserModeStringsBase[Counter];

                        //
                        // Copy the string data to user-mode.
                        //

                        RtlCopyMemory (UserModeStringDataBase,
                                       CapturedStrings[Counter].Buffer,
                                       CapturedStrings[Counter].MaximumLength);

                        CapturedStrings[Counter].Buffer = (PWSTR)UserModeStringDataBase;

                        //
                        // Copy the string descriptor.
                        //

                        RtlCopyMemory (&UserModeStringsBase[Counter],
                                       &CapturedStrings[Counter],
                                       sizeof(UNICODE_STRING));

                        //
                        // Adjust the string data base.
                        //

                        UserModeStringDataBase += CapturedStrings[Counter].MaximumLength;
                    }
                    else {
                        UserModeParameterBase[Counter] = Parameters[Counter];
                    }
                } 
            } except (EXCEPTION_EXECUTE_HANDLER) {
            }
        }
        else {
            ParameterBlock = Parameters;
        }
    }

    //
    // Call the hard error sender.
    //

    Status = ExpRaiseHardError (ErrorStatus,
                                NumberOfParameters,
                                UnicodeStringParameterMask,
                                ParameterBlock,
                                ValidResponseOptions,
                                &LocalResponse);

    //
    // If the parameter block was allocated, it needs to be freed.
    //

    if (ParameterBlock && ParameterBlock != Parameters) {
        UserModeSize = 0;
        ZwFreeVirtualMemory (NtCurrentProcess(),
                             (PVOID *)&ParameterBlock,
                             &UserModeSize,
                             MEM_RELEASE);
    }
    *Response = LocalResponse;

    return Status;
}

NTSTATUS
NtSetDefaultHardErrorPort (
    __in HANDLE DefaultHardErrorPort
    )
{
    NTSTATUS Status;

    PAGED_CODE();

    if (!SeSinglePrivilegeCheck(SeTcbPrivilege, KeGetPreviousMode())) {
        return STATUS_PRIVILEGE_NOT_HELD;
    }

    if (HardErrorState == STARTED) {
        return STATUS_UNSUCCESSFUL;
    }

    Status = ObReferenceObjectByHandle (DefaultHardErrorPort,
                                        0,
                                        LpcPortObjectType,
                                        KeGetPreviousMode(),
                                        (PVOID *)&ExpDefaultErrorPort,
                                        NULL);

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

    ExReadyForErrors = TRUE;
    HardErrorState = STARTED;
    ExpDefaultErrorPortProcess = PsGetCurrentProcess();
    ObReferenceObject (ExpDefaultErrorPortProcess);

    return STATUS_SUCCESS;
}

VOID
__cdecl
_purecall()
{
    ASSERTMSG("_purecall() was called", FALSE);
    ExRaiseStatus(STATUS_NOT_IMPLEMENTED);
}

