1 /** @file
2   Core Timer Services
3 
4 Copyright (c) 2006 - 2013, Intel Corporation. All rights reserved.<BR>
5 This program and the accompanying materials
6 are licensed and made available under the terms and conditions of the BSD License
7 which accompanies this distribution.  The full text of the license may be found at
8 http://opensource.org/licenses/bsd-license.php
9 
10 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
11 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
12 
13 **/
14 
15 
16 #include "DxeMain.h"
17 #include "Event.h"
18 
19 //
20 // Internal data
21 //
22 
23 LIST_ENTRY       mEfiTimerList = INITIALIZE_LIST_HEAD_VARIABLE (mEfiTimerList);
24 EFI_LOCK         mEfiTimerLock = EFI_INITIALIZE_LOCK_VARIABLE (TPL_HIGH_LEVEL - 1);
25 EFI_EVENT        mEfiCheckTimerEvent = NULL;
26 
27 EFI_LOCK         mEfiSystemTimeLock = EFI_INITIALIZE_LOCK_VARIABLE (TPL_HIGH_LEVEL);
28 UINT64           mEfiSystemTime = 0;
29 
30 //
31 // Timer functions
32 //
33 /**
34   Inserts the timer event.
35 
36   @param  Event                  Points to the internal structure of timer event
37                                  to be installed
38 
39 **/
40 VOID
CoreInsertEventTimer(IN IEVENT * Event)41 CoreInsertEventTimer (
42   IN IEVENT   *Event
43   )
44 {
45   UINT64          TriggerTime;
46   LIST_ENTRY      *Link;
47   IEVENT          *Event2;
48 
49   ASSERT_LOCKED (&mEfiTimerLock);
50 
51   //
52   // Get the timer's trigger time
53   //
54   TriggerTime = Event->Timer.TriggerTime;
55 
56   //
57   // Insert the timer into the timer database in assending sorted order
58   //
59   for (Link = mEfiTimerList.ForwardLink; Link != &mEfiTimerList; Link = Link->ForwardLink) {
60     Event2 = CR (Link, IEVENT, Timer.Link, EVENT_SIGNATURE);
61 
62     if (Event2->Timer.TriggerTime > TriggerTime) {
63       break;
64     }
65   }
66 
67   InsertTailList (Link, &Event->Timer.Link);
68 }
69 
70 /**
71   Returns the current system time.
72 
73   @return The current system time
74 
75 **/
76 UINT64
CoreCurrentSystemTime(VOID)77 CoreCurrentSystemTime (
78   VOID
79   )
80 {
81   UINT64          SystemTime;
82 
83   CoreAcquireLock (&mEfiSystemTimeLock);
84   SystemTime = mEfiSystemTime;
85   CoreReleaseLock (&mEfiSystemTimeLock);
86 
87   return SystemTime;
88 }
89 
90 /**
91   Checks the sorted timer list against the current system time.
92   Signals any expired event timer.
93 
94   @param  CheckEvent             Not used
95   @param  Context                Not used
96 
97 **/
98 VOID
99 EFIAPI
CoreCheckTimers(IN EFI_EVENT CheckEvent,IN VOID * Context)100 CoreCheckTimers (
101   IN EFI_EVENT            CheckEvent,
102   IN VOID                 *Context
103   )
104 {
105   UINT64                  SystemTime;
106   IEVENT                  *Event;
107 
108   //
109   // Check the timer database for expired timers
110   //
111   CoreAcquireLock (&mEfiTimerLock);
112   SystemTime = CoreCurrentSystemTime ();
113 
114   while (!IsListEmpty (&mEfiTimerList)) {
115     Event = CR (mEfiTimerList.ForwardLink, IEVENT, Timer.Link, EVENT_SIGNATURE);
116 
117     //
118     // If this timer is not expired, then we're done
119     //
120     if (Event->Timer.TriggerTime > SystemTime) {
121       break;
122     }
123 
124     //
125     // Remove this timer from the timer queue
126     //
127 
128     RemoveEntryList (&Event->Timer.Link);
129     Event->Timer.Link.ForwardLink = NULL;
130 
131     //
132     // Signal it
133     //
134     CoreSignalEvent (Event);
135 
136     //
137     // If this is a periodic timer, set it
138     //
139     if (Event->Timer.Period != 0) {
140       //
141       // Compute the timers new trigger time
142       //
143       Event->Timer.TriggerTime = Event->Timer.TriggerTime + Event->Timer.Period;
144 
145       //
146       // If that's before now, then reset the timer to start from now
147       //
148       if (Event->Timer.TriggerTime <= SystemTime) {
149         Event->Timer.TriggerTime = SystemTime;
150         CoreSignalEvent (mEfiCheckTimerEvent);
151       }
152 
153       //
154       // Add the timer
155       //
156       CoreInsertEventTimer (Event);
157     }
158   }
159 
160   CoreReleaseLock (&mEfiTimerLock);
161 }
162 
163 
164 /**
165   Initializes timer support.
166 
167 **/
168 VOID
CoreInitializeTimer(VOID)169 CoreInitializeTimer (
170   VOID
171   )
172 {
173   EFI_STATUS  Status;
174 
175   Status = CoreCreateEventInternal (
176              EVT_NOTIFY_SIGNAL,
177              TPL_HIGH_LEVEL - 1,
178              CoreCheckTimers,
179              NULL,
180              NULL,
181              &mEfiCheckTimerEvent
182              );
183   ASSERT_EFI_ERROR (Status);
184 }
185 
186 
187 /**
188   Called by the platform code to process a tick.
189 
190   @param  Duration               The number of 100ns elasped since the last call
191                                  to TimerTick
192 
193 **/
194 VOID
195 EFIAPI
CoreTimerTick(IN UINT64 Duration)196 CoreTimerTick (
197   IN UINT64   Duration
198   )
199 {
200   IEVENT          *Event;
201 
202   //
203   // Check runtiem flag in case there are ticks while exiting boot services
204   //
205   CoreAcquireLock (&mEfiSystemTimeLock);
206 
207   //
208   // Update the system time
209   //
210   mEfiSystemTime += Duration;
211 
212   //
213   // If the head of the list is expired, fire the timer event
214   // to process it
215   //
216   if (!IsListEmpty (&mEfiTimerList)) {
217     Event = CR (mEfiTimerList.ForwardLink, IEVENT, Timer.Link, EVENT_SIGNATURE);
218 
219     if (Event->Timer.TriggerTime <= mEfiSystemTime) {
220       CoreSignalEvent (mEfiCheckTimerEvent);
221     }
222   }
223 
224   CoreReleaseLock (&mEfiSystemTimeLock);
225 }
226 
227 
228 
229 /**
230   Sets the type of timer and the trigger time for a timer event.
231 
232   @param  UserEvent              The timer event that is to be signaled at the
233                                  specified time
234   @param  Type                   The type of time that is specified in
235                                  TriggerTime
236   @param  TriggerTime            The number of 100ns units until the timer
237                                  expires
238 
239   @retval EFI_SUCCESS            The event has been set to be signaled at the
240                                  requested time
241   @retval EFI_INVALID_PARAMETER  Event or Type is not valid
242 
243 **/
244 EFI_STATUS
245 EFIAPI
CoreSetTimer(IN EFI_EVENT UserEvent,IN EFI_TIMER_DELAY Type,IN UINT64 TriggerTime)246 CoreSetTimer (
247   IN EFI_EVENT            UserEvent,
248   IN EFI_TIMER_DELAY      Type,
249   IN UINT64               TriggerTime
250   )
251 {
252   IEVENT      *Event;
253 
254   Event = UserEvent;
255 
256   if (Event == NULL) {
257     return EFI_INVALID_PARAMETER;
258   }
259 
260   if (Event->Signature != EVENT_SIGNATURE) {
261     return EFI_INVALID_PARAMETER;
262   }
263 
264   if ((UINT32)Type > TimerRelative  || (Event->Type & EVT_TIMER) == 0) {
265     return EFI_INVALID_PARAMETER;
266   }
267 
268   CoreAcquireLock (&mEfiTimerLock);
269 
270   //
271   // If the timer is queued to the timer database, remove it
272   //
273   if (Event->Timer.Link.ForwardLink != NULL) {
274     RemoveEntryList (&Event->Timer.Link);
275     Event->Timer.Link.ForwardLink = NULL;
276   }
277 
278   Event->Timer.TriggerTime = 0;
279   Event->Timer.Period = 0;
280 
281   if (Type != TimerCancel) {
282 
283     if (Type == TimerPeriodic) {
284       if (TriggerTime == 0) {
285         gTimer->GetTimerPeriod (gTimer, &TriggerTime);
286       }
287       Event->Timer.Period = TriggerTime;
288     }
289 
290     Event->Timer.TriggerTime = CoreCurrentSystemTime () + TriggerTime;
291     CoreInsertEventTimer (Event);
292 
293     if (TriggerTime == 0) {
294       CoreSignalEvent (mEfiCheckTimerEvent);
295     }
296   }
297 
298   CoreReleaseLock (&mEfiTimerLock);
299 
300   return EFI_SUCCESS;
301 }
302