From: Arnd Bergmann Date: Wed, 7 Mar 2018 21:30:59 +0000 (-0500) Subject: bnxt: fix bnxt_hwrm_fw_set_time for y2038 X-Git-Tag: v4.1.12-124.31.3~935 X-Git-Url: https://www.infradead.org/git/?a=commitdiff_plain;h=ef5225cde629877e4e830e9c9b557872c9931297;p=users%2Fjedix%2Flinux-maple.git bnxt: fix bnxt_hwrm_fw_set_time for y2038 Orabug: 27648355, 27648339 On 32-bit architectures, rtc_time_to_tm() returns incorrect results in 2038 or later, and do_gettimeofday() is broken for the same reason. This changes the code to use ktime_get_real_seconds() and time64_to_tm() instead, both of them are 2038-safe, and we can also get rid of the CONFIG_RTC_LIB dependency that way. Signed-off-by: Arnd Bergmann Signed-off-by: David S. Miller (cherry picked from commit 7dfaa7bc99498da1c6c4a48bee8d2d5265161a8c) Signed-off-by: Brian Maly Reviewed-by: Jack Vogel --- diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt.c b/drivers/net/ethernet/broadcom/bnxt/bnxt.c index 2a5d48083799..37e52dd87f8f 100644 --- a/drivers/net/ethernet/broadcom/bnxt/bnxt.c +++ b/drivers/net/ethernet/broadcom/bnxt/bnxt.c @@ -4884,16 +4884,14 @@ hwrm_ver_get_exit: int bnxt_hwrm_fw_set_time(struct bnxt *bp) { -#if IS_ENABLED(CONFIG_RTC_LIB) struct hwrm_fw_set_time_input req = {0}; - struct rtc_time tm; - struct timeval tv; + struct tm tm; + time64_t now = ktime_get_real_seconds(); if (bp->hwrm_spec_code < 0x10400) return -EOPNOTSUPP; - do_gettimeofday(&tv); - rtc_time_to_tm(tv.tv_sec, &tm); + time64_to_tm(now, 0, &tm); bnxt_hwrm_cmd_hdr_init(bp, &req, HWRM_FW_SET_TIME, -1, -1); req.year = cpu_to_le16(1900 + tm.tm_year); req.month = 1 + tm.tm_mon; @@ -4902,9 +4900,6 @@ int bnxt_hwrm_fw_set_time(struct bnxt *bp) req.minute = tm.tm_min; req.second = tm.tm_sec; return hwrm_send_message(bp, &req, sizeof(req), HWRM_CMD_TIMEOUT); -#else - return -EOPNOTSUPP; -#endif } static int bnxt_hwrm_port_qstats(struct bnxt *bp) diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt_compat.h b/drivers/net/ethernet/broadcom/bnxt/bnxt_compat.h index 4bc0a935b9d6..98f5ef72a716 100644 --- a/drivers/net/ethernet/broadcom/bnxt/bnxt_compat.h +++ b/drivers/net/ethernet/broadcom/bnxt/bnxt_compat.h @@ -8,6 +8,7 @@ * the Free Software Foundation. */ +#include #include #include #include @@ -24,3 +25,99 @@ #ifndef SWITCHDEV_SET_OPS #define SWITCHDEV_SET_OPS(netdev, ops) ((netdev)->switchdev_ops = (ops)) #endif + +/* + * * Nonzero if YEAR is a leap year (every 4 years, + * * except every 100th isn't, and every 400th is). + * */ +static int __isleap(long year) +{ + return (year) % 4 == 0 && ((year) % 100 != 0 || (year) % 400 == 0); +} + +/* do a mathdiv for long type */ +static long math_div(long a, long b) +{ + return a / b - (a % b < 0); +} + +/* How many leap years between y1 and y2, y1 must less or equal to y2 */ +static long leaps_between(long y1, long y2) +{ + long leaps1 = math_div(y1 - 1, 4) - math_div(y1 - 1, 100) + + math_div(y1 - 1, 400); + long leaps2 = math_div(y2 - 1, 4) - math_div(y2 - 1, 100) + + math_div(y2 - 1, 400); + return leaps2 - leaps1; +} + +/* How many days come before each month (0-12). */ +static const unsigned short __mon_yday[2][13] = { + /* Normal years. */ + {0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365}, + /* Leap years. */ + {0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366} +}; + +#define SECS_PER_HOUR (60 * 60) +#define SECS_PER_DAY (SECS_PER_HOUR * 24) + +/** + * * time64_to_tm - converts the calendar time to local broken-down time + * * + * * @totalsecs the number of seconds elapsed since 00:00:00 on January 1, 1970, + * * Coordinated Universal Time (UTC). + * * @offset offset seconds adding to totalsecs. + * * @result pointer to struct tm variable to receive broken-down time + * */ +void time64_to_tm(time64_t totalsecs, int offset, struct tm *result) +{ + long days, rem, y; + int remainder; + const unsigned short *ip; + + days = div_s64_rem(totalsecs, SECS_PER_DAY, &remainder); + rem = remainder; + rem += offset; + while (rem < 0) { + rem += SECS_PER_DAY; + --days; + } + while (rem >= SECS_PER_DAY) { + rem -= SECS_PER_DAY; + ++days; + } + + result->tm_hour = rem / SECS_PER_HOUR; + rem %= SECS_PER_HOUR; + result->tm_min = rem / 60; + result->tm_sec = rem % 60; + + /* January 1, 1970 was a Thursday. */ + result->tm_wday = (4 + days) % 7; + if (result->tm_wday < 0) + result->tm_wday += 7; + + y = 1970; + + while (days < 0 || days >= (__isleap(y) ? 366 : 365)) { + /* Guess a corrected year, assuming 365 days per year. */ + long yg = y + math_div(days, 365); + + /* Adjust DAYS and Y to match the guessed year. */ + days -= (yg - y) * 365 + leaps_between(y, yg); + y = yg; + } + + result->tm_year = y - 1900; + + result->tm_yday = days; + + ip = __mon_yday[__isleap(y)]; + for (y = 11; days < ip[y]; y--) + continue; + days -= ip[y]; + result->tm_mon = y; + result->tm_mday = days + 1; +} +