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