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