TianoCore EDK2 master
Loading...
Searching...
No Matches
GenericWatchdogDxe.c
Go to the documentation of this file.
1
10#include <PiDxe.h>
11
12#include <Library/BaseLib.h>
14#include <Library/DebugLib.h>
15#include <Library/IoLib.h>
16#include <Library/PcdLib.h>
19#include <Library/UefiLib.h>
21
24
25#include "GenericWatchdog.h"
26
27/* The number of 100ns periods (the unit of time passed to these functions)
28 in a second */
29#define TIME_UNITS_PER_SECOND 10000000
30
31/* In cases where the compare register was set manually, information about
32 how long the watchdog was asked to wait cannot be retrieved from hardware.
33 It is therefore stored here. 0 means the timer is not running. */
34STATIC UINT64 mTimerPeriod = 0;
35
36/* disables watchdog interaction after Exit Boot Services */
37STATIC BOOLEAN mExitedBootServices = FALSE;
38
39#define MAX_UINT48 0xFFFFFFFFFFFFULL
40
42STATIC EFI_WATCHDOG_TIMER_NOTIFY mWatchdogNotify;
43STATIC EFI_EVENT mEfiExitBootServicesEvent;
44
52UINT64
54 VOID
55 )
56{
57 UINT64 MaxWatchdogOffsetValue;
58 UINT32 WatchdogIId;
59 UINT8 WatchdogArchRevision;
60
61 WatchdogIId = MmioRead32 (GENERIC_WDOG_IID_REG);
62 WatchdogArchRevision = (WatchdogIId >> GENERIC_WDOG_IID_ARCH_REV_SHIFT) & GENERIC_WDOG_IID_ARCH_REV_MASK;
63
64 if (WatchdogArchRevision == 0) {
65 MaxWatchdogOffsetValue = MAX_UINT32;
66 } else {
67 MaxWatchdogOffsetValue = MAX_UINT48;
68 }
69
70 return MaxWatchdogOffsetValue;
71}
72
74VOID
75WatchdogWriteOffsetRegister (
76 UINT64 Value
77 )
78{
79 MmioWrite32 (GENERIC_WDOG_OFFSET_REG_LOW, Value & MAX_UINT32);
80 if (GetMaxWatchdogOffsetRegisterValue () == MAX_UINT48) {
81 MmioWrite32 (GENERIC_WDOG_OFFSET_REG_HIGH, (Value >> 32) & MAX_UINT16);
82 }
83}
84
86VOID
87WatchdogWriteCompareRegister (
88 UINT64 Value
89 )
90{
91 MmioWrite32 (GENERIC_WDOG_COMPARE_VALUE_REG_LOW, Value & MAX_UINT32);
92 MmioWrite32 (GENERIC_WDOG_COMPARE_VALUE_REG_HIGH, (Value >> 32) & MAX_UINT32);
93}
94
96VOID
97WatchdogEnable (
98 VOID
99 )
100{
101 MmioWrite32 (GENERIC_WDOG_CONTROL_STATUS_REG, GENERIC_WDOG_ENABLED);
102}
103
104STATIC
105VOID
106WatchdogDisable (
107 VOID
108 )
109{
110 MmioWrite32 (GENERIC_WDOG_CONTROL_STATUS_REG, GENERIC_WDOG_DISABLED);
111}
112
116STATIC
117VOID
118EFIAPI
120 IN EFI_EVENT Event,
121 IN VOID *Context
122 )
123{
124 WatchdogDisable ();
125 mTimerPeriod = 0;
126 mExitedBootServices = TRUE;
127}
128
129/* This function is called when the watchdog's first signal (WS0) goes high.
130 It uses the ResetSystem Runtime Service to reset the board.
131*/
132STATIC
133VOID
134EFIAPI
135WatchdogInterruptHandler (
136 IN HARDWARE_INTERRUPT_SOURCE Source,
137 IN EFI_SYSTEM_CONTEXT SystemContext
138 )
139{
140 STATIC CONST CHAR16 ResetString[] = L"The generic watchdog timer ran out.";
141
142 WatchdogDisable ();
143
144 mInterruptProtocol->EndOfInterrupt (mInterruptProtocol, Source);
145
146 //
147 // The notify function should be called with the elapsed number of ticks
148 // since the watchdog was armed, which should exceed the timer period.
149 // We don't actually know the elapsed number of ticks, so let's return
150 // the timer period plus 1.
151 //
152 if (mWatchdogNotify != NULL) {
153 mWatchdogNotify (mTimerPeriod + 1);
154 }
155
156 gRT->ResetSystem (
158 EFI_TIMEOUT,
159 StrSize (ResetString),
160 (CHAR16 *)ResetString
161 );
162
163 // If we got here then the reset didn't work
164 ASSERT (FALSE);
165}
166
191STATIC
193EFIAPI
197 )
198{
199 if ((mWatchdogNotify == NULL) && (NotifyFunction == NULL)) {
200 return EFI_INVALID_PARAMETER;
201 }
202
203 if ((mWatchdogNotify != NULL) && (NotifyFunction != NULL)) {
204 return EFI_ALREADY_STARTED;
205 }
206
207 mWatchdogNotify = NotifyFunction;
208 return EFI_SUCCESS;
209}
210
227STATIC
229EFIAPI
232 IN UINT64 TimerPeriod // In 100ns units
233 )
234{
235 UINTN SystemCount;
236 UINT64 MaxWatchdogOffsetValue;
237 UINT64 TimerFrequencyHz;
238 UINT64 NumTimerTicks;
239
240 // If we've exited Boot Services but TimerPeriod isn't zero, this
241 // indicates that the caller is doing something wrong.
242 if (mExitedBootServices && (TimerPeriod != 0)) {
243 mTimerPeriod = 0;
244 WatchdogDisable ();
245 return EFI_DEVICE_ERROR;
246 }
247
248 // If TimerPeriod is 0 this is a request to stop the watchdog.
249 if (TimerPeriod == 0) {
250 mTimerPeriod = 0;
251 WatchdogDisable ();
252 return EFI_SUCCESS;
253 }
254
255 // Work out how many timer ticks will equate to TimerPeriod
256 TimerFrequencyHz = ArmGenericTimerGetTimerFreq ();
257 ASSERT (TimerFrequencyHz != 0);
258 mTimerPeriod = TimerPeriod;
259 NumTimerTicks = (TimerFrequencyHz * TimerPeriod) / TIME_UNITS_PER_SECOND;
260
261 /* If the number of required ticks is greater than the max the watchdog's
262 offset register (WOR) can hold, we need to manually compute and set
263 the compare register (WCV) */
264 MaxWatchdogOffsetValue = GetMaxWatchdogOffsetRegisterValue ();
265 if (NumTimerTicks > MaxWatchdogOffsetValue) {
266 /* We need to enable the watchdog *before* writing to the compare register,
267 because enabling the watchdog causes an "explicit refresh", which
268 clobbers the compare register (WCV). In order to make sure this doesn't
269 trigger an interrupt, set the offset to max. */
270 WatchdogWriteOffsetRegister (MaxWatchdogOffsetValue);
271 WatchdogEnable ();
272 SystemCount = ArmGenericTimerGetSystemCount ();
273 WatchdogWriteCompareRegister (SystemCount + NumTimerTicks);
274 } else {
275 WatchdogWriteOffsetRegister (NumTimerTicks);
276 WatchdogEnable ();
277 }
278
279 return EFI_SUCCESS;
280}
281
298STATIC
300EFIAPI
303 OUT UINT64 *TimerPeriod
304 )
305{
306 if (TimerPeriod == NULL) {
307 return EFI_INVALID_PARAMETER;
308 }
309
310 *TimerPeriod = mTimerPeriod;
311
312 return EFI_SUCCESS;
313}
314
351};
352
354EFIAPI
355GenericWatchdogEntry (
356 IN EFI_HANDLE ImageHandle,
357 IN EFI_SYSTEM_TABLE *SystemTable
358 )
359{
360 EFI_STATUS Status;
361 EFI_HANDLE Handle;
362
363 Status = gBS->LocateProtocol (
364 &gHardwareInterrupt2ProtocolGuid,
365 NULL,
366 (VOID **)&mInterruptProtocol
367 );
368 ASSERT_EFI_ERROR (Status);
369
370 /* Make sure the Watchdog Timer Architectural Protocol has not been installed
371 in the system yet.
372 This will avoid conflicts with the universal watchdog */
373 ASSERT_PROTOCOL_ALREADY_INSTALLED (NULL, &gEfiWatchdogTimerArchProtocolGuid);
374
375 // Install interrupt handler
376 Status = mInterruptProtocol->RegisterInterruptSource (
377 mInterruptProtocol,
378 FixedPcdGet32 (PcdGenericWatchdogEl2IntrNum),
379 WatchdogInterruptHandler
380 );
381 if (EFI_ERROR (Status)) {
382 return Status;
383 }
384
385 Status = mInterruptProtocol->SetTriggerType (
386 mInterruptProtocol,
387 FixedPcdGet32 (PcdGenericWatchdogEl2IntrNum),
388 EFI_HARDWARE_INTERRUPT2_TRIGGER_EDGE_RISING
389 );
390 if (EFI_ERROR (Status)) {
391 goto UnregisterHandler;
392 }
393
394 WatchdogDisable ();
395
396 // Install the Timer Architectural Protocol onto a new handle
397 Handle = NULL;
398 Status = gBS->InstallMultipleProtocolInterfaces (
399 &Handle,
400 &gEfiWatchdogTimerArchProtocolGuid,
402 NULL
403 );
404 if (EFI_ERROR (Status)) {
405 goto UnregisterHandler;
406 }
407
408 // Register for an ExitBootServicesEvent
409 Status = gBS->CreateEvent (
410 EVT_SIGNAL_EXIT_BOOT_SERVICES,
411 TPL_NOTIFY,
413 NULL,
414 &mEfiExitBootServicesEvent
415 );
416 ASSERT_EFI_ERROR (Status);
417
418 return EFI_SUCCESS;
419
420UnregisterHandler:
421 // Unregister the handler
422 mInterruptProtocol->RegisterInterruptSource (
423 mInterruptProtocol,
424 FixedPcdGet32 (PcdGenericWatchdogEl2IntrNum),
425 NULL
426 );
427 return Status;
428}
UINT64 UINTN
UINTN EFIAPI StrSize(IN CONST CHAR16 *String)
Definition: String.c:72
STATIC EFI_STATUS EFIAPI WatchdogRegisterHandler(IN EFI_WATCHDOG_TIMER_ARCH_PROTOCOL *This, IN EFI_WATCHDOG_TIMER_NOTIFY NotifyFunction)
STATIC VOID EFIAPI WatchdogExitBootServicesEvent(IN EFI_EVENT Event, IN VOID *Context)
STATIC EFI_STATUS EFIAPI WatchdogGetTimerPeriod(IN EFI_WATCHDOG_TIMER_ARCH_PROTOCOL *This, OUT UINT64 *TimerPeriod)
STATIC UINT64 GetMaxWatchdogOffsetRegisterValue(VOID)
STATIC EFI_STATUS EFIAPI WatchdogSetTimerPeriod(IN EFI_WATCHDOG_TIMER_ARCH_PROTOCOL *This, IN UINT64 TimerPeriod)
STATIC EFI_WATCHDOG_TIMER_ARCH_PROTOCOL mWatchdogTimer
UINT32 EFIAPI MmioRead32(IN UINTN Address)
Definition: IoLib.c:262
UINT32 EFIAPI MmioWrite32(IN UINTN Address, IN UINT32 Value)
Definition: IoLib.c:309
EFI_RUNTIME_SERVICES * gRT
#define NULL
Definition: Base.h:319
#define CONST
Definition: Base.h:259
#define STATIC
Definition: Base.h:264
#define TRUE
Definition: Base.h:301
#define FALSE
Definition: Base.h:307
#define IN
Definition: Base.h:279
#define OUT
Definition: Base.h:284
#define ASSERT_EFI_ERROR(StatusParameter)
Definition: DebugLib.h:462
#define ASSERT_PROTOCOL_ALREADY_INSTALLED(Handle, Guid)
Definition: DebugLib.h:535
VOID(EFIAPI * EFI_WATCHDOG_TIMER_NOTIFY)(IN UINT64 Time)
Definition: WatchdogTimer.h:37
#define FixedPcdGet32(TokenName)
Definition: PcdLib.h:92
VOID EFIAPI NotifyFunction(IN EFI_EVENT Event, IN VOID *Context)
Definition: ScsiBus.c:1492
RETURN_STATUS EFI_STATUS
Definition: UefiBaseType.h:29
VOID * EFI_EVENT
Definition: UefiBaseType.h:37
VOID * EFI_HANDLE
Definition: UefiBaseType.h:33
#define EFI_SUCCESS
Definition: UefiBaseType.h:112
EFI_BOOT_SERVICES * gBS
@ EfiResetCold