diff options
author | Ard Biesheuvel <ard.biesheuvel@linaro.org> | 2015-06-09 05:15:35 -0400 |
---|---|---|
committer | Alexandre Belloni <alexandre.belloni@free-electrons.com> | 2015-06-24 19:13:44 -0400 |
commit | b2bd2370a25b78a7090ac701836d414cdb731bea (patch) | |
tree | c8add915914b5d963912ba61d065624d3f5ebcfa | |
parent | c86a6c28957a9e8e9a71582a32e96971ad411ffe (diff) |
rtc: efi: use correct EFI 'epoch'
The rtc-efi driver declares that the EFI 'epoch' is 1/1/1998, but
the UEFI spec does not define it at all. It does define a range of
[1900, 9999] for the 'Year' member of the EFI_TIME struct, so let's
use 1900 as the minimum year and not 1998.
Also, move the validation of the year to the convert_from_efi_time()
routine where all other EFI_TIME fields are validated as well.
This prevents rtc_read_time() failures when the RTC that backs the
EFI time services is set to a date before 1998, e.g., when it has
lost power.
This also optimizes the compute_wday() routine, by replacing the for
loop with a simple arithmetic expression, and by reusing the yearday
value that we need to compute anyway when populating the
rtc_time::tm_yday field.
Cc: Alessandro Zummo <a.zummo@towertech.it>
Cc: Alexandre Belloni <alexandre.belloni@free-electrons.com>
Cc: rtc-linux@googlegroups.com
Signed-off-by: Ard Biesheuvel <ard.biesheuvel@linaro.org>
Signed-off-by: Alexandre Belloni <alexandre.belloni@free-electrons.com>
-rw-r--r-- | drivers/rtc/rtc-efi.c | 39 |
1 files changed, 14 insertions, 25 deletions
diff --git a/drivers/rtc/rtc-efi.c b/drivers/rtc/rtc-efi.c index c5fbabb1e0fc..3806961b4348 100644 --- a/drivers/rtc/rtc-efi.c +++ b/drivers/rtc/rtc-efi.c | |||
@@ -24,10 +24,6 @@ | |||
24 | #include <linux/efi.h> | 24 | #include <linux/efi.h> |
25 | 25 | ||
26 | #define EFI_ISDST (EFI_TIME_ADJUST_DAYLIGHT|EFI_TIME_IN_DAYLIGHT) | 26 | #define EFI_ISDST (EFI_TIME_ADJUST_DAYLIGHT|EFI_TIME_IN_DAYLIGHT) |
27 | /* | ||
28 | * EFI Epoch is 1/1/1998 | ||
29 | */ | ||
30 | #define EFI_RTC_EPOCH 1998 | ||
31 | 27 | ||
32 | /* | 28 | /* |
33 | * returns day of the year [0-365] | 29 | * returns day of the year [0-365] |
@@ -38,31 +34,24 @@ compute_yday(efi_time_t *eft) | |||
38 | /* efi_time_t.month is in the [1-12] so, we need -1 */ | 34 | /* efi_time_t.month is in the [1-12] so, we need -1 */ |
39 | return rtc_year_days(eft->day, eft->month - 1, eft->year); | 35 | return rtc_year_days(eft->day, eft->month - 1, eft->year); |
40 | } | 36 | } |
37 | |||
41 | /* | 38 | /* |
42 | * returns day of the week [0-6] 0=Sunday | 39 | * returns day of the week [0-6] 0=Sunday |
43 | * | ||
44 | * Don't try to provide a year that's before 1998, please ! | ||
45 | */ | 40 | */ |
46 | static int | 41 | static int |
47 | compute_wday(efi_time_t *eft) | 42 | compute_wday(efi_time_t *eft, int yday) |
48 | { | 43 | { |
49 | int y; | 44 | int ndays = eft->year * (365 % 7) |
50 | int ndays = 0; | 45 | + (eft->year - 1) / 4 |
51 | 46 | - (eft->year - 1) / 100 | |
52 | if (eft->year < EFI_RTC_EPOCH) { | 47 | + (eft->year - 1) / 400 |
53 | pr_err("EFI year < " __stringify(EFI_RTC_EPOCH) ", invalid date\n"); | 48 | + yday; |
54 | return -1; | ||
55 | } | ||
56 | |||
57 | for (y = EFI_RTC_EPOCH; y < eft->year; y++) | ||
58 | ndays += 365 + (is_leap_year(y) ? 1 : 0); | ||
59 | |||
60 | ndays += compute_yday(eft); | ||
61 | 49 | ||
62 | /* | 50 | /* |
63 | * 4=1/1/1998 was a Thursday | 51 | * 1/1/0000 may or may not have been a Sunday (if it ever existed at |
52 | * all) but assuming it was makes this calculation work correctly. | ||
64 | */ | 53 | */ |
65 | return (ndays + 4) % 7; | 54 | return ndays % 7; |
66 | } | 55 | } |
67 | 56 | ||
68 | static void | 57 | static void |
@@ -103,16 +92,16 @@ convert_from_efi_time(efi_time_t *eft, struct rtc_time *wtime) | |||
103 | if (!eft->month || eft->month > 12) | 92 | if (!eft->month || eft->month > 12) |
104 | return false; | 93 | return false; |
105 | wtime->tm_mon = eft->month - 1; | 94 | wtime->tm_mon = eft->month - 1; |
106 | wtime->tm_year = eft->year - 1900; | ||
107 | 95 | ||
108 | /* day of the week [0-6], Sunday=0 */ | 96 | if (eft->year < 1900 || eft->year > 9999) |
109 | wtime->tm_wday = compute_wday(eft); | ||
110 | if (wtime->tm_wday < 0) | ||
111 | return false; | 97 | return false; |
98 | wtime->tm_year = eft->year - 1900; | ||
112 | 99 | ||
113 | /* day in the year [1-365]*/ | 100 | /* day in the year [1-365]*/ |
114 | wtime->tm_yday = compute_yday(eft); | 101 | wtime->tm_yday = compute_yday(eft); |
115 | 102 | ||
103 | /* day of the week [0-6], Sunday=0 */ | ||
104 | wtime->tm_wday = compute_wday(eft, wtime->tm_yday); | ||
116 | 105 | ||
117 | switch (eft->daylight & EFI_ISDST) { | 106 | switch (eft->daylight & EFI_ISDST) { |
118 | case EFI_ISDST: | 107 | case EFI_ISDST: |