/** Searches the performance measurement log from the beginning of the log for the first matching record that contains a zero end time and fills in a valid end time. Searches the performance measurement log from the beginning of the log for the first record that matches Handle, Token, Module and Identifier and has an end time value of zero. If the record can not be found then return EFI_NOT_FOUND. If the record is found and TimeStamp is not zero, then the end time in the record is filled in with the value specified by TimeStamp. If the record is found and TimeStamp is zero, then the end time in the matching record is filled in with the current time stamp value. @param Handle Pointer to environment specific context used to identify the component being measured. @param Token Pointer to a Null-terminated ASCII string that identifies the component being measured. @param Module Pointer to a Null-terminated ASCII string that identifies the module being measured. @param TimeStamp 64-bit time stamp. @param Identifier 32-bit identifier. If the value is 0, the found record is same as the one found by EndGauge of PERFORMANCE_PROTOCOL. @retval EFI_SUCCESS The end of the measurement was recorded. @retval EFI_NOT_FOUND The specified measurement record could not be found. **/ EFI_STATUS EFIAPI EndGaugeEx ( IN CONST VOID *Handle, OPTIONAL IN CONST CHAR8 *Token, OPTIONAL IN CONST CHAR8 *Module, OPTIONAL IN UINT64 TimeStamp, IN UINT32 Identifier ) { GAUGE_DATA_ENTRY_EX *GaugeEntryExArray; UINT32 Index; AcquireSpinLock (&mSmmPerfLock); if (TimeStamp == 0) { TimeStamp = GetPerformanceCounter (); } Index = SmmSearchForGaugeEntry (Handle, Token, Module, Identifier); if (Index >= mGaugeData->NumberOfEntries) { ReleaseSpinLock (&mSmmPerfLock); return EFI_NOT_FOUND; } GaugeEntryExArray = (GAUGE_DATA_ENTRY_EX *) (mGaugeData + 1); GaugeEntryExArray[Index].EndTimeStamp = TimeStamp; ReleaseSpinLock (&mSmmPerfLock); return EFI_SUCCESS; }
/** Prints an assert message containing a filename, line number, and description. This may be followed by a breakpoint or a dead loop. Print a message of the form "ASSERT <FileName>(<LineNumber>): <Description>\n" to the debug output device. If DEBUG_PROPERTY_ASSERT_BREAKPOINT_ENABLED bit of PcdDebugProperyMask is set then CpuBreakpoint() is called. Otherwise, if DEBUG_PROPERTY_ASSERT_DEADLOOP_ENABLED bit of PcdDebugProperyMask is set then CpuDeadLoop() is called. If neither of these bits are set, then this function returns immediately after the message is printed to the debug output device. DebugAssert() must actively prevent recursion. If DebugAssert() is called while processing another DebugAssert(), then DebugAssert() must return immediately. If FileName is NULL, then a <FileName> string of "(NULL) Filename" is printed. If Description is NULL, then a <Description> string of "(NULL) Description" is printed. @param FileName The pointer to the name of the source file that generated the assert condition. @param LineNumber The line number in the source file that generated the assert condition @param Description The pointer to the description of the assert condition. **/ VOID EFIAPI DebugAssert ( IN CONST CHAR8 *FileName, IN UINTN LineNumber, IN CONST CHAR8 *Description ) { CHAR8 Buffer[MAX_DEBUG_MESSAGE_LENGTH]; // // Generate the ASSERT() message in Ascii format // AsciiSPrint (Buffer, sizeof (Buffer), "ASSERT %a(%d): %a\n", FileName, LineNumber, Description); // // Send the print string to the Console Output device // AcquireSpinLock (&mInternalDebugLock); SerialPortWrite ((UINT8 *) Buffer, AsciiStrLen(Buffer)); ReleaseSpinLock (&mInternalDebugLock); // // Generate a Breakpoint, DeadLoop, or NOP based on PCD settings // if ((PcdGet8(PcdDebugPropertyMask) & DEBUG_PROPERTY_ASSERT_BREAKPOINT_ENABLED) != 0) { CpuBreakpoint (); } else if ((PcdGet8(PcdDebugPropertyMask) & DEBUG_PROPERTY_ASSERT_DEADLOOP_ENABLED) != 0) { CpuDeadLoop (); } }
/** The page fault handler that on-demand read PI CpuSaveStates for framework use. If the fault is not targeted to mFrameworkSmst->CpuSaveState range, the function will return FALSE to let PageFaultHandlerHook know it needs to pass the fault over to original page fault handler. @retval TRUE The page fault is correctly handled. @retval FALSE The page fault is not handled and is passed through to original handler. **/ BOOLEAN PageFaultHandler ( VOID ) { BOOLEAN IsHandled; UINT64 *PageTable; UINT64 PFAddress; UINTN NumCpuStatePages; ASSERT (mPageTableHookEnabled); AcquireSpinLock (&mPFLock); PageTable = (UINT64*)(UINTN)(AsmReadCr3 () & mPhyMask); PFAddress = AsmReadCr2 (); NumCpuStatePages = EFI_SIZE_TO_PAGES (mNumberOfProcessors * sizeof (EFI_SMM_CPU_SAVE_STATE)); IsHandled = FALSE; if (((UINTN)mFrameworkSmst->CpuSaveState & ~(SIZE_2MB-1)) == (PFAddress & ~(SIZE_2MB-1))) { if ((UINTN)mFrameworkSmst->CpuSaveState <= PFAddress && PFAddress < (UINTN)mFrameworkSmst->CpuSaveState + EFI_PAGES_TO_SIZE (NumCpuStatePages) ) { mCpuStatePageTable[BitFieldRead64 (PFAddress, 12, 20)] |= BIT0 | BIT1; // present and rw CpuFlushTlb (); ReadWriteCpuStatePage (PFAddress & ~(SIZE_4KB-1), TRUE); IsHandled = TRUE; } else { ASSERT (FALSE); } } ReleaseSpinLock (&mPFLock); return IsHandled; }
static FORCEINLINE VOID ReleaseTimerListLock ( __inout PTIMER_LIST TimerList ) { ReleaseSpinLock(&TimerList->Lock); }
/** This function will be called from AP reset code if BSP uses WakeUpAP. @param ExchangeInfo Pointer to the MP exchange info buffer @param NumApsExecuting Number of curret executing AP **/ VOID EFIAPI ApCFunction ( IN MP_CPU_EXCHANGE_INFO *ExchangeInfo, IN UINTN NumApsExecuting ) { PEI_CPU_MP_DATA *PeiCpuMpData; UINTN ProcessorNumber; EFI_AP_PROCEDURE Procedure; UINTN BistData; PeiCpuMpData = ExchangeInfo->PeiCpuMpData; if (PeiCpuMpData->InitFlag) { // // This is first time AP wakeup, get BIST information from AP stack // BistData = *(UINTN *) (PeiCpuMpData->Buffer + NumApsExecuting * PeiCpuMpData->CpuApStackSize - sizeof (UINTN)); PeiCpuMpData->CpuData[NumApsExecuting].Health.Uint32 = (UINT32) BistData; PeiCpuMpData->CpuData[NumApsExecuting].ApicId = GetInitialApicId (); if (PeiCpuMpData->CpuData[NumApsExecuting].ApicId >= 0xFF) { // // Set x2APIC mode if there are any logical processor reporting // an APIC ID of 255 or greater. // AcquireSpinLock(&PeiCpuMpData->MpLock); PeiCpuMpData->X2ApicEnable = TRUE; ReleaseSpinLock(&PeiCpuMpData->MpLock); } // // Sync BSP's Mtrr table to all wakeup APs and load microcode on APs. // MtrrSetAllMtrrs (&PeiCpuMpData->MtrrTable); MicrocodeDetect (); } else { // // Execute AP function if AP is not disabled // GetProcessorNumber (PeiCpuMpData, &ProcessorNumber); if ((PeiCpuMpData->CpuData[ProcessorNumber].State != CpuStateDisabled) && (PeiCpuMpData->ApFunction != 0)) { PeiCpuMpData->CpuData[ProcessorNumber].State = CpuStateBusy; Procedure = (EFI_AP_PROCEDURE)(UINTN)PeiCpuMpData->ApFunction; Procedure ((VOID *)(UINTN)PeiCpuMpData->ApFunctionArgument); PeiCpuMpData->CpuData[ProcessorNumber].State = CpuStateIdle; } } // // AP finished executing C code // InterlockedIncrement ((UINT32 *)&PeiCpuMpData->FinishedCount); AsmCliHltLoop (); }
/** Release a spin lock when Multi-processor supported. @param[in, out] MpSpinLock A pointer to the spin lock. **/ VOID ReleaseMpSpinLock ( IN OUT SPIN_LOCK *MpSpinLock ) { if (!MultiProcessorDebugSupport()) { return; } ReleaseSpinLock (MpSpinLock); }
/** This function is NMI handler. @param Index CPU index **/ VOID ExceptionNMIHandler ( IN UINT32 Index ) { VM_EXIT_INFO_INTERRUPTION InterruptionInformation; InterruptionInformation.Uint32 = VmRead32 (VMCS_32_RO_VMEXIT_INTERRUPTION_INFO_INDEX); AcquireSpinLock (&mHostContextCommon.DebugLock); DEBUG ((EFI_D_INFO, "(FRM) !!!ExceptionNmiHandler %d!!!\n", Index)); DEBUG ((EFI_D_INFO, "(FRM) InterruptType %d, Vector %x\n", InterruptionInformation.Bits.InterruptType, InterruptionInformation.Bits.Vector)); DumpVmcsAllField (); ReleaseSpinLock (&mHostContextCommon.DebugLock); CpuDeadLoop(); }
/** This function is interrupt windows handler. @param Index CPU index **/ VOID InterruptWindowHandler ( UINT32 Index ) { VM_ENTRY_CONTROL_INTERRUPT InterruptControl; VM_EXEC_PROCESSOR_BASES_VMEXIT_CONTROLS ProcessorBasedCtrls; VM_EXEC_PIN_BASES_VMEXIT_CONTROLS PinBasedCtls; DEBUG ((EFI_D_INFO, "W")); InterruptControl.Uint32 = mGuestContextCommon.GuestContextPerCpu[Index].LastPendingInterrupt[mGuestContextCommon.GuestContextPerCpu[Index].LastPendingInterruptTop]; if (InterruptControl.Bits.Valid == 0) { AcquireSpinLock (&mHostContextCommon.DebugLock); DEBUG ((EFI_D_ERROR, "(FRM) !!!InterruptWindowHandler Invalid Information - %d!!!\n", Index)); DumpVmcsAllField (); ReleaseSpinLock (&mHostContextCommon.DebugLock); CpuDeadLoop (); } VmWrite32 (VMCS_32_CONTROL_VMENTRY_INTERRUPTION_INFO_INDEX, InterruptControl.Uint32); mGuestContextCommon.GuestContextPerCpu[Index].LastPendingInterruptTop--; // // Disable InterruptWindows, if LastPendingInterrupt // ProcessorBasedCtrls.Uint32 = VmRead32 (VMCS_32_CONTROL_PROCESSOR_BASED_VM_EXECUTION_INDEX); if (mGuestContextCommon.GuestContextPerCpu[Index].LastPendingInterruptTop == 0) { ProcessorBasedCtrls.Bits.InterruptWindow = 0; } else { ProcessorBasedCtrls.Bits.InterruptWindow = 1; } VmWrite32 (VMCS_32_CONTROL_PROCESSOR_BASED_VM_EXECUTION_INDEX, ProcessorBasedCtrls.Uint32); PinBasedCtls.Uint32 = VmRead32 (VMCS_32_CONTROL_PIN_BASED_VM_EXECUTION_INDEX); PinBasedCtls.Bits.ExternalInterrupt = 1; VmWrite32 (VMCS_32_CONTROL_PIN_BASED_VM_EXECUTION_INDEX, PinBasedCtls.Uint32); return ; }
/** This function launch guest BSP. **/ VOID LaunchGuestBsp ( VOID ) { UINTN Flag; UINTN Rflags; Flag = SetJump (&mGuestJumpBuffer); if (Flag != 0) { DEBUG ((EFI_D_INFO, "(FRM) !!!Guest JumpBack to EFI!!!\n")); // Run successfully return ; } AsmVmPtrLoad (&mGuestContextCommon.GuestContextPerCpu[mBspIndex].Vmcs); // // Launch STM // LaunchStm (mBspIndex); AsmVmPtrLoad (&mGuestContextCommon.GuestContextPerCpu[mBspIndex].Vmcs); AcquireSpinLock (&mHostContextCommon.DebugLock); DumpVmcsAllField (); ReleaseSpinLock (&mHostContextCommon.DebugLock); AsmWbinvd (); Rflags = AsmVmLaunch (&mGuestContextCommon.GuestContextPerCpu[mBspIndex].Register); DEBUG ((EFI_D_ERROR, "(FRM) !!!LaunchGuestBsp FAIL!!!\n")); DEBUG ((EFI_D_ERROR, "(FRM) Rflags: %08x\n", Rflags)); DEBUG ((EFI_D_ERROR, "(FRM) VMCS_32_RO_VM_INSTRUCTION_ERROR: %08x\n", (UINTN)VmRead32 (VMCS_32_RO_VM_INSTRUCTION_ERROR_INDEX))); CpuDeadLoop (); }
/** Prints a debug message to the debug output device if the specified error level is enabled. If any bit in ErrorLevel is also set in PcdDebugPrintErrorLevel, then print the message specified by Format and the associated variable argument list to the debug output device. If Format is NULL, then ASSERT(). @param ErrorLevel The error level of the debug message. @param Format Format string for the debug message to print. @param ... Variable argument list whose contents are accessed based on the format string specified by Format. **/ VOID EFIAPI DebugPrint ( IN UINTN ErrorLevel, IN CONST CHAR8 *Format, ... ) { CHAR8 Buffer[MAX_DEBUG_MESSAGE_LENGTH]; VA_LIST Marker; // // If Format is NULL, then ASSERT(). // ASSERT (Format != NULL); // // Check driver debug mask value and global mask // if ((ErrorLevel & GetDebugPrintErrorLevel ()) == 0) { return; } // // Convert the DEBUG() message to an ASCII String // VA_START (Marker, Format); AsciiVSPrint (Buffer, sizeof (Buffer), Format, Marker); VA_END (Marker); // // Send the print string to a Serial Port // AcquireSpinLock (&mInternalDebugLock); SerialPortWrite ((UINT8 *) Buffer, AsciiStrLen(Buffer)); ReleaseSpinLock (&mInternalDebugLock); }
/** Internal worker function for common exception handler. @param ExceptionType Exception type. @param SystemContext Pointer to EFI_SYSTEM_CONTEXT. @param ExceptionHandlerData Pointer to exception handler data. **/ VOID CommonExceptionHandlerWorker ( IN EFI_EXCEPTION_TYPE ExceptionType, IN EFI_SYSTEM_CONTEXT SystemContext, IN EXCEPTION_HANDLER_DATA *ExceptionHandlerData ) { EXCEPTION_HANDLER_CONTEXT *ExceptionHandlerContext; RESERVED_VECTORS_DATA *ReservedVectors; EFI_CPU_INTERRUPT_HANDLER *ExternalInterruptHandler; ExceptionHandlerContext = (EXCEPTION_HANDLER_CONTEXT *) (UINTN) (SystemContext.SystemContextIa32); ReservedVectors = ExceptionHandlerData->ReservedVectors; ExternalInterruptHandler = ExceptionHandlerData->ExternalInterruptHandler; switch (ReservedVectors[ExceptionType].Attribute) { case EFI_VECTOR_HANDOFF_HOOK_BEFORE: // // Need to jmp to old IDT handler after this exception handler // ExceptionHandlerContext->ExceptionDataFlag = (mErrorCodeFlag & (1 << ExceptionType)) ? TRUE : FALSE; ExceptionHandlerContext->OldIdtHandler = ReservedVectors[ExceptionType].ExceptonHandler; break; case EFI_VECTOR_HANDOFF_HOOK_AFTER: while (TRUE) { // // If if anyone has gotten SPIN_LOCK for owner running hook after // if (AcquireSpinLockOrFail (&ReservedVectors[ExceptionType].SpinLock)) { // // Need to execute old IDT handler before running this exception handler // ReservedVectors[ExceptionType].ApicId = GetApicId (); ArchSaveExceptionContext (ExceptionType, SystemContext, ExceptionHandlerData); ExceptionHandlerContext->ExceptionDataFlag = (mErrorCodeFlag & (1 << ExceptionType)) ? TRUE : FALSE; ExceptionHandlerContext->OldIdtHandler = ReservedVectors[ExceptionType].ExceptonHandler; return; } // // If failed to acquire SPIN_LOCK, check if it was locked by processor itself // if (ReservedVectors[ExceptionType].ApicId == GetApicId ()) { // // Old IDT handler has been executed, then restore CPU exception content to // run new exception handler. // ArchRestoreExceptionContext (ExceptionType, SystemContext, ExceptionHandlerData); // // Rlease spin lock for ApicId // ReleaseSpinLock (&ReservedVectors[ExceptionType].SpinLock); break; } CpuPause (); } break; case 0xffffffff: break; default: // // It should never reach here // CpuDeadLoop (); break; } if (ExternalInterruptHandler != NULL && ExternalInterruptHandler[ExceptionType] != NULL) { (ExternalInterruptHandler[ExceptionType]) (ExceptionType, SystemContext); } else if (ExceptionType < CPU_EXCEPTION_NUM) { // // Get Spinlock to display CPU information // while (!AcquireSpinLockOrFail (&ExceptionHandlerData->DisplayMessageSpinLock)) { CpuPause (); } // // Display ExceptionType, CPU information and Image information // DumpCpuContent (ExceptionType, SystemContext); // // Release Spinlock of output message // ReleaseSpinLock (&ExceptionHandlerData->DisplayMessageSpinLock); // // Enter a dead loop if needn't to execute old IDT handler further // if (ReservedVectors[ExceptionType].Attribute != EFI_VECTOR_HANDOFF_HOOK_BEFORE) { CpuDeadLoop (); } } }
/** This function will be called from AP reset code if BSP uses WakeUpAP. @param ExchangeInfo Pointer to the MP exchange info buffer @param NumApsExecuting Number of current executing AP **/ VOID EFIAPI ApCFunction ( IN MP_CPU_EXCHANGE_INFO *ExchangeInfo, IN UINTN NumApsExecuting ) { PEI_CPU_MP_DATA *PeiCpuMpData; UINTN ProcessorNumber; EFI_AP_PROCEDURE Procedure; UINTN BistData; volatile UINT32 *ApStartupSignalBuffer; PeiCpuMpData = ExchangeInfo->PeiCpuMpData; while (TRUE) { if (PeiCpuMpData->InitFlag) { ProcessorNumber = NumApsExecuting; // // Sync BSP's Control registers to APs // RestoreVolatileRegisters (&PeiCpuMpData->CpuData[0].VolatileRegisters, FALSE); // // This is first time AP wakeup, get BIST information from AP stack // BistData = *(UINTN *) (PeiCpuMpData->Buffer + ProcessorNumber * PeiCpuMpData->CpuApStackSize - sizeof (UINTN)); PeiCpuMpData->CpuData[ProcessorNumber].Health.Uint32 = (UINT32) BistData; PeiCpuMpData->CpuData[ProcessorNumber].ApicId = GetInitialApicId (); if (PeiCpuMpData->CpuData[ProcessorNumber].ApicId >= 0xFF) { // // Set x2APIC mode if there are any logical processor reporting // an APIC ID of 255 or greater. // AcquireSpinLock(&PeiCpuMpData->MpLock); PeiCpuMpData->X2ApicEnable = TRUE; ReleaseSpinLock(&PeiCpuMpData->MpLock); } // // Sync BSP's Mtrr table to all wakeup APs and load microcode on APs. // MtrrSetAllMtrrs (&PeiCpuMpData->MtrrTable); MicrocodeDetect (); PeiCpuMpData->CpuData[ProcessorNumber].State = CpuStateIdle; } else { // // Execute AP function if AP is not disabled // GetProcessorNumber (PeiCpuMpData, &ProcessorNumber); if (PeiCpuMpData->ApLoopMode == ApInHltLoop) { // // Restore AP's volatile registers saved // RestoreVolatileRegisters (&PeiCpuMpData->CpuData[ProcessorNumber].VolatileRegisters, TRUE); } if ((PeiCpuMpData->CpuData[ProcessorNumber].State != CpuStateDisabled) && (PeiCpuMpData->ApFunction != 0)) { PeiCpuMpData->CpuData[ProcessorNumber].State = CpuStateBusy; Procedure = (EFI_AP_PROCEDURE)(UINTN)PeiCpuMpData->ApFunction; // // Invoke AP function here // Procedure ((VOID *)(UINTN)PeiCpuMpData->ApFunctionArgument); // // Re-get the processor number due to BSP/AP maybe exchange in AP function // GetProcessorNumber (PeiCpuMpData, &ProcessorNumber); PeiCpuMpData->CpuData[ProcessorNumber].State = CpuStateIdle; } } // // AP finished executing C code // InterlockedIncrement ((UINT32 *)&PeiCpuMpData->FinishedCount); // // Place AP is specified loop mode // if (PeiCpuMpData->ApLoopMode == ApInHltLoop) { // // Save AP volatile registers // SaveVolatileRegisters (&PeiCpuMpData->CpuData[ProcessorNumber].VolatileRegisters); // // Place AP in Hlt-loop // while (TRUE) { DisableInterrupts (); CpuSleep (); CpuPause (); } } ApStartupSignalBuffer = PeiCpuMpData->CpuData[ProcessorNumber].StartupApSignal; // // Clear AP start-up signal // *ApStartupSignalBuffer = 0; while (TRUE) { DisableInterrupts (); if (PeiCpuMpData->ApLoopMode == ApInMwaitLoop) { // // Place AP in Mwait-loop // AsmMonitor ((UINTN)ApStartupSignalBuffer, 0, 0); if (*ApStartupSignalBuffer != WAKEUP_AP_SIGNAL) { // // If AP start-up signal is not set, place AP into // the maximum C-state // AsmMwait (PeiCpuMpData->ApTargetCState << 4, 0); } } else if (PeiCpuMpData->ApLoopMode == ApInRunLoop) { // // Place AP in Run-loop // CpuPause (); } else { ASSERT (FALSE); } // // If AP start-up signal is written, AP is waken up // otherwise place AP in loop again // if (*ApStartupSignalBuffer == WAKEUP_AP_SIGNAL) { break; } } } }
/** Programs registers for the calling processor. This function programs registers for the calling processor. @param RegisterTable Pointer to register table of the running processor. **/ VOID SetProcessorRegister ( IN CPU_REGISTER_TABLE *RegisterTable ) { CPU_REGISTER_TABLE_ENTRY *RegisterTableEntry; UINTN Index; UINTN Value; SPIN_LOCK *MsrSpinLock; // // Traverse Register Table of this logical processor // RegisterTableEntry = (CPU_REGISTER_TABLE_ENTRY *) (UINTN) RegisterTable->RegisterTableEntry; for (Index = 0; Index < RegisterTable->TableLength; Index++, RegisterTableEntry++) { // // Check the type of specified register // switch (RegisterTableEntry->RegisterType) { // // The specified register is Control Register // case ControlRegister: switch (RegisterTableEntry->Index) { case 0: Value = AsmReadCr0 (); Value = (UINTN) BitFieldWrite64 ( Value, RegisterTableEntry->ValidBitStart, RegisterTableEntry->ValidBitStart + RegisterTableEntry->ValidBitLength - 1, (UINTN) RegisterTableEntry->Value ); AsmWriteCr0 (Value); break; case 2: Value = AsmReadCr2 (); Value = (UINTN) BitFieldWrite64 ( Value, RegisterTableEntry->ValidBitStart, RegisterTableEntry->ValidBitStart + RegisterTableEntry->ValidBitLength - 1, (UINTN) RegisterTableEntry->Value ); AsmWriteCr2 (Value); break; case 3: Value = AsmReadCr3 (); Value = (UINTN) BitFieldWrite64 ( Value, RegisterTableEntry->ValidBitStart, RegisterTableEntry->ValidBitStart + RegisterTableEntry->ValidBitLength - 1, (UINTN) RegisterTableEntry->Value ); AsmWriteCr3 (Value); break; case 4: Value = AsmReadCr4 (); Value = (UINTN) BitFieldWrite64 ( Value, RegisterTableEntry->ValidBitStart, RegisterTableEntry->ValidBitStart + RegisterTableEntry->ValidBitLength - 1, (UINTN) RegisterTableEntry->Value ); AsmWriteCr4 (Value); break; default: break; } break; // // The specified register is Model Specific Register // case Msr: // // If this function is called to restore register setting after INIT signal, // there is no need to restore MSRs in register table. // if (RegisterTableEntry->ValidBitLength >= 64) { // // If length is not less than 64 bits, then directly write without reading // AsmWriteMsr64 ( RegisterTableEntry->Index, RegisterTableEntry->Value ); } else { // // Get lock to avoid Package/Core scope MSRs programming issue in parallel execution mode // to make sure MSR read/write operation is atomic. // MsrSpinLock = GetMsrSpinLockByIndex (RegisterTableEntry->Index); AcquireSpinLock (MsrSpinLock); // // Set the bit section according to bit start and length // AsmMsrBitFieldWrite64 ( RegisterTableEntry->Index, RegisterTableEntry->ValidBitStart, RegisterTableEntry->ValidBitStart + RegisterTableEntry->ValidBitLength - 1, RegisterTableEntry->Value ); ReleaseSpinLock (MsrSpinLock); } break; // // Enable or disable cache // case CacheControl: // // If value of the entry is 0, then disable cache. Otherwise, enable cache. // if (RegisterTableEntry->Value == 0) { AsmDisableCache (); } else { AsmEnableCache (); } break; default: break; } } }
/** This function is external interrupt handler. @param Index CPU index **/ VOID ExternalInterruptHandler ( UINT32 Index ) { VM_EXIT_INFO_INTERRUPTION InterruptionInformation; // VM_EXIT_INFO_IDT_VECTORING IdtVectoring; VM_ENTRY_CONTROL_INTERRUPT InterruptControl; VM_EXEC_PROCESSOR_BASES_VMEXIT_CONTROLS ProcessorBasedCtrls; VM_EXEC_PIN_BASES_VMEXIT_CONTROLS PinBasedCtls; DEBUG ((EFI_D_INFO, "I")); InterruptControl.Uint32 = 0; #if 1 InterruptionInformation.Uint32 = VmRead32 (VMCS_32_RO_VMEXIT_INTERRUPTION_INFO_INDEX); if (InterruptionInformation.Bits.Valid == 0) { AcquireSpinLock (&mHostContextCommon.DebugLock); DEBUG ((EFI_D_ERROR, "(FRM) !!!ExternalInterruptHandler Invaid Information - %d!!!\n", Index)); DumpVmcsAllField (); ReleaseSpinLock (&mHostContextCommon.DebugLock); CpuDeadLoop (); } InterruptControl.Bits.Vector = InterruptionInformation.Bits.Vector; InterruptControl.Bits.InterruptType = InterruptionInformation.Bits.InterruptType; InterruptControl.Bits.DeliverErrorCode = InterruptionInformation.Bits.ErrorCodeValid; InterruptControl.Bits.Valid = InterruptionInformation.Bits.Valid; if (InterruptControl.Bits.DeliverErrorCode) { VmWrite32 (VMCS_32_CONTROL_VMENTRY_EXCEPTION_ERROR_CODE_INDEX, VmRead32 (VMCS_32_RO_VMEXIT_INTERRUPTION_ERROR_CODE_INDEX)); } #else IdtVectoring.Uint32 = VmRead32 (VMCS_32_RO_IDT_VECTORING_INFO_INDEX); InterruptControl.Bits.Vector = IdtVectoring.Bits.Vector; InterruptControl.Bits.InterruptType = IdtVectoring.Bits.InterruptType; InterruptControl.Bits.DeliverErrorCode = IdtVectoring.Bits.ErrorCodeValid; InterruptControl.Bits.Valid = IdtVectoring.Bits.Valid; if (InterruptControl.Bits.DeliverErrorCode) { VmWrite32 (VMCS_32_CONTROL_VMENTRY_EXCEPTION_ERROR_CODE_INDEX, VmRead32 (VMCS_32_RO_IDT_VECTORING_ERROR_CODE_INDEX)); } #endif if ((InterruptControl.Bits.InterruptType == INTERRUPT_TYPE_EXTERNAL_SOFTWARE_INTERRUPT) || (InterruptControl.Bits.InterruptType == INTERRUPT_TYPE_EXTERNAL_PRIVILEDGED_SOFTWARE_EXCEPTION) || (InterruptControl.Bits.InterruptType == INTERRUPT_TYPE_EXTERNAL_SOFTWARE_EXCEPTIONT)) { VmWrite32 (VMCS_32_CONTROL_VMENTRY_INSTRUCTION_LENGTH_INDEX, VmRead32 (VMCS_32_RO_VMEXIT_INSTRUCTION_LENGTH_INDEX)); } else { VmWrite32 (VMCS_32_CONTROL_VMENTRY_INSTRUCTION_LENGTH_INDEX, 0); } if (((VmReadN (VMCS_N_GUEST_RFLAGS_INDEX) & RFLAGS_IF) == 0) && (InterruptControl.Bits.InterruptType == INTERRUPT_TYPE_EXTERNAL_EXTERNAL_INTERRUPT)) { // // Enable InterruptWindows, and pending ... // // DEBUG ((EFI_D_INFO, "P")); if (mGuestContextCommon.GuestContextPerCpu[Index].LastPendingInterruptTop != 0) { // // NOTE: The reason for code running here is: // When ISR is set, the lower or equal IRQ will be masked, but higher IRQ will arrive to set IRR no matter EOI issued or not. // When EOI is issued, only highest ISR will be cleared. // // The PIC/8259 IRQ priority is: 0 1 (8 9 10 11 12 13 14 15) 3 4 5 6 7 // The IO-APIC IRQ priority is: // // AcquireSpinLock (&mHostContextCommon.DebugLock); // DEBUG ((EFI_D_INFO, "(FRM) !!!ExternalInterruptHandler Already Pending - %d!!!\n", Index)); // DEBUG ((EFI_D_INFO, "(FRM) InterruptControl - %x\n", InterruptControl.Uint32)); // DEBUG ((EFI_D_INFO, "(FRM) LastPendingInterrupt - %x\n", mGuestContextCommon.GuestContextPerCpu[Index].LastPendingInterrupt[mGuestContextCommon.GuestContextPerCpu[Index].LastPendingInterruptTop])); // DumpVmcsAllField (); // ReleaseSpinLock (&mHostContextCommon.DebugLock); DEBUG ((EFI_D_INFO, "D")); } mGuestContextCommon.GuestContextPerCpu[Index].LastPendingInterruptTop++; mGuestContextCommon.GuestContextPerCpu[Index].LastPendingInterrupt[mGuestContextCommon.GuestContextPerCpu[Index].LastPendingInterruptTop] = InterruptControl.Uint32; ProcessorBasedCtrls.Uint32 = VmRead32 (VMCS_32_CONTROL_PROCESSOR_BASED_VM_EXECUTION_INDEX); ProcessorBasedCtrls.Bits.InterruptWindow = 1; VmWrite32 (VMCS_32_CONTROL_PROCESSOR_BASED_VM_EXECUTION_INDEX, ProcessorBasedCtrls.Uint32); PinBasedCtls.Uint32 = VmRead32 (VMCS_32_CONTROL_PIN_BASED_VM_EXECUTION_INDEX); PinBasedCtls.Bits.ExternalInterrupt = 0; VmWrite32 (VMCS_32_CONTROL_PIN_BASED_VM_EXECUTION_INDEX, PinBasedCtls.Uint32); } else { // DEBUG ((EFI_D_INFO, "J")); // // Inject interrupt to guest // VmWrite32 (VMCS_32_CONTROL_VMENTRY_INTERRUPTION_INFO_INDEX, InterruptControl.Uint32); } return ; }
/** SMI handler for BSP. @param CpuIndex BSP processor Index @param SyncMode SMM MP sync mode **/ VOID BSPHandler ( IN UINTN CpuIndex, IN SMM_CPU_SYNC_MODE SyncMode ) { UINTN Index; MTRR_SETTINGS Mtrrs; UINTN ApCount; BOOLEAN ClearTopLevelSmiResult; UINTN PresentCount; ASSERT (CpuIndex == mSmmMpSyncData->BspIndex); ApCount = 0; // // Flag BSP's presence // mSmmMpSyncData->InsideSmm = TRUE; // // Initialize Debug Agent to start source level debug in BSP handler // InitializeDebugAgent (DEBUG_AGENT_INIT_ENTER_SMI, NULL, NULL); // // Mark this processor's presence // mSmmMpSyncData->CpuData[CpuIndex].Present = TRUE; // // Clear platform top level SMI status bit before calling SMI handlers. If // we cleared it after SMI handlers are run, we would miss the SMI that // occurs after SMI handlers are done and before SMI status bit is cleared. // ClearTopLevelSmiResult = ClearTopLevelSmiStatus(); ASSERT (ClearTopLevelSmiResult == TRUE); // // Set running processor index // gSmmCpuPrivate->SmmCoreEntryContext.CurrentlyExecutingCpu = CpuIndex; // // If Traditional Sync Mode or need to configure MTRRs: gather all available APs. // if (SyncMode == SmmCpuSyncModeTradition || SmmCpuFeaturesNeedConfigureMtrrs()) { // // Wait for APs to arrive // SmmWaitForApArrival(); // // Lock the counter down and retrieve the number of APs // mSmmMpSyncData->AllCpusInSync = TRUE; ApCount = LockdownSemaphore (&mSmmMpSyncData->Counter) - 1; // // Wait for all APs to get ready for programming MTRRs // WaitForAllAPs (ApCount); if (SmmCpuFeaturesNeedConfigureMtrrs()) { // // Signal all APs it's time for backup MTRRs // ReleaseAllAPs (); // // WaitForSemaphore() may wait for ever if an AP happens to enter SMM at // exactly this point. Please make sure PcdCpuSmmMaxSyncLoops has been set // to a large enough value to avoid this situation. // Note: For HT capable CPUs, threads within a core share the same set of MTRRs. // We do the backup first and then set MTRR to avoid race condition for threads // in the same core. // MtrrGetAllMtrrs(&Mtrrs); // // Wait for all APs to complete their MTRR saving // WaitForAllAPs (ApCount); // // Let all processors program SMM MTRRs together // ReleaseAllAPs (); // // WaitForSemaphore() may wait for ever if an AP happens to enter SMM at // exactly this point. Please make sure PcdCpuSmmMaxSyncLoops has been set // to a large enough value to avoid this situation. // ReplaceOSMtrrs (CpuIndex); // // Wait for all APs to complete their MTRR programming // WaitForAllAPs (ApCount); } } // // The BUSY lock is initialized to Acquired state // AcquireSpinLockOrFail (&mSmmMpSyncData->CpuData[CpuIndex].Busy); // // Restore SMM Configuration in S3 boot path. // if (mRestoreSmmConfigurationInS3) { // // Configure SMM Code Access Check feature if available. // ConfigSmmCodeAccessCheck (); mRestoreSmmConfigurationInS3 = FALSE; } // // Invoke SMM Foundation EntryPoint with the processor information context. // gSmmCpuPrivate->SmmCoreEntry (&gSmmCpuPrivate->SmmCoreEntryContext); // // Make sure all APs have completed their pending none-block tasks // for (Index = mMaxNumberOfCpus; Index-- > 0;) { if (Index != CpuIndex && mSmmMpSyncData->CpuData[Index].Present) { AcquireSpinLock (&mSmmMpSyncData->CpuData[Index].Busy); ReleaseSpinLock (&mSmmMpSyncData->CpuData[Index].Busy);; } } // // Perform the remaining tasks // PerformRemainingTasks (); // // If Relaxed-AP Sync Mode: gather all available APs after BSP SMM handlers are done, and // make those APs to exit SMI synchronously. APs which arrive later will be excluded and // will run through freely. // if (SyncMode != SmmCpuSyncModeTradition && !SmmCpuFeaturesNeedConfigureMtrrs()) { // // Lock the counter down and retrieve the number of APs // mSmmMpSyncData->AllCpusInSync = TRUE; ApCount = LockdownSemaphore (&mSmmMpSyncData->Counter) - 1; // // Make sure all APs have their Present flag set // while (TRUE) { PresentCount = 0; for (Index = mMaxNumberOfCpus; Index-- > 0;) { if (mSmmMpSyncData->CpuData[Index].Present) { PresentCount ++; } } if (PresentCount > ApCount) { break; } } } // // Notify all APs to exit // mSmmMpSyncData->InsideSmm = FALSE; ReleaseAllAPs (); // // Wait for all APs to complete their pending tasks // WaitForAllAPs (ApCount); if (SmmCpuFeaturesNeedConfigureMtrrs()) { // // Signal APs to restore MTRRs // ReleaseAllAPs (); // // Restore OS MTRRs // SmmCpuFeaturesReenableSmrr (); MtrrSetAllMtrrs(&Mtrrs); // // Wait for all APs to complete MTRR programming // WaitForAllAPs (ApCount); } // // Stop source level debug in BSP handler, the code below will not be // debugged. // InitializeDebugAgent (DEBUG_AGENT_INIT_EXIT_SMI, NULL, NULL); // // Signal APs to Reset states/semaphore for this processor // ReleaseAllAPs (); // // Perform pending operations for hot-plug // SmmCpuUpdate (); // // Clear the Present flag of BSP // mSmmMpSyncData->CpuData[CpuIndex].Present = FALSE; // // Gather APs to exit SMM synchronously. Note the Present flag is cleared by now but // WaitForAllAps does not depend on the Present flag. // WaitForAllAPs (ApCount); // // Reset BspIndex to -1, meaning BSP has not been elected. // if (FeaturePcdGet (PcdCpuSmmEnableBspElection)) { mSmmMpSyncData->BspIndex = (UINT32)-1; } // // Allow APs to check in from this point on // mSmmMpSyncData->Counter = 0; mSmmMpSyncData->AllCpusInSync = FALSE; }
/** Report status code listener for SMM. This is used to record the performance data for S3 Suspend Start and S3 Suspend End in FPDT. @param[in] CodeType Indicates the type of status code being reported. @param[in] Value Describes the current status of a hardware or software entity. This included information about the class and subclass that is used to classify the entity as well as an operation. @param[in] Instance The enumeration of a hardware or software entity within the system. Valid instance numbers start with 1. @param[in] CallerId This optional parameter may be used to identify the caller. This parameter allows the status code driver to apply different rules to different callers. @param[in] Data This optional parameter may be used to pass additional data. @retval EFI_SUCCESS Status code is what we expected. @retval EFI_UNSUPPORTED Status code not supported. **/ EFI_STATUS EFIAPI FpdtStatusCodeListenerSmm ( IN EFI_STATUS_CODE_TYPE CodeType, IN EFI_STATUS_CODE_VALUE Value, IN UINT32 Instance, IN EFI_GUID *CallerId, IN EFI_STATUS_CODE_DATA *Data ) { EFI_STATUS Status; UINT64 CurrentTime; EFI_ACPI_5_0_FPDT_S3_SUSPEND_RECORD S3SuspendRecord; UINT8 *NewRecordBuffer; // // Check whether status code is what we are interested in. // if ((CodeType & EFI_STATUS_CODE_TYPE_MASK) != EFI_PROGRESS_CODE) { return EFI_UNSUPPORTED; } // // Collect one or more Boot records in boot time // if (Data != NULL && CompareGuid (&Data->Type, &gEfiFirmwarePerformanceGuid)) { AcquireSpinLock (&mSmmFpdtLock); if (mBootRecordSize + Data->Size > mBootRecordMaxSize) { // // Try to allocate big SMRAM data to store Boot record. // if (mSmramIsOutOfResource) { ReleaseSpinLock (&mSmmFpdtLock); return EFI_OUT_OF_RESOURCES; } NewRecordBuffer = ReallocatePool (mBootRecordSize, mBootRecordSize + Data->Size + EXTENSION_RECORD_SIZE, mBootRecordBuffer); if (NewRecordBuffer == NULL) { ReleaseSpinLock (&mSmmFpdtLock); mSmramIsOutOfResource = TRUE; return EFI_OUT_OF_RESOURCES; } mBootRecordBuffer = NewRecordBuffer; mBootRecordMaxSize = mBootRecordSize + Data->Size + EXTENSION_RECORD_SIZE; } // // Save boot record into the temp memory space. // CopyMem (mBootRecordBuffer + mBootRecordSize, Data + 1, Data->Size); mBootRecordSize += Data->Size; ReleaseSpinLock (&mSmmFpdtLock); return EFI_SUCCESS; } if ((Value != PcdGet32 (PcdProgressCodeS3SuspendStart)) && (Value != PcdGet32 (PcdProgressCodeS3SuspendEnd))) { return EFI_UNSUPPORTED; } // // Retrieve current time. // CurrentTime = GetTimeInNanoSecond (GetPerformanceCounter ()); if (Value == PcdGet32 (PcdProgressCodeS3SuspendStart)) { // // S3 Suspend started, record the performance data and return. // mSuspendStartTime = CurrentTime; return EFI_SUCCESS; } // // We are going to S3 sleep, record S3 Suspend End performance data. // S3SuspendRecord.SuspendStart = mSuspendStartTime; S3SuspendRecord.SuspendEnd = CurrentTime; // // Save S3 suspend performance data to lock box, it will be used by Firmware Performance PEIM. // if (!mS3SuspendLockBoxSaved) { Status = SaveLockBox ( &gEfiFirmwarePerformanceGuid, &S3SuspendRecord, sizeof (EFI_ACPI_5_0_FPDT_S3_SUSPEND_RECORD) ); ASSERT_EFI_ERROR (Status); mS3SuspendLockBoxSaved = TRUE; } else { Status = UpdateLockBox ( &gEfiFirmwarePerformanceGuid, 0, &S3SuspendRecord, sizeof (EFI_ACPI_5_0_FPDT_S3_SUSPEND_RECORD) ); ASSERT_EFI_ERROR (Status); } return EFI_SUCCESS; }
/** SMI handler for AP. @param CpuIndex AP processor Index. @param ValidSmi Indicates that current SMI is a valid SMI or not. @param SyncMode SMM MP sync mode. **/ VOID APHandler ( IN UINTN CpuIndex, IN BOOLEAN ValidSmi, IN SMM_CPU_SYNC_MODE SyncMode ) { UINT64 Timer; UINTN BspIndex; MTRR_SETTINGS Mtrrs; // // Timeout BSP // for (Timer = StartSyncTimer (); !IsSyncTimerTimeout (Timer) && !mSmmMpSyncData->InsideSmm; ) { CpuPause (); } if (!mSmmMpSyncData->InsideSmm) { // // BSP timeout in the first round // if (mSmmMpSyncData->BspIndex != -1) { // // BSP Index is known // BspIndex = mSmmMpSyncData->BspIndex; ASSERT (CpuIndex != BspIndex); // // Send SMI IPI to bring BSP in // SendSmiIpi ((UINT32)gSmmCpuPrivate->ProcessorInfo[BspIndex].ProcessorId); // // Now clock BSP for the 2nd time // for (Timer = StartSyncTimer (); !IsSyncTimerTimeout (Timer) && !mSmmMpSyncData->InsideSmm; ) { CpuPause (); } if (!mSmmMpSyncData->InsideSmm) { // // Give up since BSP is unable to enter SMM // and signal the completion of this AP WaitForSemaphore (&mSmmMpSyncData->Counter); return; } } else { // // Don't know BSP index. Give up without sending IPI to BSP. // WaitForSemaphore (&mSmmMpSyncData->Counter); return; } } // // BSP is available // BspIndex = mSmmMpSyncData->BspIndex; ASSERT (CpuIndex != BspIndex); // // Mark this processor's presence // mSmmMpSyncData->CpuData[CpuIndex].Present = TRUE; if (SyncMode == SmmCpuSyncModeTradition || SmmCpuFeaturesNeedConfigureMtrrs()) { // // Notify BSP of arrival at this point // ReleaseSemaphore (&mSmmMpSyncData->CpuData[BspIndex].Run); } if (SmmCpuFeaturesNeedConfigureMtrrs()) { // // Wait for the signal from BSP to backup MTRRs // WaitForSemaphore (&mSmmMpSyncData->CpuData[CpuIndex].Run); // // Backup OS MTRRs // MtrrGetAllMtrrs(&Mtrrs); // // Signal BSP the completion of this AP // ReleaseSemaphore (&mSmmMpSyncData->CpuData[BspIndex].Run); // // Wait for BSP's signal to program MTRRs // WaitForSemaphore (&mSmmMpSyncData->CpuData[CpuIndex].Run); // // Replace OS MTRRs with SMI MTRRs // ReplaceOSMtrrs (CpuIndex); // // Signal BSP the completion of this AP // ReleaseSemaphore (&mSmmMpSyncData->CpuData[BspIndex].Run); } while (TRUE) { // // Wait for something to happen // WaitForSemaphore (&mSmmMpSyncData->CpuData[CpuIndex].Run); // // Check if BSP wants to exit SMM // if (!mSmmMpSyncData->InsideSmm) { break; } // // BUSY should be acquired by SmmStartupThisAp() // ASSERT ( !AcquireSpinLockOrFail (&mSmmMpSyncData->CpuData[CpuIndex].Busy) ); // // Invoke the scheduled procedure // (*mSmmMpSyncData->CpuData[CpuIndex].Procedure) ( (VOID*)mSmmMpSyncData->CpuData[CpuIndex].Parameter ); // // Release BUSY // ReleaseSpinLock (&mSmmMpSyncData->CpuData[CpuIndex].Busy); } if (SmmCpuFeaturesNeedConfigureMtrrs()) { // // Notify BSP the readiness of this AP to program MTRRs // ReleaseSemaphore (&mSmmMpSyncData->CpuData[BspIndex].Run); // // Wait for the signal from BSP to program MTRRs // WaitForSemaphore (&mSmmMpSyncData->CpuData[CpuIndex].Run); // // Restore OS MTRRs // SmmCpuFeaturesReenableSmrr (); MtrrSetAllMtrrs(&Mtrrs); } // // Notify BSP the readiness of this AP to Reset states/semaphore for this processor // ReleaseSemaphore (&mSmmMpSyncData->CpuData[BspIndex].Run); // // Wait for the signal from BSP to Reset states/semaphore for this processor // WaitForSemaphore (&mSmmMpSyncData->CpuData[CpuIndex].Run); // // Reset states/semaphore for this processor // mSmmMpSyncData->CpuData[CpuIndex].Present = FALSE; // // Notify BSP the readiness of this AP to exit SMM // ReleaseSemaphore (&mSmmMpSyncData->CpuData[BspIndex].Run); }
/** Adds a record at the end of the performance measurement log that records the start time of a performance measurement. Adds a record to the end of the performance measurement log that contains the Handle, Token, Module and Identifier. The end time of the new record must be set to zero. If TimeStamp is not zero, then TimeStamp is used to fill in the start time in the record. If TimeStamp is zero, the start time in the record is filled in with the value read from the current time stamp. @param Handle Pointer to environment specific context used to identify the component being measured. @param Token Pointer to a Null-terminated ASCII string that identifies the component being measured. @param Module Pointer to a Null-terminated ASCII string that identifies the module being measured. @param TimeStamp 64-bit time stamp. @param Identifier 32-bit identifier. If the value is 0, the created record is same as the one created by StartGauge of PERFORMANCE_PROTOCOL. @retval EFI_SUCCESS The data was read correctly from the device. @retval EFI_OUT_OF_RESOURCES There are not enough resources to record the measurement. **/ EFI_STATUS EFIAPI StartGaugeEx ( IN CONST VOID *Handle, OPTIONAL IN CONST CHAR8 *Token, OPTIONAL IN CONST CHAR8 *Module, OPTIONAL IN UINT64 TimeStamp, IN UINT32 Identifier ) { GAUGE_DATA_ENTRY_EX *GaugeEntryExArray; UINTN GaugeDataSize; GAUGE_DATA_HEADER *NewGaugeData; UINTN OldGaugeDataSize; GAUGE_DATA_HEADER *OldGaugeData; UINT32 Index; AcquireSpinLock (&mSmmPerfLock); Index = mGaugeData->NumberOfEntries; if (Index >= mMaxGaugeRecords) { // // Try to enlarge the scale of gauge array. // OldGaugeData = mGaugeData; OldGaugeDataSize = sizeof (GAUGE_DATA_HEADER) + sizeof (GAUGE_DATA_ENTRY_EX) * mMaxGaugeRecords; GaugeDataSize = sizeof (GAUGE_DATA_HEADER) + sizeof (GAUGE_DATA_ENTRY_EX) * mMaxGaugeRecords * 2; NewGaugeData = AllocateZeroPool (GaugeDataSize); if (NewGaugeData == NULL) { ReleaseSpinLock (&mSmmPerfLock); return EFI_OUT_OF_RESOURCES; } mGaugeData = NewGaugeData; mMaxGaugeRecords *= 2; // // Initialize new data array and migrate old data one. // mGaugeData = CopyMem (mGaugeData, OldGaugeData, OldGaugeDataSize); FreePool (OldGaugeData); } GaugeEntryExArray = (GAUGE_DATA_ENTRY_EX *) (mGaugeData + 1); GaugeEntryExArray[Index].Handle = (EFI_PHYSICAL_ADDRESS) (UINTN) Handle; if (Token != NULL) { AsciiStrnCpyS (GaugeEntryExArray[Index].Token, SMM_PERFORMANCE_STRING_SIZE, Token, SMM_PERFORMANCE_STRING_LENGTH); } if (Module != NULL) { AsciiStrnCpyS (GaugeEntryExArray[Index].Module, SMM_PERFORMANCE_STRING_SIZE, Module, SMM_PERFORMANCE_STRING_LENGTH); } GaugeEntryExArray[Index].EndTimeStamp = 0; GaugeEntryExArray[Index].Identifier = Identifier; if (TimeStamp == 0) { TimeStamp = GetPerformanceCounter (); } GaugeEntryExArray[Index].StartTimeStamp = TimeStamp; mGaugeData->NumberOfEntries++; ReleaseSpinLock (&mSmmPerfLock); return EFI_SUCCESS; }
/** This function is IO instruction handler for SMM. @param Index CPU index **/ VOID SmmIoHandler ( IN UINT32 Index ) { VM_EXIT_QUALIFICATION Qualification; UINT16 Port; UINTN *DataPtr; UINTN LinearAddr; X86_REGISTER *Reg; STM_RSC_IO_DESC *IoDesc; STM_RSC_PCI_CFG_DESC *PciCfgDesc; UINT32 PciAddress; STM_RSC_IO_DESC LocalIoDesc; STM_RSC_PCI_CFG_DESC *LocalPciCfgDescPtr; UINT8 LocalPciCfgDescBuf[STM_LOG_ENTRY_SIZE]; Reg = &mGuestContextCommonSmm.GuestContextPerCpu[Index].Register; Qualification.UintN = VmReadN (VMCS_N_RO_EXIT_QUALIFICATION_INDEX); if (Qualification.IoInstruction.Operand != 0) { Port = (UINT16)Qualification.IoInstruction.PortNum; } else { Port = (UINT16)Reg->Rdx; } DataPtr = (UINTN *)&Reg->Rax; // // We need handle case that CF9 is protected, but CF8, CFC need to be pass-through. // But DWORD CF8 programming will be caught here. // So add check here. CF8 will be bypassed, because it does not in CF9 scope. // IoDesc = GetStmResourceIo (mHostContextCommon.MleProtectedResource.Base, Port); if (IoDesc != NULL) { DEBUG ((EFI_D_ERROR, "IO violation!\n")); AddEventLogForResource (EvtHandledProtectionException, (STM_RSC *)IoDesc); SmmExceptionHandler (Index); CpuDeadLoop (); } IoDesc = GetStmResourceIo ((STM_RSC *)(UINTN)mGuestContextCommonSmm.BiosHwResourceRequirementsPtr, Port); if (IoDesc == NULL) { ZeroMem (&LocalIoDesc, sizeof(LocalIoDesc)); LocalIoDesc.Hdr.RscType = IO_RANGE; LocalIoDesc.Hdr.Length = sizeof(LocalIoDesc); LocalIoDesc.Base = Port; LocalIoDesc.Length = (UINT16)(Qualification.IoInstruction.Size + 1); AddEventLogForResource (EvtBiosAccessToUnclaimedResource, (STM_RSC *)&LocalIoDesc); } // // Check PCI - 0xCF8, 0xCFC~0xCFF access // if (Port == 0xCF8) { // Access PciAddress // // We need make sure PciAddress access and PciData access is atomic. // AcquireSpinLock (&mHostContextCommon.PciLock); } if ((Port >= 0xCFC) && (Port <= 0xCFF)) { // Access PciData // // AcquireLock to prevent 0xCF8 access // AcquireSpinLock (&mHostContextCommon.PciLock); PciAddress = IoRead32 (0xCF8); PciCfgDesc = GetStmResourcePci ( mHostContextCommon.MleProtectedResource.Base, BUS_FROM_CF8_ADDRESS(PciAddress), DEVICE_FROM_CF8_ADDRESS(PciAddress), FUNCTION_FROM_CF8_ADDRESS(PciAddress), REGISTER_FROM_CF8_ADDRESS(PciAddress) + (Port & 0x3), (Qualification.IoInstruction.Direction != 0) ? STM_RSC_PCI_CFG_R : STM_RSC_PCI_CFG_W ); if (PciCfgDesc != NULL) { DEBUG ((EFI_D_ERROR, "IO (PCI) violation!\n")); AddEventLogForResource (EvtHandledProtectionException, (STM_RSC *)PciCfgDesc); ReleaseSpinLock (&mHostContextCommon.PciLock); SmmExceptionHandler (Index); CpuDeadLoop (); } PciCfgDesc = GetStmResourcePci ( (STM_RSC *)(UINTN)mGuestContextCommonSmm.BiosHwResourceRequirementsPtr, BUS_FROM_CF8_ADDRESS(PciAddress), DEVICE_FROM_CF8_ADDRESS(PciAddress), FUNCTION_FROM_CF8_ADDRESS(PciAddress), REGISTER_FROM_CF8_ADDRESS(PciAddress) + (Port & 0x3), (Qualification.IoInstruction.Direction != 0) ? STM_RSC_PCI_CFG_R : STM_RSC_PCI_CFG_W ); if (PciCfgDesc == NULL) { DEBUG((EFI_D_ERROR, "Add unclaimed PCI_RSC!\n")); LocalPciCfgDescPtr = (STM_RSC_PCI_CFG_DESC *)LocalPciCfgDescBuf; ZeroMem (LocalPciCfgDescBuf, sizeof(LocalPciCfgDescBuf)); LocalPciCfgDescPtr->Hdr.RscType = PCI_CFG_RANGE; LocalPciCfgDescPtr->Hdr.Length = sizeof(STM_RSC_PCI_CFG_DESC); // BUGBUG: Just report this PCI device, it is hard to create PCI hierachy here. LocalPciCfgDescPtr->RWAttributes = (Qualification.IoInstruction.Direction != 0) ? STM_RSC_PCI_CFG_R : STM_RSC_PCI_CFG_W; LocalPciCfgDescPtr->Base = REGISTER_FROM_CF8_ADDRESS(PciAddress) + (Port & 0x3); LocalPciCfgDescPtr->Length = (UINT16)(Qualification.IoInstruction.Size + 1); LocalPciCfgDescPtr->OriginatingBusNumber = BUS_FROM_CF8_ADDRESS(PciAddress); LocalPciCfgDescPtr->LastNodeIndex = 0; LocalPciCfgDescPtr->PciDevicePath[0].Type = 1; LocalPciCfgDescPtr->PciDevicePath[0].Subtype = 1; LocalPciCfgDescPtr->PciDevicePath[0].Length = sizeof(STM_PCI_DEVICE_PATH_NODE); LocalPciCfgDescPtr->PciDevicePath[0].PciFunction = FUNCTION_FROM_CF8_ADDRESS(PciAddress); LocalPciCfgDescPtr->PciDevicePath[0].PciDevice = DEVICE_FROM_CF8_ADDRESS(PciAddress); AddEventLogForResource (EvtBiosAccessToUnclaimedResource, (STM_RSC *)LocalPciCfgDescPtr); } } if (Qualification.IoInstruction.Rep != 0) { UINT64 RcxMask; RcxMask = 0xFFFFFFFFFFFFFFFFull; if ((mGuestContextCommonSmm.GuestContextPerCpu[Index].Efer & IA32_EFER_MSR_MLA) == 0) { RcxMask = 0xFFFFFFFFull; } if ((Reg->Rcx & RcxMask) == 0) { // Skip if ((Port == 0xCF8) || ((Port >= 0xCFC) && (Port <= 0xCFF))) { ReleaseSpinLock (&mHostContextCommon.PciLock); } VmWriteN (VMCS_N_GUEST_RIP_INDEX, VmReadN(VMCS_N_GUEST_RIP_INDEX) + VmRead32(VMCS_32_RO_VMEXIT_INSTRUCTION_LENGTH_INDEX)); return ; } } if (Qualification.IoInstruction.String != 0) { LinearAddr = VmReadN (VMCS_N_RO_GUEST_LINEAR_ADDR_INDEX); if (VmReadN (VMCS_N_GUEST_CR0_INDEX) & CR0_PG) { DataPtr = (UINTN *)(UINTN)GuestLinearToHostPhysical (Index, LinearAddr); } else { DataPtr = (UINTN *)LinearAddr; } if ((VmReadN (VMCS_N_GUEST_RFLAGS_INDEX) & RFLAGS_DF) != 0) { if (Qualification.IoInstruction.Direction != 0) { Reg->Rdi -= Qualification.IoInstruction.Size + 1; } else { Reg->Rsi -= Qualification.IoInstruction.Size + 1; } } else { if (Qualification.IoInstruction.Direction != 0) { Reg->Rdi += Qualification.IoInstruction.Size + 1; } else { Reg->Rsi += Qualification.IoInstruction.Size + 1; } } } if (Qualification.IoInstruction.Direction != 0) { // IN switch (Qualification.IoInstruction.Size) { case 0: *(UINT8 *)DataPtr = IoRead8 (Port); goto Ret; break; case 1: *(UINT16 *)DataPtr = IoRead16 (Port); goto Ret; break; case 3: *(UINT32 *)DataPtr = IoRead32 (Port); goto Ret; break; default: break; } } else { // OUT switch (Qualification.IoInstruction.Size) { case 0: IoWrite8 (Port, (UINT8)*DataPtr); goto Ret; break; case 1: IoWrite16 (Port, (UINT16)*DataPtr); goto Ret; break; case 3: IoWrite32 (Port, (UINT32)*DataPtr); goto Ret; break; default: break; } } if ((Port == 0xCF8) || ((Port >= 0xCFC) && (Port <= 0xCFF))) { ReleaseSpinLock (&mHostContextCommon.PciLock); } DEBUG ((EFI_D_INFO, "!!!IoHandler!!!\n")); DumpVmcsAllField (); CpuDeadLoop (); Ret: if ((Port == 0xCF8) || ((Port >= 0xCFC) && (Port <= 0xCFF))) { ReleaseSpinLock (&mHostContextCommon.PciLock); } if (Qualification.IoInstruction.Rep != 0) { // replay Reg->Rcx --; return ; } VmWriteN (VMCS_N_GUEST_RIP_INDEX, VmReadN(VMCS_N_GUEST_RIP_INDEX) + VmRead32(VMCS_32_RO_VMEXIT_INSTRUCTION_LENGTH_INDEX)); return ; }
/** This function is WRMSR handler for SMM. @param Index CPU index **/ VOID SmmWriteMsrHandler ( IN UINT32 Index ) { UINT64 Data64; UINT32 MsrIndex; VM_ENTRY_CONTROLS VmEntryControls; X86_REGISTER *Reg; STM_RSC_MSR_DESC *MsrDesc; STM_RSC_MSR_DESC LocalMsrDesc; Reg = &mGuestContextCommonSmm.GuestContextPerCpu[Index].Register; MsrIndex = ReadUnaligned32 ((UINT32 *)&Reg->Rcx); MsrDesc = GetStmResourceMsr (mHostContextCommon.MleProtectedResource.Base, MsrIndex); if ((MsrDesc != NULL) && (MsrDesc->WriteMask != 0)) { DEBUG ((EFI_D_ERROR, "WRMSR (%x) violation!\n", MsrIndex)); AddEventLogForResource (EvtHandledProtectionException, (STM_RSC *)MsrDesc); SmmExceptionHandler (Index); CpuDeadLoop (); } MsrDesc = GetStmResourceMsr ((STM_RSC *)(UINTN)mGuestContextCommonSmm.BiosHwResourceRequirementsPtr, MsrIndex); if ((MsrDesc == NULL) || (MsrDesc->WriteMask == 0) || (MsrDesc->KernelModeProcessing == 0)) { ZeroMem (&LocalMsrDesc, sizeof(LocalMsrDesc)); LocalMsrDesc.Hdr.RscType = MACHINE_SPECIFIC_REG; LocalMsrDesc.Hdr.Length = sizeof(LocalMsrDesc); LocalMsrDesc.MsrIndex = MsrIndex; LocalMsrDesc.ReadMask = 0; LocalMsrDesc.WriteMask = (UINT64)-1; AddEventLogForResource (EvtBiosAccessToUnclaimedResource, (STM_RSC *)&LocalMsrDesc); } // DEBUG ((EFI_D_INFO, "!!!WriteMsrHandler!!!\n")); Data64 = LShiftU64 ((UINT64)ReadUnaligned32 ((UINT32 *)&Reg->Rdx), 32) | (UINT64)ReadUnaligned32 ((UINT32 *)&Reg->Rax); switch (MsrIndex) { case IA32_EFER_MSR_INDEX: #if 0 AcquireSpinLock (&mHostContextCommon.DebugLock); if ((Data64 & IA32_EFER_MSR_SCE) != 0) { DEBUG ((EFI_D_INFO, "!!!WriteMsrHandler - SCE!!!\n")); } if ((Data64 & IA32_EFER_MSR_XDE) != 0) { DEBUG ((EFI_D_INFO, "!!!WriteMsrHandler - XDE!!!\n")); } ReleaseSpinLock (&mHostContextCommon.DebugLock); #endif mGuestContextCommonSmm.GuestContextPerCpu[Index].Efer = Data64; // // Check IA32e mode switch // VmEntryControls.Uint32 = VmRead32 (VMCS_32_CONTROL_VMENTRY_CONTROLS_INDEX); if ((Data64 & IA32_EFER_MSR_MLE) != 0) { mGuestContextCommonSmm.GuestContextPerCpu[Index].Efer |= IA32_EFER_MSR_MLE; } else { mGuestContextCommonSmm.GuestContextPerCpu[Index].Efer &= ~IA32_EFER_MSR_MLE; } if (((mGuestContextCommonSmm.GuestContextPerCpu[Index].Efer & IA32_EFER_MSR_MLE) != 0) && ((VmReadN (VMCS_N_GUEST_CR0_INDEX) & CR0_PG) != 0)) { mGuestContextCommonSmm.GuestContextPerCpu[Index].Efer |= IA32_EFER_MSR_MLA; VmEntryControls.Bits.Ia32eGuest = 1; } else { mGuestContextCommonSmm.GuestContextPerCpu[Index].Efer &= ~IA32_EFER_MSR_MLA; VmEntryControls.Bits.Ia32eGuest = 0; } VmWrite32 (VMCS_32_CONTROL_VMENTRY_CONTROLS_INDEX, VmEntryControls.Uint32); VmWrite64 (VMCS_64_GUEST_IA32_EFER_INDEX, mGuestContextCommonSmm.GuestContextPerCpu[Index].Efer); break; case IA32_SYSENTER_CS_MSR_INDEX: VmWrite32 (VMCS_32_GUEST_IA32_SYSENTER_CS_INDEX, (UINT32)Data64); break; case IA32_SYSENTER_ESP_MSR_INDEX: VmWriteN (VMCS_N_GUEST_IA32_SYSENTER_ESP_INDEX, (UINTN)Data64); break; case IA32_SYSENTER_EIP_MSR_INDEX: VmWriteN (VMCS_N_GUEST_IA32_SYSENTER_EIP_INDEX, (UINTN)Data64); break; case IA32_FS_BASE_MSR_INDEX: VmWriteN (VMCS_N_GUEST_FS_BASE_INDEX, (UINTN)Data64); AsmWriteMsr64 (MsrIndex, Data64); // VMM does not use FS break; case IA32_GS_BASE_MSR_INDEX: VmWriteN (VMCS_N_GUEST_GS_BASE_INDEX, (UINTN)Data64); AsmWriteMsr64 (MsrIndex, Data64); // VMM does not use GS break; #if 0 case IA32_KERNAL_GS_BASE_MSR_INDEX: AsmWriteMsr64 (MsrIndex, Data64); // VMM does not use this break; case IA32_STAR_MSR_INDEX: AsmWriteMsr64 (MsrIndex, Data64); // VMM does not use this break; case IA32_LSTAR_MSR_INDEX: AsmWriteMsr64 (MsrIndex, Data64); // VMM does not use this break; case IA32_FMASK_MSR_INDEX: AsmWriteMsr64 (MsrIndex, Data64); // VMM does not use this break; #endif case IA32_SMM_MONITOR_CTL_MSR_INDEX: break; case EFI_MSR_NEHALEM_SMRR_PHYS_BASE: case EFI_MSR_NEHALEM_SMRR_PHYS_MASK: // Ignore the write break; case IA32_BIOS_UPDT_TRIG_MSR_INDEX: // Only write it when BIOS request MicrocodeUpdate MsrDesc = GetStmResourceMsr ((STM_RSC *)(UINTN)mGuestContextCommonSmm.BiosHwResourceRequirementsPtr, IA32_BIOS_UPDT_TRIG_MSR_INDEX); if (MsrDesc != NULL) { AsmWriteMsr64 (MsrIndex, Data64); } break; default: // // For rest one, we need pass back to BIOS // // // Need mask write item // if (MsrDesc != NULL) { Data64 |= MsrDesc->WriteMask; } AsmWriteMsr64 (MsrIndex, Data64); break; } VmWriteN (VMCS_N_GUEST_RIP_INDEX, VmReadN(VMCS_N_GUEST_RIP_INDEX) + VmRead32(VMCS_32_RO_VMEXIT_INSTRUCTION_LENGTH_INDEX)); return ; }