1 | ; $Id: timesupA.mac 44528 2013-02-04 14:27:54Z vboxsync $
|
---|
2 | ;; @file
|
---|
3 | ; IPRT - Time using SUPLib, the Assembly Code Template.
|
---|
4 | ;
|
---|
5 |
|
---|
6 | ;
|
---|
7 | ; Copyright (C) 2006-2011 Oracle Corporation
|
---|
8 | ;
|
---|
9 | ; This file is part of VirtualBox Open Source Edition (OSE), as
|
---|
10 | ; available from http://www.virtualbox.org. This file is free software;
|
---|
11 | ; you can redistribute it and/or modify it under the terms of the GNU
|
---|
12 | ; General Public License (GPL) as published by the Free Software
|
---|
13 | ; Foundation, in version 2 as it comes in the "COPYING" file of the
|
---|
14 | ; VirtualBox OSE distribution. VirtualBox OSE is distributed in the
|
---|
15 | ; hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
|
---|
16 | ;
|
---|
17 | ; The contents of this file may alternatively be used under the terms
|
---|
18 | ; of the Common Development and Distribution License Version 1.0
|
---|
19 | ; (CDDL) only, as it comes in the "COPYING.CDDL" file of the
|
---|
20 | ; VirtualBox OSE distribution, in which case the provisions of the
|
---|
21 | ; CDDL are applicable instead of those of the GPL.
|
---|
22 | ;
|
---|
23 | ; You may elect to license modified versions of this file under the
|
---|
24 | ; terms and conditions of either the GPL or the CDDL or both.
|
---|
25 | ;
|
---|
26 |
|
---|
27 | %ifdef RT_ARCH_X86
|
---|
28 | ;;
|
---|
29 | ; The x86 assembly implementation of the assembly routines.
|
---|
30 | ;
|
---|
31 | ; @returns Nanosecond timestamp.
|
---|
32 | ; @param pData Pointer to the nanosecond timestamp data.
|
---|
33 | ;
|
---|
34 | BEGINPROC rtTimeNanoTSInternalAsm
|
---|
35 | ;
|
---|
36 | ; Variable definitions.
|
---|
37 | ;
|
---|
38 | %define pData [ebp + 08h]
|
---|
39 | %define u64RetNanoTS_Hi [ebp - 04h]
|
---|
40 | %define u64RetNanoTS [ebp - 08h]
|
---|
41 | %define u32UpdateIntervalNS [ebp - 0ch]
|
---|
42 | %define u32UpdateIntervalTSC [ebp - 10h]
|
---|
43 | %define u64TSC_Hi [ebp - 14h]
|
---|
44 | %define u64TSC [ebp - 18h]
|
---|
45 | %define u64CurNanoTS_Hi [ebp - 1ch]
|
---|
46 | %define u64CurNanoTS [ebp - 20h]
|
---|
47 | %define u64PrevNanoTS_Hi [ebp - 24h]
|
---|
48 | %define u64PrevNanoTS [ebp - 28h]
|
---|
49 | %define u32TransactionId [ebp - 2ch]
|
---|
50 | %define u32ApicIdPlus [ebp - 30h]
|
---|
51 | %define TmpVar [ebp - 34h]
|
---|
52 | %define SavedEBX [ebp - 38h]
|
---|
53 | %define SavedEDI [ebp - 3ch]
|
---|
54 | %define SavedESI [ebp - 40h]
|
---|
55 |
|
---|
56 | ;
|
---|
57 | ; Prolog.
|
---|
58 | ;
|
---|
59 | push ebp
|
---|
60 | mov ebp, esp
|
---|
61 | sub esp, 40h
|
---|
62 | mov SavedEBX, ebx
|
---|
63 | mov SavedEDI, edi
|
---|
64 | mov SavedESI, esi
|
---|
65 |
|
---|
66 |
|
---|
67 | ;;
|
---|
68 | ;; Read the GIP data and the previous value.
|
---|
69 | ;;
|
---|
70 | .ReadGip:
|
---|
71 |
|
---|
72 |
|
---|
73 | ;
|
---|
74 | ; Load pGip and calc pGipCPU, setting u32ApicIdPlus if necessary.
|
---|
75 | ;
|
---|
76 | %ifdef IMPORTED_SUPLIB
|
---|
77 | %ifdef IN_RING0
|
---|
78 | mov esi, IMP(g_SUPGlobalInfoPage)
|
---|
79 | %else
|
---|
80 | mov esi, IMP(g_pSUPGlobalInfoPage)
|
---|
81 | mov esi, [esi]
|
---|
82 | %endif
|
---|
83 | %else
|
---|
84 | mov esi, [NAME(g_pSUPGlobalInfoPage)]
|
---|
85 | %endif
|
---|
86 | or esi, esi
|
---|
87 | jz .Rediscover
|
---|
88 | cmp dword [esi + SUPGLOBALINFOPAGE.u32Magic], SUPGLOBALINFOPAGE_MAGIC
|
---|
89 | jne .Rediscover
|
---|
90 | %ifdef ASYNC_GIP
|
---|
91 | ; u8ApicId = ASMGetApicId();
|
---|
92 | mov eax, 1
|
---|
93 | cpuid ; expensive
|
---|
94 | %ifdef NEED_TRANSACTION_ID
|
---|
95 | mov u32ApicIdPlus, ebx
|
---|
96 | %endif
|
---|
97 | ; pGipCpu = &pGip->aCPU[pGip->aiCpuFromApicId[u8ApicId]];
|
---|
98 | shr ebx, 24
|
---|
99 | movzx ebx, word [esi + ebx * 2 + SUPGLOBALINFOPAGE.aiCpuFromApicId]
|
---|
100 | mov eax, SUPGIPCPU_size
|
---|
101 | mul ebx
|
---|
102 | lea edi, [esi + eax + SUPGLOBALINFOPAGE.aCPUs] ; edi == &pGip->aCPU[u8ApicId];
|
---|
103 | %else
|
---|
104 | lea edi, [esi + SUPGLOBALINFOPAGE.aCPUs] ; edi == &pGip->aCPU[0];
|
---|
105 | %endif
|
---|
106 |
|
---|
107 | %ifdef NEED_TRANSACTION_ID
|
---|
108 | ;
|
---|
109 | ; Serialized loading of u32TransactionId.
|
---|
110 | ;
|
---|
111 | mov ebx, [edi + SUPGIPCPU.u32TransactionId]
|
---|
112 | mov u32TransactionId, ebx
|
---|
113 | %ifdef USE_LFENCE
|
---|
114 | lfence
|
---|
115 | %else
|
---|
116 | lock xor dword TmpVar, 0
|
---|
117 | %endif
|
---|
118 | %endif
|
---|
119 |
|
---|
120 | ;
|
---|
121 | ; Load the data and TSC.
|
---|
122 | ;
|
---|
123 | mov eax, [esi + SUPGLOBALINFOPAGE.u32UpdateIntervalNS]
|
---|
124 | mov u32UpdateIntervalNS, eax ; esi is now free
|
---|
125 | mov edx, [edi + SUPGIPCPU.u32UpdateIntervalTSC]
|
---|
126 | mov u32UpdateIntervalTSC, edx
|
---|
127 | rdtsc
|
---|
128 | mov ecx, [edi + SUPGIPCPU.u64NanoTS]
|
---|
129 | mov u64CurNanoTS, ecx
|
---|
130 | mov esi, [edi + SUPGIPCPU.u64NanoTS + 4]
|
---|
131 | mov u64CurNanoTS_Hi, esi
|
---|
132 | mov ebx, [edi + SUPGIPCPU.u64TSC]
|
---|
133 | mov u64TSC, ebx
|
---|
134 | mov ecx, [edi + SUPGIPCPU.u64TSC + 4]
|
---|
135 | mov u64TSC_Hi, ecx
|
---|
136 |
|
---|
137 | ; u64PrevNanoTS = ASMAtomicReadU64(pu64Prev);
|
---|
138 | ; This serializes load/save. And with the dependency on the
|
---|
139 | ; RDTSC result, we try to make sure it has completed as well.
|
---|
140 | mov esi, pData
|
---|
141 | mov esi, [esi + RTTIMENANOTSDATA.pu64Prev]
|
---|
142 | mov ebx, eax
|
---|
143 | mov ecx, edx
|
---|
144 | lock cmpxchg8b [esi]
|
---|
145 | mov u64PrevNanoTS, eax
|
---|
146 | mov u64PrevNanoTS_Hi, edx
|
---|
147 |
|
---|
148 | %undef SAVED_u64RetNanoTS
|
---|
149 | %ifdef NEED_TRANSACTION_ID
|
---|
150 | ;
|
---|
151 | ; Check that the GIP and CPU didn't change.
|
---|
152 | ; We've already serialized all the loads and stores at this point.
|
---|
153 | ;
|
---|
154 | %ifdef ASYNC_GIP
|
---|
155 | mov u64RetNanoTS, ebx
|
---|
156 | mov u64RetNanoTS_Hi, ecx
|
---|
157 | %define SAVED_u64RetNanoTS
|
---|
158 | mov eax, 1
|
---|
159 | cpuid
|
---|
160 | cmp u32ApicIdPlus, ebx
|
---|
161 | jne .ReadGip
|
---|
162 | %endif
|
---|
163 | mov esi, [edi + SUPGIPCPU.u32TransactionId]
|
---|
164 | cmp esi, u32TransactionId
|
---|
165 | jne .ReadGip
|
---|
166 | test esi, 1
|
---|
167 | jnz .ReadGip
|
---|
168 | %endif ; NEED_TRANSACTION_ID
|
---|
169 | %ifdef SAVED_u64RetNanoTS
|
---|
170 | mov ebx, u64RetNanoTS
|
---|
171 | mov ecx, u64RetNanoTS_Hi
|
---|
172 | %endif
|
---|
173 |
|
---|
174 | ;;
|
---|
175 | ;; Calc the timestamp.
|
---|
176 | ;;
|
---|
177 | ; u64RetNanoTS -= u64TSC;
|
---|
178 | sub ebx, u64TSC
|
---|
179 | sbb ecx, u64TSC_Hi
|
---|
180 |
|
---|
181 | ; if (u64RetNanoTS > u32UpdateIntervalTSC) -> jump
|
---|
182 | or ecx, ecx
|
---|
183 | jnz .OverFlow
|
---|
184 | cmp ebx, u32UpdateIntervalTSC
|
---|
185 | ja .OverFlow
|
---|
186 | mov eax, ebx
|
---|
187 | .ContinueCalcs: ; eax <= u32UpdateIntervalTSC
|
---|
188 | mul dword u32UpdateIntervalNS
|
---|
189 | div dword u32UpdateIntervalTSC
|
---|
190 | xor edx, edx
|
---|
191 |
|
---|
192 | ; u64RetNanoTS += u64CurNanoTS;
|
---|
193 | add eax, u64CurNanoTS
|
---|
194 | adc edx, u64CurNanoTS_Hi
|
---|
195 |
|
---|
196 | ;;
|
---|
197 | ;; Compare it with the previous one.
|
---|
198 | ;;
|
---|
199 | ; if (RT_LIKELY( u64RetNanoTS > u64PrevNanoTS
|
---|
200 | ; && u64RetNanoTS < u64PrevNanoTS + UINT64_C(86000000000000) /* 24h */))
|
---|
201 | ;; @todo optimize this compare (/me too tired).
|
---|
202 | mov ecx, u64PrevNanoTS_Hi
|
---|
203 | mov ebx, u64PrevNanoTS
|
---|
204 | cmp edx, ecx
|
---|
205 | ja .Compare2
|
---|
206 | jb .DeltaPrevTooBig
|
---|
207 | cmp eax, ebx
|
---|
208 | jbe .DeltaPrevTooBig
|
---|
209 |
|
---|
210 | .Compare2:
|
---|
211 | add ebx, 0x6F736000
|
---|
212 | adc ecx, 0x00004E37
|
---|
213 | cmp edx, ecx
|
---|
214 | jb .CompareDone
|
---|
215 | ja .DeltaPrevTooBig
|
---|
216 | cmp eax, ebx
|
---|
217 | jae .DeltaPrevTooBig
|
---|
218 | .CompareDone:
|
---|
219 |
|
---|
220 |
|
---|
221 | ;;
|
---|
222 | ;; Update the previous value with the u64RetNanoTS value.
|
---|
223 | ;;
|
---|
224 | .Update:
|
---|
225 | ; if (RT_LIKELY(ASMAtomicCmpXchgU64(&pData->u64Prev, u64RetNanoTS, u64PrevNanoTS)))
|
---|
226 | mov ebx, eax
|
---|
227 | mov ecx, edx
|
---|
228 | mov esi, pData
|
---|
229 | mov esi, [esi + RTTIMENANOTSDATA.pu64Prev]
|
---|
230 | mov eax, u64PrevNanoTS
|
---|
231 | mov edx, u64PrevNanoTS_Hi
|
---|
232 | lock cmpxchg8b [esi]
|
---|
233 | jnz .UpdateFailed
|
---|
234 |
|
---|
235 | .Updated:
|
---|
236 | mov eax, ebx
|
---|
237 | mov edx, ecx
|
---|
238 |
|
---|
239 | .Done:
|
---|
240 | mov esi, SavedESI
|
---|
241 | mov edi, SavedEDI
|
---|
242 | mov ebx, SavedEBX
|
---|
243 | leave
|
---|
244 | ret
|
---|
245 |
|
---|
246 |
|
---|
247 | ;;
|
---|
248 | ;; We've expired the interval, cap it. If we're here for the 2nd
|
---|
249 | ;; time without any GIP update in-between, the checks against
|
---|
250 | ;; pData->u64Prev below will force 1ns stepping.
|
---|
251 | ;;
|
---|
252 | .OverFlow:
|
---|
253 | ; u64Delta = u32UpdateIntervalTSC;
|
---|
254 | mov esi, pData
|
---|
255 | inc dword [esi + RTTIMENANOTSDATA.cExpired]
|
---|
256 | mov eax, u32UpdateIntervalTSC
|
---|
257 | jmp .ContinueCalcs
|
---|
258 |
|
---|
259 |
|
---|
260 | ;;
|
---|
261 | ;; u64DeltaPrev >= 24h
|
---|
262 | ;;
|
---|
263 | ;; eax:edx = u64RetNanoTS (to be adjusted)
|
---|
264 | ;;
|
---|
265 | .DeltaPrevTooBig:
|
---|
266 | ; uint64_t u64DeltaPrev = u64RetNanoTS - u64PrevNanoTS;
|
---|
267 | mov ebx, eax
|
---|
268 | sub ebx, u64PrevNanoTS
|
---|
269 | mov ecx, edx
|
---|
270 | sbb ecx, u64PrevNanoTS_Hi ; ebx:ecx = u64DeltaPrev
|
---|
271 |
|
---|
272 | ; else if ( (int64_t)u64DeltaPrev <= 0
|
---|
273 | ; && (int64_t)u64DeltaPrev + u32UpdateIntervalNS * 2 >= 0)
|
---|
274 | ; {
|
---|
275 | ; /* Occasional - u64RetNanoTS is in the recent 'past' relative the previous call. */
|
---|
276 | ; pData->c1nsSteps++;
|
---|
277 | ; u64RetNanoTS = u64PrevNanoTS + 1;
|
---|
278 | ; }
|
---|
279 | mov esi, u32UpdateIntervalNS
|
---|
280 | cmp ecx, 0
|
---|
281 | jl .PrevNotZero2ndTest
|
---|
282 | jg .DeltaPrevNotInRecentPast
|
---|
283 | cmp ebx, 0
|
---|
284 | ja .DeltaPrevNotInRecentPast
|
---|
285 |
|
---|
286 | .PrevNotZero2ndTest:
|
---|
287 | add esi, esi ; ASSUMES: u32UpdateIntervalNS * 2 <= 32-bit.
|
---|
288 | xor edi, edi
|
---|
289 | add esi, ebx
|
---|
290 | adc edi, ecx
|
---|
291 | test edi, edi
|
---|
292 | js .DeltaPrevNotInRecentPast
|
---|
293 |
|
---|
294 | .DeltaPrevInRecentPast:
|
---|
295 | mov esi, pData
|
---|
296 | inc dword [esi + RTTIMENANOTSDATA.c1nsSteps]
|
---|
297 | mov eax, u64PrevNanoTS
|
---|
298 | mov edx, u64PrevNanoTS_Hi
|
---|
299 | add eax, 1
|
---|
300 | adc edx, 0
|
---|
301 | jmp .Update
|
---|
302 |
|
---|
303 | .DeltaPrevNotInRecentPast:
|
---|
304 | ; else if (!u64PrevNanoTS) /* We're resuming (see TMVirtualResume). */
|
---|
305 | ; /* do nothing */;
|
---|
306 | cmp dword u64PrevNanoTS, 0
|
---|
307 | jne .DeltaPrevNotZero
|
---|
308 | cmp dword u64PrevNanoTS_Hi, 0
|
---|
309 | jne .DeltaPrevNotZero
|
---|
310 | jmp .Update
|
---|
311 |
|
---|
312 | .DeltaPrevNotZero:
|
---|
313 | ; else
|
---|
314 | ; {
|
---|
315 | ; /* Something has gone bust, if negative offset it's real bad. */
|
---|
316 | ; rtTimeNanoTSInternalBitch(pVM,
|
---|
317 | ; }
|
---|
318 |
|
---|
319 | ; call C function that does the bitching.
|
---|
320 | mov u64RetNanoTS, eax
|
---|
321 | mov u64RetNanoTS_Hi, edx
|
---|
322 |
|
---|
323 | mov edi, u64PrevNanoTS_Hi
|
---|
324 | mov esi, u64PrevNanoTS
|
---|
325 | push edi
|
---|
326 | push esi ; 4 - u64PrevNanoTS
|
---|
327 | push ecx
|
---|
328 | push ebx ; 3 - u64DeltaPrev
|
---|
329 | push edx
|
---|
330 | push eax ; 2 - u64RetNanoTS
|
---|
331 | mov eax, pData
|
---|
332 | push eax ; 1 - pData
|
---|
333 | call dword [eax + RTTIMENANOTSDATA.pfnBad]
|
---|
334 | add esp, 4*7
|
---|
335 |
|
---|
336 | mov eax, u64RetNanoTS
|
---|
337 | mov edx, u64RetNanoTS_Hi
|
---|
338 | jmp .Update
|
---|
339 |
|
---|
340 |
|
---|
341 | ;;
|
---|
342 | ;; Attempt updating the previous value, provided we're still ahead of it.
|
---|
343 | ;;
|
---|
344 | ;; There is no point in recalculating u64NanoTS because we got preempted or if
|
---|
345 | ;; we raced somebody while the GIP was updated, since these are events
|
---|
346 | ;; that might occur at any point in the return path as well.
|
---|
347 | ;;
|
---|
348 | ;; eax:edx = *pData->u64Prev
|
---|
349 | ;; ebx:ecx = u64RetNanoTS
|
---|
350 | ;;
|
---|
351 | ALIGNCODE(16)
|
---|
352 | .UpdateFailed:
|
---|
353 | mov edi, pData
|
---|
354 | lock inc dword [edi + RTTIMENANOTSDATA.cUpdateRaces]
|
---|
355 | ; for (i = 0; i < 10; i++)
|
---|
356 | mov edi, 10
|
---|
357 | .UpdateLoop:
|
---|
358 | ; if (u64PrevNanoTS >= u64NanoTS)
|
---|
359 | ; break;
|
---|
360 | cmp edx, ecx
|
---|
361 | jg .Updated
|
---|
362 | jne .UpdateLoopLess
|
---|
363 | cmp eax, ebx
|
---|
364 | jae .Updated
|
---|
365 | .UpdateLoopLess:
|
---|
366 | ; retry
|
---|
367 | lock cmpxchg8b [esi]
|
---|
368 | jz .Updated
|
---|
369 | dec edi
|
---|
370 | jnz .UpdateLoop
|
---|
371 | jmp .Updated
|
---|
372 |
|
---|
373 |
|
---|
374 | ;;
|
---|
375 | ;; The GIP is seemingly invalid, redo the discovery.
|
---|
376 | ;;
|
---|
377 | .Rediscover:
|
---|
378 | mov eax, pData
|
---|
379 | push eax
|
---|
380 | call [eax + RTTIMENANOTSDATA.pfnRediscover]
|
---|
381 | add esp, 4h
|
---|
382 | jmp .Done
|
---|
383 |
|
---|
384 | ;
|
---|
385 | ; Cleanup variables
|
---|
386 | ;
|
---|
387 | %undef pData
|
---|
388 | %undef u64Delta_Hi
|
---|
389 | %undef u64Delta
|
---|
390 | %undef u32UpdateIntervalNS
|
---|
391 | %undef u32UpdateIntervalTSC
|
---|
392 | %undef u64TSC_Hi
|
---|
393 | %undef u64TSC
|
---|
394 | %undef u64NanoTS_Hi
|
---|
395 | %undef u64NanoTS
|
---|
396 | %undef u64PrevNanoTS_Hi
|
---|
397 | %undef u64PrevNanoTS
|
---|
398 | %undef u32TransactionId
|
---|
399 | %undef u8ApicId
|
---|
400 |
|
---|
401 | %else ; AMD64
|
---|
402 |
|
---|
403 | ;;
|
---|
404 | ; The AMD64 assembly implementation of the assembly routines.
|
---|
405 | ;
|
---|
406 | ; @returns Nanosecond timestamp.
|
---|
407 | ; @param pData gcc:rdi msc:rcx Pointer to the nanosecond timestamp data.
|
---|
408 | ;
|
---|
409 | BEGINPROC rtTimeNanoTSInternalAsm
|
---|
410 | ;
|
---|
411 | ; Define variables and stack frame.
|
---|
412 | ;
|
---|
413 | %define SavedRBX [rbp - 08h]
|
---|
414 | %define SavedR12 [rbp - 10h]
|
---|
415 | %define SavedR13 [rbp - 18h]
|
---|
416 | %define SavedRDI [rbp - 20h]
|
---|
417 | %define SavedRSI [rbp - 28h]
|
---|
418 | %define TmpVar [rbp - 30h]
|
---|
419 | %define TmpVar2 [rbp - 38h]
|
---|
420 | %ifdef NEED_TRANSACTION_ID
|
---|
421 | %ifdef ASYNC_GIP
|
---|
422 | %define SavedR14 [rbp - 40h]
|
---|
423 | %define SavedR15 [rbp - 48h]
|
---|
424 | %endif
|
---|
425 | %endif
|
---|
426 |
|
---|
427 | %define pData rdi
|
---|
428 |
|
---|
429 | %define u64TSC rsi
|
---|
430 | %define pGip rsi
|
---|
431 | %define pGipCPU r8
|
---|
432 | %define u32TransactionId r9d
|
---|
433 | %define u64CurNanoTS r10
|
---|
434 | %define u64PrevNanoTS r11 ; not parameter register
|
---|
435 | %define u32UpdateIntervalTSC r12d
|
---|
436 | %define u32UpdateIntervalTSC_64 r12
|
---|
437 | %define u32UpdateIntervalNS r13d
|
---|
438 | %define u32UpdateIntervalNS_64 r13
|
---|
439 | %undef u64SavedRetNanoTS
|
---|
440 | %undef u32ApicIdPlus
|
---|
441 | %ifdef NEED_TRANSACTION_ID
|
---|
442 | %ifdef ASYNC_GIP
|
---|
443 | %define u64SavedRetNanoTS r14
|
---|
444 | %define u32ApicIdPlus r15d
|
---|
445 | %endif
|
---|
446 | %endif
|
---|
447 |
|
---|
448 | ;
|
---|
449 | ; The prolog.
|
---|
450 | ;
|
---|
451 | push rbp
|
---|
452 | mov rbp, rsp
|
---|
453 | %ifdef ASM_CALL64_MSC
|
---|
454 | sub rsp, 50h+20h
|
---|
455 | %else
|
---|
456 | sub rsp, 50h
|
---|
457 | %endif
|
---|
458 | mov SavedRBX, rbx
|
---|
459 | mov SavedR12, r12
|
---|
460 | mov SavedR13, r13
|
---|
461 | %ifdef ASM_CALL64_MSC
|
---|
462 | mov SavedRDI, rdi
|
---|
463 | mov SavedRSI, rsi
|
---|
464 | mov pData, rcx
|
---|
465 | %else
|
---|
466 | ;mov pData, rdi - already in rdi.
|
---|
467 | %endif
|
---|
468 | %ifdef SavedR14
|
---|
469 | mov SavedR14, r14
|
---|
470 | %endif
|
---|
471 | %ifdef SavedR15
|
---|
472 | mov SavedR15, r15
|
---|
473 | %endif
|
---|
474 |
|
---|
475 |
|
---|
476 | ;;
|
---|
477 | ;; Data fetch loop.
|
---|
478 | ;; We take great pain ensuring that data consistency here.
|
---|
479 | ;;
|
---|
480 | .ReadGip:
|
---|
481 |
|
---|
482 | ;
|
---|
483 | ; Load pGip and calc pGipCPU, setting u32ApicIdPlus if necessary.
|
---|
484 | ; Finding the GIP is fun...
|
---|
485 | ;
|
---|
486 | %ifdef RT_OS_WINDOWS
|
---|
487 | %ifdef IMPORTED_SUPLIB
|
---|
488 | %ifdef IN_RING0
|
---|
489 | mov rax, qword IMP(g_SUPGlobalInfoPage)
|
---|
490 | mov pGip, rax
|
---|
491 | %else
|
---|
492 | mov pGip, [IMP(g_pSUPGlobalInfoPage) wrt rip]
|
---|
493 | mov pGip, [pGip]
|
---|
494 | %endif
|
---|
495 | %else
|
---|
496 | mov pGip, [NAME(g_pSUPGlobalInfoPage) wrt rip]
|
---|
497 | %endif
|
---|
498 | %else
|
---|
499 | %ifdef IN_RING0
|
---|
500 | mov rax, qword NAME(g_SUPGlobalInfoPage)
|
---|
501 | mov pGip, rax
|
---|
502 | %else
|
---|
503 | mov pGip, [rel NAME(g_pSUPGlobalInfoPage) wrt ..gotpcrel]
|
---|
504 | mov pGip, [pGip]
|
---|
505 | %endif
|
---|
506 | %endif
|
---|
507 | or pGip, pGip
|
---|
508 | jz .Rediscover
|
---|
509 | cmp dword [pGip + SUPGLOBALINFOPAGE.u32Magic], SUPGLOBALINFOPAGE_MAGIC
|
---|
510 | jne .Rediscover
|
---|
511 |
|
---|
512 | %ifdef ASYNC_GIP
|
---|
513 | ; u8ApicId = ASMGetApicId();
|
---|
514 | mov eax, 1
|
---|
515 | cpuid ; expensive
|
---|
516 | %ifdef NEED_TRANSACTION_ID
|
---|
517 | mov u32ApicIdPlus, ebx
|
---|
518 | %endif
|
---|
519 | ; pGipCpu = &pGip->aCPU[pGip->aiCpuFromApicId[u8ApicId]];
|
---|
520 | shr ebx, 24
|
---|
521 | movzx eax, word [pGip + rbx * 2 + SUPGLOBALINFOPAGE.aiCpuFromApicId]
|
---|
522 | imul eax, SUPGIPCPU_size
|
---|
523 | lea pGipCPU, [pGip + rax + SUPGLOBALINFOPAGE.aCPUs]
|
---|
524 | %else
|
---|
525 | lea pGipCPU, [pGip + SUPGLOBALINFOPAGE.aCPUs]
|
---|
526 | %endif
|
---|
527 |
|
---|
528 | %ifdef NEED_TRANSACTION_ID
|
---|
529 | ;
|
---|
530 | ; Serialized loading of u32TransactionId.
|
---|
531 | ;
|
---|
532 | mov u32TransactionId, [pGipCPU + SUPGIPCPU.u32TransactionId]
|
---|
533 | %ifdef USE_LFENCE
|
---|
534 | lfence
|
---|
535 | %else
|
---|
536 | lock xor dword TmpVar, 0
|
---|
537 | %endif
|
---|
538 | %endif
|
---|
539 |
|
---|
540 | ;
|
---|
541 | ; Load the data and TSC.
|
---|
542 | ;
|
---|
543 | mov u32UpdateIntervalNS, [pGip + SUPGLOBALINFOPAGE.u32UpdateIntervalNS] ; before u64TSC
|
---|
544 | mov u32UpdateIntervalTSC, [pGipCPU + SUPGIPCPU.u32UpdateIntervalTSC]
|
---|
545 | rdtsc
|
---|
546 | mov u64PrevNanoTS, [pData + RTTIMENANOTSDATA.pu64Prev]
|
---|
547 | mov u64PrevNanoTS, [u64PrevNanoTS]
|
---|
548 | shl rdx, 32
|
---|
549 | %ifdef u64SavedRetNanoTS ; doing this here saves a tick or so.
|
---|
550 | mov u64SavedRetNanoTS, rax
|
---|
551 | or u64SavedRetNanoTS, rdx
|
---|
552 | %else
|
---|
553 | or rax, rdx ; rax is u64RetNanoTS.
|
---|
554 | %endif
|
---|
555 | mov u64CurNanoTS, [pGipCPU + SUPGIPCPU.u64NanoTS]
|
---|
556 | mov u64TSC, [pGipCPU + SUPGIPCPU.u64TSC]
|
---|
557 |
|
---|
558 | %ifdef NEED_TRANSACTION_ID
|
---|
559 | ;
|
---|
560 | ; Check that the GIP and CPU didn't change.
|
---|
561 | ;
|
---|
562 | ; It is crucial that the rdtsc instruction has completed before
|
---|
563 | ; we check the transaction id. The LOCK prefixed instruction with
|
---|
564 | ; dependency on the RDTSC result should do the trick, I think.
|
---|
565 | ; CPUID is serializing, so the async path is safe by default.
|
---|
566 | ;
|
---|
567 | %ifdef ASYNC_GIP
|
---|
568 | mov eax, 1
|
---|
569 | cpuid
|
---|
570 | cmp u32ApicIdPlus, ebx
|
---|
571 | jne .ReadGip
|
---|
572 | %else
|
---|
573 | lock xor qword TmpVar, rax
|
---|
574 | %endif
|
---|
575 | cmp u32TransactionId, [pGipCPU + SUPGIPCPU.u32TransactionId]
|
---|
576 | jne .ReadGip
|
---|
577 | test u32TransactionId, 1
|
---|
578 | jnz .ReadGip
|
---|
579 | %ifdef u64SavedRetNanoTS
|
---|
580 | mov rax, u64SavedRetNanoTS ; rax is u64RetNanoTS.
|
---|
581 | %endif
|
---|
582 | %endif ; NEED_TRANSACTION_ID
|
---|
583 |
|
---|
584 |
|
---|
585 | ;;
|
---|
586 | ;; Calc the timestamp.
|
---|
587 | ;;
|
---|
588 | ; u64RetNanoTS -= u64TSC;
|
---|
589 | sub rax, u64TSC
|
---|
590 | xor edx, edx
|
---|
591 |
|
---|
592 | ; if (u64RetNanoTS > u32UpdateIntervalTSC) -> jump
|
---|
593 | cmp rax, u32UpdateIntervalTSC_64
|
---|
594 | ja .OverFlow
|
---|
595 | .ContinueCalcs: ; edx = 0; eax <= u32UpdateIntervalTSC
|
---|
596 | mul u32UpdateIntervalNS
|
---|
597 | div u32UpdateIntervalTSC
|
---|
598 |
|
---|
599 | ; u64RetNanoTS += u64CurNanoTS;
|
---|
600 | add rax, u64CurNanoTS
|
---|
601 |
|
---|
602 |
|
---|
603 | ;;
|
---|
604 | ;; Compare it with the previous one.
|
---|
605 | ;;
|
---|
606 | ; if (RT_LIKELY( u64RetNanoTS > u64PrevNanoTS
|
---|
607 | ; && u64RetNanoTS < u64PrevNanoTS + UINT64_C(86000000000000) /* 24h */))
|
---|
608 | ; /* Frequent - less than 24h since last call. */;
|
---|
609 | cmp rax, u64PrevNanoTS
|
---|
610 | jbe .DeltaPrevTooBig
|
---|
611 | mov ecx, 5
|
---|
612 | shl rcx, 44 ; close enough
|
---|
613 | add rcx, u64PrevNanoTS
|
---|
614 | cmp rax, rcx
|
---|
615 | jae .DeltaPrevTooBig
|
---|
616 |
|
---|
617 |
|
---|
618 | ;;
|
---|
619 | ;; Update the previous value.
|
---|
620 | ;;
|
---|
621 | .Update:
|
---|
622 | ; if (RT_LIKELY(ASMAtomicCmpXchgU64(&pData->u64Prev, u64RetNanoTS, u64PrevNanoTS)))
|
---|
623 | mov rbx, [pData + RTTIMENANOTSDATA.pu64Prev]
|
---|
624 | mov rcx, rax
|
---|
625 | mov rax, u64PrevNanoTS
|
---|
626 | lock cmpxchg [rbx], rcx
|
---|
627 | jnz .UpdateFailed
|
---|
628 |
|
---|
629 | .Updated:
|
---|
630 | mov rax, rcx
|
---|
631 |
|
---|
632 | .Done:
|
---|
633 | mov rbx, SavedRBX
|
---|
634 | mov r12, SavedR12
|
---|
635 | mov r13, SavedR13
|
---|
636 | %ifdef SavedR14
|
---|
637 | mov r14, SavedR14
|
---|
638 | %endif
|
---|
639 | %ifdef SavedR15
|
---|
640 | mov r15, SavedR15
|
---|
641 | %endif
|
---|
642 | %ifdef ASM_CALL64_MSC
|
---|
643 | mov rdi, SavedRDI
|
---|
644 | mov rsi, SavedRSI
|
---|
645 | %endif
|
---|
646 | leave
|
---|
647 | ret
|
---|
648 |
|
---|
649 |
|
---|
650 | ;;
|
---|
651 | ;; We've expired the interval, cap it. If we're here for the 2nd
|
---|
652 | ;; time without any GIP update in-between, the checks against
|
---|
653 | ;; pData->u64Prev below will force 1ns stepping.
|
---|
654 | ;;
|
---|
655 | ALIGNCODE(16)
|
---|
656 | .OverFlow:
|
---|
657 | ; u64RetNanoTS = u32UpdateIntervalTSC;
|
---|
658 | inc dword [pData + RTTIMENANOTSDATA.cExpired]
|
---|
659 | mov eax, u32UpdateIntervalTSC
|
---|
660 | jmp .ContinueCalcs
|
---|
661 |
|
---|
662 |
|
---|
663 | ;;
|
---|
664 | ;; u64DeltaPrev >= 24h
|
---|
665 | ;;
|
---|
666 | ;; rax = u64RetNanoTS (to be adjusted)
|
---|
667 | ;;
|
---|
668 | ALIGNCODE(16)
|
---|
669 | .DeltaPrevTooBig:
|
---|
670 | ; uint64_t u64DeltaPrev = u64RetNanoTS - u64PrevNanoTS;
|
---|
671 | mov rbx, rax
|
---|
672 | sub rbx, u64PrevNanoTS
|
---|
673 |
|
---|
674 | ; else if ( (int64_t)u64DeltaPrev <= 0
|
---|
675 | ; && (int64_t)u64DeltaPrev + u32UpdateIntervalNS * 2 >= 0)
|
---|
676 | ; {
|
---|
677 | ; /* Occasional - u64NanoTS is in the recent 'past' relative the previous call. */
|
---|
678 | ; pData->c1nsSteps++;
|
---|
679 | ; u64RetNanoTS = u64PrevNanoTS + 1;
|
---|
680 | ; }
|
---|
681 | test rbx, rbx
|
---|
682 | jg .DeltaPrevNotInRecentPast
|
---|
683 |
|
---|
684 | lea rdx, [u32UpdateIntervalNS_64 + u32UpdateIntervalNS_64]
|
---|
685 | add rdx, rbx
|
---|
686 | js .DeltaPrevNotInRecentPast
|
---|
687 |
|
---|
688 | ; body
|
---|
689 | inc dword [pData + RTTIMENANOTSDATA.c1nsSteps]
|
---|
690 | lea rax, [u64PrevNanoTS + 1]
|
---|
691 | jmp .Update
|
---|
692 |
|
---|
693 | ; else if (!u64PrevNanoTS) /* We're resuming (see TMVirtualResume) / first call. */
|
---|
694 | ; /* do nothing */;
|
---|
695 | .DeltaPrevNotInRecentPast:
|
---|
696 | or u64PrevNanoTS, u64PrevNanoTS
|
---|
697 | jz .Update
|
---|
698 |
|
---|
699 | ; else
|
---|
700 | ; {
|
---|
701 | ; /* Something has gone bust, if negative offset it's real bad. */
|
---|
702 | ; rtTimeNanoTSInternalBitch(pVM,
|
---|
703 | ; }
|
---|
704 |
|
---|
705 | ; call C function that does the bitching.
|
---|
706 | mov TmpVar, rax
|
---|
707 | mov TmpVar2, pData
|
---|
708 |
|
---|
709 | %ifdef ASM_CALL64_MSC
|
---|
710 | mov rcx, pData ; param 1 - pData
|
---|
711 | mov rdx, rax ; param 2 - u64RetNanoTS
|
---|
712 | mov r8, rbx ; param 3 - u64DeltaPrev
|
---|
713 | mov r9, u64PrevNanoTS ; param 4 - u64PrevNanoTS
|
---|
714 | %else
|
---|
715 | ;mov rdi, pData - already in rdi; param 1 - pData
|
---|
716 | mov rsi, rax ; param 2 - u64RetNanoTS
|
---|
717 | mov rdx, rbx ; param 3 - u64DeltaPrev
|
---|
718 | mov rcx, u64PrevNanoTS ; param 4 - u64PrevNanoTS
|
---|
719 | %endif
|
---|
720 | call qword [pData + RTTIMENANOTSDATA.pfnBad]
|
---|
721 |
|
---|
722 | mov rax, TmpVar
|
---|
723 | mov pData, TmpVar2
|
---|
724 | jmp .Update
|
---|
725 |
|
---|
726 |
|
---|
727 | ;;
|
---|
728 | ;; Attempt updating the previous value, provided we're still ahead of it.
|
---|
729 | ;;
|
---|
730 | ;; There is no point in recalculating u64NanoTS because we got preempted or if
|
---|
731 | ;; we raced somebody while the GIP was updated, since these are events
|
---|
732 | ;; that might occur at any point in the return path as well.
|
---|
733 | ;;
|
---|
734 | ;; rax = *pData->u64Prev;
|
---|
735 | ;; rcx = u64RetNanoTS
|
---|
736 | ;;
|
---|
737 | ALIGNCODE(16)
|
---|
738 | .UpdateFailed:
|
---|
739 | lock inc dword [pData + RTTIMENANOTSDATA.cUpdateRaces]
|
---|
740 | ; for (i = 0; i < 10; i++)
|
---|
741 | mov edx, 10
|
---|
742 | .UpdateLoop:
|
---|
743 | ; if (u64PrevNanoTS >= u64RetNanoTS)
|
---|
744 | ; break;
|
---|
745 | cmp rax, rcx
|
---|
746 | jge .Updated
|
---|
747 | .UpdateLoopLess:
|
---|
748 | ; retry
|
---|
749 | lock cmpxchg [rbx], rcx
|
---|
750 | jz .Updated
|
---|
751 | dec edx
|
---|
752 | jnz .UpdateLoop
|
---|
753 | jmp .Updated
|
---|
754 |
|
---|
755 |
|
---|
756 | ;;
|
---|
757 | ;; The GIP is seemingly invalid, redo the discovery.
|
---|
758 | ;;
|
---|
759 | .Rediscover:
|
---|
760 | %ifdef ASM_CALL64_MSC
|
---|
761 | mov rcx, pData
|
---|
762 | %else
|
---|
763 | ; mov rdi, pData - already in rdi
|
---|
764 | %endif
|
---|
765 | call [pData + RTTIMENANOTSDATA.pfnRediscover]
|
---|
766 | jmp .Done
|
---|
767 |
|
---|
768 |
|
---|
769 | ;
|
---|
770 | ; Cleanup variables
|
---|
771 | ;
|
---|
772 | %undef SavedRBX
|
---|
773 | %undef SavedR12
|
---|
774 | %undef SavedR13
|
---|
775 | %undef SavedR14
|
---|
776 | %undef SavedR15
|
---|
777 | %undef SavedRDI
|
---|
778 | %undef SavedRSI
|
---|
779 | %undef pData
|
---|
780 | %undef TmpVar
|
---|
781 | %undef u64TSC
|
---|
782 | %undef pGip
|
---|
783 | %undef pGipCPU
|
---|
784 | %undef u32TransactionId
|
---|
785 | %undef u64CurNanoTS
|
---|
786 | %undef u64PrevNanoTS
|
---|
787 | %undef u32UpdateIntervalTSC
|
---|
788 | %undef u32UpdateIntervalTSC_64
|
---|
789 | %undef u32UpdateIntervalNS
|
---|
790 | %undef u64SavedRetNanoTS
|
---|
791 | %undef u32ApicIdPlus
|
---|
792 |
|
---|
793 | %endif ; AMD64
|
---|
794 | ENDPROC rtTimeNanoTSInternalAsm
|
---|
795 |
|
---|
796 |
|
---|
797 |
|
---|