Skip to content

Commit

Permalink
Find a better place to sync TSC in the kernel (supported since 10.7)
Browse files Browse the repository at this point in the history
  • Loading branch information
lvs1974 committed Feb 13, 2022
1 parent 3d62747 commit c017c4c
Show file tree
Hide file tree
Showing 5 changed files with 53 additions and 53 deletions.
4 changes: 4 additions & 0 deletions Changelog.md
Original file line number Diff line number Diff line change
@@ -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`
Expand Down
74 changes: 32 additions & 42 deletions CpuTscSync/CpuTscSync.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;



Expand Down Expand Up @@ -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) {
Expand All @@ -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;
}
Expand Down
18 changes: 9 additions & 9 deletions CpuTscSync/CpuTscSync.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,37 +16,37 @@
#define MSR_IA32_TSC 0x00000010
#define MSR_IA32_TSC_ADJUST 0x0000003b

enum { kIOPMTracePointWakeCPUs = 0x23 };

class CpuTscSyncPlugin {
public:
void init();

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:
/**
* Trampolines for original resource load callback
*/
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
Expand All @@ -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 */
4 changes: 2 additions & 2 deletions CpuTscSync/VoodooTSCSync.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
6 changes: 6 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down

0 comments on commit c017c4c

Please sign in to comment.