• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /**@file
2 
3 Copyright (c) 2006, Intel Corporation. All rights reserved.<BR>
4 This program and the accompanying materials
5 are licensed and made available under the terms and conditions of the BSD License
6 which accompanies this distribution.  The full text of the license may be found at
7 http://opensource.org/licenses/bsd-license.php
8 
9 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
10 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
11 
12 Module Name:
13 
14   Timer.c
15 
16 Abstract:
17 
18   NT Emulation Timer Architectural Protocol Driver as defined in DXE CIS
19 
20   This Timer module uses an NT Thread to simulate the timer-tick driven
21   timer service.  In the future, the Thread creation should possibly be
22   abstracted by the CPU architectural protocol
23 
24 **/
25 
26 #include "Timer.h"
27 
28 //
29 // Pointer to the CPU Architectural Protocol instance
30 //
31 EFI_CPU_ARCH_PROTOCOL   *mCpu;
32 
33 //
34 // The Timer Architectural Protocol that this driver produces
35 //
36 EFI_TIMER_ARCH_PROTOCOL mTimer = {
37   WinNtTimerDriverRegisterHandler,
38   WinNtTimerDriverSetTimerPeriod,
39   WinNtTimerDriverGetTimerPeriod,
40   WinNtTimerDriverGenerateSoftInterrupt
41 };
42 
43 //
44 // Define a global that we can use to shut down the NT timer thread when
45 // the timer is canceled.
46 //
47 BOOLEAN                 mCancelTimerThread = FALSE;
48 
49 //
50 // The notification function to call on every timer interrupt
51 //
52 EFI_TIMER_NOTIFY        mTimerNotifyFunction = NULL;
53 
54 //
55 // The current period of the timer interrupt
56 //
57 UINT64                  mTimerPeriod;
58 
59 //
60 // The thread handle for this driver
61 //
62 HANDLE                  mNtMainThreadHandle;
63 
64 //
65 // The timer value from the last timer interrupt
66 //
67 UINT32                  mNtLastTick;
68 
69 //
70 // Critical section used to update varibles shared between the main thread and
71 // the timer interrupt thread.
72 //
73 CRITICAL_SECTION        mNtCriticalSection;
74 
75 //
76 // Worker Functions
77 //
78 UINT                    mMMTimerThreadID = 0;
79 
80 VOID
81 CALLBACK
MMTimerThread(UINT wTimerID,UINT msg,DWORD dwUser,DWORD dw1,DWORD dw2)82 MMTimerThread (
83   UINT  wTimerID,
84   UINT  msg,
85   DWORD dwUser,
86   DWORD dw1,
87   DWORD dw2
88   )
89 /*++
90 
91 Routine Description:
92 
93   TODO: Add function description
94 
95 Arguments:
96 
97   wTimerID  - TODO: add argument description
98   msg       - TODO: add argument description
99   dwUser    - TODO: add argument description
100   dw1       - TODO: add argument description
101   dw2       - TODO: add argument description
102 
103 Returns:
104 
105   TODO: add return values
106 
107 --*/
108 {
109   EFI_TPL           OriginalTPL;
110   UINT32            CurrentTick;
111   UINT32            Delta;
112   EFI_TIMER_NOTIFY  CallbackFunction;
113   BOOLEAN           InterruptState;
114 
115   if (!mCancelTimerThread) {
116 
117     //
118     // Suspend the main thread until we are done.
119     // Enter the critical section before suspending
120     // and leave the critical section after resuming
121     // to avoid deadlock between main and timer thread.
122     //
123     gWinNt->EnterCriticalSection (&mNtCriticalSection);
124     gWinNt->SuspendThread (mNtMainThreadHandle);
125 
126     //
127     // If the timer thread is being canceled, then bail immediately.
128     // We check again here because there's a small window of time from when
129     // this thread was kicked off and when we suspended the main thread above.
130     //
131     if (mCancelTimerThread) {
132       gWinNt->ResumeThread (mNtMainThreadHandle);
133       gWinNt->LeaveCriticalSection (&mNtCriticalSection);
134       gWinNt->timeKillEvent (wTimerID);
135       mMMTimerThreadID = 0;
136       return ;
137     }
138 
139     mCpu->GetInterruptState (mCpu, &InterruptState);
140     while (!InterruptState) {
141       //
142       //  Resume the main thread
143       //
144       gWinNt->ResumeThread (mNtMainThreadHandle);
145       gWinNt->LeaveCriticalSection (&mNtCriticalSection);
146 
147       //
148       //  Wait for interrupts to be enabled.
149       //
150       mCpu->GetInterruptState (mCpu, &InterruptState);
151       while (!InterruptState) {
152         gWinNt->Sleep (1);
153         mCpu->GetInterruptState (mCpu, &InterruptState);
154       }
155 
156       //
157       //  Suspend the main thread until we are done
158       //
159       gWinNt->EnterCriticalSection (&mNtCriticalSection);
160       gWinNt->SuspendThread (mNtMainThreadHandle);
161       mCpu->GetInterruptState (mCpu, &InterruptState);
162     }
163 
164     //
165     //  Get the current system tick
166     //
167     CurrentTick = gWinNt->GetTickCount ();
168     Delta       = CurrentTick - mNtLastTick;
169     mNtLastTick = CurrentTick;
170 
171     //
172     //  If delay was more then 1 second, ignore it (probably debugging case)
173     //
174     if (Delta < 1000) {
175 
176       OriginalTPL = gBS->RaiseTPL (TPL_HIGH_LEVEL);
177 
178       //
179       //  Inform the firmware of an "timer interrupt".  The time
180       //  expired since the last call is 10,000 times the number
181       //  of ms.  (or 100ns units)
182       //
183       CallbackFunction = mTimerNotifyFunction;
184 
185       //
186       // Only invoke the callback function if a Non-NULL handler has been
187       // registered. Assume all other handlers are legal.
188       //
189       if (CallbackFunction != NULL) {
190         CallbackFunction ((UINT64) (Delta * 10000));
191       }
192 
193       gBS->RestoreTPL (OriginalTPL);
194 
195     }
196 
197     //
198     //  Resume the main thread
199     //
200     gWinNt->ResumeThread (mNtMainThreadHandle);
201     gWinNt->LeaveCriticalSection (&mNtCriticalSection);
202   } else {
203     gWinNt->timeKillEvent (wTimerID);
204     mMMTimerThreadID = 0;
205   }
206 
207 }
208 
209 UINT
CreateNtTimer(VOID)210 CreateNtTimer (
211   VOID
212   )
213 /*++
214 
215 Routine Description:
216 
217    It is used to emulate a platform
218   timer-driver interrupt handler.
219 
220 Returns:
221 
222   Timer ID
223 
224 --*/
225 // TODO: function comment is missing 'Arguments:'
226 {
227   UINT32  SleepCount;
228 
229   //
230   //  Set our thread priority higher than the "main" thread.
231   //
232   gWinNt->SetThreadPriority (
233             gWinNt->GetCurrentThread (),
234             THREAD_PRIORITY_HIGHEST
235             );
236 
237   //
238   //  Calc the appropriate interval
239   //
240   gWinNt->EnterCriticalSection (&mNtCriticalSection);
241   SleepCount = (UINT32) (mTimerPeriod + 5000) / 10000;
242   gWinNt->LeaveCriticalSection (&mNtCriticalSection);
243 
244   return gWinNt->timeSetEvent (
245                   SleepCount,
246                   0,
247                   MMTimerThread,
248                   (DWORD_PTR) NULL,
249                   TIME_PERIODIC | TIME_KILL_SYNCHRONOUS | TIME_CALLBACK_FUNCTION
250                   );
251 
252 }
253 
254 EFI_STATUS
255 EFIAPI
WinNtTimerDriverRegisterHandler(IN EFI_TIMER_ARCH_PROTOCOL * This,IN EFI_TIMER_NOTIFY NotifyFunction)256 WinNtTimerDriverRegisterHandler (
257   IN EFI_TIMER_ARCH_PROTOCOL           *This,
258   IN EFI_TIMER_NOTIFY                  NotifyFunction
259   )
260 /*++
261 
262 Routine Description:
263 
264   This function registers the handler NotifyFunction so it is called every time
265   the timer interrupt fires.  It also passes the amount of time since the last
266   handler call to the NotifyFunction.  If NotifyFunction is NULL, then the
267   handler is unregistered.  If the handler is registered, then EFI_SUCCESS is
268   returned.  If the CPU does not support registering a timer interrupt handler,
269   then EFI_UNSUPPORTED is returned.  If an attempt is made to register a handler
270   when a handler is already registered, then EFI_ALREADY_STARTED is returned.
271   If an attempt is made to unregister a handler when a handler is not registered,
272   then EFI_INVALID_PARAMETER is returned.  If an error occurs attempting to
273   register the NotifyFunction with the timer interrupt, then EFI_DEVICE_ERROR
274   is returned.
275 
276 Arguments:
277 
278   This           - The EFI_TIMER_ARCH_PROTOCOL instance.
279 
280   NotifyFunction - The function to call when a timer interrupt fires.  This
281                    function executes at TPL_HIGH_LEVEL.  The DXE Core will
282                    register a handler for the timer interrupt, so it can know
283                    how much time has passed.  This information is used to
284                    signal timer based events.  NULL will unregister the handler.
285 
286 Returns:
287 
288   EFI_SUCCESS           - The timer handler was registered.
289 
290   EFI_UNSUPPORTED       - The platform does not support timer interrupts.
291 
292   EFI_ALREADY_STARTED   - NotifyFunction is not NULL, and a handler is already
293                           registered.
294 
295   EFI_INVALID_PARAMETER - NotifyFunction is NULL, and a handler was not
296                           previously registered.
297 
298   EFI_DEVICE_ERROR      - The timer handler could not be registered.
299 
300 --*/
301 {
302   //
303   // Check for invalid parameters
304   //
305   if (NotifyFunction == NULL && mTimerNotifyFunction == NULL) {
306     return EFI_INVALID_PARAMETER;
307   }
308 
309   if (NotifyFunction != NULL && mTimerNotifyFunction != NULL) {
310     return EFI_ALREADY_STARTED;
311   }
312 
313   //
314   // Use Critical Section to update the notification function that is
315   // used from the timer interrupt thread.
316   //
317   gWinNt->EnterCriticalSection (&mNtCriticalSection);
318 
319   mTimerNotifyFunction = NotifyFunction;
320 
321   gWinNt->LeaveCriticalSection (&mNtCriticalSection);
322 
323   return EFI_SUCCESS;
324 }
325 
326 EFI_STATUS
327 EFIAPI
WinNtTimerDriverSetTimerPeriod(IN EFI_TIMER_ARCH_PROTOCOL * This,IN UINT64 TimerPeriod)328 WinNtTimerDriverSetTimerPeriod (
329   IN EFI_TIMER_ARCH_PROTOCOL  *This,
330   IN UINT64                   TimerPeriod
331   )
332 /*++
333 
334 Routine Description:
335 
336   This function adjusts the period of timer interrupts to the value specified
337   by TimerPeriod.  If the timer period is updated, then the selected timer
338   period is stored in EFI_TIMER.TimerPeriod, and EFI_SUCCESS is returned.  If
339   the timer hardware is not programmable, then EFI_UNSUPPORTED is returned.
340   If an error occurs while attempting to update the timer period, then the
341   timer hardware will be put back in its state prior to this call, and
342   EFI_DEVICE_ERROR is returned.  If TimerPeriod is 0, then the timer interrupt
343   is disabled.  This is not the same as disabling the CPU's interrupts.
344   Instead, it must either turn off the timer hardware, or it must adjust the
345   interrupt controller so that a CPU interrupt is not generated when the timer
346   interrupt fires.
347 
348 Arguments:
349 
350   This        - The EFI_TIMER_ARCH_PROTOCOL instance.
351 
352   TimerPeriod - The rate to program the timer interrupt in 100 nS units.  If
353                 the timer hardware is not programmable, then EFI_UNSUPPORTED is
354                 returned.  If the timer is programmable, then the timer period
355                 will be rounded up to the nearest timer period that is supported
356                 by the timer hardware.  If TimerPeriod is set to 0, then the
357                 timer interrupts will be disabled.
358 
359 Returns:
360 
361   EFI_SUCCESS      - The timer period was changed.
362 
363   EFI_UNSUPPORTED  - The platform cannot change the period of the timer interrupt.
364 
365   EFI_DEVICE_ERROR - The timer period could not be changed due to a device error.
366 
367 --*/
368 {
369 
370   //
371   // If TimerPeriod is 0, then the timer thread should be canceled
372   //
373   if (TimerPeriod == 0) {
374     //
375     // Cancel the timer thread
376     //
377     gWinNt->EnterCriticalSection (&mNtCriticalSection);
378 
379     mCancelTimerThread = TRUE;
380 
381     gWinNt->LeaveCriticalSection (&mNtCriticalSection);
382 
383     //
384     // Wait for the timer thread to exit
385     //
386 
387     if (mMMTimerThreadID) {
388       gWinNt->timeKillEvent (mMMTimerThreadID);
389     }
390 
391     mMMTimerThreadID = 0;
392 
393     //
394     // Update the timer period
395     //
396     gWinNt->EnterCriticalSection (&mNtCriticalSection);
397 
398     mTimerPeriod = TimerPeriod;
399 
400     gWinNt->LeaveCriticalSection (&mNtCriticalSection);
401 
402     //
403     // NULL out the thread handle so it will be re-created if the timer is enabled again
404     //
405 
406   } else if ((TimerPeriod > TIMER_MINIMUM_VALUE) && (TimerPeriod < TIMER_MAXIMUM_VALUE)) {
407     //
408     // If the TimerPeriod is valid, then create and/or adjust the period of the timer thread
409     //
410     gWinNt->EnterCriticalSection (&mNtCriticalSection);
411 
412     mTimerPeriod        = TimerPeriod;
413 
414     mCancelTimerThread  = FALSE;
415 
416     gWinNt->LeaveCriticalSection (&mNtCriticalSection);
417 
418     //
419     //  Get the starting tick location if we are just starting the timer thread
420     //
421     mNtLastTick = gWinNt->GetTickCount ();
422 
423     if (mMMTimerThreadID) {
424       gWinNt->timeKillEvent (mMMTimerThreadID);
425     }
426 
427     mMMTimerThreadID  = 0;
428 
429     mMMTimerThreadID  = CreateNtTimer ();
430 
431   }
432 
433   return EFI_SUCCESS;
434 }
435 
436 EFI_STATUS
437 EFIAPI
WinNtTimerDriverGetTimerPeriod(IN EFI_TIMER_ARCH_PROTOCOL * This,OUT UINT64 * TimerPeriod)438 WinNtTimerDriverGetTimerPeriod (
439   IN EFI_TIMER_ARCH_PROTOCOL            *This,
440   OUT UINT64                            *TimerPeriod
441   )
442 /*++
443 
444 Routine Description:
445 
446   This function retrieves the period of timer interrupts in 100 ns units,
447   returns that value in TimerPeriod, and returns EFI_SUCCESS.  If TimerPeriod
448   is NULL, then EFI_INVALID_PARAMETER is returned.  If a TimerPeriod of 0 is
449   returned, then the timer is currently disabled.
450 
451 Arguments:
452 
453   This        - The EFI_TIMER_ARCH_PROTOCOL instance.
454 
455   TimerPeriod - A pointer to the timer period to retrieve in 100 ns units.  If
456                 0 is returned, then the timer is currently disabled.
457 
458 Returns:
459 
460   EFI_SUCCESS           - The timer period was returned in TimerPeriod.
461 
462   EFI_INVALID_PARAMETER - TimerPeriod is NULL.
463 
464 --*/
465 {
466   if (TimerPeriod == NULL) {
467     return EFI_INVALID_PARAMETER;
468   }
469 
470   *TimerPeriod = mTimerPeriod;
471 
472   return EFI_SUCCESS;
473 }
474 
475 EFI_STATUS
476 EFIAPI
WinNtTimerDriverGenerateSoftInterrupt(IN EFI_TIMER_ARCH_PROTOCOL * This)477 WinNtTimerDriverGenerateSoftInterrupt (
478   IN EFI_TIMER_ARCH_PROTOCOL  *This
479   )
480 /*++
481 
482 Routine Description:
483 
484   This function generates a soft timer interrupt. If the platform does not support soft
485   timer interrupts, then EFI_UNSUPPORTED is returned. Otherwise, EFI_SUCCESS is returned.
486   If a handler has been registered through the EFI_TIMER_ARCH_PROTOCOL.RegisterHandler()
487   service, then a soft timer interrupt will be generated. If the timer interrupt is
488   enabled when this service is called, then the registered handler will be invoked. The
489   registered handler should not be able to distinguish a hardware-generated timer
490   interrupt from a software-generated timer interrupt.
491 
492 Arguments:
493 
494   This  -  The EFI_TIMER_ARCH_PROTOCOL instance.
495 
496 Returns:
497 
498   EFI_SUCCESS       - The soft timer interrupt was generated.
499 
500   EFI_UNSUPPORTEDT  - The platform does not support the generation of soft timer interrupts.
501 
502 --*/
503 {
504   return EFI_UNSUPPORTED;
505 }
506 
507 
508 EFI_STATUS
509 EFIAPI
WinNtTimerDriverInitialize(IN EFI_HANDLE ImageHandle,IN EFI_SYSTEM_TABLE * SystemTable)510 WinNtTimerDriverInitialize (
511   IN EFI_HANDLE        ImageHandle,
512   IN EFI_SYSTEM_TABLE  *SystemTable
513   )
514 /*++
515 
516 Routine Description:
517 
518   Initialize the Timer Architectural Protocol driver
519 
520 Arguments:
521 
522   ImageHandle - ImageHandle of the loaded driver
523 
524   SystemTable - Pointer to the System Table
525 
526 Returns:
527 
528   EFI_SUCCESS           - Timer Architectural Protocol created
529 
530   EFI_OUT_OF_RESOURCES  - Not enough resources available to initialize driver.
531 
532   EFI_DEVICE_ERROR      - A device error occured attempting to initialize the driver.
533 
534 --*/
535 {
536   EFI_STATUS  Status;
537   UINTN       Result;
538   EFI_HANDLE  Handle;
539   EFI_HANDLE  hSourceProcessHandle;
540   EFI_HANDLE  hSourceHandle;
541   EFI_HANDLE  hTargetProcessHandle;
542   //
543   // Make sure the Timer Architectural Protocol is not already installed in the system
544   //
545   ASSERT_PROTOCOL_ALREADY_INSTALLED (NULL, &gEfiTimerArchProtocolGuid);
546 
547   //
548   // Get the CPU Architectural Protocol instance
549   //
550   Status = gBS->LocateProtocol (&gEfiCpuArchProtocolGuid, NULL, (VOID**)&mCpu);
551   ASSERT_EFI_ERROR (Status);
552 
553   //
554   //  Get our handle so the timer tick thread can suspend
555   //
556   hSourceProcessHandle = gWinNt->GetCurrentProcess ();
557   hSourceHandle        = gWinNt->GetCurrentThread ();
558   hTargetProcessHandle = gWinNt->GetCurrentProcess ();
559   Result = gWinNt->DuplicateHandle (
560                     hSourceProcessHandle,
561                     hSourceHandle,
562                     hTargetProcessHandle,
563                     &mNtMainThreadHandle,
564                     0,
565                     FALSE,
566                     DUPLICATE_SAME_ACCESS
567                     );
568   if (Result == 0) {
569     return EFI_DEVICE_ERROR;
570   }
571 
572   //
573   // Initialize Critical Section used to update variables shared between the main
574   // thread and the timer interrupt thread.
575   //
576   gWinNt->InitializeCriticalSection (&mNtCriticalSection);
577 
578   //
579   // Start the timer thread at the default timer period
580   //
581   Status = mTimer.SetTimerPeriod (&mTimer, DEFAULT_TIMER_TICK_DURATION);
582   if (EFI_ERROR (Status)) {
583     gWinNt->DeleteCriticalSection (&mNtCriticalSection);
584     return Status;
585   }
586 
587   //
588   // Install the Timer Architectural Protocol onto a new handle
589   //
590   Handle = NULL;
591   Status = gBS->InstallProtocolInterface (
592                   &Handle,
593                   &gEfiTimerArchProtocolGuid,
594                   EFI_NATIVE_INTERFACE,
595                   &mTimer
596                   );
597   if (EFI_ERROR (Status)) {
598     //
599     // Cancel the timer
600     //
601     mTimer.SetTimerPeriod (&mTimer, 0);
602     gWinNt->DeleteCriticalSection (&mNtCriticalSection);
603     return Status;
604   }
605 
606   return EFI_SUCCESS;
607 }
608