/** @file CPU Common Lib implementation. Copyright (c) 2017, Intel Corporation. All rights reserved.
SPDX-License-Identifier: BSD-2-Clause-Patent **/ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define INTERRUPT_VECTOR_NUMBER 256 #define END_OF_TIMETABLE 0x3FF /** Set up flags in CR4 for XMM instruction enabling **/ VOID EFIAPI XmmInit ( VOID ) { EFI_CPUID_REGISTER Cpuid; UINTN Cr0; UINTN Cr4; /// /// Read the CPUID information /// AsmCpuid (CPUID_VERSION_INFO, &Cpuid.RegEax, &Cpuid.RegEbx, &Cpuid.RegEcx, &Cpuid.RegEdx); /// /// Check whether SSE2 is supported /// if (Cpuid.RegEdx & BIT26) { /// /// Enable XMM /// Cr0 = AsmReadCr0 (); Cr0 |= BIT1; AsmWriteCr0 (Cr0); Cr4 = AsmReadCr4 (); Cr4 |= (UINTN) (BIT9 | BIT10); AsmWriteCr4 (Cr4); } } /** Enable "Machine Check Enable" bit in Cr4 **/ VOID EFIAPI EnableMce ( VOID ) { UINTN Cr4; /// /// Enable MCE /// Cr4 = AsmReadCr4 (); Cr4 |= BIT6; AsmWriteCr4 (Cr4); } /** Mtrr Synch Up Entry **/ UINTN EFIAPI MpMtrrSynchUpEntry ( VOID ) { EFI_CPUID_REGISTER Cpuid; UINT64 MsrData; UINTN Cr0; UINTN Cr4; /// /// Read the CPUID and MSR 1Bh information /// AsmCpuid (CPUID_VERSION_INFO, &Cpuid.RegEax, &Cpuid.RegEbx, &Cpuid.RegEcx, &Cpuid.RegEdx); MsrData = AsmReadMsr64 (MSR_IA32_APIC_BASE); /// /// Set CD(Bit30) bit and clear NW(Bit29) bit of CR0 followed by a WBINVD. /// if (!(Cpuid.RegEdx & BIT24) || (MsrData & BIT8)) { AsmDisableCache (); } else { /// /// We could bypass the wbinvd by /// checking MSR 1Bh(MSR_IA32_APIC_BASE) Bit8 (1 = BSP, 0 = AP) to see if we're the BSP? /// and checking CPUID if the processor support self-snooping. /// Cr0 = AsmReadCr0 (); Cr0 &= (UINTN) ~BIT29; Cr0 |= BIT30; AsmWriteCr0 (Cr0); } /// /// Clear PGE flag Bit 7 /// Cr4 = AsmReadCr4 (); Cr4 &= (UINTN) ~BIT7; AsmWriteCr4 (Cr4); /// /// Flush all TLBs /// CpuFlushTlb (); return Cr4; } /** Mtrr Synch Up Exit **/ VOID EFIAPI MpMtrrSynchUpExit ( UINTN Cr4 ) { UINTN Cr0; /// /// Flush all TLBs the second time /// CpuFlushTlb (); /// /// Clear both the CD and NW bits of CR0. /// Cr0 = AsmReadCr0 (); Cr0 &= (UINTN) ~(BIT29 | BIT30); AsmWriteCr0 (Cr0); /// /// Set PGE Flag in CR4 if set /// AsmWriteCr4 (Cr4); } /** This procedure sends an IPI to the designated processor in the requested delivery mode with the requested vector. @param[in] ApicID - APIC ID of processor. @param[in] VectorNumber - Vector number. @param[in] DeliveryMode - I/O APIC Interrupt Deliver Modes @retval EFI_INVALID_PARAMETER - Input paramters were not correct. @retval EFI_NOT_READY - There was a pending interrupt @retval EFI_SUCCESS - Interrupt sent successfully **/ EFI_STATUS EFIAPI CpuSendIpi ( IN UINT32 ApicID, IN UINTN VectorNumber, IN UINTN DeliveryMode ) { MSR_IA32_APIC_BASE_REGISTER Msr; EFI_PHYSICAL_ADDRESS ApicBase; UINT32 IcrLow; UINT32 IcrHigh; BOOLEAN XapicEnabled; UINT32 TriggerMode; /// /// Check for valid input parameters. /// if (VectorNumber >= INTERRUPT_VECTOR_NUMBER) { return EFI_INVALID_PARAMETER; } if (DeliveryMode >= DELIVERY_MODE_MAX) { return EFI_INVALID_PARAMETER; } /// /// Fix the vector number for special interrupts like SMI and INIT. /// if (DeliveryMode == DELIVERY_MODE_SMI || DeliveryMode == DELIVERY_MODE_INIT) { VectorNumber = 0x0; } /// /// Initialze ICR high dword, since P6 family processor needs /// the destination field to be 0x0F when it is a broadcast /// IcrHigh = 0x0f000000; IcrLow = (UINT32) (VectorNumber | (LShiftU64 (DeliveryMode, 8))); TriggerMode = TRIGGER_MODE_EDGE; /// /// Interrupt trigger mode /// if (TriggerMode == TRIGGER_MODE_LEVEL) { IcrLow |= 0x8000; } /// /// Interrupt pin polarity /// IcrLow |= 0x4000; /// /// xAPIC Enabled /// Msr.Uint64 = AsmReadMsr64 (MSR_IA32_APIC_BASE); XapicEnabled = (BOOLEAN) ((Msr.Bits.EXTD == 1) && (Msr.Bits.EN == 1)); if (XapicEnabled) { IcrHigh = (UINT32) ApicID; } else { IcrHigh = (UINT32) LShiftU64 (ApicID, 24); } ApicBase = Msr.Uint64 & 0xffffff000; /* If Extended XAPIC Mode is enabled, legacy xAPIC is no longer working. So, previous MMIO offset must be transferred to MSR offset R/W. ---------------------------------------------------------------- MMIO Offset MSR Offset Register Name ---------------------------------------------------------------- 300h-310h 830h Interrupt Command Register [63:0] 831h [Reserved] ---------------------------------------------------------------- */ /// /// To write APIC register by MSR or MMIO /// if (XapicEnabled) { AsmWriteMsr64 (MSR_IA32_X2APIC_ICR, (((UINT64) LShiftU64 (IcrHigh, 32)) | (UINT64) IcrLow)); } else { *(volatile UINT32 *) (UINTN) (ApicBase + APIC_REGISTER_ICR_HIGH_OFFSET) = (UINT32) IcrHigh; *(volatile UINT32 *) (UINTN) (ApicBase + APIC_REGISTER_ICR_LOW_OFFSET) = (UINT32) IcrLow; } MicroSecondDelay (10); /// /// To get APIC register from MSR or MMIO /// if (XapicEnabled) { IcrLow = (UINT32) AsmReadMsr64 (MSR_IA32_X2APIC_ICR); } else { IcrLow = (UINT32) *(volatile UINT32 *) (UINTN) (ApicBase + APIC_REGISTER_ICR_LOW_OFFSET); } if (IcrLow & BIT12) { return EFI_NOT_READY; } MicroSecondDelay (100); return EFI_SUCCESS; } /** Get APIC ID of processor @retval APIC ID of processor **/ UINT32 GetCpuApicId ( VOID ) { EFI_CPUID_REGISTER CpuidRegisters; AsmCpuid ( CPUID_VERSION_INFO, &CpuidRegisters.RegEax, &CpuidRegisters.RegEbx, &CpuidRegisters.RegEcx, &CpuidRegisters.RegEdx ); return (UINT32) (CpuidRegisters.RegEbx >> 24); } /** Programs XAPIC registers. @param[in] Bsp - Is this BSP? **/ VOID ProgramXApic ( BOOLEAN Bsp ) { UINT64 ApicBaseReg; EFI_PHYSICAL_ADDRESS ApicBase; volatile UINT32 *EntryAddress; UINT32 EntryValue; ApicBaseReg = AsmReadMsr64 (MSR_IA32_APIC_BASE); ApicBase = ApicBaseReg & 0xffffff000ULL; /// /// Program the spurious vector entry /// EntryAddress = (UINT32 *) (UINTN) (ApicBase + APIC_REGISTER_SPURIOUS_VECTOR_OFFSET); EntryValue = *EntryAddress; EntryValue &= 0xFFFFFD0F; EntryValue |= 0x10F; *EntryAddress = EntryValue; /// /// Program the LINT1 vector entry as extINT /// EntryAddress = (UINT32 *) (UINTN) (ApicBase + APIC_REGISTER_LINT0_VECTOR_OFFSET); EntryValue = *EntryAddress; if (Bsp) { EntryValue &= 0xFFFE00FF; EntryValue |= 0x700; } else { EntryValue |= 0x10000; } *EntryAddress = EntryValue; /// /// Program the LINT1 vector entry as NMI /// EntryAddress = (UINT32 *) (UINTN) (ApicBase + APIC_REGISTER_LINT1_VECTOR_OFFSET); EntryValue = *EntryAddress; EntryValue &= 0xFFFE00FF; if (Bsp) { EntryValue |= 0x400; } else { EntryValue |= 0x10400; } *EntryAddress = EntryValue; } /** This function is to disable BIOS Write Protect in SMM phase. **/ VOID EFIAPI CpuSmmDisableBiosWriteProtect ( VOID ) { UINT32 Data32; /// /// Read memory location FED30880h OR with 00000001h, place the result in EAX, /// and write data to lower 32 bits of MSR 1FEh (sample code available) /// Data32 = MmioRead32 ((UINTN) (0xFED30880)) | (UINT32) (BIT0); AsmWriteMsr32 (0x000001FE, Data32); } /** This function is to enable BIOS Write Protect in SMM phase. **/ VOID EFIAPI CpuSmmEnableBiosWriteProtect ( VOID ) { UINT32 Data32; /// /// Read memory location FED30880h AND with FFFFFFFEh, place the result in EAX, /// and write data to lower 32 bits of MSR 1FEh (sample code available) /// Data32 = MmioRead32 ((UINTN) (0xFED30880)) & (UINT32) (~BIT0); AsmWriteMsr32 (0x000001FE, Data32); } /** This function returns the maximum number of cores supported in this physical processor package. @retval Maximum number of supported cores in the package. **/ UINT8 GetMaxSupportedCoreCount ( VOID ) { EFI_CPUID_REGISTER Cpuid; AsmCpuidEx ( 4, 0, &Cpuid.RegEax, NULL, NULL, NULL ); return (UINT8) (RShiftU64 (Cpuid.RegEax, 26) & 0x3f) + 1; } /** This function returns the actual factory-configured number of threads per core, and actual factory-configured number of cores in this physical processor package. @param[out] *ThreadsPerCore - variable that will store Maximum enabled threads per core @param[out] *NumberOfCores - variable that will store Maximum enabled cores per die **/ VOID GetSupportedCount ( OUT UINT16 *ThreadsPerCore, OPTIONAL OUT UINT16 *NumberOfCores OPTIONAL ) { EFI_CPUID_REGISTER CpuidRegs; UINT16 Threads; AsmCpuidEx (CPUID_EXTENDED_TOPOLOGY, 0, NULL, &CpuidRegs.RegEbx, NULL, NULL); Threads = (UINT16) CpuidRegs.RegEbx; if (ThreadsPerCore != NULL) { *ThreadsPerCore = Threads; } if (NumberOfCores != NULL) { AsmCpuidEx (CPUID_EXTENDED_TOPOLOGY, 1, NULL, &CpuidRegs.RegEbx, NULL, NULL); *NumberOfCores = (UINT16) (CpuidRegs.RegEbx / Threads); } } /** Check to see if the executing thread is BSP @retval TRUE Executing thread is BSP @retval FALSE Executing thread is AP **/ BOOLEAN IsBsp ( VOID ) { MSR_IA32_APIC_BASE_REGISTER Msr; Msr.Uint64 = AsmReadMsr64 (MSR_IA32_APIC_BASE); return (BOOLEAN) (Msr.Bits.BSP == 1); } BOOLEAN IsPrmrrAlreadySet ( VOID ) { return FALSE; } /** Check if this is non-core processor - HT AP thread @retval TRUE if this is HT AP thread @retval FALSE if this is core thread **/ BOOLEAN IsSecondaryThread ( VOID ) { UINT32 ApicID; EFI_CPUID_REGISTER CpuidRegisters; UINT8 CpuCount; UINT8 CoreCount; UINT8 CpuPerCore; UINT32 Mask; ApicID = GetCpuApicId (); AsmCpuid ( CPUID_VERSION_INFO, &CpuidRegisters.RegEax, &CpuidRegisters.RegEbx, &CpuidRegisters.RegEcx, &CpuidRegisters.RegEdx ); if ((CpuidRegisters.RegEdx & 0x10000000) == 0) { return FALSE; } CpuCount = (UINT8) ((CpuidRegisters.RegEbx >> 16) & 0xff); if (CpuCount == 1) { return FALSE; } AsmCpuid ( CPUID_SIGNATURE, &CpuidRegisters.RegEax, &CpuidRegisters.RegEbx, &CpuidRegisters.RegEcx, &CpuidRegisters.RegEdx ); if (CpuidRegisters.RegEax > 3) { CoreCount = GetMaxSupportedCoreCount (); } else { CoreCount = 1; } /// /// Assumes there is symmetry across core boundary, i.e. each core within a package has the same number of logical processors /// if (CpuCount == CoreCount) { return FALSE; } CpuPerCore = CpuCount / CoreCount; /// /// Assume 1 Core has no more than 8 threads /// if (CpuPerCore == 2) { Mask = 0x1; } else if (CpuPerCore <= 4) { Mask = 0x3; } else { Mask = 0x7; } if ((ApicID & Mask) == 0) { return FALSE; } else { return TRUE; } }