VirtualBox

source: vbox/trunk/src/VBox/Devices/EFI/Firmware/PcAtChipsetPkg/PcatRealTimeClockRuntimeDxe/PcRtc.c@ 105681

Last change on this file since 105681 was 101291, checked in by vboxsync, 17 months ago

EFI/FirmwareNew: Make edk2-stable202308 build on all supported platforms (using gcc at least, msvc not tested yet), bugref:4643

  • Property svn:eol-style set to native
File size: 34.4 KB
Line 
1/** @file
2 RTC Architectural Protocol GUID as defined in DxeCis 0.96.
3
4Copyright (c) 2006 - 2018, Intel Corporation. All rights reserved.<BR>
5Copyright (c) 2017, AMD Inc. All rights reserved.<BR>
6Copyright (c) 2018 - 2020, ARM Limited. All rights reserved.<BR>
7
8SPDX-License-Identifier: BSD-2-Clause-Patent
9
10**/
11
12#include "PcRtc.h"
13
14extern UINTN mRtcIndexRegister;
15extern UINTN mRtcTargetRegister;
16extern UINT16 mRtcDefaultYear;
17extern UINT16 mMinimalValidYear;
18extern UINT16 mMaximalValidYear;
19//
20// Days of month.
21//
22UINTN mDayOfMonth[] = { 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
23
24//
25// The name of NV variable to store the timezone and daylight saving information.
26//
27CHAR16 mTimeZoneVariableName[] = L"RTC";
28
29/**
30 Compare the Hour, Minute and Second of the From time and the To time.
31
32 Only compare H/M/S in EFI_TIME and ignore other fields here.
33
34 @param From the first time
35 @param To the second time
36
37 @return >0 The H/M/S of the From time is later than those of To time
38 @return ==0 The H/M/S of the From time is same as those of To time
39 @return <0 The H/M/S of the From time is earlier than those of To time
40**/
41INTN
42CompareHMS (
43 IN EFI_TIME *From,
44 IN EFI_TIME *To
45 );
46
47/**
48 To check if second date is later than first date within 24 hours.
49
50 @param From the first date
51 @param To the second date
52
53 @retval TRUE From is previous to To within 24 hours.
54 @retval FALSE From is later, or it is previous to To more than 24 hours.
55**/
56BOOLEAN
57IsWithinOneDay (
58 IN EFI_TIME *From,
59 IN EFI_TIME *To
60 );
61
62/**
63 Read RTC content through its registers using IO access.
64
65 @param Address Address offset of RTC. It is recommended to use
66 macros such as RTC_ADDRESS_SECONDS.
67
68 @return The data of UINT8 type read from RTC.
69**/
70STATIC
71UINT8
72IoRtcRead (
73 IN UINTN Address
74 )
75{
76 IoWrite8 (
77 mRtcIndexRegister,
78 (UINT8)(Address | (UINT8)(IoRead8 (mRtcIndexRegister) & 0x80))
79 );
80 return IoRead8 (mRtcTargetRegister);
81}
82
83/**
84 Write RTC through its registers using IO access.
85
86 @param Address Address offset of RTC. It is recommended to use
87 macros such as RTC_ADDRESS_SECONDS.
88 @param Data The content you want to write into RTC.
89
90**/
91STATIC
92VOID
93IoRtcWrite (
94 IN UINTN Address,
95 IN UINT8 Data
96 )
97{
98 IoWrite8 (
99 mRtcIndexRegister,
100 (UINT8)(Address | (UINT8)(IoRead8 (mRtcIndexRegister) & 0x80))
101 );
102 IoWrite8 (mRtcTargetRegister, Data);
103}
104
105/**
106 Read RTC content through its registers using MMIO access.
107
108 @param Address Address offset of RTC. It is recommended to use
109 macros such as RTC_ADDRESS_SECONDS.
110
111 @return The data of UINT8 type read from RTC.
112**/
113STATIC
114UINT8
115MmioRtcRead (
116 IN UINTN Address
117 )
118{
119 MmioWrite8 (
120 mRtcIndexRegister,
121 (UINT8)(Address | (UINT8)(MmioRead8 (mRtcIndexRegister) & 0x80))
122 );
123 return MmioRead8 (mRtcTargetRegister);
124}
125
126/**
127 Write RTC through its registers using MMIO access.
128
129 @param Address Address offset of RTC. It is recommended to use
130 macros such as RTC_ADDRESS_SECONDS.
131 @param Data The content you want to write into RTC.
132
133**/
134STATIC
135VOID
136MmioRtcWrite (
137 IN UINTN Address,
138 IN UINT8 Data
139 )
140{
141 MmioWrite8 (
142 mRtcIndexRegister,
143 (UINT8)(Address | (UINT8)(MmioRead8 (mRtcIndexRegister) & 0x80))
144 );
145 MmioWrite8 (mRtcTargetRegister, Data);
146}
147
148/**
149 Read RTC content through its registers.
150
151 @param Address Address offset of RTC. It is recommended to use
152 macros such as RTC_ADDRESS_SECONDS.
153
154 @return The data of UINT8 type read from RTC.
155**/
156STATIC
157UINT8
158RtcRead (
159 IN UINTN Address
160 )
161{
162 if (FeaturePcdGet (PcdRtcUseMmio)) {
163 return MmioRtcRead (Address);
164 }
165
166 return IoRtcRead (Address);
167}
168
169/**
170 Write RTC through its registers.
171
172 @param Address Address offset of RTC. It is recommended to use
173 macros such as RTC_ADDRESS_SECONDS.
174 @param Data The content you want to write into RTC.
175
176**/
177STATIC
178VOID
179RtcWrite (
180 IN UINTN Address,
181 IN UINT8 Data
182 )
183{
184 if (FeaturePcdGet (PcdRtcUseMmio)) {
185 MmioRtcWrite (Address, Data);
186 } else {
187 IoRtcWrite (Address, Data);
188 }
189}
190
191/**
192 Initialize RTC.
193
194 @param Global For global use inside this module.
195
196 @retval EFI_DEVICE_ERROR Initialization failed due to device error.
197 @retval EFI_SUCCESS Initialization successful.
198
199**/
200EFI_STATUS
201PcRtcInit (
202 IN PC_RTC_MODULE_GLOBALS *Global
203 )
204{
205 EFI_STATUS Status;
206 RTC_REGISTER_A RegisterA;
207 RTC_REGISTER_B RegisterB;
208 RTC_REGISTER_D RegisterD;
209 EFI_TIME Time;
210 UINTN DataSize;
211 UINT32 TimerVar;
212 BOOLEAN Enabled;
213 BOOLEAN Pending;
214
215 //
216 // Acquire RTC Lock to make access to RTC atomic
217 //
218 if (!EfiAtRuntime ()) {
219 EfiAcquireLock (&Global->RtcLock);
220 }
221
222 //
223 // Initialize RTC Register
224 //
225 // Make sure Division Chain is properly configured,
226 // or RTC clock won't "tick" -- time won't increment
227 //
228 RegisterA.Data = FixedPcdGet8 (PcdInitialValueRtcRegisterA);
229 RtcWrite (RTC_ADDRESS_REGISTER_A, RegisterA.Data);
230
231 //
232 // Read Register B
233 //
234 RegisterB.Data = RtcRead (RTC_ADDRESS_REGISTER_B);
235
236 //
237 // Clear RTC flag register
238 //
239 RtcRead (RTC_ADDRESS_REGISTER_C);
240
241 //
242 // Clear RTC register D
243 //
244 RegisterD.Data = FixedPcdGet8 (PcdInitialValueRtcRegisterD);
245 RtcWrite (RTC_ADDRESS_REGISTER_D, RegisterD.Data);
246
247 //
248 // Wait for up to 0.1 seconds for the RTC to be updated
249 //
250 Status = RtcWaitToUpdate (PcdGet32 (PcdRealTimeClockUpdateTimeout));
251 if (EFI_ERROR (Status)) {
252 //
253 // Set the variable with default value if the RTC is functioning incorrectly.
254 //
255 Global->SavedTimeZone = EFI_UNSPECIFIED_TIMEZONE;
256 Global->Daylight = 0;
257 if (!EfiAtRuntime ()) {
258 EfiReleaseLock (&Global->RtcLock);
259 }
260
261 return EFI_DEVICE_ERROR;
262 }
263
264 //
265 // Get the Time/Date/Daylight Savings values.
266 //
267 Time.Second = RtcRead (RTC_ADDRESS_SECONDS);
268 Time.Minute = RtcRead (RTC_ADDRESS_MINUTES);
269 Time.Hour = RtcRead (RTC_ADDRESS_HOURS);
270 Time.Day = RtcRead (RTC_ADDRESS_DAY_OF_THE_MONTH);
271 Time.Month = RtcRead (RTC_ADDRESS_MONTH);
272 Time.Year = RtcRead (RTC_ADDRESS_YEAR);
273
274 //
275 // Release RTC Lock.
276 //
277 if (!EfiAtRuntime ()) {
278 EfiReleaseLock (&Global->RtcLock);
279 }
280
281 //
282 // Get the data of Daylight saving and time zone, if they have been
283 // stored in NV variable during previous boot.
284 //
285 DataSize = sizeof (UINT32);
286 Status = EfiGetVariable (
287 mTimeZoneVariableName,
288 &gEfiCallerIdGuid,
289 NULL,
290 &DataSize,
291 &TimerVar
292 );
293 if (!EFI_ERROR (Status)) {
294 Time.TimeZone = (INT16)TimerVar;
295 Time.Daylight = (UINT8)(TimerVar >> 16);
296 } else {
297 Time.TimeZone = EFI_UNSPECIFIED_TIMEZONE;
298 Time.Daylight = 0;
299 }
300
301 //
302 // Validate time fields
303 //
304 Status = ConvertRtcTimeToEfiTime (&Time, RegisterB);
305 if (!EFI_ERROR (Status)) {
306 Status = RtcTimeFieldsValid (&Time);
307 }
308
309 if (EFI_ERROR (Status)) {
310 //
311 // Report Status Code to indicate that the RTC has bad date and time
312 //
313 REPORT_STATUS_CODE (
314 EFI_ERROR_CODE | EFI_ERROR_MINOR,
315 (EFI_SOFTWARE_DXE_RT_DRIVER | EFI_SW_EC_BAD_DATE_TIME)
316 );
317 Time.Second = RTC_INIT_SECOND;
318 Time.Minute = RTC_INIT_MINUTE;
319 Time.Hour = RTC_INIT_HOUR;
320 Time.Day = RTC_INIT_DAY;
321 Time.Month = RTC_INIT_MONTH;
322 Time.Year = MAX (mRtcDefaultYear, mMinimalValidYear);
323 Time.Year = MIN (Time.Year, mMaximalValidYear);
324 Time.Nanosecond = 0;
325 Time.TimeZone = EFI_UNSPECIFIED_TIMEZONE;
326 Time.Daylight = 0;
327 }
328
329 //
330 // Set RTC configuration after get original time
331 // The value of bit AIE should be reserved.
332 //
333 RegisterB.Data = FixedPcdGet8 (PcdInitialValueRtcRegisterB) | (RegisterB.Data & BIT5);
334 RtcWrite (RTC_ADDRESS_REGISTER_B, RegisterB.Data);
335
336 //
337 // Reset time value according to new RTC configuration
338 //
339 Status = PcRtcSetTime (&Time, Global);
340 if (EFI_ERROR (Status)) {
341 return EFI_DEVICE_ERROR;
342 }
343
344 //
345 // Reset wakeup time value to valid state when wakeup alarm is disabled and wakeup time is invalid.
346 // Global variable has already had valid SavedTimeZone and Daylight,
347 // so we can use them to get and set wakeup time.
348 //
349 Status = PcRtcGetWakeupTime (&Enabled, &Pending, &Time, Global);
350 if ((!EFI_ERROR (Status)) || (Enabled)) {
351 return EFI_SUCCESS;
352 }
353
354 //
355 // When wakeup time is disabled and invalid, reset wakeup time register to valid state
356 // but keep wakeup alarm disabled.
357 //
358 Time.Second = RTC_INIT_SECOND;
359 Time.Minute = RTC_INIT_MINUTE;
360 Time.Hour = RTC_INIT_HOUR;
361 Time.Day = RTC_INIT_DAY;
362 Time.Month = RTC_INIT_MONTH;
363 Time.Year = MAX (mRtcDefaultYear, mMinimalValidYear);
364 Time.Year = MIN (Time.Year, mMaximalValidYear);
365 Time.Nanosecond = 0;
366 Time.TimeZone = Global->SavedTimeZone;
367 Time.Daylight = Global->Daylight;
368
369 //
370 // Acquire RTC Lock to make access to RTC atomic
371 //
372 if (!EfiAtRuntime ()) {
373 EfiAcquireLock (&Global->RtcLock);
374 }
375
376 //
377 // Wait for up to 0.1 seconds for the RTC to be updated
378 //
379 Status = RtcWaitToUpdate (PcdGet32 (PcdRealTimeClockUpdateTimeout));
380 if (EFI_ERROR (Status)) {
381 if (!EfiAtRuntime ()) {
382 EfiReleaseLock (&Global->RtcLock);
383 }
384
385 return EFI_DEVICE_ERROR;
386 }
387
388 ConvertEfiTimeToRtcTime (&Time, RegisterB);
389
390 //
391 // Set the Y/M/D info to variable as it has no corresponding hw registers.
392 //
393 Status = EfiSetVariable (
394 L"RTCALARM",
395 &gEfiCallerIdGuid,
396 EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS | EFI_VARIABLE_NON_VOLATILE,
397 sizeof (Time),
398 &Time
399 );
400 if (EFI_ERROR (Status)) {
401 if (!EfiAtRuntime ()) {
402 EfiReleaseLock (&Global->RtcLock);
403 }
404
405 return EFI_DEVICE_ERROR;
406 }
407
408 //
409 // Inhibit updates of the RTC
410 //
411 RegisterB.Bits.Set = 1;
412 RtcWrite (RTC_ADDRESS_REGISTER_B, RegisterB.Data);
413
414 //
415 // Set RTC alarm time registers
416 //
417 RtcWrite (RTC_ADDRESS_SECONDS_ALARM, Time.Second);
418 RtcWrite (RTC_ADDRESS_MINUTES_ALARM, Time.Minute);
419 RtcWrite (RTC_ADDRESS_HOURS_ALARM, Time.Hour);
420
421 //
422 // Allow updates of the RTC registers
423 //
424 RegisterB.Bits.Set = 0;
425 RtcWrite (RTC_ADDRESS_REGISTER_B, RegisterB.Data);
426
427 //
428 // Release RTC Lock.
429 //
430 if (!EfiAtRuntime ()) {
431 EfiReleaseLock (&Global->RtcLock);
432 }
433
434 return EFI_SUCCESS;
435}
436
437/**
438 Returns the current time and date information, and the time-keeping capabilities
439 of the hardware platform.
440
441 @param Time A pointer to storage to receive a snapshot of the current time.
442 @param Capabilities An optional pointer to a buffer to receive the real time clock
443 device's capabilities.
444 @param Global For global use inside this module.
445
446 @retval EFI_SUCCESS The operation completed successfully.
447 @retval EFI_INVALID_PARAMETER Time is NULL.
448 @retval EFI_DEVICE_ERROR The time could not be retrieved due to hardware error.
449
450**/
451EFI_STATUS
452PcRtcGetTime (
453 OUT EFI_TIME *Time,
454 OUT EFI_TIME_CAPABILITIES *Capabilities OPTIONAL,
455 IN PC_RTC_MODULE_GLOBALS *Global
456 )
457{
458 EFI_STATUS Status;
459 RTC_REGISTER_B RegisterB;
460
461 //
462 // Check parameters for null pointer
463 //
464 if (Time == NULL) {
465 return EFI_INVALID_PARAMETER;
466 }
467
468 //
469 // Acquire RTC Lock to make access to RTC atomic
470 //
471 if (!EfiAtRuntime ()) {
472 EfiAcquireLock (&Global->RtcLock);
473 }
474
475 //
476 // Wait for up to 0.1 seconds for the RTC to be updated
477 //
478 Status = RtcWaitToUpdate (PcdGet32 (PcdRealTimeClockUpdateTimeout));
479 if (EFI_ERROR (Status)) {
480 if (!EfiAtRuntime ()) {
481 EfiReleaseLock (&Global->RtcLock);
482 }
483
484 return Status;
485 }
486
487 //
488 // Read Register B
489 //
490 RegisterB.Data = RtcRead (RTC_ADDRESS_REGISTER_B);
491
492 //
493 // Get the Time/Date/Daylight Savings values.
494 //
495 Time->Second = RtcRead (RTC_ADDRESS_SECONDS);
496 Time->Minute = RtcRead (RTC_ADDRESS_MINUTES);
497 Time->Hour = RtcRead (RTC_ADDRESS_HOURS);
498 Time->Day = RtcRead (RTC_ADDRESS_DAY_OF_THE_MONTH);
499 Time->Month = RtcRead (RTC_ADDRESS_MONTH);
500 Time->Year = RtcRead (RTC_ADDRESS_YEAR);
501
502 //
503 // Release RTC Lock.
504 //
505 if (!EfiAtRuntime ()) {
506 EfiReleaseLock (&Global->RtcLock);
507 }
508
509 //
510 // Get the variable that contains the TimeZone and Daylight fields
511 //
512 Time->TimeZone = Global->SavedTimeZone;
513 Time->Daylight = Global->Daylight;
514
515 //
516 // Make sure all field values are in correct range
517 //
518 Status = ConvertRtcTimeToEfiTime (Time, RegisterB);
519 if (!EFI_ERROR (Status)) {
520 Status = RtcTimeFieldsValid (Time);
521 }
522
523 if (EFI_ERROR (Status)) {
524 return EFI_DEVICE_ERROR;
525 }
526
527 //
528 // Fill in Capabilities if it was passed in
529 //
530 if (Capabilities != NULL) {
531 Capabilities->Resolution = 1;
532 //
533 // 1 hertz
534 //
535 Capabilities->Accuracy = 50000000;
536 //
537 // 50 ppm
538 //
539 Capabilities->SetsToZero = FALSE;
540 }
541
542 return EFI_SUCCESS;
543}
544
545/**
546 Sets the current local time and date information.
547
548 @param Time A pointer to the current time.
549 @param Global For global use inside this module.
550
551 @retval EFI_SUCCESS The operation completed successfully.
552 @retval EFI_INVALID_PARAMETER A time field is out of range.
553 @retval EFI_DEVICE_ERROR The time could not be set due due to hardware error.
554
555**/
556EFI_STATUS
557PcRtcSetTime (
558 IN EFI_TIME *Time,
559 IN PC_RTC_MODULE_GLOBALS *Global
560 )
561{
562 EFI_STATUS Status;
563 EFI_TIME RtcTime;
564 RTC_REGISTER_B RegisterB;
565 UINT32 TimerVar;
566
567 if (Time == NULL) {
568 return EFI_INVALID_PARAMETER;
569 }
570
571 //
572 // Make sure that the time fields are valid
573 //
574 Status = RtcTimeFieldsValid (Time);
575 if (EFI_ERROR (Status)) {
576 return Status;
577 }
578
579 CopyMem (&RtcTime, Time, sizeof (EFI_TIME));
580
581 //
582 // Acquire RTC Lock to make access to RTC atomic
583 //
584 if (!EfiAtRuntime ()) {
585 EfiAcquireLock (&Global->RtcLock);
586 }
587
588 //
589 // Wait for up to 0.1 seconds for the RTC to be updated
590 //
591 Status = RtcWaitToUpdate (PcdGet32 (PcdRealTimeClockUpdateTimeout));
592 if (EFI_ERROR (Status)) {
593 if (!EfiAtRuntime ()) {
594 EfiReleaseLock (&Global->RtcLock);
595 }
596
597 return Status;
598 }
599
600 //
601 // Write timezone and daylight to RTC variable
602 //
603 if ((Time->TimeZone == EFI_UNSPECIFIED_TIMEZONE) && (Time->Daylight == 0)) {
604 Status = EfiSetVariable (
605 mTimeZoneVariableName,
606 &gEfiCallerIdGuid,
607 0,
608 0,
609 NULL
610 );
611 if (Status == EFI_NOT_FOUND) {
612 Status = EFI_SUCCESS;
613 }
614 } else {
615 TimerVar = Time->Daylight;
616 TimerVar = (UINT32)((TimerVar << 16) | (UINT16)(Time->TimeZone));
617 Status = EfiSetVariable (
618 mTimeZoneVariableName,
619 &gEfiCallerIdGuid,
620 EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS | EFI_VARIABLE_NON_VOLATILE,
621 sizeof (TimerVar),
622 &TimerVar
623 );
624 }
625
626 if (EFI_ERROR (Status)) {
627 if (!EfiAtRuntime ()) {
628 EfiReleaseLock (&Global->RtcLock);
629 }
630
631 return EFI_DEVICE_ERROR;
632 }
633
634 //
635 // Read Register B, and inhibit updates of the RTC
636 //
637 RegisterB.Data = RtcRead (RTC_ADDRESS_REGISTER_B);
638 RegisterB.Bits.Set = 1;
639 RtcWrite (RTC_ADDRESS_REGISTER_B, RegisterB.Data);
640
641 //
642 // Store the century value to RTC before converting to BCD format.
643 //
644 if (Global->CenturyRtcAddress != 0) {
645 RtcWrite (Global->CenturyRtcAddress, DecimalToBcd8 ((UINT8)(RtcTime.Year / 100)));
646 }
647
648 ConvertEfiTimeToRtcTime (&RtcTime, RegisterB);
649
650 RtcWrite (RTC_ADDRESS_SECONDS, RtcTime.Second);
651 RtcWrite (RTC_ADDRESS_MINUTES, RtcTime.Minute);
652 RtcWrite (RTC_ADDRESS_HOURS, RtcTime.Hour);
653 RtcWrite (RTC_ADDRESS_DAY_OF_THE_MONTH, RtcTime.Day);
654 RtcWrite (RTC_ADDRESS_MONTH, RtcTime.Month);
655 RtcWrite (RTC_ADDRESS_YEAR, (UINT8)RtcTime.Year);
656
657 //
658 // Allow updates of the RTC registers
659 //
660 RegisterB.Bits.Set = 0;
661 RtcWrite (RTC_ADDRESS_REGISTER_B, RegisterB.Data);
662
663 //
664 // Release RTC Lock.
665 //
666 if (!EfiAtRuntime ()) {
667 EfiReleaseLock (&Global->RtcLock);
668 }
669
670 //
671 // Set the variable that contains the TimeZone and Daylight fields
672 //
673 Global->SavedTimeZone = Time->TimeZone;
674 Global->Daylight = Time->Daylight;
675
676 return EFI_SUCCESS;
677}
678
679/**
680 Returns the current wakeup alarm clock setting.
681
682 @param Enabled Indicates if the alarm is currently enabled or disabled.
683 @param Pending Indicates if the alarm signal is pending and requires acknowledgment.
684 @param Time The current alarm setting.
685 @param Global For global use inside this module.
686
687 @retval EFI_SUCCESS The alarm settings were returned.
688 @retval EFI_INVALID_PARAMETER Enabled is NULL.
689 @retval EFI_INVALID_PARAMETER Pending is NULL.
690 @retval EFI_INVALID_PARAMETER Time is NULL.
691 @retval EFI_DEVICE_ERROR The wakeup time could not be retrieved due to a hardware error.
692 @retval EFI_UNSUPPORTED A wakeup timer is not supported on this platform.
693
694**/
695EFI_STATUS
696PcRtcGetWakeupTime (
697 OUT BOOLEAN *Enabled,
698 OUT BOOLEAN *Pending,
699 OUT EFI_TIME *Time,
700 IN PC_RTC_MODULE_GLOBALS *Global
701 )
702{
703 EFI_STATUS Status;
704 RTC_REGISTER_B RegisterB;
705 RTC_REGISTER_C RegisterC;
706 EFI_TIME RtcTime;
707 UINTN DataSize;
708
709 //
710 // Check parameters for null pointers
711 //
712 if ((Enabled == NULL) || (Pending == NULL) || (Time == NULL)) {
713 return EFI_INVALID_PARAMETER;
714 }
715
716 //
717 // Acquire RTC Lock to make access to RTC atomic
718 //
719 if (!EfiAtRuntime ()) {
720 EfiAcquireLock (&Global->RtcLock);
721 }
722
723 //
724 // Wait for up to 0.1 seconds for the RTC to be updated
725 //
726 Status = RtcWaitToUpdate (PcdGet32 (PcdRealTimeClockUpdateTimeout));
727 if (EFI_ERROR (Status)) {
728 if (!EfiAtRuntime ()) {
729 EfiReleaseLock (&Global->RtcLock);
730 }
731
732 return EFI_DEVICE_ERROR;
733 }
734
735 //
736 // Read Register B and Register C
737 //
738 RegisterB.Data = RtcRead (RTC_ADDRESS_REGISTER_B);
739 RegisterC.Data = RtcRead (RTC_ADDRESS_REGISTER_C);
740
741 //
742 // Get the Time/Date/Daylight Savings values.
743 //
744 *Enabled = RegisterB.Bits.Aie;
745 *Pending = RegisterC.Bits.Af;
746
747 Time->Second = RtcRead (RTC_ADDRESS_SECONDS_ALARM);
748 Time->Minute = RtcRead (RTC_ADDRESS_MINUTES_ALARM);
749 Time->Hour = RtcRead (RTC_ADDRESS_HOURS_ALARM);
750 Time->Day = RtcRead (RTC_ADDRESS_DAY_OF_THE_MONTH);
751 Time->Month = RtcRead (RTC_ADDRESS_MONTH);
752 Time->Year = RtcRead (RTC_ADDRESS_YEAR);
753 Time->TimeZone = Global->SavedTimeZone;
754 Time->Daylight = Global->Daylight;
755
756 //
757 // Get the alarm info from variable
758 //
759 DataSize = sizeof (EFI_TIME);
760 Status = EfiGetVariable (
761 L"RTCALARM",
762 &gEfiCallerIdGuid,
763 NULL,
764 &DataSize,
765 &RtcTime
766 );
767 if (!EFI_ERROR (Status)) {
768 //
769 // The alarm variable exists. In this case, we read variable to get info.
770 //
771 Time->Day = RtcTime.Day;
772 Time->Month = RtcTime.Month;
773 Time->Year = RtcTime.Year;
774 }
775
776 //
777 // Release RTC Lock.
778 //
779 if (!EfiAtRuntime ()) {
780 EfiReleaseLock (&Global->RtcLock);
781 }
782
783 //
784 // Make sure all field values are in correct range
785 //
786 Status = ConvertRtcTimeToEfiTime (Time, RegisterB);
787 if (!EFI_ERROR (Status)) {
788 Status = RtcTimeFieldsValid (Time);
789 }
790
791 if (EFI_ERROR (Status)) {
792 return EFI_DEVICE_ERROR;
793 }
794
795 return EFI_SUCCESS;
796}
797
798/**
799 Sets the system wakeup alarm clock time.
800
801 @param Enabled Enable or disable the wakeup alarm.
802 @param Time If Enable is TRUE, the time to set the wakeup alarm for.
803 If Enable is FALSE, then this parameter is optional, and may be NULL.
804 @param Global For global use inside this module.
805
806 @retval EFI_SUCCESS If Enable is TRUE, then the wakeup alarm was enabled.
807 If Enable is FALSE, then the wakeup alarm was disabled.
808 @retval EFI_INVALID_PARAMETER A time field is out of range.
809 @retval EFI_DEVICE_ERROR The wakeup time could not be set due to a hardware error.
810 @retval EFI_UNSUPPORTED A wakeup timer is not supported on this platform.
811
812**/
813EFI_STATUS
814PcRtcSetWakeupTime (
815 IN BOOLEAN Enable,
816 IN EFI_TIME *Time OPTIONAL,
817 IN PC_RTC_MODULE_GLOBALS *Global
818 )
819{
820 EFI_STATUS Status;
821 EFI_TIME RtcTime;
822 RTC_REGISTER_B RegisterB;
823 EFI_TIME_CAPABILITIES Capabilities;
824
825 ZeroMem (&RtcTime, sizeof (RtcTime));
826
827 if (Enable) {
828 if (Time == NULL) {
829 return EFI_INVALID_PARAMETER;
830 }
831
832 //
833 // Make sure that the time fields are valid
834 //
835 Status = RtcTimeFieldsValid (Time);
836 if (EFI_ERROR (Status)) {
837 return EFI_INVALID_PARAMETER;
838 }
839
840 //
841 // Just support set alarm time within 24 hours
842 //
843 Status = PcRtcGetTime (&RtcTime, &Capabilities, Global);
844 if (!EFI_ERROR (Status)) {
845 Status = RtcTimeFieldsValid (&RtcTime);
846 }
847
848 if (EFI_ERROR (Status)) {
849 return EFI_DEVICE_ERROR;
850 }
851
852 if (!IsWithinOneDay (&RtcTime, Time)) {
853 return EFI_UNSUPPORTED;
854 }
855
856 //
857 // Make a local copy of the time and date
858 //
859 CopyMem (&RtcTime, Time, sizeof (EFI_TIME));
860 }
861
862 //
863 // Acquire RTC Lock to make access to RTC atomic
864 //
865 if (!EfiAtRuntime ()) {
866 EfiAcquireLock (&Global->RtcLock);
867 }
868
869 //
870 // Wait for up to 0.1 seconds for the RTC to be updated
871 //
872 Status = RtcWaitToUpdate (PcdGet32 (PcdRealTimeClockUpdateTimeout));
873 if (EFI_ERROR (Status)) {
874 if (!EfiAtRuntime ()) {
875 EfiReleaseLock (&Global->RtcLock);
876 }
877
878 return EFI_DEVICE_ERROR;
879 }
880
881 //
882 // Read Register B
883 //
884 RegisterB.Data = RtcRead (RTC_ADDRESS_REGISTER_B);
885
886 if (Enable) {
887 ConvertEfiTimeToRtcTime (&RtcTime, RegisterB);
888 } else {
889 //
890 // if the alarm is disable, record the current setting.
891 //
892 RtcTime.Second = RtcRead (RTC_ADDRESS_SECONDS_ALARM);
893 RtcTime.Minute = RtcRead (RTC_ADDRESS_MINUTES_ALARM);
894 RtcTime.Hour = RtcRead (RTC_ADDRESS_HOURS_ALARM);
895 RtcTime.Day = RtcRead (RTC_ADDRESS_DAY_OF_THE_MONTH);
896 RtcTime.Month = RtcRead (RTC_ADDRESS_MONTH);
897 RtcTime.Year = RtcRead (RTC_ADDRESS_YEAR);
898 RtcTime.TimeZone = Global->SavedTimeZone;
899 RtcTime.Daylight = Global->Daylight;
900 }
901
902 //
903 // Set the Y/M/D info to variable as it has no corresponding hw registers.
904 //
905 Status = EfiSetVariable (
906 L"RTCALARM",
907 &gEfiCallerIdGuid,
908 EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS | EFI_VARIABLE_NON_VOLATILE,
909 sizeof (RtcTime),
910 &RtcTime
911 );
912 if (EFI_ERROR (Status)) {
913 if (!EfiAtRuntime ()) {
914 EfiReleaseLock (&Global->RtcLock);
915 }
916
917 return EFI_DEVICE_ERROR;
918 }
919
920 //
921 // Inhibit updates of the RTC
922 //
923 RegisterB.Bits.Set = 1;
924 RtcWrite (RTC_ADDRESS_REGISTER_B, RegisterB.Data);
925
926 if (Enable) {
927 //
928 // Set RTC alarm time
929 //
930 RtcWrite (RTC_ADDRESS_SECONDS_ALARM, RtcTime.Second);
931 RtcWrite (RTC_ADDRESS_MINUTES_ALARM, RtcTime.Minute);
932 RtcWrite (RTC_ADDRESS_HOURS_ALARM, RtcTime.Hour);
933
934 RegisterB.Bits.Aie = 1;
935 } else {
936 RegisterB.Bits.Aie = 0;
937 }
938
939 //
940 // Allow updates of the RTC registers
941 //
942 RegisterB.Bits.Set = 0;
943 RtcWrite (RTC_ADDRESS_REGISTER_B, RegisterB.Data);
944
945 //
946 // Release RTC Lock.
947 //
948 if (!EfiAtRuntime ()) {
949 EfiReleaseLock (&Global->RtcLock);
950 }
951
952 return EFI_SUCCESS;
953}
954
955/**
956 Checks an 8-bit BCD value, and converts to an 8-bit value if valid.
957
958 This function checks the 8-bit BCD value specified by Value.
959 If valid, the function converts it to an 8-bit value and returns it.
960 Otherwise, return 0xff.
961
962 @param Value The 8-bit BCD value to check and convert
963
964 @return The 8-bit value converted. Or 0xff if Value is invalid.
965
966**/
967UINT8
968CheckAndConvertBcd8ToDecimal8 (
969 IN UINT8 Value
970 )
971{
972 if ((Value < 0xa0) && ((Value & 0xf) < 0xa)) {
973 return BcdToDecimal8 (Value);
974 }
975
976 return 0xff;
977}
978
979/**
980 Converts time read from RTC to EFI_TIME format defined by UEFI spec.
981
982 This function converts raw time data read from RTC to the EFI_TIME format
983 defined by UEFI spec.
984 If data mode of RTC is BCD, then converts it to decimal,
985 If RTC is in 12-hour format, then converts it to 24-hour format.
986
987 @param Time On input, the time data read from RTC to convert
988 On output, the time converted to UEFI format
989 @param RegisterB Value of Register B of RTC, indicating data mode
990 and hour format.
991
992 @retval EFI_INVALID_PARAMETER Parameters passed in are invalid.
993 @retval EFI_SUCCESS Convert RTC time to EFI time successfully.
994
995**/
996EFI_STATUS
997ConvertRtcTimeToEfiTime (
998 IN OUT EFI_TIME *Time,
999 IN RTC_REGISTER_B RegisterB
1000 )
1001{
1002 BOOLEAN IsPM;
1003 UINT8 Century;
1004
1005 // IsPM only makes sense for 12-hour format.
1006 if (RegisterB.Bits.Mil == 0) {
1007 if ((Time->Hour & 0x80) != 0) {
1008 IsPM = TRUE;
1009 } else {
1010 IsPM = FALSE;
1011 }
1012
1013 Time->Hour = (UINT8)(Time->Hour & 0x7f);
1014 }
1015
1016 if (RegisterB.Bits.Dm == 0) {
1017 Time->Year = CheckAndConvertBcd8ToDecimal8 ((UINT8)Time->Year);
1018 Time->Month = CheckAndConvertBcd8ToDecimal8 (Time->Month);
1019 Time->Day = CheckAndConvertBcd8ToDecimal8 (Time->Day);
1020 Time->Hour = CheckAndConvertBcd8ToDecimal8 (Time->Hour);
1021 Time->Minute = CheckAndConvertBcd8ToDecimal8 (Time->Minute);
1022 Time->Second = CheckAndConvertBcd8ToDecimal8 (Time->Second);
1023 }
1024
1025 if ((Time->Year == 0xff) || (Time->Month == 0xff) || (Time->Day == 0xff) ||
1026 (Time->Hour == 0xff) || (Time->Minute == 0xff) || (Time->Second == 0xff))
1027 {
1028 return EFI_INVALID_PARAMETER;
1029 }
1030
1031 //
1032 // For minimal/maximum year range [1970, 2069],
1033 // Century is 19 if RTC year >= 70,
1034 // Century is 20 otherwise.
1035 //
1036 Century = (UINT8)(mMinimalValidYear / 100);
1037 if (Time->Year < mMinimalValidYear % 100) {
1038 Century++;
1039 }
1040
1041 Time->Year = (UINT16)(Century * 100 + Time->Year);
1042
1043 //
1044 // If time is in 12 hour format, convert it to 24 hour format
1045 //
1046 if (RegisterB.Bits.Mil == 0) {
1047 if (IsPM && (Time->Hour < 12)) {
1048 Time->Hour = (UINT8)(Time->Hour + 12);
1049 }
1050
1051 if (!IsPM && (Time->Hour == 12)) {
1052 Time->Hour = 0;
1053 }
1054 }
1055
1056 Time->Nanosecond = 0;
1057
1058 return EFI_SUCCESS;
1059}
1060
1061/**
1062 Wait for a period for the RTC to be ready.
1063
1064 @param Timeout Tell how long it should take to wait.
1065
1066 @retval EFI_DEVICE_ERROR RTC device error.
1067 @retval EFI_SUCCESS RTC is updated and ready.
1068**/
1069EFI_STATUS
1070RtcWaitToUpdate (
1071 UINTN Timeout
1072 )
1073{
1074 RTC_REGISTER_A RegisterA;
1075 RTC_REGISTER_D RegisterD;
1076
1077 //
1078 // See if the RTC is functioning correctly
1079 //
1080 RegisterD.Data = RtcRead (RTC_ADDRESS_REGISTER_D);
1081
1082 if (RegisterD.Bits.Vrt == 0) {
1083 return EFI_DEVICE_ERROR;
1084 }
1085
1086 //
1087 // Wait for up to 0.1 seconds for the RTC to be ready.
1088 //
1089 Timeout = (Timeout / 10) + 1;
1090 RegisterA.Data = RtcRead (RTC_ADDRESS_REGISTER_A);
1091 while (RegisterA.Bits.Uip == 1 && Timeout > 0) {
1092 MicroSecondDelay (10);
1093 RegisterA.Data = RtcRead (RTC_ADDRESS_REGISTER_A);
1094 Timeout--;
1095 }
1096
1097 RegisterD.Data = RtcRead (RTC_ADDRESS_REGISTER_D);
1098 if ((Timeout == 0) || (RegisterD.Bits.Vrt == 0)) {
1099 return EFI_DEVICE_ERROR;
1100 }
1101
1102 return EFI_SUCCESS;
1103}
1104
1105/**
1106 See if all fields of a variable of EFI_TIME type is correct.
1107
1108 @param Time The time to be checked.
1109
1110 @retval EFI_INVALID_PARAMETER Some fields of Time are not correct.
1111 @retval EFI_SUCCESS Time is a valid EFI_TIME variable.
1112
1113**/
1114EFI_STATUS
1115RtcTimeFieldsValid (
1116 IN EFI_TIME *Time
1117 )
1118{
1119 if ((Time->Year < mMinimalValidYear) ||
1120 (Time->Year > mMaximalValidYear) ||
1121 (Time->Month < 1) ||
1122 (Time->Month > 12) ||
1123 (!DayValid (Time)) ||
1124 (Time->Hour > 23) ||
1125 (Time->Minute > 59) ||
1126 (Time->Second > 59) ||
1127 (Time->Nanosecond > 999999999) ||
1128 (!((Time->TimeZone == EFI_UNSPECIFIED_TIMEZONE) || ((Time->TimeZone >= -1440) && (Time->TimeZone <= 1440)))) ||
1129 ((Time->Daylight & (~(EFI_TIME_ADJUST_DAYLIGHT | EFI_TIME_IN_DAYLIGHT))) != 0))
1130 {
1131 return EFI_INVALID_PARAMETER;
1132 }
1133
1134 return EFI_SUCCESS;
1135}
1136
1137/**
1138 See if field Day of an EFI_TIME is correct.
1139
1140 @param Time Its Day field is to be checked.
1141
1142 @retval TRUE Day field of Time is correct.
1143 @retval FALSE Day field of Time is NOT correct.
1144**/
1145BOOLEAN
1146DayValid (
1147 IN EFI_TIME *Time
1148 )
1149{
1150 //
1151 // The validity of Time->Month field should be checked before
1152 //
1153 ASSERT (Time->Month >= 1);
1154 ASSERT (Time->Month <= 12);
1155 if ((Time->Day < 1) ||
1156 (Time->Day > mDayOfMonth[Time->Month - 1]) ||
1157 ((Time->Month == 2) && (!IsLeapYear (Time) && (Time->Day > 28)))
1158 )
1159 {
1160 return FALSE;
1161 }
1162
1163 return TRUE;
1164}
1165
1166/**
1167 Check if it is a leap year.
1168
1169 @param Time The time to be checked.
1170
1171 @retval TRUE It is a leap year.
1172 @retval FALSE It is NOT a leap year.
1173**/
1174BOOLEAN
1175IsLeapYear (
1176 IN EFI_TIME *Time
1177 )
1178{
1179 if (Time->Year % 4 == 0) {
1180 if (Time->Year % 100 == 0) {
1181 if (Time->Year % 400 == 0) {
1182 return TRUE;
1183 } else {
1184 return FALSE;
1185 }
1186 } else {
1187 return TRUE;
1188 }
1189 } else {
1190 return FALSE;
1191 }
1192}
1193
1194/**
1195 Converts time from EFI_TIME format defined by UEFI spec to RTC format.
1196
1197 This function converts time from EFI_TIME format defined by UEFI spec to RTC format.
1198 If data mode of RTC is BCD, then converts EFI_TIME to it.
1199 If RTC is in 12-hour format, then converts EFI_TIME to it.
1200
1201 @param Time On input, the time data read from UEFI to convert
1202 On output, the time converted to RTC format
1203 @param RegisterB Value of Register B of RTC, indicating data mode
1204**/
1205VOID
1206ConvertEfiTimeToRtcTime (
1207 IN OUT EFI_TIME *Time,
1208 IN RTC_REGISTER_B RegisterB
1209 )
1210{
1211 BOOLEAN IsPM;
1212
1213 IsPM = TRUE;
1214 //
1215 // Adjust hour field if RTC is in 12 hour mode
1216 //
1217 if (RegisterB.Bits.Mil == 0) {
1218 if (Time->Hour < 12) {
1219 IsPM = FALSE;
1220 }
1221
1222 if (Time->Hour >= 13) {
1223 Time->Hour = (UINT8)(Time->Hour - 12);
1224 } else if (Time->Hour == 0) {
1225 Time->Hour = 12;
1226 }
1227 }
1228
1229 //
1230 // Set the Time/Date values.
1231 //
1232 Time->Year = (UINT16)(Time->Year % 100);
1233
1234 if (RegisterB.Bits.Dm == 0) {
1235 Time->Year = DecimalToBcd8 ((UINT8)Time->Year);
1236 Time->Month = DecimalToBcd8 (Time->Month);
1237 Time->Day = DecimalToBcd8 (Time->Day);
1238 Time->Hour = DecimalToBcd8 (Time->Hour);
1239 Time->Minute = DecimalToBcd8 (Time->Minute);
1240 Time->Second = DecimalToBcd8 (Time->Second);
1241 }
1242
1243 //
1244 // If we are in 12 hour mode and PM is set, then set bit 7 of the Hour field.
1245 //
1246 if ((RegisterB.Bits.Mil == 0) && IsPM) {
1247 Time->Hour = (UINT8)(Time->Hour | 0x80);
1248 }
1249}
1250
1251/**
1252 Compare the Hour, Minute and Second of the From time and the To time.
1253
1254 Only compare H/M/S in EFI_TIME and ignore other fields here.
1255
1256 @param From the first time
1257 @param To the second time
1258
1259 @return >0 The H/M/S of the From time is later than those of To time
1260 @return ==0 The H/M/S of the From time is same as those of To time
1261 @return <0 The H/M/S of the From time is earlier than those of To time
1262**/
1263INTN
1264CompareHMS (
1265 IN EFI_TIME *From,
1266 IN EFI_TIME *To
1267 )
1268{
1269 if ((From->Hour > To->Hour) ||
1270 ((From->Hour == To->Hour) && (From->Minute > To->Minute)) ||
1271 ((From->Hour == To->Hour) && (From->Minute == To->Minute) && (From->Second > To->Second)))
1272 {
1273 return 1;
1274 } else if ((From->Hour == To->Hour) && (From->Minute == To->Minute) && (From->Second == To->Second)) {
1275 return 0;
1276 } else {
1277 return -1;
1278 }
1279}
1280
1281/**
1282 To check if second date is later than first date within 24 hours.
1283
1284 @param From the first date
1285 @param To the second date
1286
1287 @retval TRUE From is previous to To within 24 hours.
1288 @retval FALSE From is later, or it is previous to To more than 24 hours.
1289**/
1290BOOLEAN
1291IsWithinOneDay (
1292 IN EFI_TIME *From,
1293 IN EFI_TIME *To
1294 )
1295{
1296 BOOLEAN Adjacent;
1297
1298 Adjacent = FALSE;
1299
1300 //
1301 // The validity of From->Month field should be checked before
1302 //
1303 ASSERT (From->Month >= 1);
1304 ASSERT (From->Month <= 12);
1305
1306 if (From->Year == To->Year) {
1307 if (From->Month == To->Month) {
1308 if ((From->Day + 1) == To->Day) {
1309 if ((CompareHMS (From, To) >= 0)) {
1310 Adjacent = TRUE;
1311 }
1312 } else if (From->Day == To->Day) {
1313 if ((CompareHMS (From, To) <= 0)) {
1314 Adjacent = TRUE;
1315 }
1316 }
1317 } else if (((From->Month + 1) == To->Month) && (To->Day == 1)) {
1318 if ((From->Month == 2) && !IsLeapYear (From)) {
1319 if (From->Day == 28) {
1320 if ((CompareHMS (From, To) >= 0)) {
1321 Adjacent = TRUE;
1322 }
1323 }
1324 } else if (From->Day == mDayOfMonth[From->Month - 1]) {
1325 if ((CompareHMS (From, To) >= 0)) {
1326 Adjacent = TRUE;
1327 }
1328 }
1329 }
1330 } else if (((From->Year + 1) == To->Year) &&
1331 (From->Month == 12) &&
1332 (From->Day == 31) &&
1333 (To->Month == 1) &&
1334 (To->Day == 1))
1335 {
1336 if ((CompareHMS (From, To) >= 0)) {
1337 Adjacent = TRUE;
1338 }
1339 }
1340
1341 return Adjacent;
1342}
1343
1344/**
1345 Get the century RTC address from the ACPI FADT table.
1346
1347 @return The century RTC address or 0 if not found.
1348**/
1349UINT8
1350GetCenturyRtcAddress (
1351 VOID
1352 )
1353{
1354 EFI_ACPI_2_0_FIXED_ACPI_DESCRIPTION_TABLE *Fadt;
1355
1356 Fadt = (EFI_ACPI_2_0_FIXED_ACPI_DESCRIPTION_TABLE *)EfiLocateFirstAcpiTable (
1357 EFI_ACPI_2_0_FIXED_ACPI_DESCRIPTION_TABLE_SIGNATURE
1358 );
1359
1360 if ((Fadt != NULL) &&
1361 (Fadt->Century > RTC_ADDRESS_REGISTER_D) && (Fadt->Century < 0x80)
1362 )
1363 {
1364 return Fadt->Century;
1365 } else {
1366 return 0;
1367 }
1368}
1369
1370/**
1371 Notification function of ACPI Table change.
1372
1373 This is a notification function registered on ACPI Table change event.
1374 It saves the Century address stored in ACPI FADT table.
1375
1376 @param Event Event whose notification function is being invoked.
1377 @param Context Pointer to the notification function's context.
1378
1379**/
1380VOID
1381EFIAPI
1382PcRtcAcpiTableChangeCallback (
1383 IN EFI_EVENT Event,
1384 IN VOID *Context
1385 )
1386{
1387 EFI_STATUS Status;
1388 EFI_TIME Time;
1389 UINT8 CenturyRtcAddress;
1390 UINT8 Century;
1391
1392 CenturyRtcAddress = GetCenturyRtcAddress ();
1393 if ((CenturyRtcAddress != 0) && (mModuleGlobal.CenturyRtcAddress != CenturyRtcAddress)) {
1394 mModuleGlobal.CenturyRtcAddress = CenturyRtcAddress;
1395 Status = PcRtcGetTime (&Time, NULL, &mModuleGlobal);
1396 if (!EFI_ERROR (Status)) {
1397 Century = (UINT8)(Time.Year / 100);
1398 Century = DecimalToBcd8 (Century);
1399 DEBUG ((DEBUG_INFO, "PcRtc: Write 0x%x to CMOS location 0x%x\n", Century, mModuleGlobal.CenturyRtcAddress));
1400 RtcWrite (mModuleGlobal.CenturyRtcAddress, Century);
1401 }
1402 }
1403}
Note: See TracBrowser for help on using the repository browser.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette