]> git.proxmox.com Git - mirror_edk2.git/blame - MdePkg/Library/SecPeiDxeTimerLibCpu/X86TimerLib.c
MdePkg: Replace BSD License with BSD+Patent License
[mirror_edk2.git] / MdePkg / Library / SecPeiDxeTimerLibCpu / X86TimerLib.c
CommitLineData
e386b444 1/** @file\r
2 Timer Library functions built upon local APIC on IA32/x64.\r
3\r
53fa8748 4 Copyright (c) 2006 - 2015, Intel Corporation. All rights reserved.<BR>\r
9344f092 5 SPDX-License-Identifier: BSD-2-Clause-Patent\r
e386b444 6\r
e386b444 7**/\r
8\r
f734a10a
A
9#include <Base.h>\r
10#include <Library/TimerLib.h>\r
11#include <Library/BaseLib.h>\r
12#include <Library/IoLib.h>\r
f734a10a 13#include <Library/PcdLib.h>\r
1553d073 14#include <Library/DebugLib.h>\r
f734a10a 15\r
53fa8748 16#define APIC_SVR 0x0f0\r
9cd1d1c1 17#define APIC_LVTERR 0x370\r
1553d073 18#define APIC_TMICT 0x380\r
9cd1d1c1 19#define APIC_TMCCT 0x390\r
20#define APIC_TDCR 0x3e0\r
e386b444 21\r
22//\r
23// The following array is used in calculating the frequency of local APIC\r
24// timer. Refer to IA-32 developers' manual for more details.\r
25//\r
26GLOBAL_REMOVE_IF_UNREFERENCED\r
27CONST UINT8 mTimerLibLocalApicDivisor[] = {\r
28 0x02, 0x04, 0x08, 0x10,\r
29 0x02, 0x04, 0x08, 0x10,\r
30 0x20, 0x40, 0x80, 0x01,\r
31 0x20, 0x40, 0x80, 0x01\r
32};\r
33\r
34/**\r
35 Internal function to retrieve the base address of local APIC.\r
36\r
53fa8748
HW
37 This function will ASSERT if:\r
38 The local APIC is not globally enabled.\r
39 The local APIC is not working under XAPIC mode.\r
40 The local APIC is not software enabled.\r
41\r
e386b444 42 @return The base address of local APIC\r
43\r
44**/\r
e386b444 45UINTN\r
42eedea9 46EFIAPI\r
e386b444 47InternalX86GetApicBase (\r
48 VOID\r
49 )\r
50{\r
53fa8748
HW
51 UINTN MsrValue;\r
52 UINTN ApicBase;\r
53\r
54 MsrValue = (UINTN) AsmReadMsr64 (27);\r
55 ApicBase = MsrValue & 0xffffff000ULL;\r
56\r
57 //\r
58 // Check the APIC Global Enable bit (bit 11) in IA32_APIC_BASE MSR.\r
59 // This bit will be 1, if local APIC is globally enabled.\r
60 //\r
61 ASSERT ((MsrValue & BIT11) != 0);\r
62\r
63 //\r
64 // Check the APIC Extended Mode bit (bit 10) in IA32_APIC_BASE MSR.\r
65 // This bit will be 0, if local APIC is under XAPIC mode.\r
66 //\r
67 ASSERT ((MsrValue & BIT10) == 0);\r
68\r
69 //\r
70 // Check the APIC Software Enable/Disable bit (bit 8) in Spurious-Interrupt\r
71 // Vector Register.\r
72 // This bit will be 1, if local APIC is software enabled.\r
73 //\r
74 ASSERT ((MmioRead32 (ApicBase + APIC_SVR) & BIT8) != 0);\r
75\r
76 return ApicBase;\r
e386b444 77}\r
78\r
79/**\r
80 Internal function to return the frequency of the local APIC timer.\r
81\r
e386b444 82 @param ApicBase The base address of memory mapped registers of local APIC.\r
83\r
84 @return The frequency of the timer in Hz.\r
85\r
86**/\r
e386b444 87UINT32\r
42eedea9 88EFIAPI\r
e386b444 89InternalX86GetTimerFrequency (\r
90 IN UINTN ApicBase\r
91 )\r
92{\r
93 return\r
94 PcdGet32(PcdFSBClock) /\r
9cd1d1c1 95 mTimerLibLocalApicDivisor[MmioBitFieldRead32 (ApicBase + APIC_TDCR, 0, 3)];\r
e386b444 96}\r
97\r
98/**\r
99 Internal function to read the current tick counter of local APIC.\r
100\r
e386b444 101 @param ApicBase The base address of memory mapped registers of local APIC.\r
102\r
103 @return The tick counter read.\r
104\r
105**/\r
e386b444 106INT32\r
42eedea9 107EFIAPI\r
e386b444 108InternalX86GetTimerTick (\r
109 IN UINTN ApicBase\r
110 )\r
111{\r
9cd1d1c1 112 return MmioRead32 (ApicBase + APIC_TMCCT);\r
e386b444 113}\r
114\r
3eb695cf
JF
115/**\r
116 Internal function to read the initial timer count of local APIC.\r
117\r
118 @param ApicBase The base address of memory mapped registers of local APIC.\r
119\r
120 @return The initial timer count read.\r
121\r
122**/\r
123UINT32\r
124InternalX86GetInitTimerCount (\r
125 IN UINTN ApicBase\r
126 )\r
127{\r
128 return MmioRead32 (ApicBase + APIC_TMICT);\r
129}\r
130\r
e386b444 131/**\r
132 Stalls the CPU for at least the given number of ticks.\r
133\r
134 Stalls the CPU for at least the given number of ticks. It's invoked by\r
135 MicroSecondDelay() and NanoSecondDelay().\r
136\r
53fa8748
HW
137 This function will ASSERT if the APIC timer intial count returned from\r
138 InternalX86GetInitTimerCount() is zero.\r
139\r
e386b444 140 @param ApicBase The base address of memory mapped registers of local APIC.\r
141 @param Delay A period of time to delay in ticks.\r
142\r
143**/\r
e386b444 144VOID\r
42eedea9 145EFIAPI\r
e386b444 146InternalX86Delay (\r
147 IN UINTN ApicBase,\r
148 IN UINT32 Delay\r
149 )\r
150{\r
151 INT32 Ticks;\r
3eb695cf
JF
152 UINT32 Times;\r
153 UINT32 InitCount;\r
154 UINT32 StartTick;\r
e386b444 155\r
156 //\r
3eb695cf
JF
157 // In case Delay is too larger, separate it into several small delay slot.\r
158 // Devided Delay by half value of Init Count is to avoid Delay close to\r
159 // the Init Count, timeout maybe missing if the time consuming between 2\r
160 // GetApicTimerCurrentCount() invoking is larger than the time gap between\r
161 // Delay and the Init Count.\r
e386b444 162 //\r
3eb695cf 163 InitCount = InternalX86GetInitTimerCount (ApicBase);\r
53fa8748 164 ASSERT (InitCount != 0);\r
3eb695cf
JF
165 Times = Delay / (InitCount / 2);\r
166 Delay = Delay % (InitCount / 2);\r
e386b444 167\r
168 //\r
3eb695cf 169 // Get Start Tick and do delay\r
e386b444 170 //\r
3eb695cf
JF
171 StartTick = InternalX86GetTimerTick (ApicBase);\r
172 do {\r
173 //\r
174 // Wait until time out by Delay value\r
175 //\r
176 do {\r
177 CpuPause ();\r
178 //\r
179 // Get Ticks from Start to Current.\r
180 //\r
181 Ticks = StartTick - InternalX86GetTimerTick (ApicBase);\r
182 //\r
183 // Ticks < 0 means Timer wrap-arounds happens.\r
184 //\r
185 if (Ticks < 0) {\r
186 Ticks += InitCount;\r
187 }\r
188 } while ((UINT32)Ticks < Delay);\r
189\r
190 //\r
191 // Update StartTick and Delay for next delay slot\r
192 //\r
193 StartTick -= (StartTick > Delay) ? Delay : (Delay - InitCount);\r
194 Delay = InitCount / 2;\r
195 } while (Times-- > 0);\r
e386b444 196}\r
197\r
198/**\r
199 Stalls the CPU for at least the given number of microseconds.\r
200\r
201 Stalls the CPU for the number of microseconds specified by MicroSeconds.\r
202\r
203 @param MicroSeconds The minimum number of microseconds to delay.\r
204\r
8cefc2ee 205 @return The value of MicroSeconds inputted.\r
e386b444 206\r
207**/\r
208UINTN\r
209EFIAPI\r
210MicroSecondDelay (\r
211 IN UINTN MicroSeconds\r
212 )\r
213{\r
214 UINTN ApicBase;\r
215\r
216 ApicBase = InternalX86GetApicBase ();\r
217 InternalX86Delay (\r
218 ApicBase,\r
219 (UINT32)DivU64x32 (\r
220 MultU64x64 (\r
221 InternalX86GetTimerFrequency (ApicBase),\r
222 MicroSeconds\r
223 ),\r
224 1000000u\r
225 )\r
226 );\r
227 return MicroSeconds;\r
228}\r
229\r
230/**\r
231 Stalls the CPU for at least the given number of nanoseconds.\r
232\r
233 Stalls the CPU for the number of nanoseconds specified by NanoSeconds.\r
234\r
235 @param NanoSeconds The minimum number of nanoseconds to delay.\r
236\r
8cefc2ee 237 @return The value of NanoSeconds inputted.\r
e386b444 238\r
239**/\r
240UINTN\r
241EFIAPI\r
242NanoSecondDelay (\r
243 IN UINTN NanoSeconds\r
244 )\r
245{\r
246 UINTN ApicBase;\r
247\r
248 ApicBase = InternalX86GetApicBase ();\r
249 InternalX86Delay (\r
250 ApicBase,\r
251 (UINT32)DivU64x32 (\r
252 MultU64x64 (\r
253 InternalX86GetTimerFrequency (ApicBase),\r
254 NanoSeconds\r
255 ),\r
256 1000000000u\r
257 )\r
258 );\r
259 return NanoSeconds;\r
260}\r
261\r
262/**\r
71871514 263 Retrieves the current value of a 64-bit free running performance counter.\r
264\r
265 The counter can either count up by 1 or count down by 1. If the physical\r
e386b444 266 performance counter counts by a larger increment, then the counter values\r
267 must be translated. The properties of the counter can be retrieved from\r
268 GetPerformanceCounterProperties().\r
269\r
270 @return The current value of the free running performance counter.\r
271\r
272**/\r
273UINT64\r
274EFIAPI\r
275GetPerformanceCounter (\r
276 VOID\r
277 )\r
278{\r
279 return (UINT64)(UINT32)InternalX86GetTimerTick (InternalX86GetApicBase ());\r
280}\r
281\r
282/**\r
283 Retrieves the 64-bit frequency in Hz and the range of performance counter\r
284 values.\r
285\r
286 If StartValue is not NULL, then the value that the performance counter starts\r
287 with immediately after is it rolls over is returned in StartValue. If\r
288 EndValue is not NULL, then the value that the performance counter end with\r
289 immediately before it rolls over is returned in EndValue. The 64-bit\r
290 frequency of the performance counter in Hz is always returned. If StartValue\r
291 is less than EndValue, then the performance counter counts up. If StartValue\r
292 is greater than EndValue, then the performance counter counts down. For\r
293 example, a 64-bit free running counter that counts up would have a StartValue\r
294 of 0 and an EndValue of 0xFFFFFFFFFFFFFFFF. A 24-bit free running counter\r
295 that counts down would have a StartValue of 0xFFFFFF and an EndValue of 0.\r
296\r
297 @param StartValue The value the performance counter starts with when it\r
298 rolls over.\r
299 @param EndValue The value that the performance counter ends with before\r
300 it rolls over.\r
301\r
302 @return The frequency in Hz.\r
303\r
304**/\r
305UINT64\r
306EFIAPI\r
307GetPerformanceCounterProperties (\r
308 OUT UINT64 *StartValue, OPTIONAL\r
309 OUT UINT64 *EndValue OPTIONAL\r
310 )\r
311{\r
312 UINTN ApicBase;\r
313\r
314 ApicBase = InternalX86GetApicBase ();\r
315\r
316 if (StartValue != NULL) {\r
3eb695cf 317 *StartValue = (UINT64)InternalX86GetInitTimerCount (ApicBase);\r
e386b444 318 }\r
319\r
320 if (EndValue != NULL) {\r
321 *EndValue = 0;\r
322 }\r
323\r
9cd1d1c1 324 return (UINT64) InternalX86GetTimerFrequency (ApicBase);\r
e386b444 325}\r
b9610b9c 326\r
327/**\r
328 Converts elapsed ticks of performance counter to time in nanoseconds.\r
329\r
330 This function converts the elapsed ticks of running performance counter to\r
331 time value in unit of nanoseconds.\r
332\r
333 @param Ticks The number of elapsed ticks of running performance counter.\r
334\r
335 @return The elapsed time in nanoseconds.\r
336\r
337**/\r
338UINT64\r
339EFIAPI\r
340GetTimeInNanoSecond (\r
341 IN UINT64 Ticks\r
342 )\r
343{\r
344 UINT64 Frequency;\r
345 UINT64 NanoSeconds;\r
346 UINT64 Remainder;\r
347 INTN Shift;\r
348\r
349 Frequency = GetPerformanceCounterProperties (NULL, NULL);\r
350\r
351 //\r
352 // Ticks\r
353 // Time = --------- x 1,000,000,000\r
354 // Frequency\r
355 //\r
356 NanoSeconds = MultU64x32 (DivU64x64Remainder (Ticks, Frequency, &Remainder), 1000000000u);\r
357\r
358 //\r
359 // Ensure (Remainder * 1,000,000,000) will not overflow 64-bit.\r
360 // Since 2^29 < 1,000,000,000 = 0x3B9ACA00 < 2^30, Remainder should < 2^(64-30) = 2^34,\r
361 // i.e. highest bit set in Remainder should <= 33.\r
362 //\r
363 Shift = MAX (0, HighBitSet64 (Remainder) - 33);\r
364 Remainder = RShiftU64 (Remainder, (UINTN) Shift);\r
365 Frequency = RShiftU64 (Frequency, (UINTN) Shift);\r
366 NanoSeconds += DivU64x64Remainder (MultU64x32 (Remainder, 1000000000u), Frequency, NULL);\r
367\r
368 return NanoSeconds;\r
369}\r