PcRtc.c 36 KB

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