ArmPkg: only attempt buildin MmCommunicationDxe for AArch64
[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
53 VOID
54 EFIAPI
55 TimerCallback (UINT64 DeltaMs)
56 {
57 EFI_TPL OriginalTPL;
58 EFI_TIMER_NOTIFY CallbackFunction;
59
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
79 EFI_STATUS
80 EFIAPI
81 EmuTimerDriverRegisterHandler (
82 IN EFI_TIMER_ARCH_PROTOCOL *This,
83 IN EFI_TIMER_NOTIFY NotifyFunction
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 mTimerNotifyFunction = NotifyFunction;
146
147 return EFI_SUCCESS;
148 }
149
150 EFI_STATUS
151 EFIAPI
152 EmuTimerDriverSetTimerPeriod (
153 IN EFI_TIMER_ARCH_PROTOCOL *This,
154 IN UINT64 TimerPeriod
155 )
156 /*++
157
158 Routine Description:
159
160 This function adjusts the period of timer interrupts to the value specified
161 by TimerPeriod. If the timer period is updated, then the selected timer
162 period is stored in EFI_TIMER.TimerPeriod, and EFI_SUCCESS is returned. If
163 the timer hardware is not programmable, then EFI_UNSUPPORTED is returned.
164 If an error occurs while attempting to update the timer period, then the
165 timer hardware will be put back in its state prior to this call, and
166 EFI_DEVICE_ERROR is returned. If TimerPeriod is 0, then the timer interrupt
167 is disabled. This is not the same as disabling the CPU's interrupts.
168 Instead, it must either turn off the timer hardware, or it must adjust the
169 interrupt controller so that a CPU interrupt is not generated when the timer
170 interrupt fires.
171
172 Arguments:
173
174 This - The EFI_TIMER_ARCH_PROTOCOL instance.
175
176 TimerPeriod - The rate to program the timer interrupt in 100 nS units. If
177 the timer hardware is not programmable, then EFI_UNSUPPORTED is
178 returned. If the timer is programmable, then the timer period
179 will be rounded up to the nearest timer period that is supported
180 by the timer hardware. If TimerPeriod is set to 0, then the
181 timer interrupts will be disabled.
182
183 Returns:
184
185 EFI_SUCCESS - The timer period was changed.
186
187 EFI_UNSUPPORTED - The platform cannot change the period of the timer interrupt.
188
189 EFI_DEVICE_ERROR - The timer period could not be changed due to a device error.
190
191 **/
192 {
193
194 //
195 // If TimerPeriod is 0, then the timer thread should be canceled
196 // If the TimerPeriod is valid, then create and/or adjust the period of the timer thread
197 //
198 if (TimerPeriod == 0
199 || ((TimerPeriod > TIMER_MINIMUM_VALUE)
200 && (TimerPeriod < TIMER_MAXIMUM_VALUE))) {
201 mTimerPeriodMs = DivU64x32 (TimerPeriod + 5000, 10000);
202
203 gEmuThunk->SetTimer (mTimerPeriodMs, TimerCallback);
204 }
205
206 return EFI_SUCCESS;
207 }
208
209 EFI_STATUS
210 EFIAPI
211 EmuTimerDriverGetTimerPeriod (
212 IN EFI_TIMER_ARCH_PROTOCOL *This,
213 OUT UINT64 *TimerPeriod
214 )
215 /*++
216
217 Routine Description:
218
219 This function retrieves the period of timer interrupts in 100 ns units,
220 returns that value in TimerPeriod, and returns EFI_SUCCESS. If TimerPeriod
221 is NULL, then EFI_INVALID_PARAMETER is returned. If a TimerPeriod of 0 is
222 returned, then the timer is currently disabled.
223
224 Arguments:
225
226 This - The EFI_TIMER_ARCH_PROTOCOL instance.
227
228 TimerPeriod - A pointer to the timer period to retrieve in 100 ns units. If
229 0 is returned, then the timer is currently disabled.
230
231 Returns:
232
233 EFI_SUCCESS - The timer period was returned in TimerPeriod.
234
235 EFI_INVALID_PARAMETER - TimerPeriod is NULL.
236
237 **/
238 {
239 if (TimerPeriod == NULL) {
240 return EFI_INVALID_PARAMETER;
241 }
242
243 *TimerPeriod = MultU64x32 (mTimerPeriodMs, 10000);
244
245 return EFI_SUCCESS;
246 }
247
248 EFI_STATUS
249 EFIAPI
250 EmuTimerDriverGenerateSoftInterrupt (
251 IN EFI_TIMER_ARCH_PROTOCOL *This
252 )
253 /*++
254
255 Routine Description:
256
257 This function generates a soft timer interrupt. If the platform does not support soft
258 timer interrupts, then EFI_UNSUPPORTED is returned. Otherwise, EFI_SUCCESS is returned.
259 If a handler has been registered through the EFI_TIMER_ARCH_PROTOCOL.RegisterHandler()
260 service, then a soft timer interrupt will be generated. If the timer interrupt is
261 enabled when this service is called, then the registered handler will be invoked. The
262 registered handler should not be able to distinguish a hardware-generated timer
263 interrupt from a software-generated timer interrupt.
264
265 Arguments:
266
267 This - The EFI_TIMER_ARCH_PROTOCOL instance.
268
269 Returns:
270
271 EFI_SUCCESS - The soft timer interrupt was generated.
272
273 EFI_UNSUPPORTED - The platform does not support the generation of soft timer interrupts.
274
275 **/
276 {
277 return EFI_UNSUPPORTED;
278 }
279
280 EFI_STATUS
281 EFIAPI
282 EmuTimerDriverInitialize (
283 IN EFI_HANDLE ImageHandle,
284 IN EFI_SYSTEM_TABLE *SystemTable
285 )
286 /*++
287
288 Routine Description:
289
290 Initialize the Timer Architectural Protocol driver
291
292 Arguments:
293
294 ImageHandle - ImageHandle of the loaded driver
295
296 SystemTable - Pointer to the System Table
297
298 Returns:
299
300 EFI_SUCCESS - Timer Architectural Protocol created
301
302 EFI_OUT_OF_RESOURCES - Not enough resources available to initialize driver.
303
304 EFI_DEVICE_ERROR - A device error occured attempting to initialize the driver.
305
306 **/
307 {
308 EFI_STATUS Status;
309 EFI_HANDLE Handle;
310
311 //
312 // Make sure the Timer Architectural Protocol is not already installed in the system
313 //
314 ASSERT_PROTOCOL_ALREADY_INSTALLED (NULL, &gEfiTimerArchProtocolGuid);
315
316 //
317 // Get the CPU Architectural Protocol instance
318 //
319 Status = gBS->LocateProtocol (&gEfiCpuArchProtocolGuid, NULL, (void *)&mCpu);
320 ASSERT_EFI_ERROR (Status);
321
322 //
323 // Start the timer thread at the default timer period
324 //
325 Status = mTimer.SetTimerPeriod (&mTimer, DEFAULT_TIMER_TICK_DURATION);
326 if (EFI_ERROR (Status)) {
327 return Status;
328 }
329
330 //
331 // Install the Timer Architectural Protocol onto a new handle
332 //
333 Handle = NULL;
334 Status = gBS->InstallProtocolInterface (
335 &Handle,
336 &gEfiTimerArchProtocolGuid,
337 EFI_NATIVE_INTERFACE,
338 &mTimer
339 );
340 if (EFI_ERROR (Status)) {
341 return Status;
342 }
343
344
345 return EFI_SUCCESS;
346 }