[U-Boot] [PATCH v2 1/6] drivers: rtc: resolve year 2038 problem in rtc_to_tm

Arnd Bergmann arnd at arndb.de
Mon Jul 9 08:13:29 UTC 2018


On Sun, Jul 8, 2018 at 11:32 PM, Alexander Graf <agraf at suse.de> wrote:
>
>
> On 07.07.18 23:39, Heinrich Schuchardt wrote:
>> Our implementation of rtc_to_tm() cannot handle dates of more than
>> 0x7fffffff seconds after 1970-01-01.
>>
>> Adopt the Linux kernel implementation.
>>
>> Signed-off-by: Heinrich Schuchardt <xypron.glpk at gmx.de>
>
> This patch is slightly out of scope for efi-next and the other patches
> are independent of it FWIW, so I'll leave it to others to review.
>
> I'll CC Arnd though, as I'm quite sure he has a good grip on anything
> 2038 :)

Ok, I'll point out some common issues with RTC devices:

>> diff --git a/drivers/rtc/i2c_rtc_emul.c b/drivers/rtc/i2c_rtc_emul.c
>> index bad61c377f..d4b33e59d6 100644
>> --- a/drivers/rtc/i2c_rtc_emul.c
>> +++ b/drivers/rtc/i2c_rtc_emul.c
>> @@ -96,7 +96,9 @@ static int sandbox_i2c_rtc_get(struct udevice *dev, struct rtc_time *time)
>>               now = plat->base_time;
>>       }
>>
>> -     return rtc_to_tm(now + plat->offset, time);
>> +     rtc_to_tm(now + plat->offset, time);
>> +
>> +     return 0;
>>  }

Looking at the sources here:
https://github.com/u-boot/u-boot/blob/master/drivers/rtc/i2c_rtc_emul.c

I found that 'now' is sometimes an 'int' and sometimes a 'long', but
both of them
are typically signed 32-bit numbers that still overflow. To actually make this
patch have any effect, u-boot would consistently need to use 64-bit signed
numbers in all the common code as well as in drivers for devices that support
times after 2038.

In particular the rtc_mktime() should use the same types as rtc_to_tm(), since
it does the reverse operation.

>> diff --git a/include/rtc.h b/include/rtc.h
>> index 746624d799..0d964d56d5 100644
>> --- a/include/rtc.h
>> +++ b/include/rtc.h
>> @@ -208,7 +208,18 @@ void rtc_write32(int reg, u32 value);
>>   * rtc_init() - Set up the real time clock ready for use
>>   */
>>  void rtc_init(void);
>> -#endif
>> +#endif /* CONFIG_DM_RTC */
>> +
>> +/**
>> + * is_leap_year - Check if year is a leap year
>> + *
>> + * @year     Year
>> + * @return   1 if leap year
>> + */
>> +static inline bool is_leap_year(unsigned int year)
>> +{
>> +     return (!(year % 4) && (year % 100)) || !(year % 400);
>> +}

Note that many hardware RTC implementations get the the
leap year computation wrong: Since 2000 was a leap year but
2100 is not, a typical shortcut was to assume that every fourth
year is a leap year. In RTC devices that have a two-digit
BCD number for storing the year, this means we can use the 00-99
range either to encode years 2000 through 2099, or (using the
typical wrapping) assuming that all numbers larger 69 are 1970 through
1999, while numbers from 00 to 69 are 2000 through 2099.

The i2c test driver has an unsigned 8-bit number for the year, starting
at 1900, so it could support years up to 2155, which is also a common
value in hardware RTCs, but I would assume that many of those also
have a problem on March 1, 2100, working on the assumption that there
is a Feb 29 that year, which requires adding another day to the
calculation after that day.

      Arnd


More information about the U-Boot mailing list