//** Copyright (C) 1996-2000 Intel Corporation. All rights reserved. //** //** The information and source code contained herein is the exclusive //** property of Intel Corporation and may not be disclosed, examined //** or reproduced in whole or in part without explicit written authorization //** from the company. //** //########################################################################### //----------------------------------------------------------------------------- // Version control information follows. // // // 10 Jun 1999 Bugcheck Bernard Lint // M. Jayakumar (Muthurajan.Jayakumar@intel.com) // Thierry Fevrier /////////////////////////////////////////////////////////////////////////////// // // Module Name: OSMCA.C - Merced OS Machine Check Handler // // Description: // This module has OS Machine Check Handler Reference Code. // // Contents: HalpOsMcaInit() // HalpCmcHandler() // HalpMcaHandler() // HalpMcRzHandlr() // HalpMcWkupHandlr() // HalpProcMcaHndlr() // HalpPlatMcaHndlr() // // // Target Platform: Merced // // Reuse: None // ////////////////////////////////////////////////////////////////////////////M// #include "halp.h" #include "nthal.h" #include "arc.h" #include "i64fw.h" #include "check.h" #include "iosapic.h" #include "inbv.h" #include "osmca.h" // pmdata.c: CPE definitions. extern ULONG HalpMaxCPEImplemented; extern ULONG HalpCPEIntIn[]; // i64fw.c: HAL Private Data structure for SAL/PAL extern HALP_SAL_PAL_DATA HalpSalPalData; // i64fwasm.s: low-level protection data structures extern KSPIN_LOCK HalpMcaSpinLock; extern KSPIN_LOCK HalpCmcSpinLock; extern KSPIN_LOCK HalpCpeSpinLock; // // IA64 MCE Info structures to keep track of MCE features // available on installed hardware. // HALP_MCA_INFO HalpMcaInfo; HALP_CMC_INFO HalpCmcInfo; HALP_CPE_INFO HalpCpeInfo; KERNEL_MCE_DELIVERY HalpMceKernelDelivery; volatile ULONG HalpOsMcaInProgress = 0; // // SAL_MC_SET_PARAMS.time_out // ULONGLONG HalpMcRendezTimeOut = HALP_DEFAULT_MC_RENDEZ_TIMEOUT; // // HalpProcessorMcaRecords: // // Number of MCA records pre-allocated per processor. // ULONGLONG HalpProcessorMcaRecords = HALP_DEFAULT_PROCESSOR_MCA_RECORDS; // // HalpProcessorInitRecords: // // Number of INIT records pre-allocated per processor. // ULONGLONG HalpProcessorInitRecords = HALP_DEFAULT_PROCESSOR_INIT_RECORDS; // // HalpMceLogsMaxCount: // // Maximum number of saved logs. // ULONG HalpMceLogsMaxCount = HALP_MCELOGS_MAXCOUNT; // // HAL Private Error Device GUIDs: // [useful for kdexts] // ERROR_DEVICE_GUID HalpErrorProcessorGuid = ERROR_PROCESSOR_GUID; ERROR_DEVICE_GUID HalpErrorMemoryGuid = ERROR_MEMORY_GUID; ERROR_DEVICE_GUID HalpErrorPciBusGuid = ERROR_PCI_BUS_GUID; ERROR_DEVICE_GUID HalpErrorPciComponentGuid = ERROR_PCI_COMPONENT_GUID; ERROR_DEVICE_GUID HalpErrorSystemEventLogGuid = ERROR_SYSTEM_EVENT_LOG_GUID; ERROR_DEVICE_GUID HalpErrorSmbiosGuid = ERROR_SMBIOS_GUID; ERROR_DEVICE_GUID HalpErrorPlatformSpecificGuid = ERROR_PLATFORM_SPECIFIC_GUID; ERROR_DEVICE_GUID HalpErrorPlatformBusGuid = ERROR_PLATFORM_BUS_GUID; ERROR_DEVICE_GUID HalpErrorPlatformHostControllerGuid = ERROR_PLATFORM_HOST_CONTROLLER_GUID; // // HAL Private Error Definitions: // [useful for kdexts] // Actually in this case, the typed pointers allow also the inclusion of the symbols definitions // without the data structures sizes. // PERROR_MODINFO HalpPErrorModInfo; PERROR_PROCESSOR_CPUID_INFO HalpPErrorProcessorCpuIdInfo; PERROR_PROCESSOR HalpPErrorProcessor; PERROR_PROCESSOR_STATIC_INFO HalpPErrorProcessorStaticInfo; PERROR_MEMORY HalpPErrorMemory; PERROR_PCI_BUS HalpPErrorPciBus; PERROR_PCI_COMPONENT HalpPErrorPciComponent; PERROR_SYSTEM_EVENT_LOG HalpPErrorSystemEventLog; PERROR_SMBIOS HalpPErrorSmbios; PERROR_PLATFORM_SPECIFIC HalpPErrorPlatformSpecific; PERROR_PLATFORM_BUS HalpPErrorPlatformBus; PERROR_PLATFORM_HOST_CONTROLLER HalpPErrorPlatformHostController; // // MCA/CMC/CPE state catchers // ERROR_SEVERITY HalpMcaProcessLog( PMCA_EXCEPTION McaLog ); BOOLEAN HalpPreAllocateMceTypeRecords( ULONG EventType, ULONG Number ); VOID HalpMcaBugCheck( ULONG McaBugCheckType, PMCA_EXCEPTION McaLog, ULONGLONG McaAllocatedLogSize, ULONGLONG Arg4 ); #ifdef ALLOC_PRAGMA #pragma alloc_text(INIT, HalpInitializeOSMCA) #pragma alloc_text(INIT, HalpAllocateMceStacks) #pragma alloc_text(INIT, HalpPreAllocateMceRecords) #pragma alloc_text(INIT, HalpPreAllocateMceTypeRecords) #pragma alloc_text(PAGELK, HalpMcaHandler) #pragma alloc_text(PAGELK, HalpMcaProcessLog) #pragma alloc_text(PAGELK, HalpMcaBugCheck) #pragma alloc_text(PAGELK, HalpGetErrLog) #pragma alloc_text(PAGELK, HalpClrErrLog) #pragma alloc_text(PAGE, HalpGetMceInformation) #endif // ALLOC_PRAGMA BOOLEAN HalpSaveEventLog( PSINGLE_LIST_ENTRY HeadList, PERROR_RECORD_HEADER RecordHeader, ULONG Tag, POOL_TYPE PoolType, PKSPIN_LOCK SpinLock ) { PSINGLE_LIST_ENTRY entry, previousEntry; SIZE_T logSize; PERROR_RECORD_HEADER savedLog; KIRQL oldIrql; // // Allocate and Initialize the new entry // logSize = RecordHeader->Length; if ( !logSize ) { HalDebugPrint(( HAL_ERROR, "HAL!HalpSaveEventLog: record length is zeroed.\n" )); return FALSE; } entry = (PSINGLE_LIST_ENTRY)ExAllocatePoolWithTag( PoolType, sizeof(*entry) + logSize, Tag ); if ( entry == NULL ) { HalDebugPrint(( HAL_ERROR, "HAL!HalpSaveEventLog: Event log allocation failed.\n" )); return FALSE; } entry->Next = NULL; savedLog = (PERROR_RECORD_HEADER)((ULONG_PTR)entry + sizeof(*entry)); RtlCopyMemory( savedLog, RecordHeader, logSize ); // // Insert the new entry with protection. // KeRaiseIrql( HIGH_LEVEL, &oldIrql ); KiAcquireSpinLock( SpinLock ); previousEntry = HeadList; while( previousEntry->Next != NULL ) { previousEntry = previousEntry->Next; } previousEntry->Next = entry; KiReleaseSpinLock( SpinLock ); KeLowerIrql( oldIrql ); return TRUE; } // HalpSaveEventLog() #define HalpSaveCorrectedMcaLog( _McaLog ) \ HalpSaveEventLog( &HalpMcaInfo.CorrectedLogs, (PERROR_RECORD_HEADER)(_McaLog), 'CacM', NonPagedPool, &HalpMcaSpinLock ) NTSTATUS HalpCheckForMcaLogs( VOID ) /*++ Routine Description: This routine checks the FW early during boot if a MCA event log is present. The log is considered as "previous". This routine is called at phase 1 on BSP only, from HalpPreAllocateMceRecords(). it is executed on the standard kernel stacks. Arguments: None Return Value: STATUS_NO_MEMORY if mca log allocation failed. STATUS_SUCCESS otherwise, regardless of FW interfaces failures. --*/ { NTSTATUS status; PERROR_RECORD_HEADER log; log = ExAllocatePoolWithTag( NonPagedPool, HalpMcaInfo.Stats.MaxLogSize, 'PacM' ); if ( !log ) { return( STATUS_NO_MEMORY ); } status = HalpGetFwMceLog( MCA_EVENT, log, &HalpMcaInfo.Stats, HALP_FWMCE_DONOT_CLEAR_LOG ); if ( status != STATUS_NOT_FOUND ) { // // Successful log collection or invalid record or unsuccessful FW Interface calls // are considered as a trigger for the MCA log consumers to collect them from the FW. // InterlockedIncrement( &HalpMcaInfo.Stats.McaPreviousCount ); } ExFreePoolWithTag( log, 'PacM' ); return( STATUS_SUCCESS ); } // HalpCheckForMcaLogs() BOOLEAN HalpPreAllocateMceTypeRecords( ULONG EventType, ULONG Number ) { SAL_PAL_RETURN_VALUES rv = {0}; ULONGLONG defaultEventRecords; PVOID log; SIZE_T logSize; PHYSICAL_ADDRESS physicalAddr; if ( (EventType != MCA_EVENT) && (EventType != INIT_EVENT) ) { ASSERTMSG( "HAL!HalpPreAllocateMceTypeRecords: unknown event type!\n", FALSE ); return FALSE; } // // On BSP only, call SAL to get maximum size of EventType record // if ( Number == 0 ) { rv = HalpGetStateInfoSize( EventType ); if ( !SAL_SUCCESSFUL(rv) ) { HalDebugPrint(( HAL_ERROR, "HAL!HalpPreAllocateMceTypeRecords: SAL_GET_STATE_INFO_SIZE failed...\n" )); return FALSE; } logSize = rv.ReturnValues[1]; } if ( EventType == MCA_EVENT ) { if ( Number == 0 ) { // Update HalpMcaInfo, without protection. This is not required. HalpMcaInfo.Stats.MaxLogSize = (ULONG)logSize; } else { logSize = (SIZE_T)HalpMcaInfo.Stats.MaxLogSize; } defaultEventRecords = HalpProcessorMcaRecords; } else { ASSERTMSG( "HAL!HalpPreAllocateMceTypeRecords: invalid event type!\n", EventType == INIT_EVENT ); if ( Number == 0 ) { // Update HalpInitInfo, without protection. This is not required. HalpInitInfo.MaxLogSize = (ULONG)logSize; } else { logSize = (SIZE_T)HalpInitInfo.MaxLogSize; } defaultEventRecords = HalpProcessorInitRecords; } // Determine size of allocation logSize = ROUND_TO_PAGES( (logSize * defaultEventRecords) ); // // Allocate Event Records buffer // physicalAddr.QuadPart = 0xffffffffffffffffI64; log = MmAllocateContiguousMemory( logSize, physicalAddr ); if ( log == NULL ) { HalDebugPrint(( HAL_ERROR, "HAL!HalpPreAllocateMceTypeRecords: SAL %s Event Records allocation failed (0x%Ix)...\n", ( EventType == MCA_EVENT ) ? "MCA" : "INIT", logSize )); return FALSE; } // // Update KPCR entry. // { volatile KPCR * const pcr = KeGetPcr(); PSAL_EVENT_RESOURCES eventResources; if ( EventType == MCA_EVENT ) { eventResources = pcr->OsMcaResourcePtr; } eventResources->EventPool = log; eventResources->EventPoolSize = (ULONG) logSize; } return TRUE; } // HalpPreAllocateMceTypeRecords() BOOLEAN HalpPreAllocateMceRecords( IN ULONG Number ) { NTSTATUS status; // // Pre-Allocate MCA records // if ( !HalpPreAllocateMceTypeRecords( MCA_EVENT , Number ) ) { return FALSE; } // // Check for MCA logs. // These might be logs related to previous boot sessions. // status = HalpCheckForMcaLogs(); if ( !NT_SUCCESS( status ) ) { return FALSE; } return TRUE; } // HalpPreAllocateMceRecords() BOOLEAN HalpAllocateMceStacks( IN ULONG Number ) { PHYSICAL_ADDRESS physicalAddr; PVOID mem; PVOID mcaStateDump, mcaBackStore, mcaStack; ULONGLONG mcaStateDumpPhysical; ULONGLONG mcaBackStoreLimit, mcaStackLimit; ULONG length; // // Allocate MCA/INIT stacks // length = HALP_MCA_STATEDUMP_SIZE + HALP_MCA_BACKSTORE_SIZE + HALP_MCA_STACK_SIZE; physicalAddr.QuadPart = 0xffffffffffffffffI64; mem = MmAllocateContiguousMemory( length, physicalAddr ); if ( mem == NULL ) { HalDebugPrint(( HAL_ERROR, "HAL!HalpAllocateMceStacks: MCA State Dump allocation failed (0x%Ix)...\n", length )); return FALSE; } // // The layout in memory by increasing addresses is: // // Bottom of stack // . // . // . // Initial Stack // State Dump Area // . // . // Initial BSP // . // . // . // BSP Limit // mcaStack = mem; mcaStackLimit = (ULONGLONG)mem + HALP_MCA_STACK_SIZE; mem = (PCHAR) mem + HALP_MCA_STACK_SIZE; mcaStateDump = mem; mcaStateDumpPhysical = MmGetPhysicalAddress(mem).QuadPart; mem = (PCHAR) mem + HALP_MCA_STATEDUMP_SIZE; mcaBackStore = mem; mcaBackStoreLimit = (ULONGLONG)mem + (ULONGLONG)(ULONG)HALP_MCA_BACKSTORE_SIZE; // // Update PCR MCA, INIT stacks // { volatile KPCR * const pcr = KeGetPcr(); PSAL_EVENT_RESOURCES eventResources; eventResources = pcr->OsMcaResourcePtr; eventResources->StateDump = mcaStateDump; eventResources->StateDumpPhysical = mcaStateDumpPhysical; eventResources->BackStore = mcaBackStore; eventResources->BackStoreLimit = mcaBackStoreLimit; eventResources->Stack = (PCHAR) mcaStackLimit; eventResources->StackLimit = (ULONGLONG) mcaStack; } return TRUE; } // HalpPreAllocateMceRecords() //++ // Name: HalpInitializeOSMCA() // // Routine Description: // // This routine registers MCA init's // // Arguments On Entry: // arg0 = Function ID // // Success/Failure (0/!0) //-- BOOLEAN HalpInitializeOSMCA( IN ULONG Number ) { SAL_PAL_RETURN_VALUES rv = {0}; ULONGLONG gp_reg; // // Register SAL_MC_RendezVous parameters with SAL // rv = HalpSalSetParams(0, RendzType, IntrVecType, MC_RZ_VECTOR, HalpMcRendezTimeOut); if ( !SAL_SUCCESSFUL(rv) ) { HalDebugPrint(( HAL_ERROR, "HAL!HalpInitializeOSMCA: SAL_MC_SET_PARAMS.rendezvous vector failed...\n" )); return FALSE; } // // Register WakeUp parameters with SAL // rv = HalpSalSetParams(0, WakeUpType, IntrVecType, MC_WKUP_VECTOR,0); if ( !SAL_SUCCESSFUL(rv) ) { HalDebugPrint(( HAL_ERROR, "HAL!HalpInitializeOSMCA: SAL_MC_SET_PARAMS.wakeup vector failed...\n" )); return FALSE; } // // Allocate MCA, INIT stacks // if ( !HalpAllocateMceStacks( Number ) ) { return FALSE; } // // Pre-Allocate desired number of MCA,INIT records // HalpMcaInfo.KernelToken = (PVOID)(ULONG_PTR)HALP_KERNEL_TOKEN; if ( !HalpPreAllocateMceRecords( Number ) ) { return FALSE; } // // Initialize HAL private CMC, CPE structures. // if ( HalpFeatureBits & HAL_CMC_PRESENT ) { rv = HalpGetStateInfoSize( CMC_EVENT ); if ( SAL_SUCCESSFUL( rv ) ) { if ( rv.ReturnValues[1] >= sizeof( ERROR_RECORD_HEADER ) ) { HalpCmcInfo.Stats.MaxLogSize = (ULONG)rv.ReturnValues[1]; HalpCmcInfo.KernelToken = (PVOID)(ULONG_PTR)HALP_KERNEL_TOKEN; HalpCmcInfo.KernelLogs.MaxCount = HalpMceLogsMaxCount; HalpCmcInfo.DriverLogs.MaxCount = HalpMceLogsMaxCount; HalpCmcInfo.Stats.PollingInterval = HAL_CMC_INTERRUPTS_BASED; HalpCmcInfo.ThresholdCounter = 0; } else { HalDebugPrint(( HAL_ERROR, "HAL!HalpGetFeatureBits: Invalid max CMC log size from SAL\n" )); HalpFeatureBits &= ~HAL_CMC_PRESENT; } } else { HalDebugPrint(( HAL_ERROR, "HAL!HalpInitializeOSMCA: SAL_GET_STATE_INFO_SIZE.CMC failed...\n" )); HalpFeatureBits &= ~HAL_CMC_PRESENT; } } if ( HalpFeatureBits & HAL_CPE_PRESENT ) { rv = HalpGetStateInfoSize( CPE_EVENT ); if ( SAL_SUCCESSFUL( rv ) ) { if ( rv.ReturnValues[1] >= sizeof( ERROR_RECORD_HEADER ) ) { HalpCpeInfo.Stats.MaxLogSize = (ULONG)rv.ReturnValues[1]; HalpCpeInfo.KernelToken = (PVOID)(ULONG_PTR)HALP_KERNEL_TOKEN; HalpCpeInfo.KernelLogs.MaxCount = HalpMceLogsMaxCount; HalpCpeInfo.DriverLogs.MaxCount = HalpMceLogsMaxCount; HalpCpeInfo.ThresholdCounter = 0; } else { HalDebugPrint(( HAL_ERROR, "HAL!HalpGetFeatureBits: Invalid max CPE log size from SAL\n" )); HalpFeatureBits &= ~HAL_CPE_PRESENT; } } else { HalDebugPrint(( HAL_ERROR, "HAL!HalpInitializeOSMCA: SAL_GET_STATE_INFO_SIZE.CPE failed...\n" )); HalpFeatureBits &= ~HAL_CPE_PRESENT; } } // // Register OsMcaDispatch (OS_MCA) physical address with SAL // gp_reg = GetGp(); rv = HalpSalSetVectors(0, MchkEvent, MmGetPhysicalAddress((fptr)(((PLabel*)HalpOsMcaDispatch1)->fPtr)), gp_reg,0); if ( !SAL_SUCCESSFUL(rv) ) { HalDebugPrint(( HAL_ERROR, "HAL!HalpInitializeOSMCA: SAL_SET_VECTOR.MCA vector failed...\n" )); return FALSE; } // // Register OsInitDispatch physical address with SAL // rv = HalpSalSetVectors(0, InitEvent, MmGetPhysicalAddress((fptr)(((PLabel*)HalpOsInitDispatch)->fPtr)), gp_reg,0); if ( !SAL_SUCCESSFUL(rv) ) { HalDebugPrint(( HAL_ERROR, "HAL!HalpInitializeOSMCA: SAL_SET_VECTOR.INIT vector failed...\n" )); return FALSE; } return TRUE; } // HalpInitializeOSMCA() //EndProc////////////////////////////////////////////////////////////////////// VOID HalpMcaBugCheck( ULONG McaBugCheckType, PMCA_EXCEPTION McaLog, ULONGLONG McaAllocatedLogSize, ULONGLONG SalStatus ) //++ // Name: HalpMcaBugCheck() // // Routine Description: // // This function is called to bugcheck the system in a case of a fatal MCA // or fatal FW interface errors. The OS must guarantee as much as possible // error containment in this path. // With the current implementation, this function should be only called from // the OS_MCA path. For other MCA specific wrappers of KeBugCheckEx, one should // HalpMcaKeBugCheckEx(). // // Arguments On Entry: // ULONG McaBugCheckType // PMCA_EXCEPTION McaLog // ULONGLONG McaAllocatedLogSize // ULONGLONG SalStatus // // Return: // None. // // Implementation notes: // This code CANNOT [as default rules - at least entry and through fatal MCAs handling] // - make any system call // - attempt to acquire any spinlock used by any code outside the MCA handler // - change the interrupt state. // Passing data to non-MCA code must be done using manual semaphore instructions. // This code should minimize the path and the global or memory allocated data accesses. // This code should only access MCA-namespace structures. // This code is called under the MP protection of HalpMcaSpinLock and with the flag // HalpOsMcaInProgress set. // //-- { if ( HalpOsMcaInProgress ) { // // Enable InbvDisplayString calls to make it through to bootvid driver. // if ( InbvIsBootDriverInstalled() ) { InbvAcquireDisplayOwnership(); InbvResetDisplay(); InbvSolidColorFill(0,0,639,479,4); // make the screen blue InbvSetTextColor(15); InbvInstallDisplayStringFilter((INBV_DISPLAY_STRING_FILTER)NULL); InbvEnableDisplayString(TRUE); // enable display string InbvSetScrollRegion(0,0,639,479); // set to use entire screen } HalDisplayString (MSG_MCA_HARDWARE_ERROR); HalDisplayString (MSG_HARDWARE_ERROR2); // // Thierry 09/2000: // // - if desired, process the MCA log HERE... // // and use HalDisplayString() to dump info for the field or hardware vendor. // The processing could be based on processor or platform independent record definitions. // HalDisplayString( MSG_HALT ); if ( HalpMcaInfo.NoBugCheck == 0 ) { KeBugCheckEx( MACHINE_CHECK_EXCEPTION, (ULONG_PTR)McaBugCheckType, (ULONG_PTR)McaLog, (ULONG_PTR)McaAllocatedLogSize, (ULONG_PTR)SalStatus ); } } if ( ((*KdDebuggerNotPresent) == FALSE) && ((*KdDebuggerEnabled) != FALSE) ) { KeEnterKernelDebugger(); } while( TRUE ) { // ; // Simply sit here so the MCA HARDWARE ERROR screen does not get corrupted... // } // noreturn } // HalpMcaBugCheck() ERROR_SEVERITY HalpMcaProcessLog( PMCA_EXCEPTION McaLog ) //++ // Name: HalpMcaProcessLog() // // Routine Description: // // This function is called to process the MCA event log in the OS_MCA path. // // Arguments On Entry: // PMCA_EXCEPTION McaLog - Pointer to the MCA event log. // // Return: // ERROR_SEVERITY // // Implementation notes: // This code CANNOT [as default rules] // - make any system call // - attempt to acquire any spinlock used by any code outside the MCA handler // - change the interrupt state. // Passing data to non-MCA code must be done using manual semaphore instructions. // This code should minimize the path and the global or memory allocated data accesses. // This code should only access MCA-namespace structures. // This code is called under the MP protection of HalpMcaSpinLock and with the flag // HalpOsMcaInProgress set. // //-- { ERROR_SEVERITY mcaSeverity; mcaSeverity = McaLog->ErrorSeverity; switch( mcaSeverity ) { case ErrorFatal: break; case ErrorRecoverable: // // Thierry - FIXFIX 08/2000: // /////////////////////////////////////////////////////////////// // // Call to kernel supported recovery will be here.... // /////////////////////////////////////////////////////////////// // // However, for now we do not recover so flag it as ErrorFatal. mcaSeverity = ErrorFatal; break; case ErrorCorrected: default: // // These ERRROR_SEVERITY values have no HAL MCA specific handling. // As specified by the SAL Specs July 2000, we should not get these values in this path. // break; } // // If OEM driver has registered an exception callback for MCA event, // call it here and save returned error severity value. // if ( HalpMcaInfo.DriverInfo.ExceptionCallback ) { mcaSeverity = HalpMcaInfo.DriverInfo.ExceptionCallback( HalpMcaInfo.DriverInfo.DeviceContext, McaLog ); } // // Save corrected log for future kernel notification. // if ( (HalpMcaInfo.KernelDelivery) && (mcaSeverity == ErrorCorrected) ) { InterlockedIncrement( &HalpMcaInfo.Stats.McaCorrectedCount ); #if 0 // // Thierry - 09/16/2000: ToBeDone. // Saving the corrected MCA log records requires careful rendez-vous configuration // handling, possible OS_MCA monarch selection, MCA logs (pre-)allocations and // special locking in case a consumer accesses the logs queue on another processor. // // The kernel-WMI and/or OEM MCA driver notification is done in HalpMcaHandler(). // if ( !HalpSaveCorrectedMcaLog( McaLog ) ) { InterlockedIncrement( &HalpMcaInfo.CorrectedLogsLost ); } #endif // 0 // // The kernel-WMI and/or OEM MCA driver notification for corrected MCA event // is done in HalpMcaHandler(). // } // // Thierry 10/17/2000 BUGBUG // // The FW does not save the MCA log in NVRAM and we have no official date from Intel // when the SAL will be doing it. // So for now, return as ErrorFatal and let dump the log through the debugger. // // Before Sal Rev , the error logs were completely erroneous... // if ( HalpSalPalData.SalRevision.Revision < HALP_SAL_REVISION_MAX ) { return( ErrorFatal ); } else { return( mcaSeverity ); } } // HalpMcaProcessLog() SAL_PAL_RETURN_VALUES HalpMcaHandler( ULONG64 RendezvousState, PPAL_MINI_SAVE_AREA Pmsa ) //++ // Name: HalpMcaHandler() // // Routine Description: // // This is the OsMca handler for firmware uncorrected errors // It is our option to run this in physical or virtual mode. // // Arguments On Entry: // None. // // Conditions On Entry: 09/2000 implementation. // - PSR state: at least, // PSR.dt = 1, PSR.it = 1, PSR.rt = 1 - virtual mode. // PSR.ic = 1, PSR.i = 0 - Interruption resources collection enabled, // Interrupts off. // PSR.mc = 1 - MCA masked for this processor. // - SalToOsHndOff initialized. // - s0 = MinStatePtr. // - s1 = IA64 PAL Processor State Parameter. // - s2 = PALE_CHECK return address. // - Processor registers state saved in myStateDump[] by osmcaProcStateDump(). // - myStackFrame[0] = ar.rsc // - myStackFrame[1] = ar.pfs // - myStackFrame[2] = ar.ifs // - myStackFrame[3] = ar.bspstore // - myStackFrame[4] = ar.rnat // - myStackFrame[5] = ar.bsp - ar.bspstore // - ar.bspstore = myBspStore // - sp = &mySp[sizeof(mySp[])] // // Return: // rtn0=Success/Failure (0/!0) // rtn1=Alternate MinState Pointer if any else NULL // // Implementation notes: // This code CANNOT [as default rules - at least entry and through fatal MCAs handling] // - make any system call // - attempt to acquire any spinlock used by any code outside the MCA handler // - change the interrupt state. // Passing data to non-MCA code must be done using manual semaphore instructions. // This code should minimize the path and the global or memory allocated data accesses. // This code should only access MCA-namespace structures and should not access globals // until it is safe. // //-- { SAL_PAL_RETURN_VALUES rv; LONGLONG salStatus; BOOLEAN mcWakeUp; PMCA_EXCEPTION mcaLog; ULONGLONG mcaAllocatedLogSize; PSAL_EVENT_RESOURCES mcaResources; KIRQL oldIrql; BOOLEAN raisedIrql; volatile KPCR * const pcr = KeGetPcr(); // // Acquire MCA spinlock protecting OS_MCA resources. // // Thierry 10/06/2000: FIXFIX. // we will move this MP synchronization in HalpOsMcaDispatch after current discussions // with Intel about MP MCA handling are completed. // Expecting responses from Intel about these. // // // If we are running below MCA_LEVEL then we need to raise irql to // MCA_LEVEL. // if (KeGetCurrentIrql() < MCA_LEVEL) { KeRaiseIrql(MCA_LEVEL, &oldIrql); raisedIrql = TRUE; } else { raisedIrql = FALSE; } // // Enable interrupts while we spin on the MCA spinlock. This will allow the // monarch processor to IPI us if it needs to. // HalpEnableInterrupts(); HalpAcquireMcaSpinLock( &HalpMcaSpinLock ); HalpDisableInterrupts(); HalpOsMcaInProgress++; // // Save OsToSal minimum state // mcaResources = pcr->OsMcaResourcePtr; mcaResources->OsToSalHandOff.SalReturnAddress = mcaResources->SalToOsHandOff.SalReturnAddress; mcaResources->OsToSalHandOff.SalGlobalPointer = mcaResources->SalToOsHandOff.SalGlobalPointer; // // update local variables with pre-initialized MCA log data. // mcaLog = (PMCA_EXCEPTION)(mcaResources->EventPool); mcaAllocatedLogSize = mcaResources->EventPoolSize; if ( !mcaLog || !mcaAllocatedLogSize ) { // // The following code should never happen or the implementation of the HAL MCA logs // pre-allocation failed miserably. This would be a development error. // HalpMcaBugCheck( (ULONG_PTR)HAL_BUGCHECK_MCA_ASSERT, mcaLog, mcaAllocatedLogSize, (ULONGLONG)0 ); } // // Get the MCA logs // salStatus = (LONGLONG)0; while( salStatus >= 0 ) { ERROR_SEVERITY errorSeverity; rv = HalpGetStateInfo( MCA_EVENT, mcaLog ); salStatus = rv.ReturnValues[0]; switch( salStatus ) { case SAL_STATUS_SUCCESS: errorSeverity = HalpMcaProcessLog( mcaLog ); if ( errorSeverity == ErrorFatal ) { // // We are now going down with a MACHINE_CHECK_EXCEPTION. // No return... // HalpMcaBugCheck( HAL_BUGCHECK_MCA_FATAL, mcaLog, mcaAllocatedLogSize, 0 ); } else { // // Ideally we would have recovered the error at this point. // However we don't currently handle MCA error recovery // yet. Once we do then this "else clause" should be // deleted. // HalpMcaBugCheck( HAL_BUGCHECK_MCA_NONFATAL, mcaLog, mcaAllocatedLogSize, 0 ); } rv = HalpClearStateInfo( MCA_EVENT ); if ( !SAL_SUCCESSFUL(rv) ) { // // Current consideration for this implementation - 08/2000: // if clearing the event fails, we assume that FW has a real problem; // continuing will be dangerous. We bugcheck. // HalpMcaBugCheck( HAL_BUGCHECK_MCA_CLEAR_STATEINFO, mcaLog, mcaAllocatedLogSize, rv.ReturnValues[0] ); } // SAL_STATUS_SUCCESS, SAL_STATUS_SUCCESS_MORE_RECORDS ... and // ErrorSeverity != ErrorFatal. // // Call the registered kernel handler. // // Thierry 08/2000 - FIXFIX: // The errorSeverity check is under comments. It should not be commented for the // final version. However, we wanted to have kernel notification if we are getting // log error severity != ErrorFatal or != ErrorRecoverable. if ( /* (errorSeverity == ErrorCorrected) && */ ( HalpMcaInfo.KernelDelivery || HalpMcaInfo.DriverInfo.DpcCallback ) ) { InterlockedExchange( &HalpMcaInfo.DpcNotification, 1 ); } break; case SAL_STATUS_NO_INFORMATION_AVAILABLE: // // The salStatus value will break the salStatus loop. // rv.ReturnValues[0] = SAL_STATUS_SUCCESS; break; case SAL_STATUS_SUCCESS_WITH_OVERFLOW: case SAL_STATUS_INVALID_ARGUMENT: case SAL_STATUS_ERROR: case SAL_STATUS_VA_NOT_REGISTERED: default: // Thierry 08/00: WARNING - SAL July 2000 - v2.90. // default includes possible unknown positive salStatus values. HalpMcaBugCheck( HAL_BUGCHECK_MCA_GET_STATEINFO, mcaLog, mcaAllocatedLogSize, salStatus ); break; } } // // If we get here then one of two things have happened. Either the SAL // didn't return any records or we got a recoverable error, handled it, and // the HAL_BUGCHECK_MCA_NONFATAL bugcheck above in the SAL_STATUS_SUCCESS // case above has been removed. // // Once we add code to recover from MCAs and support returning to the SAL we // need to change this bugcheck so it is only called if we received no error // records in response to SAL_GET_STATEINFO. // HalpMcaBugCheck( HAL_BUGCHECK_MCA_NONFATAL, 0, 0, 0 ); // // Currently 08/2000, we do not support the modification of the minstate. // mcaResources->OsToSalHandOff.MinStateSavePtr = mcaResources->SalToOsHandOff.MinStateSavePtr; mcaResources->OsToSalHandOff.Result = rv.ReturnValues[0]; // // If error was corrected and MCA non-monarch processors are in rendez vous, // we will have to wake them up. // mcWakeUp = ( (rv.ReturnValues[0] == SAL_STATUS_SUCCESS) && HalpSalRendezVousSucceeded( mcaResources->SalToOsHandOff ) ); // // Release MCA spinlock protecting OS_MCA resources. // HalpOsMcaInProgress = 0; HalpReleaseMcaSpinLock( &HalpMcaSpinLock ); if (raisedIrql) { KeLowerIrql(oldIrql); } // // If required, let's wake MCA non-monarch processors up. // if ( mcWakeUp ) { HalpMcWakeUp(); } return( rv ); } // HalpMcaHandler() //++ // Name: HalpGetErrLogSize() // // Routine Description: // // This is a wrapper that will call SAL_GET_STATE_INFO_SIZE // // Arguments On Entry: // arg0 = Reserved // arg1 = Event Type (MCA,INIT,CMC,CPE) // // Returns // rtn0=Success/Failure (0/!0) // rtn1=Size //-- SAL_PAL_RETURN_VALUES HalpGetErrLogSize( ULONGLONG Res, ULONGLONG eType ) { SAL_PAL_RETURN_VALUES rv = {0}; HalpSalCall(SAL_GET_STATE_INFO_SIZE, eType, 0,0,0,0,0,0, &rv); return(rv); } //EndProc////////////////////////////////////////////////////////////////////// //++ // Name: HalpGetErrLog() // // Routine Description: // // This is a wrapper that will call SAL_GET_STATE_INFO // // Arguments On Entry: // arg0 = Reserved // arg1 = Event Type (MCA,INIT,CMC) // arg3 = pBuffer // // Success/Failure (0/!0) //-- SAL_PAL_RETURN_VALUES HalpGetErrLog( ULONGLONG Res, ULONGLONG eType, ULONGLONG* pBuff ) { SAL_PAL_RETURN_VALUES rv={0}; HalpSalCall(SAL_GET_STATE_INFO, eType, 0, (ULONGLONG)pBuff, 0,0,0,0, &rv); // // Regardless of the call success or failure, fix the record to store // the processor number the SAL_PROC was executed on. // This feature is requested by WMI. // HalpSetFwMceLogProcessorNumber( (PERROR_RECORD_HEADER)pBuff ); return(rv); } //EndProc////////////////////////////////////////////////////////////////////// //++ // Name: HalpClrErrLog() // // Routine Description: // // This is a wrapper that will call SAL_CLEAR_STATE_INFO // // Arguments On Entry: // arg0 = Reserved // arg1 = Event Type (MCA,INIT,CMC,CPE) // // Success/Failure (0/!0) //-- SAL_PAL_RETURN_VALUES HalpClrErrLog( ULONGLONG Res, ULONGLONG eType ) { SAL_PAL_RETURN_VALUES rv={0}; HalpSalCall( SAL_CLEAR_STATE_INFO, eType, 0,0,0,0,0,0, &rv ); return(rv); } //EndProc////////////////////////////////////////////////////////////////////// //++ // Name: HalpSalSetParams() // // Routine Description: // // This is a wrapper that will call SAL_MC_SET_PARAMS // // Arguments On Entry: // arg0 = Reserved // arg1 = Parameter Type (rendz. or wakeup) // arg2 = Event Type (interrupt/semaphore) // arg3 = Interrupt Vector or Memory Address // arg4 = Timeout value for rendezvous // // Success/Failure (0/!0) //-- SAL_PAL_RETURN_VALUES HalpSalSetParams(ULONGLONG Res, ULONGLONG pType, ULONGLONG eType, ULONGLONG VecAdd, ULONGLONG tValue) { SAL_PAL_RETURN_VALUES rv={0}; HalpSalCall(SAL_MC_SET_PARAMS, pType, eType, VecAdd,tValue,0,0,0,&rv); return(rv); } //EndProc////////////////////////////////////////////////////////////////////// //++ // Name: HalpSalSetVectors() // // Routine Description: // // This is a wrapper that will call SAL_SET_VECTORS // // Arguments On Entry: // arg0 = Reserved // arg1 = Event Type (MCA, INIT..) // arg2 = Physical Address of handler // arg3 = gp // arg4 = length of event handler in bytes // // Success/Failure (0/!0) //-- SAL_PAL_RETURN_VALUES HalpSalSetVectors( ULONGLONG Res, ULONGLONG eType, PHYSICAL_ADDRESS Addr, ULONGLONG gpValue, ULONGLONG szHndlr) { SAL_PAL_RETURN_VALUES rv={0}; if ( eType == InitEvent ) { // // Thierry 08/2000: // Current implementation assumes that OS decides the monarch inside OS_INIT. // This implies handler_2, gp_2, length_2 are identical to handler_1, gp_1, length_1. // HalpSalCall(SAL_SET_VECTORS, eType, (ULONGLONG)Addr.QuadPart, gpValue, szHndlr, (ULONGLONG)Addr.QuadPart, gpValue, szHndlr, &rv); } else { HalpSalCall(SAL_SET_VECTORS, eType, (ULONGLONG)Addr.QuadPart, gpValue,szHndlr,0,0,0,&rv); } return(rv); } //EndProc////////////////////////////////////////////////////////////////////// //++ // Name: HalpSalRendz() // // Routine Description: // // This is a wrapper that will call SAL_MC_RENDEZ // // Arguments On Entry: // arg0 = Reserved // // Success/Failure (0/!0) //-- SAL_PAL_RETURN_VALUES HalpSalRendz(void) { SAL_PAL_RETURN_VALUES rv={0}; HalpSalCall(SAL_MC_RENDEZ, 0, 0, 0,0,0,0,0,&rv); return(rv); } VOID HalpMcWakeUp( VOID ) /*++ Routine Description: This function does IPI to wakeup the MC non-monarch processors. Arguments: None. Return Value: None. Remarks: This function is assumed to be executed on the MC monarch processor. --*/ { USHORT LogicalCpu; USHORT ProcessorID; USHORT monarchID; // // Scan the processor set and request an interprocessor interrupt on // each of the specified targets. // monarchID = (USHORT)PCR->HalReserved[PROCESSOR_ID_INDEX]; for (LogicalCpu = 0; LogicalCpu < HalpMpInfo.ProcessorCount; LogicalCpu++) { // // Only IPI processors that are started. // if (HalpActiveProcessors & (1 << HalpProcessorInfo[LogicalCpu].NtProcessorNumber)) { ProcessorID = HalpProcessorInfo[LogicalCpu].LocalApicID; // // Request interprocessor interrupt on target physicalCpu. // if ( ProcessorID != monarchID ) { HalpSendIPI(ProcessorID, MC_WKUP_VECTOR); } } } } // HalpMcWakeUp() VOID HalpCMCEnable( VOID ) /*++ Routine Description: This routine sets the processor CMCV register with CMCI_VECTOR. Arguments: None. Return Value: None. --*/ { if ( HalpFeatureBits & HAL_CMC_PRESENT ) { HalpWriteCMCVector( CMCI_VECTOR ); } return; } // HalpCMCEnable() VOID HalpCMCDisable( VOID ) /*++ Routine Description: This routine resets the processor CMCV register. Arguments: None Return Value: None --*/ { HalpWriteCMCVector( 0x10000ui64 ); return; } // HalpCMCDisable() NTSTATUS HalpGenerateCMCInterrupt( VOID ) /*++ Routine Description: This routine is used for testing CMC processing. It is called indirectly using HalSetSystemInformation.HalGenerateCmcInterrupt. The expectation is that the caller has done whatever processing to simulate the error that the SAL expects prior to calling this function. Arguments: CmcVector: CMC Vector value. Value: STATUS_SUCCESS --*/ { if (HalpFeatureBits & HAL_CMC_PRESENT) { HalpSendIPI( (USHORT)PCR->HalReserved[PROCESSOR_ID_INDEX], CMCI_VECTOR | DELIVER_FIXED); return STATUS_SUCCESS; } return STATUS_NOT_SUPPORTED; } ULONG_PTR HalpSetCMCVector( IN ULONG_PTR CmcVector ) /*++ Routine Description: This routine sets the processor CMCV register with specified vector. This function is the broadcast function for HalpCMCDisableForAllProcessors(). Arguments: CmcVector: CMC Vector value. Value: STATUS_SUCCESS --*/ { HalpWriteCMCVector( (ULONG64)CmcVector ); return((ULONG_PTR)(ULONG)(STATUS_SUCCESS)); } // HalpSetCmcVector() VOID HalpCMCDisableForAllProcessors( VOID ) /*++ Routine Description: This routine disables processor CMC on every processor in the host configuration by executing HalpSetCmcVector( 0ui64 ) on every processor in a synchronous manner. Arguments: None. Value: None. --*/ { // // Can not do an IPI if the processors are above IPI level such // as we are in the kernel debugger. // if (KeGetCurrentIrql() < IPI_LEVEL) { (VOID)KiIpiGenericCall( HalpSetCMCVector, (ULONG_PTR)0x10000ui64 ); } else { HalpSetCMCVector(0x10000ui64); } return; } // HalpCMCDisableForAllProcessors() VOID HalpCMCIHandler ( IN PKINTERRUPT_ROUTINE Interrupt, IN PKTRAP_FRAME TrapFrame ) /*++ Routine Description: Processor Interrupt routine for CMC interrupts. Arguments: TrapFrame - Captured trap frame address. Return Parameters: None. Notes: Thierry 08/2000: This function does not do much, it flags the PCR InOsCmc field and calls the second-level handler: HalpCmcHandler(). However, this was implmented this way so this function abstracts the standard interrupts resources from the purely CMC processing in HalpCmcHandler(). --*/ { volatile KPCR * const pcr = KeGetPcr(); pcr->InOsCmc = TRUE; HalpCmcHandler(); pcr->InOsCmc = FALSE; return; } // HalpCMCIHandler() VOID HalpCmcProcessLog( PCMC_EXCEPTION CmcLog ) /*++ Routine Description: This function does a simple processing check a IA64 CMC log. Arguments: CmcLog - Provides CMC log address Return Parameters: None. Notes: Currently simply checking and outputing log contents for checked hal only. --*/ { #if DBG // // Simple log processing for first debugging... // GUID processorDeviceGuid = ERROR_PROCESSOR_GUID; BOOLEAN processorDeviceFound; PERROR_RECORD_HEADER header = (PERROR_RECORD_HEADER)CmcLog; PERROR_SECTION_HEADER section, sectionMax; if ( header->ErrorSeverity != ErrorCorrected ) { HalDebugPrint(( HAL_ERROR, "HAL!HalpCmcProcessLog: CMC record with severity [%d] != corrected!!!\n", header->ErrorSeverity )); } // // SAL spec BUGBUG 08/2000: we should have put the length of the header in the definition. // Same for section header. // processorDeviceFound = FALSE; section = (PERROR_SECTION_HEADER)((ULONG_PTR)header + sizeof(*header)); sectionMax = (PERROR_SECTION_HEADER)((ULONG_PTR)header + header->Length); while( section < sectionMax ) { if ( IsEqualGUID( §ion->Guid, &processorDeviceGuid ) ) { PERROR_PROCESSOR processorRecord = (PERROR_PROCESSOR)section; processorDeviceFound = TRUE; // // Minimum processing here. This will enhance with testing and most common // occurences. // if ( processorRecord->Valid.StateParameter ) { ULONGLONG stateParameter = processorRecord->StateParameter.StateParameter; // // At any time more than one error could be valid // if((stateParameter >> ERROR_PROCESSOR_STATE_PARAMETER_CACHE_CHECK_SHIFT) & ERROR_PROCESSOR_STATE_PARAMETER_CACHE_CHECK_MASK) { // // cache error // HalDebugPrint(( HAL_INFO, "HAL!HalpCmcProcessLog: Corrected Processor CACHE Machine Check error\n" )); } if((stateParameter >> ERROR_PROCESSOR_STATE_PARAMETER_TLB_CHECK_SHIFT) & ERROR_PROCESSOR_STATE_PARAMETER_TLB_CHECK_MASK) { // // tlb error // HalDebugPrint(( HAL_INFO, "HAL!HalpCmcProcessLog: Corrected Processor TLB Machine Check error\n" )); } if((stateParameter >> ERROR_PROCESSOR_STATE_PARAMETER_BUS_CHECK_SHIFT) & ERROR_PROCESSOR_STATE_PARAMETER_BUS_CHECK_MASK) { // // bus error // HalDebugPrint(( HAL_INFO, "HAL!HalpCmcProcessLog: Corrected Processor BUS Machine Check error\n" )); } if((stateParameter >> ERROR_PROCESSOR_STATE_PARAMETER_UNKNOWN_CHECK_SHIFT) & ERROR_PROCESSOR_STATE_PARAMETER_UNKNOWN_CHECK_MASK) { // // unknown error // HalDebugPrint(( HAL_INFO, "HAL!HalpCmcProcessLog: Corrected Processor UNKNOWN Machine Check error\n" )); } } } } if ( !processorDeviceFound ) { HalDebugPrint(( HAL_ERROR, "HAL!HalpCmcProcessLog: CMC log without processor device record!!!\n")); } #endif // DBG return; } // HalpCmcProcessLog() //++ // Name: HalpCmcHandler() // // Routine Description: // // This is the second level CMC Interrupt Handler for FW corrected errors. // // Arguments On Entry: // None. // // Return. // None. // // Notes: // This function calls the kernel notification and inserts the OEM CMC driver dpc if // registered. // Accessing the CMC logs at this level could be inacceptable because of the possible // large size of the logs and the time required to collect them. // The collection of the logs is delayed until the work item calls // HalQuerySystemInformation.HalCmcLogInformation. //-- VOID HalpCmcHandler( VOID ) { LARGE_INTEGER CurrentTime; // // Internal housekeeping. // InterlockedIncrement( &HalpCmcInfo.Stats.CmcInterruptCount ); // // Notify the kernel if registered. // if ( HalpCmcInfo.KernelDelivery ) { if ( !HalpCmcInfo.KernelDelivery( HalpCmcInfo.KernelToken, CmcAvailable, NULL ) ) { InterlockedIncrement( &HalpCmcInfo.Stats.KernelDeliveryFails ); } } // // Notify the OEM CMC driver if registered. // if ( HalpCmcInfo.DriverInfo.DpcCallback ) { if ( !KeInsertQueueDpc( &HalpCmcInfo.DriverDpc, NULL, NULL ) ) { InterlockedIncrement( &HalpCmcInfo.Stats.DriverDpcQueueFails ); } } // // Now check if we are receiving CMC more frequently than our // threshold and if so call kernel to switch to polled mode // if (HalpCmcInfo.ThresholdMaximum != 0) { CurrentTime = KeQueryPerformanceCounter(NULL); KiAcquireSpinLock(&HalpCmcSpinLock); if (HalpCmcInfo.Stats.PollingInterval == HAL_CMC_INTERRUPTS_BASED) { if ( (CurrentTime.QuadPart - HalpCmcInfo.LastTime.QuadPart) < HalpCmcInfo.ThresholdTime.QuadPart) { if (++HalpCmcInfo.ThresholdCounter > HalpCmcInfo.ThresholdMaximum) { // // We have crossed the threshold so we need to // downgrade to polling mode. We switch down to polling // every 60 seconds as per the Intel Itanium Error // Handling guide // HalpCmcInfo.Stats.PollingInterval = HALP_CMC_DEFAULT_POLLING_INTERVAL; KiReleaseSpinLock(&HalpCmcSpinLock); HalpCMCDisableForAllProcessors(); if (HalpCmcInfo.KernelDelivery != NULL) { HalpCmcInfo.KernelDelivery( HalpCmcInfo.KernelToken, CmcSwitchToPolledMode, (PVOID)UlongToPtr(HalpCmcInfo.Stats.PollingInterval) ); } } else { KiReleaseSpinLock(&HalpCmcSpinLock); } } else { HalpCmcInfo.LastTime = CurrentTime; HalpCmcInfo.ThresholdCounter = 0; KiReleaseSpinLock(&HalpCmcSpinLock); } } else { KiReleaseSpinLock(&HalpCmcSpinLock); } } } // HalpCmcHandler() //EndProc////////////////////////////////////////////////////////////////////// //++ // Name: HalpCpeHandler() // // Routine Description: // // This is the second level CPE Interrupt Handler for Platform corrected errors. // // Arguments On Entry: // None. // // Return. // None. // // Notes: // This function calls the kernel notification and inserts the OEM CPE driver dpc if // registered. // Accessing the CPE logs at this level could be inacceptable because of the possible // large size of the logs and the time required to collect them. // The collection of the logs is delayed until the work item calls // HalQuerySystemInformation.HalCpeLogInformation. //-- VOID HalpCpeHandler( VOID ) { LARGE_INTEGER CurrentTime; KIRQL Irql; // // Internal housekeeping. // InterlockedIncrement( &HalpCpeInfo.Stats.CpeInterruptCount ); // // Disable further CPE interrupts. We have to do this now because WMI // doesn't call SAL_GET_STATE_INFO until much later and the SAL routine // is what actually resets the LEVEL triggered interrupt source. // // We will reenable interrupts on the way back out of the HalpClrErrLog // call. // HalpCPEDisable(); // // Notify the kernel if registered. // if ( HalpCpeInfo.KernelDelivery ) { if ( !HalpCpeInfo.KernelDelivery( HalpCpeInfo.KernelToken, CpeAvailable, NULL ) ) { InterlockedIncrement( &HalpCpeInfo.Stats.KernelDeliveryFails ); } } // // Notify the OEM CPE driver if registered. // if ( HalpCpeInfo.DriverInfo.DpcCallback ) { if ( !KeInsertQueueDpc( &HalpCpeInfo.DriverDpc, NULL, NULL ) ) { InterlockedIncrement( &HalpCpeInfo.Stats.DriverDpcQueueFails ); } } // // Now check if we are receiving Cpe more frequently than our // threshold and if so call kernel to switch to polled mode // if (HalpCpeInfo.ThresholdMaximum != 0) { CurrentTime = KeQueryPerformanceCounter(NULL); KiAcquireSpinLock(&HalpCpeSpinLock); if (HalpCpeInfo.Stats.PollingInterval == HAL_CPE_INTERRUPTS_BASED) { if ( (CurrentTime.QuadPart - HalpCpeInfo.LastTime.QuadPart) < HalpCpeInfo.ThresholdTime.QuadPart) { if (++HalpCpeInfo.ThresholdCounter > HalpCpeInfo.ThresholdMaximum) { // // We have crossed the threshold so we need to // downgrade to polling mode. We switch down to polling // every 60 seconds as per the Intel Itanium Error // Handling guide // HalpCpeInfo.Stats.PollingInterval = HALP_CPE_DEFAULT_POLLING_INTERVAL; KiReleaseSpinLock(&HalpCpeSpinLock); if (HalpCpeInfo.KernelDelivery != NULL) { HalpCpeInfo.KernelDelivery( HalpCpeInfo.KernelToken, CpeSwitchToPolledMode, (PVOID)UlongToPtr(HalpCpeInfo.Stats.PollingInterval) ); } } else { KiReleaseSpinLock(&HalpCpeSpinLock); } } else { HalpCpeInfo.LastTime = CurrentTime; HalpCpeInfo.ThresholdCounter = 0; KiReleaseSpinLock(&HalpCpeSpinLock); } } else { KiReleaseSpinLock(&HalpCpeSpinLock); } } } // HalpCpeHandler() //EndProc////////////////////////////////////////////////////////////////////// VOID HalpMcRzHandler ( IN PKINTERRUPT_ROUTINE Interrupt, IN PKTRAP_FRAME TrapFrame ) /*++ Routine Description: Arguements: Return Parameters: --*/ { SAL_PAL_RETURN_VALUES rv={0}; HalpDisableInterrupts(); rv=HalpSalRendz(); HalpEnableInterrupts(); // do any Isr clean up and re-enable the interrupts & MC's return; } //EndProc////////////////////////////////////////////////////////////////////// VOID HalpMcWkupHandler ( IN PKINTERRUPT_ROUTINE Interrupt, IN PKTRAP_FRAME TrapFrame ) /*++ Routine Description: Arguements: Return Parameters: --*/ { return; } //EndProc////////////////////////////////////////////////////////////////////// VOID HalpCPEIHandler ( IN PKINTERRUPT_ROUTINE Interrupt, IN PKTRAP_FRAME TrapFrame ) /*++ Routine Description: Processor Interrupt routine for CPE interrupts. Arguments: TrapFrame - Captured trap frame address. Return Parameters: None. Notes: Thierry 08/2000: This function does not do much, it flags the PCR InOsCpe field and calls the second-level handler: HalpCpeHandler(). However, this was implmented this way so this function abstracts the standard interrupts resources from the purely CPE processing in HalpCpeHandler(). --*/ { volatile KPCR * const pcr = KeGetPcr(); pcr->InOsCpe = TRUE; HalpCpeHandler(); pcr->InOsCpe = FALSE; return; } // HalpCPEIHandler() VOID HalpCPEEnable ( VOID ) /*++ Routine Description: This routine sets the default HAL CPE handling regardless of the user specified registry setting. It enables the supported Platform Interrupt sources and exposes the initial interrupt/polling based mode used for CPE. The user specified registry setting is handled via HalpMcaInit() at the end of phase 1. Arguments: None. Return Parameters: None. Implementation Notes: The following implementation assumes that this code is executed on BSP. --*/ { ULONG i; if ( HalpFeatureBits & HAL_CPE_PRESENT ) { ULONG maxCPE = HalpMaxCPEImplemented; if ( maxCPE ) { // // Pick up the information from HalpCPEIntIn, HalpCPEDestination, HalpCPEVectorFlags, // HalpCPEIoSapicVector. // for (i=0 ; i != maxCPE; i++ ) { HalpEnableRedirEntry( HalpCPEIntIn[i] ); } // // Initialize the remaining fields of HAL private CPE info structure. // HalpCpeInfo.Stats.PollingInterval = HAL_CPE_INTERRUPTS_BASED; } else { // // We will implement Polling model. // HalpCpeInfo.Stats.PollingInterval = HALP_CPE_DEFAULT_POLLING_INTERVAL; } } else { HalpCpeInfo.Stats.PollingInterval = HAL_CPE_DISABLED; } } // HalpCPEEnable() VOID HalpCPEDisable ( VOID ) /*++ Routine Description: This routine disables the SAPIC Platform Interrupt Sources. Note that if HalpMaxCPEImplemented is 0, the function does nothing. Arguments: None. Return Parameters: None. --*/ { // // Pick up the information from HalpCPEIntIn, HalpCPEDestination, HalpCPEVectorFlags, // HalpCPEIoSapicVector ULONG i; for (i=0; i < HalpMaxCPEImplemented; i++) { HalpDisableRedirEntry(HalpCPEIntIn[i]); } } // HalpCPEDisable() VOID HalpMCADisable( VOID ) { PHYSICAL_ADDRESS NULL_PHYSICAL_ADDRESS = {0}; SAL_PAL_RETURN_VALUES rv = {0}; char Lid; ULONGLONG gp_reg = GetGp(); // Disable CMCs HalpCMCDisableForAllProcessors(); // Disable CPE Interrupts HalpCPEDisable(); //DeRegister Rendez. Paramters with SAL #define NULL_VECTOR 0xF rv = HalpSalSetParams(0,RendzType, IntrVecType, NULL_VECTOR, HalpMcRendezTimeOut ); // Deregister WakeUp parameters with SAL rv=HalpSalSetParams(0, WakeUpType, IntrVecType, NULL_VECTOR,0); // Deregister OsMcaDispatch (OS_MCA) physical address with SAL rv=HalpSalSetVectors(0, MchkEvent, NULL_PHYSICAL_ADDRESS, gp_reg,0); // Deregister OsInitDispatch physical address with SAL rv=HalpSalSetVectors(0, InitEvent, NULL_PHYSICAL_ADDRESS, gp_reg,0); } // HalpMCADisable() NTSTATUS HalpGetMceInformation( PHAL_ERROR_INFO ErrorInfo, PULONG ErrorInfoLength ) /*++ Routine Description: This routine is called by HaliQuerySystemInformation for the HalErrorInformation class. Arguments: ErrorInfo : pointer to HAL_ERROR_INFO structure. ErrorInfoLength : size of the valid memory structure pointed by ErrorInfo. Return Value: STATUS_SUCCESS if successful error status otherwise --*/ { NTSTATUS status; ULONG cpePollingInterval; PAGED_CODE(); ASSERT( ErrorInfo ); ASSERT( ErrorInfoLength ); // // Backward compatibility only. // if ( !ErrorInfo->Version || ( ErrorInfo->Version > HAL_ERROR_INFO_VERSION ) ) { return( STATUS_REVISION_MISMATCH ); } // // Zero Reserved field. // ErrorInfo->Reserved = 0; // // Collect MCA info under protection if required. // ErrorInfo->McaMaxSize = HalpMcaInfo.Stats.MaxLogSize; ErrorInfo->McaPreviousEventsCount = HalpMcaInfo.Stats.McaPreviousCount; ErrorInfo->McaCorrectedEventsCount = HalpMcaInfo.Stats.McaCorrectedCount; // approximation. ErrorInfo->McaKernelDeliveryFails = HalpMcaInfo.Stats.KernelDeliveryFails; // approximation. ErrorInfo->McaDriverDpcQueueFails = HalpMcaInfo.Stats.DriverDpcQueueFails; // approximation. ErrorInfo->McaReserved = 0; // // Collect CMC info under protection if required. // ErrorInfo->CmcMaxSize = HalpCmcInfo.Stats.MaxLogSize; ErrorInfo->CmcPollingInterval = HalpCmcInfo.Stats.PollingInterval; ErrorInfo->CmcInterruptsCount = HalpCmcInfo.Stats.CmcInterruptCount; // approximation. ErrorInfo->CmcKernelDeliveryFails = HalpCmcInfo.Stats.KernelDeliveryFails; // approximation. ErrorInfo->CmcDriverDpcQueueFails = HalpCmcInfo.Stats.DriverDpcQueueFails; // approximation. HalpAcquireCmcMutex(); ErrorInfo->CmcGetStateFails = HalpCmcInfo.Stats.GetStateFails; ErrorInfo->CmcClearStateFails = HalpCmcInfo.Stats.ClearStateFails; ErrorInfo->CmcLogId = HalpCmcInfo.Stats.LogId; HalpReleaseCmcMutex(); ErrorInfo->CmcReserved = 0; // // Collect CPE info under protection if required. // ErrorInfo->CpeMaxSize = HalpCpeInfo.Stats.MaxLogSize; ErrorInfo->CpePollingInterval = HalpCpeInfo.Stats.PollingInterval; ErrorInfo->CpeInterruptsCount = HalpCpeInfo.Stats.CpeInterruptCount; // approximation. ErrorInfo->CpeKernelDeliveryFails = HalpCpeInfo.Stats.KernelDeliveryFails; // approximation. ErrorInfo->CpeDriverDpcQueueFails = HalpCpeInfo.Stats.DriverDpcQueueFails; // approximation. HalpAcquireCpeMutex(); ErrorInfo->CpeGetStateFails = HalpCpeInfo.Stats.GetStateFails; ErrorInfo->CpeClearStateFails = HalpCpeInfo.Stats.ClearStateFails; ErrorInfo->CpeLogId = HalpCpeInfo.Stats.LogId; HalpReleaseCpeMutex(); // CpeInterruptSources: Number of SAPIC Platform Interrup Sources supported by HAL. ErrorInfo->CpeInterruptSources = HalpMaxCPEImplemented; // // Update KernelTokens // ErrorInfo->McaKernelToken = (ULONGLONG) HalpMcaInfo.KernelToken; ErrorInfo->CmcKernelToken = (ULONGLONG) HalpCmcInfo.KernelToken; ErrorInfo->CpeKernelToken = (ULONGLONG) HalpCpeInfo.KernelToken; ErrorInfo->KernelReserved[3] = (ULONGLONG) 0; *ErrorInfoLength = sizeof(*ErrorInfo); return( STATUS_SUCCESS ); } // HalpGetMceInformation()