2 Emu Emulation Timer Architectural Protocol Driver as defined in DXE CIS
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
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
16 #include <Protocol/Timer.h>
17 #include <Protocol/Cpu.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>
28 // Pointer to the CPU Architectural Protocol instance
30 EFI_CPU_ARCH_PROTOCOL
*mCpu
;
33 // The Timer Architectural Protocol that this driver produces
35 EFI_TIMER_ARCH_PROTOCOL mTimer
= {
36 EmuTimerDriverRegisterHandler
,
37 EmuTimerDriverSetTimerPeriod
,
38 EmuTimerDriverGetTimerPeriod
,
39 EmuTimerDriverGenerateSoftInterrupt
43 // The notification function to call on every timer interrupt
45 EFI_TIMER_NOTIFY mTimerNotifyFunction
= NULL
;
48 // The current period of the timer interrupt
50 UINT64 mTimerPeriodMs
;
55 TimerCallback (UINT64 DeltaMs
)
58 EFI_TIMER_NOTIFY CallbackFunction
;
61 OriginalTPL
= gBS
->RaiseTPL (TPL_HIGH_LEVEL
);
63 if (OriginalTPL
< TPL_HIGH_LEVEL
) {
64 CallbackFunction
= mTimerNotifyFunction
;
67 // Only invoke the callback function if a Non-NULL handler has been
68 // registered. Assume all other handlers are legal.
70 if (CallbackFunction
!= NULL
) {
71 CallbackFunction (MultU64x32 (DeltaMs
, 10000));
75 gBS
->RestoreTPL (OriginalTPL
);
81 EmuTimerDriverRegisterHandler (
82 IN EFI_TIMER_ARCH_PROTOCOL
*This
,
83 IN EFI_TIMER_NOTIFY NotifyFunction
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
103 This - The EFI_TIMER_ARCH_PROTOCOL instance.
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.
113 EFI_SUCCESS - The timer handler was registered.
115 EFI_UNSUPPORTED - The platform does not support timer interrupts.
117 EFI_ALREADY_STARTED - NotifyFunction is not NULL, and a handler is already
120 EFI_INVALID_PARAMETER - NotifyFunction is NULL, and a handler was not
121 previously registered.
123 EFI_DEVICE_ERROR - The timer handler could not be registered.
128 // Check for invalid parameters
130 if (NotifyFunction
== NULL
&& mTimerNotifyFunction
== NULL
) {
131 return EFI_INVALID_PARAMETER
;
134 if (NotifyFunction
!= NULL
&& mTimerNotifyFunction
!= NULL
) {
135 return EFI_ALREADY_STARTED
;
138 if (NotifyFunction
== NULL
) {
140 gEmuThunk
->SetTimer (0, TimerCallback
);
141 } else if (mTimerNotifyFunction
== NULL
) {
143 gEmuThunk
->SetTimer (mTimerPeriodMs
, TimerCallback
);
145 mTimerNotifyFunction
= NotifyFunction
;
152 EmuTimerDriverSetTimerPeriod (
153 IN EFI_TIMER_ARCH_PROTOCOL
*This
,
154 IN UINT64 TimerPeriod
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
174 This - The EFI_TIMER_ARCH_PROTOCOL instance.
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.
185 EFI_SUCCESS - The timer period was changed.
187 EFI_UNSUPPORTED - The platform cannot change the period of the timer interrupt.
189 EFI_DEVICE_ERROR - The timer period could not be changed due to a device error.
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
199 || ((TimerPeriod
> TIMER_MINIMUM_VALUE
)
200 && (TimerPeriod
< TIMER_MAXIMUM_VALUE
))) {
201 mTimerPeriodMs
= DivU64x32 (TimerPeriod
+ 5000, 10000);
203 gEmuThunk
->SetTimer (mTimerPeriodMs
, TimerCallback
);
211 EmuTimerDriverGetTimerPeriod (
212 IN EFI_TIMER_ARCH_PROTOCOL
*This
,
213 OUT UINT64
*TimerPeriod
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.
226 This - The EFI_TIMER_ARCH_PROTOCOL instance.
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.
233 EFI_SUCCESS - The timer period was returned in TimerPeriod.
235 EFI_INVALID_PARAMETER - TimerPeriod is NULL.
239 if (TimerPeriod
== NULL
) {
240 return EFI_INVALID_PARAMETER
;
243 *TimerPeriod
= MultU64x32 (mTimerPeriodMs
, 10000);
250 EmuTimerDriverGenerateSoftInterrupt (
251 IN EFI_TIMER_ARCH_PROTOCOL
*This
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.
267 This - The EFI_TIMER_ARCH_PROTOCOL instance.
271 EFI_SUCCESS - The soft timer interrupt was generated.
273 EFI_UNSUPPORTED - The platform does not support the generation of soft timer interrupts.
277 return EFI_UNSUPPORTED
;
282 EmuTimerDriverInitialize (
283 IN EFI_HANDLE ImageHandle
,
284 IN EFI_SYSTEM_TABLE
*SystemTable
290 Initialize the Timer Architectural Protocol driver
294 ImageHandle - ImageHandle of the loaded driver
296 SystemTable - Pointer to the System Table
300 EFI_SUCCESS - Timer Architectural Protocol created
302 EFI_OUT_OF_RESOURCES - Not enough resources available to initialize driver.
304 EFI_DEVICE_ERROR - A device error occured attempting to initialize the driver.
312 // Make sure the Timer Architectural Protocol is not already installed in the system
314 ASSERT_PROTOCOL_ALREADY_INSTALLED (NULL
, &gEfiTimerArchProtocolGuid
);
317 // Get the CPU Architectural Protocol instance
319 Status
= gBS
->LocateProtocol (&gEfiCpuArchProtocolGuid
, NULL
, (void *)&mCpu
);
320 ASSERT_EFI_ERROR (Status
);
323 // Start the timer thread at the default timer period
325 Status
= mTimer
.SetTimerPeriod (&mTimer
, DEFAULT_TIMER_TICK_DURATION
);
326 if (EFI_ERROR (Status
)) {
331 // Install the Timer Architectural Protocol onto a new handle
334 Status
= gBS
->InstallProtocolInterface (
336 &gEfiTimerArchProtocolGuid
,
337 EFI_NATIVE_INTERFACE
,
340 if (EFI_ERROR (Status
)) {