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