From a62cdf2e26c5b504165f6196020ba0909f0c1a65 Mon Sep 17 00:00:00 2001 From: vit9696 Date: Sun, 14 Feb 2021 00:32:40 +0300 Subject: [PATCH] OcCpuLib: Implement ASM CPU frequency calculation for HSW and lower --- Changelog.md | 1 + Library/OcCpuLib/FrequencyDetect.c | 69 ++++----------- Library/OcCpuLib/Ia32/MeasureTicks.c | 77 +++++++++++++++++ Library/OcCpuLib/OcCpuInternals.h | 17 ++++ Library/OcCpuLib/OcCpuLib.inf | 2 + Library/OcCpuLib/X64/MeasureTicks.nasm | 112 +++++++++++++++++++++++++ 6 files changed, 223 insertions(+), 55 deletions(-) create mode 100644 Library/OcCpuLib/Ia32/MeasureTicks.c create mode 100644 Library/OcCpuLib/X64/MeasureTicks.nasm diff --git a/Changelog.md b/Changelog.md index 576e3215..a3a6ce78 100644 --- a/Changelog.md +++ b/Changelog.md @@ -3,6 +3,7 @@ OpenCore Changelog #### v0.6.7 - Fixed ocvalidate return code to be non-zero when issues are found - Added `OEM` values to `PlatformInfo` in `Automatic` mode +- Improved CPU frequency calculation on Haswell and earlier #### v0.6.6 - Added keyboard and pointer entry scroll support in OpenCanopy diff --git a/Library/OcCpuLib/FrequencyDetect.c b/Library/OcCpuLib/FrequencyDetect.c index c7627700..6340e16a 100644 --- a/Library/OcCpuLib/FrequencyDetect.c +++ b/Library/OcCpuLib/FrequencyDetect.c @@ -129,15 +129,14 @@ InternalCalculateTSCFromPMTimer ( // STATIC UINT64 TSCFrequency = 0; - UINTN TimerAddr; + UINT16 TimerAddr; UINTN VariableSize; - UINT64 Tsc0; - UINT64 Tsc1; + UINT64 TscTicksDelta; UINT32 AcpiTick0; UINT32 AcpiTick1; UINT32 AcpiTicksDelta; - UINT32 AcpiTicksTarget; - UINT32 TimerResolution; + UINT32 AcpiTicksDuration; + BOOLEAN HasInterrupts; EFI_TPL PrevTpl; EFI_STATUS Status; @@ -169,8 +168,7 @@ InternalCalculateTSCFromPMTimer ( } if (TSCFrequency == 0) { - TimerAddr = InternalGetPmTimerAddr (NULL); - TimerResolution = 10; + TimerAddr = (UINT16) InternalGetPmTimerAddr (NULL); if (TimerAddr != 0) { // @@ -187,63 +185,24 @@ InternalCalculateTSCFromPMTimer ( // The code below can handle overflow with AcpiTicksTarget of up to 24-bit size, // on both available sizes of ACPI PM Timers (24-bit and 32-bit). // - // 357954 clocks of ACPI timer (100ms) + // 357954 clocks of ACPI timer (200ms) // - AcpiTicksTarget = V_ACPI_TMR_FREQUENCY / TimerResolution; + AcpiTicksDuration = V_ACPI_TMR_FREQUENCY / 10; // // Disable all events to ensure that nobody interrupts us. // PrevTpl = gBS->RaiseTPL (TPL_HIGH_LEVEL); + HasInterrupts = SaveAndDisableInterrupts (); + AsmMeasureTicks (AcpiTicksDuration, TimerAddr, &AcpiTicksDelta, &TscTicksDelta); + if (HasInterrupts) { + EnableInterrupts (); + } + gBS->RestoreTPL (PrevTpl); - AcpiTick0 = IoRead32 (TimerAddr); - Tsc0 = AsmReadTsc (); - - do { - CpuPause (); - - // - // Check how many AcpiTicks have passed since we started. - // - AcpiTick1 = IoRead32 (TimerAddr); - - if (AcpiTick0 <= AcpiTick1) { - // - // No overflow. - // - AcpiTicksDelta = AcpiTick1 - AcpiTick0; - } else if (AcpiTick0 - AcpiTick1 <= 0x00FFFFFF) { - // - // Overflow, 24-bit timer. - // - AcpiTicksDelta = 0x00FFFFFF - AcpiTick0 + AcpiTick1; - } else { - // - // Overflow, 32-bit timer. - // - AcpiTicksDelta = MAX_UINT32 - AcpiTick0 + AcpiTick1; - } - - // - // Keep checking AcpiTicks until target is reached. - // - } while (AcpiTicksDelta < AcpiTicksTarget); - - Tsc1 = AsmReadTsc (); - - // - // On some systems we may end up waiting for notably longer than 100ms, - // despite disabling all events. Divide by actual time passed as suggested - // by asava's Clover patch r2668. - // TSCFrequency = DivU64x32 ( - MultU64x32 (Tsc1 - Tsc0, V_ACPI_TMR_FREQUENCY), AcpiTicksDelta + MultU64x32 (TscTicksDelta, V_ACPI_TMR_FREQUENCY), AcpiTicksDelta ); - - // - // Restore to normal TPL. - // - gBS->RestoreTPL (PrevTpl); } } diff --git a/Library/OcCpuLib/Ia32/MeasureTicks.c b/Library/OcCpuLib/Ia32/MeasureTicks.c new file mode 100644 index 00000000..81011722 --- /dev/null +++ b/Library/OcCpuLib/Ia32/MeasureTicks.c @@ -0,0 +1,77 @@ +/** @file + Copyright (C) 2020, vit9696. All rights reserved. + + All rights reserved. + + This program and the accompanying materials + are licensed and made available under the terms and conditions of the BSD License + which accompanies this distribution. The full text of the license may be found at + http://opensource.org/licenses/bsd-license.php + + THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, + WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. +**/ + +#include "OcCpuInternals.h" + +#include +#include + +VOID +EFIAPI +AsmMeasureTicks ( + IN UINT32 AcpiTicksDuration, + IN UINT16 TimerAddr, + OUT UINT32 *AcpiTicksDelta, + OUT UINT64 *TscTicksDelta + ) +{ + UINT32 AcpiTick0; + UINT32 AcpiTick1; + UINT32 AcpiCurrentDelta; + UINT64 Tsc0; + UINT64 Tsc1; + + AcpiTick0 = IoRead32 (TimerAddr); + Tsc0 = AsmReadTsc (); + + do { + CpuPause (); + + // + // Check how many AcpiTicks have passed since we started. + // + AcpiTick1 = IoRead32 (TimerAddr); + + if (AcpiTick0 <= AcpiTick1) { + // + // No overflow. + // + AcpiCurrentDelta = AcpiTick1 - AcpiTick0; + } else if (AcpiTick0 - AcpiTick1 <= 0x00FFFFFF) { + // + // Overflow, 24-bit timer. + // + AcpiCurrentDelta = 0x00FFFFFF - AcpiTick0 + AcpiTick1; + } else { + // + // Overflow, 32-bit timer. + // + AcpiCurrentDelta = MAX_UINT32 - AcpiTick0 + AcpiTick1; + } + + // + // Keep checking AcpiTicks until target is reached. + // + } while (AcpiCurrentDelta < AcpiTicksDuration); + + Tsc1 = AsmReadTsc (); + + // + // On some systems we may end up waiting for notably longer than 100ms, + // despite disabling all events. Divide by actual time passed as suggested + // by asava's Clover patch r2668. + // + *TscTicksDelta = Tsc1 - Tsc0; + *AcpiTicksDelta = AcpiCurrentDelta; +} diff --git a/Library/OcCpuLib/OcCpuInternals.h b/Library/OcCpuLib/OcCpuInternals.h index c284e12e..180ac098 100755 --- a/Library/OcCpuLib/OcCpuInternals.h +++ b/Library/OcCpuLib/OcCpuInternals.h @@ -41,6 +41,23 @@ AsmReadIntelMicrocodeRevision ( VOID ); +/** + Measures TSC and ACPI ticks over specified ACPI tick amount. + + @param[in] AcpiTicksDuration Number of ACPI ticks for calculation. + @param[in] TimerAddr ACPI timer address. + @param[out] AcpiTicksDelta Reported ACPI ticks delta. + @param[out] TscTicksDelta Reported TSC ticks delta. +**/ +VOID +EFIAPI +AsmMeasureTicks ( + IN UINT32 AcpiTicksDuration, + IN UINT16 TimerAddr, + OUT UINT32 *AcpiTicksDelta, + OUT UINT64 *TscTicksDelta + ); + /** Detect Apple CPU major type. diff --git a/Library/OcCpuLib/OcCpuLib.inf b/Library/OcCpuLib/OcCpuLib.inf index 3fe99363..d560a15b 100755 --- a/Library/OcCpuLib/OcCpuLib.inf +++ b/Library/OcCpuLib/OcCpuLib.inf @@ -52,8 +52,10 @@ [Sources.Ia32] Ia32/Atomic.nasm + Ia32/MeasureTicks.c Ia32/Microcode.nasm [Sources.X64] X64/Atomic.nasm + X64/MeasureTicks.nasm X64/Microcode.nasm diff --git a/Library/OcCpuLib/X64/MeasureTicks.nasm b/Library/OcCpuLib/X64/MeasureTicks.nasm new file mode 100644 index 00000000..7e56311d --- /dev/null +++ b/Library/OcCpuLib/X64/MeasureTicks.nasm @@ -0,0 +1,112 @@ +;------------------------------------------------------------------------------ +; @file +; Copyright (C) 2020, vit9696. All rights reserved. +; +; All rights reserved. +; +; This program and the accompanying materials +; are licensed and made available under the terms and conditions of the BSD License +; which accompanies this distribution. The full text of the license may be found at +; http://opensource.org/licenses/bsd-license.php +; +; THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +; WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. +;------------------------------------------------------------------------------ + +BITS 64 +DEFAULT REL + +SECTION .text + +;------------------------------------------------------------------------------ +; VOID +; EFIAPI +; AsmMeasureTicks ( +; IN UINT32 AcpiTicksDuration, ///< ecx +; IN UINT16 TimerAddr, ///< dx +; OUT UINT32 *AcpiTicksDelta, ///< r8 +; OUT UINT64 *TscTicksDelta ///< r9 +; ); +;------------------------------------------------------------------------------ +global ASM_PFX(AsmMeasureTicks) +ASM_PFX(AsmMeasureTicks): + ; Push preserved registers. + push rsi + push rdi + push rbp + push rbx + + ; Preserve TimerAddr in r11d as rdtsc overrides edx. + mov r11d, edx + + ; Read AcpiTicks0 into ebx. + in eax, dx + mov ebx, eax + + ; Read Tsc0 into r10. + rdtsc + shl rdx, 32 + or rax, rdx + mov r10, rax + + ; Store MAX_UINT32 - AcpiTick0 into ebp. + mov ebp, ebx + not ebp + + ; Store MAX_UINT24 - AcpiTick0 into edi. + mov edi, 0x00ffffff + sub edi, ebx + +CalculationLoop: + nop ; or pause. + + ; Read AcpiTicks1 into esi. + mov edx, r11d + in eax, dx + mov esi, eax + + ; Calculate AcpiTicks0 - AcpiTicks1 into eax. + mov eax, ebx + sub eax, esi + jbe NoOverflow + + ; Check for 32-bit overflow. + cmp eax, 0x00ffffff + ja Overflow32 + +Overflow24: + ; AcpiCurrentDelta (esi) = AcpiTicks1 (esi) + MAX_UINT24 - AcpiTick0 (edi) + add esi, edi + jmp ContinueLoop + +NoOverflow: + ; AcpiCurrentDelta (esi) = AcpiTicks1 (esi) - AcpiTicks0 (ebx) + sub esi, ebx + jmp ContinueLoop + +Overflow32: + ; AcpiCurrentDelta (esi) = AcpiTicks1 (esi) + MAX_UINT32 - AcpiTick0 (ebp) + add esi, ebp + +ContinueLoop: + cmp esi, ecx + jb CalculationLoop + + ; Read Tsc1 into rax. + rdtsc + shl rdx, 32 + or rax, rdx + + ; Calculate TscDelta into rax. + sub rax, r10 + + ; Store TscDelta and AcpiDelta. + mov qword [r9], rax + mov dword [r8], esi + + ; Pop preserved registers & return. + pop rbx + pop rbp + pop rdi + pop rsi + ret -- GitLab