RET
// OS X comm page time offsets
-// http://www.opensource.apple.com/source/xnu/xnu-1699.26.8/osfmk/i386/cpu_capabilities.h
+// https://opensource.apple.com/source/xnu/xnu-4570.1.46/osfmk/i386/cpu_capabilities.h
+
+#define commpage_version 0x1e
+
#define v12_nt_tsc_base 0x50
#define v12_nt_scale 0x58
#define v12_nt_shift 0x5c
#define v12_nt_ns_base 0x60
#define v12_nt_generation 0x68
-#define v12_gtod_generation 0x6c
-#define v12_gtod_ns_base 0x70
-#define v12_gtod_sec_base 0x78
+#define v12_gtod_generation 0x6c // obsolete since High Sierra (v13)
+#define v12_gtod_ns_base 0x70 // obsolete since High Sierra (v13)
+#define v12_gtod_sec_base 0x78 // obsolete since High Sierra (v13)
+
+#define v13_gtod_ns_base 0xd0
+#define v13_gtod_sec_ofs 0xd8
+#define v13_gtod_frac_ofs 0xe0
+#define v13_gtod_scale 0xe8
+#define v13_gtod_tkspersec 0xf0
TEXT runtime·nanotime(SB),NOSPLIT,$0-8
MOVQ $0x7fffffe00000, BP /* comm page base */
// are used in the systime fallback, as the timeval address
// filled in by the system call.
MOVQ $0x7fffffe00000, BP /* comm page base */
+ CMPW commpage_version(BP), $13
+ JB v12 /* sierra and older */
+
+ // This is the new code, for macOS High Sierra (v13) and newer.
+v13:
+ // Loop trying to take a consistent snapshot
+ // of the time parameters.
+timeloop13:
+ MOVQ v13_gtod_ns_base(BP), R12
+
+ MOVL v12_nt_generation(BP), CX
+ TESTL CX, CX
+ JZ timeloop13
+ RDTSC
+ MOVQ v12_nt_tsc_base(BP), SI
+ MOVL v12_nt_scale(BP), DI
+ MOVQ v12_nt_ns_base(BP), BX
+ CMPL v12_nt_generation(BP), CX
+ JNE timeloop13
+
+ MOVQ v13_gtod_sec_ofs(BP), R8
+ MOVQ v13_gtod_frac_ofs(BP), R9
+ MOVQ v13_gtod_scale(BP), R10
+ MOVQ v13_gtod_tkspersec(BP), R11
+ CMPQ v13_gtod_ns_base(BP), R12
+ JNE timeloop13
+
+ // Compute monotonic time
+ // mono = ((tsc - nt_tsc_base) * nt_scale) >> 32 + nt_ns_base
+ // The multiply and shift extracts the top 64 bits of the 96-bit product.
+ SHLQ $32, DX
+ ADDQ DX, AX
+ SUBQ SI, AX
+ MULQ DI
+ SHRQ $32, AX:DX
+ ADDQ BX, AX
+
+ // Subtract startNano base to return the monotonic runtime timer
+ // which is an offset from process boot.
+ MOVQ AX, BX
+ MOVQ runtime·startNano(SB), CX
+ SUBQ CX, BX
+ MOVQ BX, monotonic+16(FP)
+
+ // Now compute the 128-bit wall time:
+ // wall = ((mono - gtod_ns_base) * gtod_scale) + gtod_offs
+ // The parameters are updated every second, so if we found them
+ // outdated (that is, more than one second is passed from the ns base),
+ // fallback to the syscall.
+ TESTQ R12, R12
+ JZ systime
+ SUBQ R12, AX
+ CMPQ R11, AX
+ JB systime
+ MULQ R10
+ ADDQ R9, AX
+ ADCQ R8, DX
+
+ // Convert the 128-bit wall time into (sec,nsec).
+ // High part (seconds) is already good to go, while low part
+ // (fraction of seconds) must be converted to nanoseconds.
+ MOVQ DX, sec+0(FP)
+ MOVQ $1000000000, CX
+ MULQ CX
+ MOVQ DX, nsec+8(FP)
+ RET
+
+ // This is the legacy code needed for macOS Sierra (v12) and older.
+v12:
// Loop trying to take a consistent snapshot
// of the time parameters.
timeloop: