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
;
59 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
);
80 EmuTimerDriverRegisterHandler (
81 IN EFI_TIMER_ARCH_PROTOCOL
*This
,
82 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
);
146 mTimerNotifyFunction
= NotifyFunction
;
153 EmuTimerDriverSetTimerPeriod (
154 IN EFI_TIMER_ARCH_PROTOCOL
*This
,
155 IN UINT64 TimerPeriod
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
176 This - The EFI_TIMER_ARCH_PROTOCOL instance.
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.
187 EFI_SUCCESS - The timer period was changed.
189 EFI_UNSUPPORTED - The platform cannot change the period of the timer interrupt.
191 EFI_DEVICE_ERROR - The timer period could not be changed due to a device error.
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
199 if ( (TimerPeriod
== 0)
200 || ( (TimerPeriod
> TIMER_MINIMUM_VALUE
)
201 && (TimerPeriod
< TIMER_MAXIMUM_VALUE
)))
203 mTimerPeriodMs
= DivU64x32 (TimerPeriod
+ 5000, 10000);
205 gEmuThunk
->SetTimer (mTimerPeriodMs
, TimerCallback
);
213 EmuTimerDriverGetTimerPeriod (
214 IN EFI_TIMER_ARCH_PROTOCOL
*This
,
215 OUT UINT64
*TimerPeriod
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.
229 This - The EFI_TIMER_ARCH_PROTOCOL instance.
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.
236 EFI_SUCCESS - The timer period was returned in TimerPeriod.
238 EFI_INVALID_PARAMETER - TimerPeriod is NULL.
242 if (TimerPeriod
== NULL
) {
243 return EFI_INVALID_PARAMETER
;
246 *TimerPeriod
= MultU64x32 (mTimerPeriodMs
, 10000);
253 EmuTimerDriverGenerateSoftInterrupt (
254 IN EFI_TIMER_ARCH_PROTOCOL
*This
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.
271 This - The EFI_TIMER_ARCH_PROTOCOL instance.
275 EFI_SUCCESS - The soft timer interrupt was generated.
277 EFI_UNSUPPORTED - The platform does not support the generation of soft timer interrupts.
281 return EFI_UNSUPPORTED
;
286 EmuTimerDriverInitialize (
287 IN EFI_HANDLE ImageHandle
,
288 IN EFI_SYSTEM_TABLE
*SystemTable
295 Initialize the Timer Architectural Protocol driver
299 ImageHandle - ImageHandle of the loaded driver
301 SystemTable - Pointer to the System Table
305 EFI_SUCCESS - Timer Architectural Protocol created
307 EFI_OUT_OF_RESOURCES - Not enough resources available to initialize driver.
309 EFI_DEVICE_ERROR - A device error occurred attempting to initialize the driver.
317 // Make sure the Timer Architectural Protocol is not already installed in the system
319 ASSERT_PROTOCOL_ALREADY_INSTALLED (NULL
, &gEfiTimerArchProtocolGuid
);
322 // Get the CPU Architectural Protocol instance
324 Status
= gBS
->LocateProtocol (&gEfiCpuArchProtocolGuid
, NULL
, (void *)&mCpu
);
325 ASSERT_EFI_ERROR (Status
);
328 // Start the timer thread at the default timer period
330 Status
= mTimer
.SetTimerPeriod (&mTimer
, DEFAULT_TIMER_TICK_DURATION
);
331 if (EFI_ERROR (Status
)) {
336 // Install the Timer Architectural Protocol onto a new handle
339 Status
= gBS
->InstallProtocolInterface (
341 &gEfiTimerArchProtocolGuid
,
342 EFI_NATIVE_INTERFACE
,
345 if (EFI_ERROR (Status
)) {