Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1565 lines
42 KiB

  1. /*++
  2. Module Name:
  3. ixmca.c
  4. Abstract:
  5. HAL component of the Machine Check Architecture.
  6. All exported MCA functionality is present in this file.
  7. Author:
  8. Srikanth Kambhatla (Intel)
  9. Revision History:
  10. Anil Aggarwal (Intel)
  11. Changes incorporated as per design review with Microsoft
  12. --*/
  13. #include <bugcodes.h>
  14. #include <halp.h>
  15. #include <stdlib.h>
  16. #include <stdio.h>
  17. #include <nthal.h>
  18. //
  19. // Structure to keep track of MCA features available on installed hardware
  20. //
  21. typedef struct _MCA_INFO {
  22. FAST_MUTEX Mutex;
  23. UCHAR NumBanks; // Number of Banks present
  24. ULONGLONG Bank0Config; // Bank0 configuration setup by BIOS.
  25. // This will be used as mask when
  26. // setting up bank 0
  27. MCA_DRIVER_INFO DriverInfo; // Info about registered driver
  28. KERNEL_MCA_DELIVERY WmiMcaCallback; // WMI corrected MC handler
  29. } MCA_INFO, *PMCA_INFO;
  30. //
  31. // Default MCA Bank configuration
  32. //
  33. #define MCA_DEFAULT_BANK_CONF 0xFFFFFFFFFFFFFFFF
  34. //
  35. // MCA architecture related defines
  36. //
  37. #define MCA_NUM_REGS 4
  38. #define MCE_VALID 0x01
  39. #define MCA_VECTOR 18
  40. //
  41. // MSR register addresses for MCA
  42. //
  43. #define MCG_CAP 0x179
  44. #define MCG_STATUS 0x17a
  45. #define MCG_CTL 0x17b
  46. #define MCG_EAX 0x180
  47. #define MCG_EBX 0x181
  48. #define MCG_ECX 0x182
  49. #define MCG_EDX 0x183
  50. #define MCG_ESI 0x184
  51. #define MCG_EDI 0x185
  52. #define MCG_EBP 0x186
  53. #define MCG_ESP 0x187
  54. #define MCG_EFLAGS 0x188
  55. #define MCG_EIP 0x189
  56. #define MC0_CTL 0x400
  57. #define MC0_STATUS 0x401
  58. #define MC0_ADDR 0x402
  59. #define MC0_MISC 0x403
  60. #define PENTIUM_MC_ADDR 0x0
  61. #define PENTIUM_MC_TYPE 0x1
  62. //
  63. // Bit Masks for MCG_CAP
  64. //
  65. #define MCA_CNT_MASK 0xFF
  66. #define MCG_CTL_PRESENT 0x100
  67. #define MCG_EXT_PRESENT 0x200
  68. typedef struct _MCG_CAPABILITY {
  69. union {
  70. struct {
  71. ULONG McaCnt:8;
  72. ULONG McaCntlPresent:1;
  73. ULONG McaExtPresent:1;
  74. ULONG Reserved_1: 6;
  75. ULONG McaExtCnt: 8;
  76. ULONG Reserved_2: 8;
  77. ULONG Reserved_3;
  78. } hw;
  79. ULONGLONG QuadPart;
  80. } u;
  81. } MCG_CAPABILITY, *PMCG_CAPABILITY;
  82. //
  83. // V2 defines up through Eip
  84. //
  85. #define MCG_EFLAGS_OFFSET (MCG_EFLAGS-MCG_EAX+1)
  86. //
  87. // Writing all 1's to MCG_CTL register enables logging.
  88. //
  89. #define MCA_MCGCTL_ENABLE_LOGGING 0xffffffffffffffff
  90. //
  91. // Bit interpretation of MCG_STATUS register
  92. //
  93. #define MCG_MC_INPROGRESS 0x4
  94. #define MCG_EIP_VALID 0x2
  95. #define MCG_RESTART_EIP_VALID 0x1
  96. //
  97. // Defines for the size of TSS and the initial stack to operate on
  98. //
  99. #define MINIMUM_TSS_SIZE 0x68
  100. //
  101. // Global Varibles
  102. //
  103. MCA_INFO HalpMcaInfo;
  104. BOOLEAN HalpMcaInterfaceLocked = FALSE;
  105. extern KAFFINITY HalpActiveProcessors;
  106. #if !defined(_WIN64) || defined(PICACPI)
  107. extern UCHAR HalpClockMcaQueueDpc;
  108. #endif
  109. extern UCHAR MsgMCEPending[];
  110. extern WCHAR rgzSessionManager[];
  111. extern WCHAR rgzEnableMCE[];
  112. extern WCHAR rgzEnableMCA[];
  113. extern WCHAR rgzEnableCMC[];
  114. extern WCHAR rgzNoMCABugCheck[];
  115. extern UCHAR HalpGenuineIntel[];
  116. extern UCHAR HalpAuthenticAMD[];
  117. //
  118. // External prototypes
  119. //
  120. VOID
  121. HalpMcaCurrentProcessorSetTSS (
  122. IN PULONG pTSS
  123. );
  124. VOID
  125. HalpSetCr4MCEBit (
  126. VOID
  127. );
  128. //
  129. // Internal prototypes
  130. //
  131. VOID
  132. HalpMcaInit (
  133. VOID
  134. );
  135. NTSTATUS
  136. HalpMcaReadProcessorException (
  137. IN OUT PMCA_EXCEPTION Exception,
  138. IN BOOLEAN NonRestartableOnly
  139. );
  140. VOID
  141. HalpMcaQueueDpc(
  142. VOID
  143. );
  144. VOID
  145. HalpMcaGetConfiguration (
  146. OUT PULONG MCEEnabled,
  147. OUT PULONG MCAEnabled,
  148. OUT PULONG CMCEnabled,
  149. OUT PULONG NoMCABugCheck
  150. );
  151. VOID
  152. HalpMcaLockInterface(
  153. VOID
  154. );
  155. VOID
  156. HalpMcaUnlockInterface(
  157. VOID
  158. );
  159. NTSTATUS
  160. HalpMcaReadRegisterInterface(
  161. IN UCHAR BankNumber,
  162. IN OUT PMCA_EXCEPTION Exception
  163. );
  164. #ifdef ALLOC_PRAGMA
  165. #pragma alloc_text(INIT, HalpMcaInit)
  166. #pragma alloc_text(PAGELK, HalpMcaCurrentProcessorSetConfig)
  167. #pragma alloc_text(INIT, HalpMcaGetConfiguration)
  168. #pragma alloc_text(PAGE, HalpGetMcaLog)
  169. #pragma alloc_text(PAGE, HalpMceRegisterKernelDriver)
  170. #pragma alloc_text(PAGE, HalpMcaRegisterDriver)
  171. #pragma alloc_text(PAGE, HalpMcaLockInterface)
  172. #pragma alloc_text(PAGE, HalpMcaUnlockInterface)
  173. #pragma alloc_text(PAGE, HalpGetMceInformation)
  174. #endif
  175. // Set the following to check async capability
  176. ULONG HalpCMCEnabled = 0;
  177. ULONG HalpNoMCABugCheck = FALSE;
  178. BOOLEAN HalpMcaBlockErrorClearing = FALSE;
  179. //
  180. // HalpClearMachineCheckStatusRegistersOnInit indicates whether this is a
  181. // pre-P4 processor that needs to have its machine check status registers
  182. // cleared during initialization (boot or resume from hibernate). See
  183. // HalpMcaInit().
  184. //
  185. BOOLEAN HalpClearMachineCheckStatusRegistersOnInit = FALSE;
  186. VOID
  187. HalpMcaLockInterface(
  188. VOID
  189. )
  190. {
  191. ExAcquireFastMutex(&HalpMcaInfo.Mutex);
  192. #if DBG
  193. ASSERT(HalpMcaInterfaceLocked == FALSE);
  194. HalpMcaInterfaceLocked = TRUE;
  195. #endif
  196. }
  197. VOID
  198. HalpMcaUnlockInterface(
  199. VOID
  200. )
  201. {
  202. #if DBG
  203. ASSERT(HalpMcaInterfaceLocked == TRUE);
  204. HalpMcaInterfaceLocked = FALSE;
  205. #endif
  206. ExReleaseFastMutex(&HalpMcaInfo.Mutex);
  207. }
  208. //
  209. // All the initialization code for MCA goes here
  210. //
  211. VOID
  212. HalpMcaInit (
  213. VOID
  214. )
  215. /*++
  216. Routine Description:
  217. This routine is called to do all the initialization work
  218. Arguments:
  219. None
  220. Return Value:
  221. STATUS_SUCCESS if successful
  222. error status otherwise
  223. --*/
  224. {
  225. ULONGLONG MsrCapability;
  226. KIRQL OldIrql;
  227. KAFFINITY ActiveProcessors, CurrentAffinity;
  228. ULONGLONG MsrMceType;
  229. ULONG MCEEnabled;
  230. ULONG MCAEnabled;
  231. PULONG pTSS;
  232. PKPRCB Prcb;
  233. ULONG Junk;
  234. //
  235. // This lock synchronises access to the log area when we call the
  236. // logger on multiple processors.
  237. //
  238. // Note: This must be initialized regardless of whether or not
  239. // this processor supports MCA.
  240. //
  241. ExInitializeFastMutex (&HalpMcaInfo.Mutex);
  242. //
  243. // If this processor does not support MCA, nothing more to do.
  244. //
  245. if ( (!(HalpFeatureBits & HAL_MCE_PRESENT)) &&
  246. (!(HalpFeatureBits & HAL_MCA_PRESENT)) ) {
  247. return; // nothing to do
  248. }
  249. HalpMcaGetConfiguration(&MCEEnabled, &MCAEnabled, &HalpCMCEnabled, &HalpNoMCABugCheck);
  250. if ( (HalpFeatureBits & HAL_MCE_PRESENT) &&
  251. (!(HalpFeatureBits & HAL_MCA_PRESENT)) ) {
  252. if (MCEEnabled == FALSE) {
  253. // User has not enabled MCE capability.
  254. HalpFeatureBits &= ~(HAL_MCE_PRESENT | HAL_MCA_PRESENT);
  255. return;
  256. }
  257. #if DBG
  258. DbgPrint("MCE feature is enabled via registry\n");
  259. #endif // DBG
  260. MsrMceType = RDMSR(PENTIUM_MC_TYPE);
  261. if (((PLARGE_INTEGER)(&MsrMceType))->LowPart & MCE_VALID) {
  262. //
  263. // On an AST PREMMIA MX machine we seem to have a Machine
  264. // Check Pending always.
  265. //
  266. HalDisplayString(MsgMCEPending);
  267. HalpFeatureBits &= ~(HAL_MCE_PRESENT | HAL_MCA_PRESENT);
  268. return;
  269. }
  270. }
  271. //
  272. // If MCA is available, find out the number of banks available and
  273. // also get the platform specific bank 0 configuration
  274. //
  275. if ( HalpFeatureBits & HAL_MCA_PRESENT ) {
  276. if (MCAEnabled == FALSE) {
  277. //
  278. // User has disabled MCA capability.
  279. //
  280. #if DBG
  281. DbgPrint("MCA feature is disabled via registry\n");
  282. #endif // DBG
  283. HalpFeatureBits &= ~(HAL_MCE_PRESENT | HAL_MCA_PRESENT);
  284. return;
  285. }
  286. MsrCapability = RDMSR(MCG_CAP);
  287. HalpMcaInfo.NumBanks = (UCHAR)(MsrCapability & MCA_CNT_MASK);
  288. if (HalpMcaInfo.NumBanks == 0) {
  289. //
  290. // The processor claims to support MCA, but it doesn't have any
  291. // MCA error reporting banks. Don't enable either MCA or MCE.
  292. //
  293. // The VMWare "processor" falls into this category. It reports
  294. // HAL_MCE_PRESENT and HAL_MCA_PRESENT, but any attempt to read
  295. // MCA-related MSRs returns all zeros.
  296. //
  297. HalpFeatureBits &= ~(HAL_MCE_PRESENT | HAL_MCA_PRESENT);
  298. return;
  299. }
  300. //
  301. // Find out the Bank 0 configuration setup by BIOS. This will be used
  302. // as a mask when writing to Bank 0
  303. //
  304. HalpMcaInfo.Bank0Config = RDMSR(MC0_CTL);
  305. }
  306. ASSERT(HalpFeatureBits & HAL_MCE_PRESENT);
  307. //
  308. // If this is an older x86, we will clear the status register for each
  309. // bank during init. Pentium IIIs and older don't support the retention of
  310. // machine check information across a warm reset. However, some BIOSes
  311. // don't properly clear the machine check registers during init, resulting
  312. // in spurious eventlog entries that do not indicate a reliability problem.
  313. // We clear the status registers during init so that they are at least in a
  314. // consistent state.
  315. //
  316. // A similar situation seems to hold for pre-K8 AMD processors.
  317. //
  318. // Current IA32-compatible processors made by other manufacturers are known
  319. // not to support MCA, so there are no machine check registers.
  320. //
  321. // Assume that this is a processor for which we don't want trust the
  322. // state of the machine check registers across a reset. We will clear
  323. // the status registers on init.
  324. //
  325. HalpClearMachineCheckStatusRegistersOnInit = TRUE;
  326. //
  327. // Determine whether this is a processor on which warm boot MCA
  328. // information is to be trusted.
  329. //
  330. Prcb = KeGetCurrentPrcb();
  331. if (Prcb->CpuID) {
  332. UCHAR Buffer[13];
  333. //
  334. // Determine the processor type
  335. //
  336. HalpCpuID (0, &Junk, (PULONG) Buffer+0, (PULONG) Buffer+2, (PULONG) Buffer+1);
  337. Buffer[12] = 0;
  338. if ( ((strcmp(Buffer, HalpGenuineIntel) == 0) ||
  339. (strcmp(Buffer, HalpAuthenticAMD) == 0)) &&
  340. (Prcb->CpuType >= 0xF) ) {
  341. //
  342. // This is an:
  343. // Intel P4 processor or greater
  344. // AMD K8 processor or greater
  345. //
  346. // We can trust the state of the machine check registers on
  347. // this processor.
  348. //
  349. HalpClearMachineCheckStatusRegistersOnInit = FALSE;
  350. }
  351. }
  352. //
  353. // Initialize on each processor
  354. //
  355. ActiveProcessors = HalpActiveProcessors;
  356. for (CurrentAffinity = 1; ActiveProcessors; CurrentAffinity <<= 1) {
  357. if (ActiveProcessors & CurrentAffinity) {
  358. ActiveProcessors &= ~CurrentAffinity;
  359. KeSetSystemAffinityThread (CurrentAffinity);
  360. //
  361. // Initialize MCA support on this processor
  362. //
  363. //
  364. // Allocate memory for this processor's TSS and it's own
  365. // private stack
  366. //
  367. pTSS = (PULONG)ExAllocatePoolWithTag(NonPagedPool,
  368. MINIMUM_TSS_SIZE,
  369. HAL_POOL_TAG
  370. );
  371. if (!pTSS) {
  372. //
  373. // This allocation is critical.
  374. //
  375. KeBugCheckEx(HAL_MEMORY_ALLOCATION,
  376. MINIMUM_TSS_SIZE,
  377. 2,
  378. (ULONG_PTR)__FILE__,
  379. __LINE__
  380. );
  381. }
  382. RtlZeroMemory(pTSS, MINIMUM_TSS_SIZE);
  383. OldIrql = KfRaiseIrql(HIGH_LEVEL);
  384. HalpMcaCurrentProcessorSetTSS(pTSS);
  385. HalpMcaCurrentProcessorSetConfig();
  386. KeLowerIrql(OldIrql);
  387. }
  388. }
  389. //
  390. // Restore threads affinity
  391. //
  392. KeRevertToUserAffinityThread();
  393. }
  394. VOID
  395. HalpMcaCurrentProcessorSetConfig (
  396. VOID
  397. )
  398. /*++
  399. Routine Description:
  400. This routine sets/modifies the configuration of the machine check
  401. architecture on the current processor. Input is specification of
  402. the control register MCi_CTL for each bank of the MCA architecture.
  403. which controls the generation of machine check exceptions for errors
  404. logged to that bank.
  405. If MCA is not available on this processor, check if MCE is available.
  406. If so, enable MCE in CR4
  407. Arguments:
  408. Context: Array of values of MCi_CTL for each bank of MCA.
  409. If NULL, use MCA_DEFAULT_BANK_CONF values for each bank
  410. Return Value:
  411. None
  412. --*/
  413. {
  414. ULONGLONG MciCtl;
  415. ULONGLONG McgCap;
  416. ULONGLONG McgCtl;
  417. ULONG BankNum;
  418. if (HalpFeatureBits & HAL_MCA_PRESENT) {
  419. //
  420. // MCA is available. Initialize MCG_CTL register if present
  421. // Writing all 1's enable MCE or MCA Error Exceptions
  422. //
  423. McgCap = RDMSR(MCG_CAP);
  424. if (McgCap & MCG_CTL_PRESENT) {
  425. McgCtl = MCA_MCGCTL_ENABLE_LOGGING;
  426. WRMSR(MCG_CTL, McgCtl);
  427. }
  428. //
  429. // Enable all MCA errors
  430. //
  431. for ( BankNum = 0; BankNum < HalpMcaInfo.NumBanks; BankNum++ ) {
  432. //
  433. // If this is an older x86, clear the status register for this
  434. // bank. See HalpMcaInit() for more information about why this
  435. // is necessary.
  436. //
  437. if (HalpClearMachineCheckStatusRegistersOnInit) {
  438. WRMSR(MC0_STATUS + (BankNum * MCA_NUM_REGS), 0);
  439. }
  440. //
  441. // Use MCA_DEFAULT_BANK_CONF for each bank
  442. //
  443. MciCtl = MCA_DEFAULT_BANK_CONF;
  444. //
  445. // If this is bank 0, use HalpMcaInfo.Bank0Config as a mask
  446. //
  447. if (BankNum == 0) {
  448. MciCtl &= HalpMcaInfo.Bank0Config;
  449. }
  450. WRMSR(MC0_CTL + (BankNum * MCA_NUM_REGS), MciCtl);
  451. }
  452. }
  453. //
  454. // Enable MCE bit in CR4
  455. //
  456. HalpSetCr4MCEBit();
  457. }
  458. NTSTATUS
  459. HalpMceRegisterKernelDriver(
  460. IN PKERNEL_ERROR_HANDLER_INFO DriverInfo,
  461. IN ULONG InfoSize
  462. )
  463. /*++
  464. Routine Description:
  465. This routine is called by the kernel (via HalSetSystemInformation)
  466. to register its presence. This is mostly for WMI callbacks registration.
  467. Arguments:
  468. DriverInfo: Contains kernel info about the callbacks and associated objects.
  469. Return Value:
  470. Unless a MCA driver is already registered OR one of the two callback
  471. routines are NULL, this routine returns Success.
  472. Implementation Notes:
  473. - the current implementation assumes the kernel registers its callbacks
  474. earlier than a driver will. The current kernel registration is done by
  475. WMI and should be done at WMI-Phase 0.
  476. - the registrations do not consider if the HAL supports or not the MCA
  477. functionalities. It simply registers the callbacks if no other callback was
  478. registered before. This allows us to allow some flexibility if a machine event
  479. functionality is enabled AFTER the hal initialization (e.g. HalpGetFeatureBits())
  480. through the mean of a registry key or driver event, for example.
  481. --*/
  482. {
  483. NTSTATUS status;
  484. PAGED_CODE();
  485. if ( DriverInfo == NULL ) {
  486. return STATUS_INVALID_PARAMETER;
  487. }
  488. //
  489. // Backward compatibility only.
  490. //
  491. if ( (DriverInfo->Version != 0) &&
  492. (DriverInfo->Version > KERNEL_ERROR_HANDLER_VERSION) ) {
  493. return STATUS_REVISION_MISMATCH;
  494. }
  495. //
  496. // Register Kernel MCA notification.
  497. //
  498. status = STATUS_UNSUCCESSFUL;
  499. HalpMcaLockInterface();
  500. if ( HalpMcaInfo.WmiMcaCallback == NULL ) {
  501. HalpMcaInfo.WmiMcaCallback = DriverInfo->KernelMcaDelivery;
  502. status = STATUS_SUCCESS;
  503. }
  504. HalpMcaUnlockInterface();
  505. return status;
  506. } // HalpMceRegisterKernelDriver
  507. NTSTATUS
  508. HalpMcaRegisterDriver(
  509. IN PMCA_DRIVER_INFO DriverInfo
  510. )
  511. /*++
  512. Routine Description:
  513. This routine is called by the driver (via HalSetSystemInformation)
  514. to register its presence. Only one driver can be registered at a time.
  515. Arguments:
  516. DriverInfo: Contains info about the callback routine and the DriverObject
  517. Return Value:
  518. Unless a MCA driver is already registered OR one of the two callback
  519. routines are NULL, this routine returns Success.
  520. --*/
  521. {
  522. KIRQL OldIrql;
  523. PVOID UnlockHandle;
  524. NTSTATUS Status;
  525. PAGED_CODE();
  526. Status = STATUS_UNSUCCESSFUL;
  527. if ( (HalpFeatureBits & HAL_MCE_PRESENT) && (DriverInfo->ExceptionCallback != NULL) ) {
  528. HalpMcaLockInterface();
  529. //
  530. // Register driver
  531. //
  532. if ( HalpMcaInfo.DriverInfo.ExceptionCallback == NULL ) {
  533. // register driver
  534. HalpMcaInfo.DriverInfo.ExceptionCallback = DriverInfo->ExceptionCallback;
  535. HalpMcaInfo.DriverInfo.DeviceContext = DriverInfo->DeviceContext;
  536. Status = STATUS_SUCCESS;
  537. }
  538. HalpMcaUnlockInterface();
  539. } else if ( DriverInfo->ExceptionCallback == NULL) {
  540. HalpMcaLockInterface();
  541. // Only deregistering myself is permitted
  542. if (HalpMcaInfo.DriverInfo.DeviceContext == DriverInfo->DeviceContext) {
  543. HalpMcaInfo.DriverInfo.ExceptionCallback = NULL;
  544. HalpMcaInfo.DriverInfo.DeviceContext = NULL;
  545. Status = STATUS_SUCCESS;
  546. }
  547. HalpMcaUnlockInterface();
  548. }
  549. return Status;
  550. }
  551. #define MAX_MCA_NONFATAL_RETRIES 50000 // BUGBUG
  552. #define MCA_NONFATAL_ERORRS_BEFORE_WBINVD 3
  553. NTSTATUS
  554. HalpGetMcaLog (
  555. IN OUT PMCA_EXCEPTION Exception,
  556. IN ULONG BufferSize,
  557. OUT PULONG Length
  558. )
  559. /*++
  560. Routine Description:
  561. This is the entry point for driver to read the bank logs
  562. Called by HaliQuerySystemInformation()
  563. Arguments:
  564. Exception: Buffer into which the error is reported
  565. BufferSize: Size of the passed buffer
  566. Length: of the result
  567. Return Value:
  568. Success or failure
  569. --*/
  570. {
  571. KAFFINITY ActiveProcessors, CurrentAffinity;
  572. NTSTATUS Status;
  573. ULONG p1, p2;
  574. ULONGLONG p3;
  575. static ULONG McaStatusCount = 0;
  576. static ULONG SavedBank = 0;
  577. static ULONGLONG SavedMcaStatus = 0;
  578. static KAFFINITY SavedAffinity = 0;
  579. PAGED_CODE();
  580. if (! (HalpFeatureBits & HAL_MCA_PRESENT)) {
  581. return(STATUS_NO_SUCH_DEVICE);
  582. }
  583. //
  584. // The following is a hack added for RC2 of the .NET Server release.
  585. // It should be removed when the HAL is changed in such a way that
  586. // HalpMcaCurrentProcessorSetConfig() is called on resume from
  587. // hibernate.
  588. //
  589. // Pentium IIIs don't support the retention of machine check
  590. // information across a warm reset. However, some BIOSes don't
  591. // properly clear the machine check registers during init, resulting
  592. // in spurious eventlog entries that do not indicate a reliability
  593. // problem. HalpMcaCurrentProcessorSetConfig() clears the status
  594. // registers so that they are at least in a consistent state.
  595. //
  596. // A similar situation seems to hold for pre-K8 AMD processors.
  597. //
  598. // But on resume from hibernate, HalpMcaCurrentProcessorSetConfig()
  599. // currently is not called. This means that spurious machine checks
  600. // might be present in the machine check MSRs when we get here. So
  601. // on older Intel x86 machines, we never return anything from the MSRs.
  602. //
  603. if (HalpClearMachineCheckStatusRegistersOnInit) {
  604. //
  605. // This is a processor for which we cannot trust the contents of the
  606. // MSRs. Do not return any machine check information.
  607. //
  608. return STATUS_NOT_FOUND;
  609. }
  610. //
  611. // Don't allow the logging driver to read machine check information.
  612. // Only WMI is allowed to retrieve this information.
  613. //
  614. if ( *(PULONG)Exception != HALP_KERNEL_TOKEN ) {
  615. return STATUS_NOT_FOUND;
  616. }
  617. switch (BufferSize) {
  618. case MCA_EXCEPTION_V1_SIZE:
  619. Exception->VersionNumber = 1;
  620. break;
  621. case MCA_EXCEPTION_V2_SIZE:
  622. Exception->VersionNumber = 2;
  623. break;
  624. default:
  625. return(STATUS_INVALID_PARAMETER);
  626. }
  627. ActiveProcessors = HalpActiveProcessors;
  628. Status = STATUS_NOT_FOUND;
  629. HalpMcaLockInterface();
  630. *Length = 0;
  631. for (CurrentAffinity = 1; ActiveProcessors; CurrentAffinity <<= 1) {
  632. if (ActiveProcessors & CurrentAffinity) {
  633. ActiveProcessors &= ~CurrentAffinity;
  634. KeSetSystemAffinityThread (CurrentAffinity);
  635. //
  636. // Check this processor for an exception
  637. //
  638. Status = HalpMcaReadProcessorException (Exception, FALSE);
  639. //
  640. // Avoiding going into an infinite loop reporting a non-fatal
  641. // single bit MCA error. This can be the result of the same
  642. // error being generated repeatedly by the hardware.
  643. //
  644. if (Status == STATUS_SUCCESS) {
  645. //
  646. // Check to see if the same processor is reporting the
  647. // same MCA status.
  648. //
  649. if ((CurrentAffinity == SavedAffinity) &&
  650. (SavedBank == Exception->u.Mca.BankNumber) &&
  651. (SavedMcaStatus == Exception->u.Mca.Status.QuadPart)) {
  652. //
  653. // Check to see if the same error is generated more than
  654. // n times. Currently n==5. If so, bugcheck the system.
  655. //
  656. // N.B. This code disabled because it can cause unnecessary
  657. // bugchecks if the same error is reported infrequently
  658. // during a long system uptime.
  659. //
  660. #if 0
  661. if (McaStatusCount >= MAX_MCA_NONFATAL_RETRIES) {
  662. if (Exception->VersionNumber == 1) {
  663. //
  664. // Parameters for bugcheck
  665. //
  666. p1 = ((PLARGE_INTEGER)(&Exception->u.Mce.Type))->LowPart;
  667. p2 = 0;
  668. p3 = Exception->u.Mce.Address;
  669. } else {
  670. //
  671. // Parameters for bugcheck
  672. //
  673. p1 = Exception->u.Mca.BankNumber;
  674. p2 = Exception->u.Mca.Address.Address;
  675. p3 = Exception->u.Mca.Status.QuadPart;
  676. }
  677. //
  678. // We need a new bugcheck code for this case.
  679. //
  680. KeBugCheckEx (
  681. MACHINE_CHECK_EXCEPTION,
  682. p1,
  683. p2,
  684. ((PLARGE_INTEGER)(&p3))->HighPart,
  685. ((PLARGE_INTEGER)(&p3))->LowPart
  686. );
  687. } else
  688. #endif
  689. {
  690. //
  691. // This error is seen more than 1 once. If it has
  692. // occurred more than
  693. // MCA_NONFATAL_ERORRS_BEFORE_WBINVD times, write
  694. // back and Invalid cache to see if the error can be
  695. // cleared.
  696. //
  697. McaStatusCount++;
  698. if (McaStatusCount >=
  699. MCA_NONFATAL_ERORRS_BEFORE_WBINVD) {
  700. _asm {
  701. wbinvd
  702. }
  703. }
  704. }
  705. } else {
  706. //
  707. // First time we have seen this error, save the Status,
  708. // affinity and clear the count.
  709. //
  710. SavedMcaStatus = Exception->u.Mca.Status.QuadPart;
  711. SavedBank = Exception->u.Mca.BankNumber;
  712. McaStatusCount = 0;
  713. SavedAffinity = CurrentAffinity;
  714. }
  715. } else {
  716. //
  717. // Either we did not get an error or it will be fatal.
  718. // If we did not get an error and we are doing the processor
  719. // that reported the last error, clear things so we do not
  720. // match previous errors. Since each time we look for an
  721. // error we start over with the first processor we do not
  722. // have to worry about multiple processor having stuck
  723. // errors. We will only be able to see the first one.
  724. //
  725. if (SavedAffinity == CurrentAffinity) {
  726. SavedMcaStatus = 0;
  727. SavedBank = 0;
  728. McaStatusCount = 0;
  729. SavedAffinity = 0;
  730. }
  731. }
  732. //
  733. // If found, return current information
  734. //
  735. if (Status != STATUS_NOT_FOUND) {
  736. ASSERT (Status != STATUS_SEVERITY_ERROR);
  737. *Length = BufferSize;
  738. break;
  739. }
  740. }
  741. }
  742. //
  743. // Restore threads affinity, release mutex, and return
  744. //
  745. KeRevertToUserAffinityThread();
  746. HalpMcaUnlockInterface();
  747. return Status;
  748. }
  749. VOID
  750. HalpMcaExceptionHandler (
  751. VOID
  752. )
  753. /*++
  754. Routine Description:
  755. This is the MCA exception handler.
  756. Arguments:
  757. None
  758. Return Value:
  759. None
  760. --*/
  761. {
  762. NTSTATUS Status;
  763. MCA_EXCEPTION BankLog;
  764. ULONG p1;
  765. ULONGLONG McgStatus, p3;
  766. //
  767. // In the event that we are going to bugcheck (likely outcome of
  768. // entering this routine), don't clear the MCA error logs in the
  769. // hardware. We want them to be available at boot time so they
  770. // can be copied to the system event log.
  771. //
  772. HalpMcaBlockErrorClearing = TRUE;
  773. if (!(HalpFeatureBits & HAL_MCA_PRESENT) ) {
  774. //
  775. // If we have ONLY MCE (and not MCA), read the MC_ADDR and MC_TYPE
  776. // MSRs, print the values and bugcheck as the errors are not
  777. // restartable.
  778. //
  779. BankLog.VersionNumber = 1;
  780. BankLog.ExceptionType = HAL_MCE_RECORD;
  781. BankLog.u.Mce.Address = RDMSR(PENTIUM_MC_ADDR);
  782. BankLog.u.Mce.Type = RDMSR(PENTIUM_MC_TYPE);
  783. Status = STATUS_SEVERITY_ERROR;
  784. //
  785. // Parameters for bugcheck
  786. //
  787. p1 = ((PLARGE_INTEGER)(&BankLog.u.Mce.Type))->LowPart;
  788. p3 = BankLog.u.Mce.Address;
  789. } else {
  790. McgStatus = RDMSR(MCG_STATUS);
  791. ASSERT( (McgStatus & MCG_MC_INPROGRESS) != 0);
  792. BankLog.VersionNumber = 2;
  793. Status = HalpMcaReadProcessorException (&BankLog, TRUE);
  794. //
  795. // Clear MCIP bit in MCG_STATUS register
  796. //
  797. McgStatus = 0;
  798. WRMSR(MCG_STATUS, McgStatus);
  799. //
  800. // Parameters for bugcheck
  801. //
  802. p1 = BankLog.u.Mca.BankNumber;
  803. p3 = BankLog.u.Mca.Status.QuadPart;
  804. }
  805. if (Status == STATUS_SEVERITY_ERROR) {
  806. //
  807. // Call the exception callback of the driver so that
  808. // the error can be logged to NVRAM
  809. //
  810. if (HalpMcaInfo.DriverInfo.ExceptionCallback) {
  811. HalpMcaInfo.DriverInfo.ExceptionCallback (
  812. HalpMcaInfo.DriverInfo.DeviceContext,
  813. &BankLog
  814. );
  815. }
  816. if (HalpNoMCABugCheck == FALSE) {
  817. //
  818. // Bugcheck
  819. //
  820. KeBugCheckEx(MACHINE_CHECK_EXCEPTION,
  821. p1,
  822. (ULONG_PTR) &BankLog,
  823. ((PLARGE_INTEGER)(&p3))->HighPart,
  824. ((PLARGE_INTEGER)(&p3))->LowPart);
  825. // NOT REACHED
  826. }
  827. }
  828. HalpMcaBlockErrorClearing = FALSE;
  829. //
  830. // Must be restartable. Indicate to the timer tick routine that a
  831. // DPC needs to be queued for WMI.
  832. //
  833. // NOTE: This used to check for the MCA logging driver being registered.
  834. // We no longer deliver corrected machine checks to the driver. They only
  835. // go to WMI.
  836. //
  837. if ( HalpMcaInfo.WmiMcaCallback != NULL ) {
  838. HalpClockMcaQueueDpc = 1;
  839. }
  840. }
  841. VOID
  842. HalpMcaQueueDpc(
  843. VOID
  844. )
  845. /*++
  846. Routine Description:
  847. Gets called from the timer tick to tell WMI about a corrected machine
  848. check.
  849. --*/
  850. {
  851. ASSERT( HalpMcaInfo.WmiMcaCallback != NULL );
  852. HalpMcaInfo.WmiMcaCallback( (PVOID)HALP_KERNEL_TOKEN, McaAvailable, NULL );
  853. }
  854. NTSTATUS
  855. HalpMcaReadRegisterInterface(
  856. IN UCHAR BankNumber,
  857. IN OUT PMCA_EXCEPTION Exception
  858. )
  859. /*++
  860. Routine Description:
  861. This routine reads the given MCA register number from the
  862. current processor and returns the result in the Exception
  863. structure.
  864. Arguments:
  865. BankNumber Number of MCA Bank to be examined.
  866. Exception Buffer into which the error is reported
  867. Return Value:
  868. STATUS_SUCCESS if an error was found and the data copied into
  869. the excption buffer.
  870. STATUS_UNSUCCESSFUL if the register contains no error information.
  871. STATUS_NOT_FOUND if the register number exceeds the maximum number
  872. of registers.
  873. STATUS_INVALID_PARAMETER if the Exception record is of an unknown type.
  874. --*/
  875. {
  876. ULONGLONG McgStatus;
  877. MCI_STATS istatus;
  878. NTSTATUS ReturnStatus;
  879. MCG_CAPABILITY cap;
  880. ULONG Reg;
  881. //
  882. // Are they asking for a valid register?
  883. //
  884. if (BankNumber >= HalpMcaInfo.NumBanks) {
  885. return STATUS_NOT_FOUND;
  886. }
  887. //
  888. // The exception buffer should tell us if it's version 1 or
  889. // 2. Anything else we don't know how to deal with.
  890. //
  891. if ((Exception->VersionNumber < 1) ||
  892. (Exception->VersionNumber > 2)) {
  893. return STATUS_INVALID_PARAMETER;
  894. }
  895. //
  896. // Read the global status register
  897. //
  898. McgStatus = RDMSR(MCG_STATUS);
  899. //
  900. // Read the Status MSR of the requested bank
  901. //
  902. istatus.QuadPart = RDMSR(MC0_STATUS + BankNumber * MCA_NUM_REGS);
  903. if (istatus.MciStats.Valid == 0) {
  904. //
  905. // No error in this bank.
  906. //
  907. return STATUS_UNSUCCESSFUL;
  908. }
  909. //
  910. // Set the return status to indicate whether execution can be continued.
  911. // STATUS_SUCCESS means "An exception was found, and execution can be
  912. // continued." STATUS_SEVERITY_ERROR means "An exception was found, and
  913. // execution must not be continued."
  914. //
  915. // If a machine check exception is not in progress, then execution can be
  916. // continued. This happens when polling for hardware-corrected errors
  917. // finds an error that the hardware corrected without interrupting
  918. // execution. (Note that this case also applies to an error that was fatal
  919. // to an earlier boot. The system bugchecked, and initial polling on the
  920. // reboot is now finding the error.)
  921. //
  922. // If a machine check exception is in progress, then execution can be
  923. // restarted only if the error has been corrected and the necessary
  924. // restart information is intact (restart EIP valid and processor context
  925. // not corrupt).
  926. //
  927. // This code used to check only for the restart information being valid.
  928. // These bits do indicate whether there is valid context for restarting
  929. // from an error, but there has still been an error, and unless we plan
  930. // to correct the error, we should not continue. Currently we do not do
  931. // any correction or containment in software, so all uncorrected errors
  932. // are fatal.
  933. //
  934. ReturnStatus = STATUS_SUCCESS;
  935. if ( ((McgStatus & MCG_MC_INPROGRESS) != 0) &&
  936. ( (istatus.MciStats.UnCorrected == 1) ||
  937. ((McgStatus & MCG_RESTART_EIP_VALID) == 0) ||
  938. (istatus.MciStats.Damage == 1) ) ) {
  939. ReturnStatus = STATUS_SEVERITY_ERROR;
  940. }
  941. //
  942. // Complete extended exception record, if requested.
  943. //
  944. if (Exception->VersionNumber == 2) {
  945. cap.u.QuadPart = RDMSR(MCG_CAP);
  946. if (cap.u.hw.McaExtCnt > 0) {
  947. // Get Version 2 stuff from MSRs.
  948. Exception->ExtCnt = cap.u.hw.McaExtCnt;
  949. if (Exception->ExtCnt > MCA_EXTREG_V2MAX) {
  950. Exception->ExtCnt = MCA_EXTREG_V2MAX;
  951. }
  952. for (Reg = 0; Reg < Exception->ExtCnt; Reg++) {
  953. Exception->ExtReg[Reg] = RDMSR(MCG_EAX+Reg);
  954. }
  955. if (cap.u.hw.McaExtCnt >= MCG_EFLAGS_OFFSET) {
  956. if (RDMSR(MCG_EFLAGS) == 0) {
  957. // Let user know he got only Version 1 data.
  958. Exception->VersionNumber = 1;
  959. }
  960. }
  961. } else {
  962. // Let user know he got only Version 1 data.
  963. Exception->VersionNumber = 1;
  964. }
  965. }
  966. //
  967. // Complete exception record
  968. //
  969. Exception->ExceptionType = HAL_MCA_RECORD;
  970. Exception->TimeStamp.QuadPart = 0;
  971. Exception->ProcessorNumber = KeGetCurrentProcessorNumber();
  972. Exception->Reserved1 = 0;
  973. Exception->u.Mca.BankNumber = BankNumber;
  974. RtlZeroMemory(Exception->u.Mca.Reserved2, sizeof(Exception->u.Mca.Reserved2));
  975. Exception->u.Mca.Status = istatus;
  976. Exception->u.Mca.Address.QuadPart = 0;
  977. Exception->u.Mca.Misc = 0;
  978. if (KeGetCurrentIrql() != CLOCK2_LEVEL) {
  979. KeQuerySystemTime(&Exception->TimeStamp);
  980. }
  981. if (istatus.MciStats.AddressValid) {
  982. Exception->u.Mca.Address.QuadPart =
  983. RDMSR(MC0_ADDR + BankNumber * MCA_NUM_REGS);
  984. }
  985. //
  986. // Although MiscValid should never be set on P6 it
  987. // seems that sometimes it is. It is not implemented
  988. // on P6 and above so don't read it there.
  989. //
  990. if (istatus.MciStats.MiscValid &&
  991. (KeGetCurrentPrcb()->CpuType != 6)) {
  992. Exception->u.Mca.Misc =
  993. RDMSR(MC0_MISC + BankNumber * MCA_NUM_REGS);
  994. }
  995. //
  996. // Clear Machine Check in MCi_STATUS register
  997. //
  998. if (HalpMcaBlockErrorClearing == FALSE) {
  999. WRMSR(MC0_STATUS + Exception->u.Mca.BankNumber * MCA_NUM_REGS, 0);
  1000. //
  1001. // Clear Register state in MCG_EFLAGS
  1002. //
  1003. if (Exception->VersionNumber != 1) {
  1004. WRMSR(MCG_EFLAGS, 0);
  1005. }
  1006. }
  1007. //
  1008. // When the Valid bit of status register is cleared, hardware may write
  1009. // a new buffered error report into the error reporting area. The
  1010. // serializing instruction is required to permit the update to complete
  1011. //
  1012. HalpSerialize();
  1013. return(ReturnStatus);
  1014. }
  1015. NTSTATUS
  1016. HalpMcaReadProcessorException (
  1017. IN OUT PMCA_EXCEPTION Exception,
  1018. IN BOOLEAN NonRestartableOnly
  1019. )
  1020. /*++
  1021. Routine Description:
  1022. This routine logs the errors from the MCA banks on one processor.
  1023. Necessary checks for the restartability are performed. The routine
  1024. 1> Checks for restartability, and for each bank identifies valid bank
  1025. entries and logs error.
  1026. 2> If the error is not restartable provides additional information about
  1027. bank and the MCA registers.
  1028. 3> Resets the Status registers for each bank
  1029. Arguments:
  1030. Exception: Into which we log the error if found
  1031. NonRestartableOnly: Get any error vs. look for error that is not-restartable
  1032. Return Values:
  1033. STATUS_SEVERITY_ERROR: Detected non-restartable error.
  1034. STATUS_SUCCESS: Successfully logged bank values
  1035. STATUS_NOT_FOUND: No error found on any bank
  1036. --*/
  1037. {
  1038. UCHAR BankNumber;
  1039. NTSTATUS ReturnStatus = STATUS_NOT_FOUND;
  1040. //
  1041. // scan banks on current processor and log contents of first valid bank
  1042. // reporting error. Once we find a valid error, no need to read remaining
  1043. // banks. It is the application responsibility to read more errors.
  1044. //
  1045. for (BankNumber = 0; BankNumber < HalpMcaInfo.NumBanks; BankNumber++) {
  1046. ReturnStatus = HalpMcaReadRegisterInterface(BankNumber, Exception);
  1047. if ((ReturnStatus == STATUS_UNSUCCESSFUL) ||
  1048. ((ReturnStatus == STATUS_SUCCESS) &&
  1049. (NonRestartableOnly == TRUE))) {
  1050. //
  1051. // No error in this bank or only looking for non-restartable
  1052. // errors, skip this one.
  1053. //
  1054. ReturnStatus = STATUS_NOT_FOUND;
  1055. continue;
  1056. }
  1057. ASSERT((ReturnStatus == STATUS_SUCCESS) ||
  1058. (ReturnStatus == STATUS_SEVERITY_ERROR));
  1059. break;
  1060. }
  1061. return(ReturnStatus);
  1062. }
  1063. VOID
  1064. HalpMcaGetConfiguration (
  1065. OUT PULONG MCEEnabled,
  1066. OUT PULONG MCAEnabled,
  1067. OUT PULONG CMCEnabled,
  1068. OUT PULONG NoMCABugCheck
  1069. )
  1070. /*++
  1071. Routine Description:
  1072. This routine stores the Machine Check configuration information.
  1073. Arguments:
  1074. MCEEnabled - Pointer to the MCEEnabled indicator.
  1075. 0 = False, 1 = True (0 if value not present in Registry).
  1076. MCAEnabled - Pointer to the MCAEnabled indicator.
  1077. 0 = False, 1 = True (1 if value not present in Registry).
  1078. Return Value:
  1079. None.
  1080. --*/
  1081. {
  1082. RTL_QUERY_REGISTRY_TABLE Parameters[5];
  1083. ULONG DefaultDataMCE;
  1084. ULONG DefaultDataMCA;
  1085. ULONG DefaultDataCMC;
  1086. ULONG DefaultNoMCABugCheck;
  1087. RtlZeroMemory(Parameters, sizeof(Parameters));
  1088. DefaultDataMCE = *MCEEnabled = FALSE;
  1089. DefaultDataMCA = *MCAEnabled = TRUE;
  1090. DefaultDataCMC = *CMCEnabled = 60; // default polling interval
  1091. DefaultNoMCABugCheck = *NoMCABugCheck = FALSE;
  1092. //
  1093. // Gather all of the "user specified" information from
  1094. // the registry.
  1095. //
  1096. Parameters[0].Flags = RTL_QUERY_REGISTRY_DIRECT;
  1097. Parameters[0].Name = rgzEnableMCE;
  1098. Parameters[0].EntryContext = MCEEnabled;
  1099. Parameters[0].DefaultType = REG_DWORD;
  1100. Parameters[0].DefaultData = &DefaultDataMCE;
  1101. Parameters[0].DefaultLength = sizeof(ULONG);
  1102. Parameters[1].Flags = RTL_QUERY_REGISTRY_DIRECT;
  1103. Parameters[1].Name = rgzEnableMCA;
  1104. Parameters[1].EntryContext = MCAEnabled;
  1105. Parameters[1].DefaultType = REG_DWORD;
  1106. Parameters[1].DefaultData = &DefaultDataMCA;
  1107. Parameters[1].DefaultLength = sizeof(ULONG);
  1108. Parameters[2].Flags = RTL_QUERY_REGISTRY_DIRECT;
  1109. Parameters[2].Name = rgzEnableCMC;
  1110. Parameters[2].EntryContext = CMCEnabled;
  1111. Parameters[2].DefaultType = REG_DWORD;
  1112. Parameters[2].DefaultData = &DefaultDataCMC;
  1113. Parameters[2].DefaultLength = sizeof(ULONG);
  1114. Parameters[3].Flags = RTL_QUERY_REGISTRY_DIRECT;
  1115. Parameters[3].Name = rgzNoMCABugCheck;
  1116. Parameters[3].EntryContext = NoMCABugCheck;
  1117. Parameters[3].DefaultType = REG_DWORD;
  1118. Parameters[3].DefaultData = &DefaultNoMCABugCheck;
  1119. Parameters[3].DefaultLength = sizeof(ULONG);
  1120. RtlQueryRegistryValues(
  1121. RTL_REGISTRY_CONTROL | RTL_REGISTRY_OPTIONAL,
  1122. rgzSessionManager,
  1123. Parameters,
  1124. NULL,
  1125. NULL
  1126. );
  1127. //
  1128. // Make sure the value for CMCEnabled is valid. If less than 0, set it to
  1129. // 0 (disabled). If greater than 0, make sure polling isn't too frequent.
  1130. //
  1131. if ( *(PLONG)CMCEnabled <= 0 ) {
  1132. *CMCEnabled = 0;
  1133. } else if ( *CMCEnabled < 15 ) {
  1134. *CMCEnabled = 15;
  1135. }
  1136. }
  1137. VOID
  1138. HalHandleMcheck (
  1139. IN PKTRAP_FRAME TrapFrame,
  1140. IN PKEXCEPTION_FRAME ExceptionFrame
  1141. )
  1142. {
  1143. HalpMcaExceptionHandler();
  1144. }
  1145. NTSTATUS
  1146. HalpGetMceInformation(
  1147. PHAL_ERROR_INFO ErrorInfo,
  1148. PULONG ErrorInfoLength
  1149. )
  1150. /*++
  1151. Routine Description:
  1152. This routine is called by HaliQuerySystemInformation for the HalErrorInformation class.
  1153. Arguments:
  1154. ErrorInfo : pointer to HAL_ERROR_INFO structure.
  1155. ErrorInfoLength : size of the valid memory structure pointed by ErrorInfo.
  1156. Return Value:
  1157. STATUS_SUCCESS if successful
  1158. error status otherwise
  1159. --*/
  1160. {
  1161. NTSTATUS status;
  1162. ULONG savedVersion;
  1163. PAGED_CODE();
  1164. ASSERT( ErrorInfo != NULL );
  1165. ASSERT( *ErrorInfoLength == sizeof(HAL_ERROR_INFO) );
  1166. //
  1167. // Backward compatibility only.
  1168. //
  1169. if ( (ErrorInfo->Version == 0) || (ErrorInfo->Version > HAL_ERROR_INFO_VERSION) ) {
  1170. return STATUS_REVISION_MISMATCH;
  1171. }
  1172. ASSERT( ErrorInfo->Version == HAL_ERROR_INFO_VERSION );
  1173. //
  1174. // Zero the output structure, then in the few fields that are meaningful.
  1175. //
  1176. savedVersion = ErrorInfo->Version;
  1177. RtlZeroMemory( ErrorInfo, sizeof(HAL_ERROR_INFO) );
  1178. ErrorInfo->Version = savedVersion;
  1179. ErrorInfo->McaMaxSize = sizeof(MCA_EXCEPTION);
  1180. ErrorInfo->CmcMaxSize = sizeof(MCA_EXCEPTION);
  1181. ErrorInfo->McaPreviousEventsCount = 1; // Set to 1 to get WMI to poll immediately
  1182. if ( (HalpFeatureBits & HAL_MCA_PRESENT) == 0 ) {
  1183. ErrorInfo->CmcPollingInterval = HAL_CMC_DISABLED;
  1184. } else {
  1185. ErrorInfo->CmcPollingInterval = HalpCMCEnabled;
  1186. }
  1187. ErrorInfo->CpePollingInterval = HAL_CPE_DISABLED;
  1188. ErrorInfo->McaKernelToken = HALP_KERNEL_TOKEN;
  1189. ErrorInfo->CmcKernelToken = HALP_KERNEL_TOKEN;
  1190. *ErrorInfoLength = sizeof(HAL_ERROR_INFO);
  1191. return STATUS_SUCCESS;
  1192. } // HalpGetMceInformation