1 /* 2 * PROJECT: ReactOS Kernel 3 * LICENSE: GPL - See COPYING in the top level directory 4 * FILE: ntoskrnl/ex/harderr.c 5 * PURPOSE: Error Functions and Status/Exception Dispatching/Raising 6 * PROGRAMMERS: Alex Ionescu (alex@relsoft.net) 7 */ 8 9 /* INCLUDES *****************************************************************/ 10 11 #include <ntoskrnl.h> 12 #define NDEBUG 13 #include <debug.h> 14 15 /* GLOBALS ******************************************************************/ 16 17 BOOLEAN ExReadyForErrors = FALSE; 18 PVOID ExpDefaultErrorPort = NULL; 19 PEPROCESS ExpDefaultErrorPortProcess = NULL; 20 21 /* FUNCTIONS ****************************************************************/ 22 23 /*++ 24 * @name ExpSystemErrorHandler 25 * 26 * For now it's a stub 27 * 28 * @param ErrorStatus 29 * FILLME 30 * 31 * @param NumberOfParameters 32 * FILLME 33 * 34 * @param UnicodeStringParameterMask 35 * FILLME 36 * 37 * @param Parameters 38 * FILLME 39 * 40 * @param ValidResponseOptions 41 * FILLME 42 * 43 * @param Response 44 * FILLME 45 * 46 * @return None 47 * 48 * @remarks None 49 * 50 *--*/ 51 NTSTATUS 52 NTAPI 53 ExpSystemErrorHandler(IN NTSTATUS ErrorStatus, 54 IN ULONG NumberOfParameters, 55 IN ULONG UnicodeStringParameterMask, 56 IN PULONG_PTR Parameters, 57 IN BOOLEAN Shutdown) 58 { 59 ULONG_PTR BugCheckParameters[MAXIMUM_HARDERROR_PARAMETERS] = {0, 0, 0, 0}; 60 ULONG i; 61 62 /* Sanity check */ 63 ASSERT(NumberOfParameters <= MAXIMUM_HARDERROR_PARAMETERS); 64 65 /* 66 * KeBugCheck expects MAXIMUM_HARDERROR_PARAMETERS parameters, 67 * but we might get called with less, so use a local buffer here. 68 */ 69 for (i = 0; i < NumberOfParameters; i++) 70 { 71 /* Copy them over */ 72 BugCheckParameters[i] = Parameters[i]; 73 } 74 75 /* FIXME: STUB */ 76 KeBugCheckEx(FATAL_UNHANDLED_HARD_ERROR, 77 ErrorStatus, 78 (ULONG_PTR)BugCheckParameters, 79 0, 80 0); 81 return STATUS_SUCCESS; 82 } 83 84 /*++ 85 * @name ExpRaiseHardError 86 * @implemented 87 * 88 * See ExRaiseHardError and NtRaiseHardError, same parameters. 89 * 90 * This function performs the central work for both ExRaiseHardError 91 * and NtRaiseHardError. ExRaiseHardError is the service for kernel-mode 92 * that copies the parameters to user-mode, and NtRaiseHardError is the 93 * service for both kernel-mode and user-mode that performs parameters 94 * validation and capture if necessary. 95 * 96 *--*/ 97 NTSTATUS 98 NTAPI 99 ExpRaiseHardError(IN NTSTATUS ErrorStatus, 100 IN ULONG NumberOfParameters, 101 IN ULONG UnicodeStringParameterMask, 102 IN PULONG_PTR Parameters, 103 IN ULONG ValidResponseOptions, 104 OUT PULONG Response) 105 { 106 NTSTATUS Status; 107 PEPROCESS Process = PsGetCurrentProcess(); 108 PETHREAD Thread = PsGetCurrentThread(); 109 UCHAR Buffer[PORT_MAXIMUM_MESSAGE_LENGTH]; 110 PHARDERROR_MSG Message = (PHARDERROR_MSG)Buffer; 111 HANDLE PortHandle; 112 KPROCESSOR_MODE PreviousMode = KeGetPreviousMode(); 113 114 PAGED_CODE(); 115 116 /* Check if this error will shutdown the system */ 117 if (ValidResponseOptions == OptionShutdownSystem) 118 { 119 /* 120 * Check if we have the privileges. 121 * 122 * NOTE: In addition to the Shutdown privilege we also check whether 123 * the caller has the Tcb privilege. The purpose is to allow only 124 * SYSTEM processes to "shutdown" the system on hard errors (BSOD) 125 * while forbidding regular processes to do so. This behaviour differs 126 * from Windows, where any user-mode process, as soon as it has the 127 * Shutdown privilege, can trigger a hard-error BSOD. 128 */ 129 if (!SeSinglePrivilegeCheck(SeTcbPrivilege, PreviousMode) || 130 !SeSinglePrivilegeCheck(SeShutdownPrivilege, PreviousMode)) 131 { 132 /* No rights */ 133 *Response = ResponseNotHandled; 134 return STATUS_PRIVILEGE_NOT_HELD; 135 } 136 137 /* Don't handle any new hard errors */ 138 ExReadyForErrors = FALSE; 139 } 140 141 /* Check if hard errors are not disabled */ 142 if (!Thread->HardErrorsAreDisabled) 143 { 144 /* Check if we can't do errors anymore, and this is serious */ 145 if (!ExReadyForErrors && NT_ERROR(ErrorStatus)) 146 { 147 /* Use the system handler */ 148 ExpSystemErrorHandler(ErrorStatus, 149 NumberOfParameters, 150 UnicodeStringParameterMask, 151 Parameters, 152 (PreviousMode != KernelMode) ? TRUE : FALSE); 153 } 154 } 155 156 /* 157 * Enable hard error processing if it is enabled for the process 158 * or if the exception status forces it. 159 */ 160 if ((Process->DefaultHardErrorProcessing & SEM_FAILCRITICALERRORS) || 161 (ErrorStatus & HARDERROR_OVERRIDE_ERRORMODE)) 162 { 163 /* Check if we have an exception port */ 164 if (Process->ExceptionPort) 165 { 166 /* Use the port */ 167 PortHandle = Process->ExceptionPort; 168 } 169 else 170 { 171 /* Use our default system port */ 172 PortHandle = ExpDefaultErrorPort; 173 } 174 } 175 else 176 { 177 /* Don't process the error */ 178 PortHandle = NULL; 179 } 180 181 /* If hard errors are disabled, do nothing */ 182 if (Thread->HardErrorsAreDisabled) PortHandle = NULL; 183 184 /* 185 * If this is not the system thread, check whether hard errors are 186 * disabled for this thread on user-mode side, and if so, do nothing. 187 */ 188 if (!Thread->SystemThread && (PortHandle != NULL)) 189 { 190 /* Check if we have a TEB */ 191 PTEB Teb = PsGetCurrentThread()->Tcb.Teb; 192 if (Teb) 193 { 194 _SEH2_TRY 195 { 196 if (Teb->HardErrorMode & RTL_SEM_FAILCRITICALERRORS) 197 { 198 PortHandle = NULL; 199 } 200 } 201 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) 202 { 203 NOTHING; 204 } 205 _SEH2_END; 206 } 207 } 208 209 /* Now check if we have a port */ 210 if (PortHandle == NULL) 211 { 212 /* Just return to caller */ 213 *Response = ResponseReturnToCaller; 214 return STATUS_SUCCESS; 215 } 216 217 /* Check if this is the default process */ 218 if (Process == ExpDefaultErrorPortProcess) 219 { 220 /* We can't handle the error, check if this is critical */ 221 if (NT_ERROR(ErrorStatus)) 222 { 223 /* It is, invoke the system handler */ 224 ExpSystemErrorHandler(ErrorStatus, 225 NumberOfParameters, 226 UnicodeStringParameterMask, 227 Parameters, 228 (PreviousMode != KernelMode) ? TRUE : FALSE); 229 230 /* If we survived, return to caller */ 231 *Response = ResponseReturnToCaller; 232 return STATUS_SUCCESS; 233 } 234 } 235 236 /* Setup the LPC Message */ 237 Message->h.u1.Length = (sizeof(HARDERROR_MSG) << 16) | 238 (sizeof(HARDERROR_MSG) - sizeof(PORT_MESSAGE)); 239 Message->h.u2.ZeroInit = 0; 240 Message->h.u2.s2.Type = LPC_ERROR_EVENT; 241 Message->Status = ErrorStatus & ~HARDERROR_OVERRIDE_ERRORMODE; 242 Message->ValidResponseOptions = ValidResponseOptions; 243 Message->UnicodeStringParameterMask = UnicodeStringParameterMask; 244 Message->NumberOfParameters = NumberOfParameters; 245 KeQuerySystemTime(&Message->ErrorTime); 246 247 /* Copy the parameters */ 248 if (Parameters) 249 { 250 RtlMoveMemory(&Message->Parameters, 251 Parameters, 252 sizeof(ULONG_PTR) * NumberOfParameters); 253 } 254 255 /* Send the LPC Message */ 256 Status = LpcRequestWaitReplyPort(PortHandle, 257 (PPORT_MESSAGE)Message, 258 (PPORT_MESSAGE)Message); 259 if (NT_SUCCESS(Status)) 260 { 261 /* Check what kind of response we got */ 262 if ((Message->Response != ResponseReturnToCaller) && 263 (Message->Response != ResponseNotHandled) && 264 (Message->Response != ResponseAbort) && 265 (Message->Response != ResponseCancel) && 266 (Message->Response != ResponseIgnore) && 267 (Message->Response != ResponseNo) && 268 (Message->Response != ResponseOk) && 269 (Message->Response != ResponseRetry) && 270 (Message->Response != ResponseYes) && 271 (Message->Response != ResponseTryAgain) && 272 (Message->Response != ResponseContinue)) 273 { 274 /* Reset to a default one */ 275 Message->Response = ResponseReturnToCaller; 276 } 277 278 /* Set the response */ 279 *Response = Message->Response; 280 } 281 else 282 { 283 /* Set the response */ 284 *Response = ResponseReturnToCaller; 285 } 286 287 /* Return status */ 288 return Status; 289 } 290 291 /*++ 292 * @name ExRaiseAccessViolation 293 * @implemented 294 * 295 * The ExRaiseAccessViolation routine can be used with structured exception 296 * handling to throw a driver-determined exception for a memory access 297 * violation that occurs when a driver processes I/O requests. 298 * See: http://msdn.microsoft.com/library/en-us/Kernel_r/hh/Kernel_r/k102_71b4c053-599c-4a6d-8a59-08aae6bdc534.xml.asp?frame=true 299 * http://www.osronline.com/ddkx/kmarch/k102_814i.htm 300 * 301 * @return None 302 * 303 * @remarks None 304 * 305 *--*/ 306 VOID 307 NTAPI 308 ExRaiseAccessViolation(VOID) 309 { 310 /* Raise the Right Status */ 311 RtlRaiseStatus(STATUS_ACCESS_VIOLATION); 312 } 313 314 /*++ 315 * @name ExRaiseDatatypeMisalignment 316 * @implemented 317 * 318 * ExRaiseDatatypeMisalignment raises an exception with the exception 319 * code set to STATUS_DATATYPE_MISALIGNMENT 320 * See: MSDN / DDK 321 * http://www.osronline.com/ddkx/kmarch/k102_814i.htm 322 * 323 * @return None 324 * 325 * @remarks None 326 * 327 *--*/ 328 VOID 329 NTAPI 330 ExRaiseDatatypeMisalignment(VOID) 331 { 332 /* Raise the Right Status */ 333 RtlRaiseStatus(STATUS_DATATYPE_MISALIGNMENT); 334 } 335 336 /*++ 337 * @name ExSystemExceptionFilter 338 * @implemented 339 * 340 * TODO: Add description 341 * 342 * @return FILLME 343 * 344 * @remarks None 345 * 346 *--*/ 347 LONG 348 NTAPI 349 ExSystemExceptionFilter(VOID) 350 { 351 return KeGetPreviousMode() != KernelMode ? 352 EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH; 353 } 354 355 /*++ 356 * @name ExRaiseHardError 357 * @implemented 358 * 359 * See NtRaiseHardError and ExpRaiseHardError. 360 * 361 * @param ErrorStatus 362 * Error Code 363 * 364 * @param NumberOfParameters 365 * Number of optional parameters in Parameters array 366 * 367 * @param UnicodeStringParameterMask 368 * Optional string parameter (can be only one per error code) 369 * 370 * @param Parameters 371 * Array of ULONG parameters for use in error message string 372 * 373 * @param ValidResponseOptions 374 * See HARDERROR_RESPONSE_OPTION for possible values description 375 * 376 * @param Response 377 * Pointer to HARDERROR_RESPONSE enumeration 378 * 379 * @return Status 380 * 381 *--*/ 382 NTSTATUS 383 NTAPI 384 ExRaiseHardError(IN NTSTATUS ErrorStatus, 385 IN ULONG NumberOfParameters, 386 IN ULONG UnicodeStringParameterMask, 387 IN PULONG_PTR Parameters, 388 IN ULONG ValidResponseOptions, 389 OUT PULONG Response) 390 { 391 NTSTATUS Status; 392 SIZE_T Size; 393 UNICODE_STRING CapturedParams[MAXIMUM_HARDERROR_PARAMETERS]; 394 ULONG i; 395 PVOID UserData = NULL; 396 PHARDERROR_USER_PARAMETERS UserParams; 397 PWSTR BufferBase; 398 ULONG SafeResponse = ResponseNotHandled; 399 400 PAGED_CODE(); 401 402 /* Check if we have parameters */ 403 if (Parameters) 404 { 405 /* Check if we have strings */ 406 if (UnicodeStringParameterMask) 407 { 408 /* Calculate the required size */ 409 Size = FIELD_OFFSET(HARDERROR_USER_PARAMETERS, Buffer[0]); 410 411 /* Loop each parameter */ 412 for (i = 0; i < NumberOfParameters; i++) 413 { 414 /* Check if it's part of the mask */ 415 if (UnicodeStringParameterMask & (1 << i)) 416 { 417 /* Copy it */ 418 RtlMoveMemory(&CapturedParams[i], 419 (PVOID)Parameters[i], 420 sizeof(UNICODE_STRING)); 421 422 /* Increase the size */ 423 Size += CapturedParams[i].MaximumLength; 424 } 425 } 426 427 /* Allocate the user data region */ 428 Status = ZwAllocateVirtualMemory(NtCurrentProcess(), 429 &UserData, 430 0, 431 &Size, 432 MEM_COMMIT, 433 PAGE_READWRITE); 434 if (!NT_SUCCESS(Status)) 435 { 436 /* Return failure */ 437 *Response = ResponseNotHandled; 438 return Status; 439 } 440 441 /* Set the pointers to our data */ 442 UserParams = UserData; 443 BufferBase = UserParams->Buffer; 444 445 /* Enter SEH block as we are writing to user-mode space */ 446 _SEH2_TRY 447 { 448 /* Loop parameters again */ 449 for (i = 0; i < NumberOfParameters; i++) 450 { 451 /* Check if we are in the mask */ 452 if (UnicodeStringParameterMask & (1 << i)) 453 { 454 /* Update the base */ 455 UserParams->Parameters[i] = (ULONG_PTR)&UserParams->Strings[i]; 456 457 /* Copy the string buffer */ 458 RtlMoveMemory(BufferBase, 459 CapturedParams[i].Buffer, 460 CapturedParams[i].MaximumLength); 461 462 /* Set buffer */ 463 CapturedParams[i].Buffer = BufferBase; 464 465 /* Copy the string structure */ 466 UserParams->Strings[i] = CapturedParams[i]; 467 468 /* Update the pointer */ 469 BufferBase += CapturedParams[i].MaximumLength; 470 } 471 else 472 { 473 /* No need to copy any strings */ 474 UserParams->Parameters[i] = Parameters[i]; 475 } 476 } 477 } 478 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) 479 { 480 /* Return the exception code */ 481 Status = _SEH2_GetExceptionCode(); 482 DPRINT1("ExRaiseHardError - Exception when writing data to user-mode, Status 0x%08lx\n", Status); 483 } 484 _SEH2_END; 485 } 486 else 487 { 488 /* Just keep the data as is */ 489 UserData = Parameters; 490 } 491 } 492 493 /* Now call the worker function */ 494 Status = ExpRaiseHardError(ErrorStatus, 495 NumberOfParameters, 496 UnicodeStringParameterMask, 497 UserData, 498 ValidResponseOptions, 499 &SafeResponse); 500 501 /* Check if we had done user-mode allocation */ 502 if ((UserData) && (UserData != Parameters)) 503 { 504 /* We did! Delete it */ 505 Size = 0; 506 ZwFreeVirtualMemory(NtCurrentProcess(), 507 &UserData, 508 &Size, 509 MEM_RELEASE); 510 } 511 512 /* Return status and the response */ 513 *Response = SafeResponse; 514 return Status; 515 } 516 517 /*++ 518 * @name NtRaiseHardError 519 * @implemented 520 * 521 * This function sends HARDERROR_MSG LPC message to a hard-error listener, 522 * typically CSRSS.EXE. See NtSetDefaultHardErrorPort for more information. 523 * See also: http://undocumented.ntinternals.net/UserMode/Undocumented%20Functions/Error/NtRaiseHardError.html 524 * 525 * @param ErrorStatus 526 * Error Code 527 * 528 * @param NumberOfParameters 529 * Number of optional parameters in Parameters array 530 * 531 * @param UnicodeStringParameterMask 532 * Optional string parameter (can be only one per error code) 533 * 534 * @param Parameters 535 * Array of ULONG_PTR parameters for use in error message string 536 * 537 * @param ValidResponseOptions 538 * See HARDERROR_RESPONSE_OPTION for possible values description 539 * 540 * @param Response 541 * Pointer to HARDERROR_RESPONSE enumeration 542 * 543 * @return Status 544 * 545 * @remarks NtRaiseHardError constitutes an easy way to display messages 546 * in GUI without loading any Win32 API libraries. 547 * 548 *--*/ 549 NTSTATUS 550 NTAPI 551 NtRaiseHardError(IN NTSTATUS ErrorStatus, 552 IN ULONG NumberOfParameters, 553 IN ULONG UnicodeStringParameterMask, 554 IN PULONG_PTR Parameters, 555 IN ULONG ValidResponseOptions, 556 OUT PULONG Response) 557 { 558 NTSTATUS Status = STATUS_SUCCESS; 559 PULONG_PTR SafeParams = NULL; 560 ULONG SafeResponse = ResponseNotHandled; 561 UNICODE_STRING SafeString; 562 ULONG i; 563 ULONG ParamSize = 0; 564 KPROCESSOR_MODE PreviousMode = ExGetPreviousMode(); 565 566 PAGED_CODE(); 567 568 /* Validate parameter count */ 569 if (NumberOfParameters > MAXIMUM_HARDERROR_PARAMETERS) 570 { 571 /* Fail */ 572 return STATUS_INVALID_PARAMETER_2; 573 } 574 575 /* Make sure we have some at least */ 576 if ((Parameters != NULL) && (NumberOfParameters == 0)) 577 { 578 /* Fail */ 579 return STATUS_INVALID_PARAMETER_2; 580 } 581 582 /* Check if we were called from user-mode */ 583 if (PreviousMode != KernelMode) 584 { 585 /* First validate the responses */ 586 switch (ValidResponseOptions) 587 { 588 /* Check all valid cases */ 589 case OptionAbortRetryIgnore: 590 case OptionOk: 591 case OptionOkCancel: 592 case OptionRetryCancel: 593 case OptionYesNo: 594 case OptionYesNoCancel: 595 case OptionShutdownSystem: 596 case OptionOkNoWait: 597 case OptionCancelTryContinue: 598 break; 599 600 /* Anything else is invalid */ 601 default: 602 return STATUS_INVALID_PARAMETER_4; 603 } 604 605 /* Check if we have parameters */ 606 if (Parameters) 607 { 608 /* Calculate size of the parameters */ 609 ParamSize = sizeof(ULONG_PTR) * NumberOfParameters; 610 611 /* Allocate a safe buffer */ 612 SafeParams = ExAllocatePoolWithTag(PagedPool, ParamSize, TAG_ERR); 613 if (!SafeParams) 614 { 615 return STATUS_INSUFFICIENT_RESOURCES; 616 } 617 } 618 619 /* Enter SEH Block */ 620 _SEH2_TRY 621 { 622 /* Validate the response pointer */ 623 ProbeForWriteUlong(Response); 624 625 /* Check if we have parameters */ 626 if (Parameters) 627 { 628 /* Validate the parameter pointers */ 629 ProbeForRead(Parameters, ParamSize, sizeof(ULONG_PTR)); 630 631 /* Copy them */ 632 RtlCopyMemory(SafeParams, Parameters, ParamSize); 633 634 /* Now check if there's strings in it */ 635 if (UnicodeStringParameterMask) 636 { 637 /* Loop every string */ 638 for (i = 0; i < NumberOfParameters; i++) 639 { 640 /* Check if this parameter is a string */ 641 if (UnicodeStringParameterMask & (1 << i)) 642 { 643 /* Probe the structure */ 644 ProbeForRead((PVOID)SafeParams[i], 645 sizeof(UNICODE_STRING), 646 sizeof(ULONG_PTR)); 647 648 /* Capture it */ 649 RtlCopyMemory(&SafeString, 650 (PVOID)SafeParams[i], 651 sizeof(UNICODE_STRING)); 652 653 /* Probe the buffer */ 654 ProbeForRead(SafeString.Buffer, 655 SafeString.MaximumLength, 656 sizeof(UCHAR)); 657 } 658 } 659 } 660 } 661 } 662 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) 663 { 664 /* Free captured buffer */ 665 if (SafeParams) ExFreePoolWithTag(SafeParams, TAG_ERR); 666 667 /* Return the exception code */ 668 _SEH2_YIELD(return _SEH2_GetExceptionCode()); 669 } 670 _SEH2_END; 671 672 /* Call the system function directly, because we probed */ 673 Status = ExpRaiseHardError(ErrorStatus, 674 NumberOfParameters, 675 UnicodeStringParameterMask, 676 SafeParams, 677 ValidResponseOptions, 678 &SafeResponse); 679 680 /* Free captured buffer */ 681 if (SafeParams) ExFreePoolWithTag(SafeParams, TAG_ERR); 682 683 /* Enter SEH Block to return the response */ 684 _SEH2_TRY 685 { 686 /* Return the response */ 687 *Response = SafeResponse; 688 } 689 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) 690 { 691 /* Get the exception code */ 692 Status = _SEH2_GetExceptionCode(); 693 } 694 _SEH2_END; 695 } 696 else 697 { 698 /* Reuse variable */ 699 SafeParams = Parameters; 700 701 /* 702 * Call the Executive Function. It will probe 703 * and copy pointers to user-mode. 704 */ 705 Status = ExRaiseHardError(ErrorStatus, 706 NumberOfParameters, 707 UnicodeStringParameterMask, 708 SafeParams, 709 ValidResponseOptions, 710 &SafeResponse); 711 712 /* Return the response */ 713 *Response = SafeResponse; 714 } 715 716 /* Return status */ 717 return Status; 718 } 719 720 /*++ 721 * @name NtSetDefaultHardErrorPort 722 * @implemented 723 * 724 * NtSetDefaultHardErrorPort is typically called only once. After the call, 725 * the kernel sets a BOOLEAN flag named ExReadyForErrors to TRUE, and all other 726 * attempts to change the default port fail with STATUS_UNSUCCESSFUL error code. 727 * See: http://undocumented.ntinternals.net/UserMode/Undocumented%20Functions/Error/NtSetDefaultHardErrorPort.html 728 * https://web.archive.org/web/20070716133753/http://www.windowsitlibrary.com/Content/356/08/2.html 729 * 730 * @param PortHandle 731 * Handle to named port object 732 * 733 * @return Status 734 * 735 * @remarks Privileges: SE_TCB_PRIVILEGE 736 * 737 *--*/ 738 NTSTATUS 739 NTAPI 740 NtSetDefaultHardErrorPort(IN HANDLE PortHandle) 741 { 742 KPROCESSOR_MODE PreviousMode = ExGetPreviousMode(); 743 NTSTATUS Status = STATUS_UNSUCCESSFUL; 744 745 PAGED_CODE(); 746 747 /* Check if we have the privileges */ 748 if (!SeSinglePrivilegeCheck(SeTcbPrivilege, PreviousMode)) 749 { 750 DPRINT1("NtSetDefaultHardErrorPort: Caller requires " 751 "the SeTcbPrivilege privilege!\n"); 752 return STATUS_PRIVILEGE_NOT_HELD; 753 } 754 755 /* Only called once during bootup, make sure we weren't called yet */ 756 if (!ExReadyForErrors) 757 { 758 /* Reference the hard-error port */ 759 Status = ObReferenceObjectByHandle(PortHandle, 760 0, 761 LpcPortObjectType, 762 PreviousMode, 763 (PVOID*)&ExpDefaultErrorPort, 764 NULL); 765 if (NT_SUCCESS(Status)) 766 { 767 /* Keep also a reference to the process handling the hard errors */ 768 ExpDefaultErrorPortProcess = PsGetCurrentProcess(); 769 ObReferenceObject(ExpDefaultErrorPortProcess); 770 ExReadyForErrors = TRUE; 771 Status = STATUS_SUCCESS; 772 } 773 } 774 775 /* Return status to caller */ 776 return Status; 777 } 778 779 VOID 780 __cdecl 781 _purecall(VOID) 782 { 783 /* Not supported in Kernel Mode */ 784 RtlRaiseStatus(STATUS_NOT_IMPLEMENTED); 785 } 786 787 /* EOF */ 788