From c017c4cff3c4a05f0cb6dacf977df9c3da6c3717 Mon Sep 17 00:00:00 2001 From: lvs1974 Date: Sun, 13 Feb 2022 18:17:26 +0100 Subject: [PATCH] Find a better place to sync TSC in the kernel (supported since 10.7) --- Changelog.md | 4 ++ CpuTscSync/CpuTscSync.cpp | 74 ++++++++++++++++-------------------- CpuTscSync/CpuTscSync.hpp | 18 ++++----- CpuTscSync/VoodooTSCSync.cpp | 4 +- README.md | 6 +++ 5 files changed, 53 insertions(+), 53 deletions(-) diff --git a/Changelog.md b/Changelog.md index cab7bee..8164575 100755 --- a/Changelog.md +++ b/Changelog.md @@ -1,5 +1,9 @@ CpuTscSync Changelog =================== +#### v1.0.7 +- Find a better place to sync TSC in the kernel (supported since 10.7) +- boot-args `-cputsclock` or `TSC_sync_margin` can be used to select older method to sync TSC + #### v1.0.6 - Override one more kernel method `IOPlatformActionsPostResume` to perform sync as early as possible - README extended with an additional hint related to `TSC_sync_margin=0` diff --git a/CpuTscSync/CpuTscSync.cpp b/CpuTscSync/CpuTscSync.cpp index 88fe9a0..1a9b09b 100755 --- a/CpuTscSync/CpuTscSync.cpp +++ b/CpuTscSync/CpuTscSync.cpp @@ -15,10 +15,11 @@ static CpuTscSyncPlugin *callbackCpuf = nullptr; _Atomic(bool) CpuTscSyncPlugin::tsc_synced = false; -_Atomic(bool) CpuTscSyncPlugin::clock_get_calendar_called_after_wake = false; +_Atomic(bool) CpuTscSyncPlugin::use_trace_point_method_to_sync = false; +_Atomic(bool) CpuTscSyncPlugin::use_clock_get_calendar_to_sync = false; +_Atomic(bool) CpuTscSyncPlugin::kernel_is_awake = false; _Atomic(uint16_t) CpuTscSyncPlugin::cores_ready = 0; _Atomic(uint64_t) CpuTscSyncPlugin::tsc_frequency = 0; -_Atomic(uint64_t) CpuTscSyncPlugin::xnu_thread_tid = -1UL; @@ -68,17 +69,20 @@ void CpuTscSyncPlugin::tsc_adjust_or_reset() void CpuTscSyncPlugin::reset_sync_flag() { tsc_synced = false; - xnu_thread_tid = -1UL; } -bool CpuTscSyncPlugin::is_clock_get_calendar_called_after_wake() +bool CpuTscSyncPlugin::is_non_legacy_method_used_to_sync() { - return clock_get_calendar_called_after_wake; + return use_trace_point_method_to_sync || use_clock_get_calendar_to_sync; } void CpuTscSyncPlugin::init() { callbackCpuf = this; + use_trace_point_method_to_sync = (getKernelVersion() >= KernelVersion::Lion); + use_clock_get_calendar_to_sync = (getKernelVersion() >= KernelVersion::ElCapitan) && (checkKernelArgument("TSC_sync_margin") || checkKernelArgument("-cputsclock")); + if (use_clock_get_calendar_to_sync) + use_trace_point_method_to_sync = false; lilu.onPatcherLoadForce( [](void *user, KernelPatcher &patcher) { @@ -99,81 +103,67 @@ void CpuTscSyncPlugin::xcpm_urgency(int urgency, uint64_t rt_period, uint64_t rt IOReturn CpuTscSyncPlugin::IOHibernateSystemHasSlept() { + DBGLOG("cputs", "IOHibernateSystemHasSlept is called"); tsc_synced = false; - xnu_thread_tid = -1UL; + kernel_is_awake = false; return FunctionCast(IOHibernateSystemHasSlept, callbackCpuf->orgIOHibernateSystemHasSlept)(); } -IOReturn CpuTscSyncPlugin::IOHibernateSystemWake() +void CpuTscSyncPlugin::IOPMrootDomain_tracePoint( void *that, uint8_t point ) { - // post pone tsc sync in IOPMrootDomain::powerChangeDone, - // it will be executed later, after calculating wakeup time - tsc_synced = false; - xnu_thread_tid = thread_tid(current_thread()); - DBGLOG("cputs", "IOHibernateSystemWake is called"); - return FunctionCast(IOHibernateSystemWake, callbackCpuf->orgIOHibernateSystemWake)(); + if (callbackCpuf->use_trace_point_method_to_sync && point == kIOPMTracePointWakeCPUs) { + DBGLOG("cputs", "IOPMrootDomain::tracePoint with point = kIOPMTracePointWakeCPUs is called"); + tsc_synced = false; + tsc_adjust_or_reset(); + } + FunctionCast(IOPMrootDomain_tracePoint, callbackCpuf->orgIOPMrootDomain_tracePoint)(that, point); + kernel_is_awake = true; } void CpuTscSyncPlugin::clock_get_calendar_microtime(clock_sec_t *secs, clock_usec_t *microsecs) { FunctionCast(clock_get_calendar_microtime, callbackCpuf->org_clock_get_calendar_microtime)(secs, microsecs); - if (xnu_thread_tid == thread_tid(current_thread())) { - xnu_thread_tid = -1UL; + if (callbackCpuf->use_clock_get_calendar_to_sync && kernel_is_awake) { DBGLOG("cputs", "clock_get_calendar_microtime is called after wake"); tsc_adjust_or_reset(); } } -void CpuTscSyncPlugin::IOPlatformActionsPostResume(void) -{ - FunctionCast(IOPlatformActionsPostResume, callbackCpuf->orgIOPlatformActionsPostResume)(); - DBGLOG("cputs", "IOPlatformActionsPostResume is called"); - tsc_adjust_or_reset(); -} - void CpuTscSyncPlugin::processKernel(KernelPatcher &patcher) { if (!kernel_routed) { - clock_get_calendar_called_after_wake = (getKernelVersion() >= KernelVersion::ElCapitan); - if (clock_get_calendar_called_after_wake) { - DBGLOG("cputs", "_clock_get_calendar_microtime will be used to sync tsc after wake"); - } - KernelPatcher::RouteRequest requests_for_long_jump[] { {"_IOHibernateSystemHasSlept", IOHibernateSystemHasSlept, orgIOHibernateSystemHasSlept}, - {"_IOHibernateSystemWake", IOHibernateSystemWake, orgIOHibernateSystemWake} }; - size_t size = arrsize(requests_for_long_jump) - (clock_get_calendar_called_after_wake ? 0 : 1); - if (!patcher.routeMultipleLong(KernelPatcher::KernelID, requests_for_long_jump, size)) + if (!patcher.routeMultipleLong(KernelPatcher::KernelID, requests_for_long_jump, arrsize(requests_for_long_jump))) SYSLOG("cputs", "patcher.routeMultiple for %s is failed with error %d", requests_for_long_jump[0].symbol, patcher.getError()); patcher.clearError(); KernelPatcher::RouteRequest requests[] { {"_xcpm_urgency", xcpm_urgency, org_xcpm_urgency}, + {"__ZN14IOPMrootDomain10tracePointEh", IOPMrootDomain_tracePoint, orgIOPMrootDomain_tracePoint}, {"_clock_get_calendar_microtime", clock_get_calendar_microtime, org_clock_get_calendar_microtime } }; - size = arrsize(requests) - (clock_get_calendar_called_after_wake ? 0 : 1); + size_t size = arrsize(requests) - (use_clock_get_calendar_to_sync ? 0 : 1); if (!patcher.routeMultiple(KernelPatcher::KernelID, requests, size)) SYSLOG("cputs", "patcher.routeMultiple for %s is failed with error %d", requests[0].symbol, patcher.getError()); patcher.clearError(); - - if (patcher.solveSymbol(KernelPatcher::KernelID, "__Z27IOPlatformActionsPostResumev")) - { - KernelPatcher::RouteRequest requests[] { - {"__Z27IOPlatformActionsPostResumev", IOPlatformActionsPostResume, orgIOPlatformActionsPostResume} - }; - - if (!patcher.routeMultiple(KernelPatcher::KernelID, requests)) - SYSLOG("cputs", "patcher.routeMultiple for %s is failed with error %d", requests[0].symbol, patcher.getError()); + + if (use_trace_point_method_to_sync) { + DBGLOG("cputs", "__ZN14IOPMrootDomain10tracePointEh method will be used to sync TSC after wake"); + } + else if (use_clock_get_calendar_to_sync) { + DBGLOG("cputs", "_clock_get_calendar_microtime method will be used to sync TSC after wake"); + } + else { + DBGLOG("cputs", "Legacy setPowerState method will be used to sync TSC after wake"); } - - patcher.clearError(); kernel_routed = true; } diff --git a/CpuTscSync/CpuTscSync.hpp b/CpuTscSync/CpuTscSync.hpp index cfa3d5c..47f65a8 100755 --- a/CpuTscSync/CpuTscSync.hpp +++ b/CpuTscSync/CpuTscSync.hpp @@ -16,6 +16,7 @@ #define MSR_IA32_TSC 0x00000010 #define MSR_IA32_TSC_ADJUST 0x0000003b +enum { kIOPMTracePointWakeCPUs = 0x23 }; class CpuTscSyncPlugin { public: @@ -23,11 +24,12 @@ class CpuTscSyncPlugin { private: _Atomic(bool) kernel_routed = false; - static _Atomic(bool) tsc_synced; - static _Atomic(bool) clock_get_calendar_called_after_wake; + static _Atomic(bool) tsc_synced; + static _Atomic(bool) use_trace_point_method_to_sync; + static _Atomic(bool) use_clock_get_calendar_to_sync; + static _Atomic(bool) kernel_is_awake; static _Atomic(uint16_t) cores_ready; static _Atomic(uint64_t) tsc_frequency; - static _Atomic(uint64_t) xnu_thread_tid; private: /** @@ -35,18 +37,16 @@ class CpuTscSyncPlugin { */ mach_vm_address_t org_xcpm_urgency {0}; mach_vm_address_t orgIOHibernateSystemHasSlept {0}; - mach_vm_address_t orgIOHibernateSystemWake {0}; + mach_vm_address_t orgIOPMrootDomain_tracePoint {0}; mach_vm_address_t org_clock_get_calendar_microtime {0}; - mach_vm_address_t orgIOPlatformActionsPostResume {0}; - + /** * Hooked functions */ static void xcpm_urgency(int urgency, uint64_t rt_period, uint64_t rt_deadline); static IOReturn IOHibernateSystemHasSlept(void); - static IOReturn IOHibernateSystemWake(); + static void IOPMrootDomain_tracePoint( void *that, uint8_t point ); static void clock_get_calendar_microtime(clock_sec_t *secs, clock_usec_t *microsecs); - static void IOPlatformActionsPostResume(void); /** * Patch kernel @@ -63,7 +63,7 @@ class CpuTscSyncPlugin { public: static void tsc_adjust_or_reset(); static void reset_sync_flag(); - static bool is_clock_get_calendar_called_after_wake(); + static bool is_non_legacy_method_used_to_sync(); }; #endif /* kern_cputs_hpp */ diff --git a/CpuTscSync/VoodooTSCSync.cpp b/CpuTscSync/VoodooTSCSync.cpp index 1101aa6..18dbef9 100644 --- a/CpuTscSync/VoodooTSCSync.cpp +++ b/CpuTscSync/VoodooTSCSync.cpp @@ -41,8 +41,8 @@ bool VoodooTSCSync::start(IOService *provider) { } IOReturn VoodooTSCSync::setPowerState(unsigned long state, IOService *whatDevice){ - DBGLOG("cputs", "changing power state to %lu", state); - if (!CpuTscSyncPlugin::is_clock_get_calendar_called_after_wake()) { + if (!CpuTscSyncPlugin::is_non_legacy_method_used_to_sync()) { + DBGLOG("cputs", "changing power state to %lu", state); if (state == PowerStateOff) CpuTscSyncPlugin::reset_sync_flag(); if (state == PowerStateOn) diff --git a/README.md b/README.md index c41ec3b..a20011c 100644 --- a/README.md +++ b/README.md @@ -7,6 +7,12 @@ It is a Lilu plugin, combining functionality of VoodooTSCSync and disabling xcpm **WARNING**: if you still get kernel panic like "Non-monotonic time: invoke at 0xxxxxxxxxxx, runnable....", you can try to add `TSC_sync_margin=0` into your boot-args. See [CpuTscSync Monterey kernel panic on wake up #1900" for more details](https://github.com/acidanthera/bugtracker/issues/1900) +#### Boot-args +- `-cputsdbg` turns on debugging output +- `-cputsbeta` enables loading on unsupported osx +- `-cputsoff` disables kext loading +- `-cputsclock` forces using of method clock_get_calendar_microtime to sync TSC (the same method is used when boot-arg `TSC_sync_margin` is specified) + #### Credits - [Apple](https://www.apple.com) for macOS - [vit9696](https://github.com/vit9696) for [Lilu.kext](https://github.com/vit9696/Lilu)