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