]> git.proxmox.com Git - mirror_edk2.git/blame - EmulatorPkg/TimerDxe/Timer.c
UefiCpuPkg: Move AsmRelocateApLoopStart from Mpfuncs.nasm to AmdSev.nasm
[mirror_edk2.git] / EmulatorPkg / TimerDxe / Timer.c
CommitLineData
949f388f 1/*++ @file\r
2 Emu Emulation Timer Architectural Protocol Driver as defined in DXE CIS\r
3\r
4 This Timer module uses an Emu Thread to simulate the timer-tick driven\r
5 timer service. In the future, the Thread creation should possibly be\r
6 abstracted by the CPU architectural protocol\r
7\r
daea123d 8Copyright (c) 2004 - 2016, Intel Corporation. All rights reserved.<BR>\r
949f388f 9Portions copyright (c) 2010 - 2011, Apple Inc. All rights reserved.\r
e3ba31da 10SPDX-License-Identifier: BSD-2-Clause-Patent\r
949f388f 11\r
12\r
13**/\r
14\r
15#include "PiDxe.h"\r
16#include <Protocol/Timer.h>\r
17#include <Protocol/Cpu.h>\r
18#include "Timer.h"\r
19#include <Library/BaseLib.h>\r
20#include <Library/DebugLib.h>\r
21#include <Library/UefiLib.h>\r
22#include <Library/UefiDriverEntryPoint.h>\r
23#include <Library/MemoryAllocationLib.h>\r
24#include <Library/UefiBootServicesTableLib.h>\r
25#include <Library/EmuThunkLib.h>\r
26\r
27//\r
28// Pointer to the CPU Architectural Protocol instance\r
29//\r
a550d468 30EFI_CPU_ARCH_PROTOCOL *mCpu;\r
949f388f 31\r
32//\r
33// The Timer Architectural Protocol that this driver produces\r
34//\r
a550d468 35EFI_TIMER_ARCH_PROTOCOL mTimer = {\r
949f388f 36 EmuTimerDriverRegisterHandler,\r
37 EmuTimerDriverSetTimerPeriod,\r
38 EmuTimerDriverGetTimerPeriod,\r
39 EmuTimerDriverGenerateSoftInterrupt\r
40};\r
41\r
42//\r
43// The notification function to call on every timer interrupt\r
44//\r
a550d468 45EFI_TIMER_NOTIFY mTimerNotifyFunction = NULL;\r
949f388f 46\r
47//\r
48// The current period of the timer interrupt\r
49//\r
a550d468 50UINT64 mTimerPeriodMs;\r
949f388f 51\r
52VOID\r
53EFIAPI\r
a550d468
MK
54TimerCallback (\r
55 UINT64 DeltaMs\r
56 )\r
949f388f 57{\r
58 EFI_TPL OriginalTPL;\r
59 EFI_TIMER_NOTIFY CallbackFunction;\r
60\r
949f388f 61 OriginalTPL = gBS->RaiseTPL (TPL_HIGH_LEVEL);\r
62\r
63 if (OriginalTPL < TPL_HIGH_LEVEL) {\r
64 CallbackFunction = mTimerNotifyFunction;\r
65\r
66 //\r
67 // Only invoke the callback function if a Non-NULL handler has been\r
68 // registered. Assume all other handlers are legal.\r
69 //\r
70 if (CallbackFunction != NULL) {\r
e148512e 71 CallbackFunction (MultU64x32 (DeltaMs, 10000));\r
949f388f 72 }\r
73 }\r
74\r
75 gBS->RestoreTPL (OriginalTPL);\r
949f388f 76}\r
77\r
78EFI_STATUS\r
79EFIAPI\r
80EmuTimerDriverRegisterHandler (\r
a550d468
MK
81 IN EFI_TIMER_ARCH_PROTOCOL *This,\r
82 IN EFI_TIMER_NOTIFY NotifyFunction\r
949f388f 83 )\r
a550d468 84\r
949f388f 85/*++\r
86\r
87Routine Description:\r
88\r
89 This function registers the handler NotifyFunction so it is called every time\r
90 the timer interrupt fires. It also passes the amount of time since the last\r
91 handler call to the NotifyFunction. If NotifyFunction is NULL, then the\r
92 handler is unregistered. If the handler is registered, then EFI_SUCCESS is\r
93 returned. If the CPU does not support registering a timer interrupt handler,\r
94 then EFI_UNSUPPORTED is returned. If an attempt is made to register a handler\r
95 when a handler is already registered, then EFI_ALREADY_STARTED is returned.\r
96 If an attempt is made to unregister a handler when a handler is not registered,\r
97 then EFI_INVALID_PARAMETER is returned. If an error occurs attempting to\r
98 register the NotifyFunction with the timer interrupt, then EFI_DEVICE_ERROR\r
99 is returned.\r
100\r
101Arguments:\r
102\r
103 This - The EFI_TIMER_ARCH_PROTOCOL instance.\r
104\r
105 NotifyFunction - The function to call when a timer interrupt fires. This\r
106 function executes at TPL_HIGH_LEVEL. The DXE Core will\r
107 register a handler for the timer interrupt, so it can know\r
108 how much time has passed. This information is used to\r
109 signal timer based events. NULL will unregister the handler.\r
110\r
111Returns:\r
112\r
113 EFI_SUCCESS - The timer handler was registered.\r
114\r
115 EFI_UNSUPPORTED - The platform does not support timer interrupts.\r
116\r
117 EFI_ALREADY_STARTED - NotifyFunction is not NULL, and a handler is already\r
118 registered.\r
119\r
120 EFI_INVALID_PARAMETER - NotifyFunction is NULL, and a handler was not\r
121 previously registered.\r
122\r
123 EFI_DEVICE_ERROR - The timer handler could not be registered.\r
124\r
125**/\r
126{\r
127 //\r
128 // Check for invalid parameters\r
129 //\r
a550d468 130 if ((NotifyFunction == NULL) && (mTimerNotifyFunction == NULL)) {\r
949f388f 131 return EFI_INVALID_PARAMETER;\r
132 }\r
133\r
a550d468 134 if ((NotifyFunction != NULL) && (mTimerNotifyFunction != NULL)) {\r
949f388f 135 return EFI_ALREADY_STARTED;\r
136 }\r
137\r
138 if (NotifyFunction == NULL) {\r
139 /* Disable timer. */\r
140 gEmuThunk->SetTimer (0, TimerCallback);\r
141 } else if (mTimerNotifyFunction == NULL) {\r
142 /* Enable Timer. */\r
143 gEmuThunk->SetTimer (mTimerPeriodMs, TimerCallback);\r
144 }\r
a550d468 145\r
949f388f 146 mTimerNotifyFunction = NotifyFunction;\r
147\r
148 return EFI_SUCCESS;\r
149}\r
150\r
151EFI_STATUS\r
152EFIAPI\r
153EmuTimerDriverSetTimerPeriod (\r
154 IN EFI_TIMER_ARCH_PROTOCOL *This,\r
155 IN UINT64 TimerPeriod\r
156 )\r
a550d468 157\r
949f388f 158/*++\r
159\r
160Routine Description:\r
161\r
162 This function adjusts the period of timer interrupts to the value specified\r
163 by TimerPeriod. If the timer period is updated, then the selected timer\r
164 period is stored in EFI_TIMER.TimerPeriod, and EFI_SUCCESS is returned. If\r
165 the timer hardware is not programmable, then EFI_UNSUPPORTED is returned.\r
166 If an error occurs while attempting to update the timer period, then the\r
167 timer hardware will be put back in its state prior to this call, and\r
168 EFI_DEVICE_ERROR is returned. If TimerPeriod is 0, then the timer interrupt\r
169 is disabled. This is not the same as disabling the CPU's interrupts.\r
170 Instead, it must either turn off the timer hardware, or it must adjust the\r
171 interrupt controller so that a CPU interrupt is not generated when the timer\r
172 interrupt fires.\r
173\r
174Arguments:\r
175\r
176 This - The EFI_TIMER_ARCH_PROTOCOL instance.\r
177\r
178 TimerPeriod - The rate to program the timer interrupt in 100 nS units. If\r
179 the timer hardware is not programmable, then EFI_UNSUPPORTED is\r
180 returned. If the timer is programmable, then the timer period\r
181 will be rounded up to the nearest timer period that is supported\r
182 by the timer hardware. If TimerPeriod is set to 0, then the\r
183 timer interrupts will be disabled.\r
184\r
185Returns:\r
186\r
187 EFI_SUCCESS - The timer period was changed.\r
188\r
189 EFI_UNSUPPORTED - The platform cannot change the period of the timer interrupt.\r
190\r
191 EFI_DEVICE_ERROR - The timer period could not be changed due to a device error.\r
192\r
193**/\r
194{\r
949f388f 195 //\r
196 // If TimerPeriod is 0, then the timer thread should be canceled\r
197 // If the TimerPeriod is valid, then create and/or adjust the period of the timer thread\r
198 //\r
a550d468
MK
199 if ( (TimerPeriod == 0)\r
200 || ( (TimerPeriod > TIMER_MINIMUM_VALUE)\r
201 && (TimerPeriod < TIMER_MAXIMUM_VALUE)))\r
202 {\r
949f388f 203 mTimerPeriodMs = DivU64x32 (TimerPeriod + 5000, 10000);\r
204\r
205 gEmuThunk->SetTimer (mTimerPeriodMs, TimerCallback);\r
206 }\r
207\r
208 return EFI_SUCCESS;\r
209}\r
210\r
211EFI_STATUS\r
212EFIAPI\r
213EmuTimerDriverGetTimerPeriod (\r
a550d468
MK
214 IN EFI_TIMER_ARCH_PROTOCOL *This,\r
215 OUT UINT64 *TimerPeriod\r
949f388f 216 )\r
a550d468 217\r
949f388f 218/*++\r
219\r
220Routine Description:\r
221\r
222 This function retrieves the period of timer interrupts in 100 ns units,\r
223 returns that value in TimerPeriod, and returns EFI_SUCCESS. If TimerPeriod\r
224 is NULL, then EFI_INVALID_PARAMETER is returned. If a TimerPeriod of 0 is\r
225 returned, then the timer is currently disabled.\r
226\r
227Arguments:\r
228\r
229 This - The EFI_TIMER_ARCH_PROTOCOL instance.\r
230\r
231 TimerPeriod - A pointer to the timer period to retrieve in 100 ns units. If\r
232 0 is returned, then the timer is currently disabled.\r
233\r
234Returns:\r
235\r
236 EFI_SUCCESS - The timer period was returned in TimerPeriod.\r
237\r
238 EFI_INVALID_PARAMETER - TimerPeriod is NULL.\r
239\r
240**/\r
241{\r
242 if (TimerPeriod == NULL) {\r
243 return EFI_INVALID_PARAMETER;\r
244 }\r
245\r
e148512e 246 *TimerPeriod = MultU64x32 (mTimerPeriodMs, 10000);\r
949f388f 247\r
248 return EFI_SUCCESS;\r
249}\r
250\r
251EFI_STATUS\r
252EFIAPI\r
253EmuTimerDriverGenerateSoftInterrupt (\r
254 IN EFI_TIMER_ARCH_PROTOCOL *This\r
255 )\r
a550d468 256\r
949f388f 257/*++\r
258\r
259Routine Description:\r
260\r
261 This function generates a soft timer interrupt. If the platform does not support soft\r
262 timer interrupts, then EFI_UNSUPPORTED is returned. Otherwise, EFI_SUCCESS is returned.\r
263 If a handler has been registered through the EFI_TIMER_ARCH_PROTOCOL.RegisterHandler()\r
264 service, then a soft timer interrupt will be generated. If the timer interrupt is\r
265 enabled when this service is called, then the registered handler will be invoked. The\r
266 registered handler should not be able to distinguish a hardware-generated timer\r
267 interrupt from a software-generated timer interrupt.\r
268\r
269Arguments:\r
270\r
271 This - The EFI_TIMER_ARCH_PROTOCOL instance.\r
272\r
273Returns:\r
274\r
275 EFI_SUCCESS - The soft timer interrupt was generated.\r
276\r
daea123d 277 EFI_UNSUPPORTED - The platform does not support the generation of soft timer interrupts.\r
949f388f 278\r
279**/\r
280{\r
281 return EFI_UNSUPPORTED;\r
282}\r
283\r
284EFI_STATUS\r
285EFIAPI\r
286EmuTimerDriverInitialize (\r
287 IN EFI_HANDLE ImageHandle,\r
288 IN EFI_SYSTEM_TABLE *SystemTable\r
289 )\r
a550d468 290\r
949f388f 291/*++\r
292\r
293Routine Description:\r
294\r
295 Initialize the Timer Architectural Protocol driver\r
296\r
297Arguments:\r
298\r
299 ImageHandle - ImageHandle of the loaded driver\r
300\r
301 SystemTable - Pointer to the System Table\r
302\r
303Returns:\r
304\r
305 EFI_SUCCESS - Timer Architectural Protocol created\r
306\r
307 EFI_OUT_OF_RESOURCES - Not enough resources available to initialize driver.\r
308\r
f034c05c 309 EFI_DEVICE_ERROR - A device error occurred attempting to initialize the driver.\r
949f388f 310\r
311**/\r
312{\r
313 EFI_STATUS Status;\r
314 EFI_HANDLE Handle;\r
315\r
316 //\r
317 // Make sure the Timer Architectural Protocol is not already installed in the system\r
318 //\r
319 ASSERT_PROTOCOL_ALREADY_INSTALLED (NULL, &gEfiTimerArchProtocolGuid);\r
320\r
321 //\r
322 // Get the CPU Architectural Protocol instance\r
323 //\r
324 Status = gBS->LocateProtocol (&gEfiCpuArchProtocolGuid, NULL, (void *)&mCpu);\r
325 ASSERT_EFI_ERROR (Status);\r
326\r
1ef41207 327 //\r
328 // Start the timer thread at the default timer period\r
329 //\r
330 Status = mTimer.SetTimerPeriod (&mTimer, DEFAULT_TIMER_TICK_DURATION);\r
331 if (EFI_ERROR (Status)) {\r
332 return Status;\r
333 }\r
334\r
949f388f 335 //\r
336 // Install the Timer Architectural Protocol onto a new handle\r
337 //\r
338 Handle = NULL;\r
339 Status = gBS->InstallProtocolInterface (\r
340 &Handle,\r
341 &gEfiTimerArchProtocolGuid,\r
342 EFI_NATIVE_INTERFACE,\r
343 &mTimer\r
344 );\r
345 if (EFI_ERROR (Status)) {\r
346 return Status;\r
347 }\r
348\r
949f388f 349 return EFI_SUCCESS;\r
350}\r