diff mbox series

[1/3] powerpc: mac: fix rtc read functions

Message ID 20180618140518.2920804-1-arnd@arndb.de
State New
Headers show
Series [1/3] powerpc: mac: fix rtc read functions | expand

Commit Message

Arnd Bergmann June 18, 2018, 2:05 p.m. UTC
As Mathieu pointed out, my conversion to time64_t was incorrect and resulted
in negative times to be read from the RTC. The problem is that during the
conversion from a byte array to a time64_t, the 'unsigned char' variable
holding the top byte gets turned into a negative signed 32-bit integer
before being assigned to the 64-bit variable for any times after 1972.

This changes the logic to cast to an unsigned 32-bit number first for
the Macintosh time and then convert that to the Unix time, which then gives
us a time in the documented 1904..2040 year range. I decided not to use
the longer 1970..2106 range that other drivers use, for consistency with
the literal interpretation of the register, but that could be easily
changed if we decide we want to support any Mac after 2040.

Just to be on the safe side, I'm also adding a WARN_ON that will trigger
if either the year 2040 has come and is observed by this driver, or we
run into an RTC that got set back to a pre-1970 date for some reason
(the two are indistinguishable).

The same code exists in arch/m68k/ and is patched in an identical way now
in a separate patch.

Fixes: 5bfd643583b2 ("powerpc: use time64_t in read_persistent_clock")
Reported-by: Mathieu Malaterre <malat@debian.org>
Signed-off-by: Arnd Bergmann <arnd@arndb.de>

---
 arch/powerpc/platforms/powermac/time.c | 21 ++++++++++++++++-----
 1 file changed, 16 insertions(+), 5 deletions(-)

-- 
2.9.0

Comments

Mathieu Malaterre June 18, 2018, 7:10 p.m. UTC | #1
On Mon, Jun 18, 2018 at 4:07 PM Arnd Bergmann <arnd@arndb.de> wrote:
>

> As Mathieu pointed out, my conversion to time64_t was incorrect and resulted

> in negative times to be read from the RTC. The problem is that during the

> conversion from a byte array to a time64_t, the 'unsigned char' variable

> holding the top byte gets turned into a negative signed 32-bit integer

> before being assigned to the 64-bit variable for any times after 1972.

>

> This changes the logic to cast to an unsigned 32-bit number first for

> the Macintosh time and then convert that to the Unix time, which then gives

> us a time in the documented 1904..2040 year range. I decided not to use

> the longer 1970..2106 range that other drivers use, for consistency with

> the literal interpretation of the register, but that could be easily

> changed if we decide we want to support any Mac after 2040.

>

> Just to be on the safe side, I'm also adding a WARN_ON that will trigger

> if either the year 2040 has come and is observed by this driver, or we

> run into an RTC that got set back to a pre-1970 date for some reason

> (the two are indistinguishable).

>

> The same code exists in arch/m68k/ and is patched in an identical way now

> in a separate patch.

>

> Fixes: 5bfd643583b2 ("powerpc: use time64_t in read_persistent_clock")

> Reported-by: Mathieu Malaterre <malat@debian.org>

> Signed-off-by: Arnd Bergmann <arnd@arndb.de>

> ---

>  arch/powerpc/platforms/powermac/time.c | 21 ++++++++++++++++-----

>  1 file changed, 16 insertions(+), 5 deletions(-)

>

> diff --git a/arch/powerpc/platforms/powermac/time.c b/arch/powerpc/platforms/powermac/time.c

> index 7c968e46736f..173a80630169 100644

> --- a/arch/powerpc/platforms/powermac/time.c

> +++ b/arch/powerpc/platforms/powermac/time.c

> @@ -42,7 +42,11 @@

>  #define DBG(x...)

>  #endif

>

> -/* Apparently the RTC stores seconds since 1 Jan 1904 */

> +/*

> + * Offset between Unix time (1970-based) and Mac time (1904-based). Cuda and PMU

> + * times wrap in 2040. If we need to handle later times, the read_time functions

> + * need to be changed to interpret wrapped times as post-2040.

> + */

>  #define RTC_OFFSET     2082844800

>

>  /*

> @@ -97,8 +101,11 @@ static time64_t cuda_get_time(void)

>         if (req.reply_len != 7)

>                 printk(KERN_ERR "cuda_get_time: got %d byte reply\n",

>                        req.reply_len);

> -       now = (req.reply[3] << 24) + (req.reply[4] << 16)

> -               + (req.reply[5] << 8) + req.reply[6];

> +       now = (u32)((req.reply[3] << 24) + (req.reply[4] << 16) +

> +                   (req.reply[5] << 8) + req.reply[6]);

> +       /* it's either after year 2040, or the RTC has gone backwards */

> +       WARN_ON(now < RTC_OFFSET);

> +

>         return now - RTC_OFFSET;

>  }

>

> @@ -140,8 +147,12 @@ static time64_t pmu_get_time(void)

>         if (req.reply_len != 4)

>                 printk(KERN_ERR "pmu_get_time: got %d byte reply from PMU\n",

>                        req.reply_len);

> -       now = (req.reply[0] << 24) + (req.reply[1] << 16)

> -               + (req.reply[2] << 8) + req.reply[3];

> +       now = (u32)((req.reply[0] << 24) + (req.reply[1] << 16) +

> +                   (req.reply[2] << 8) + req.reply[3]);

> +

> +       /* it's either after year 2040, or the RTC has gone backwards */

> +       WARN_ON(now < RTC_OFFSET);

> +

>         return now - RTC_OFFSET;

>  }


Sadly, trying again today does not work anymore. Adding some printk
just before WARN_ON:

+printk(KERN_ERR " rtc DBG pmu_get_time1: %lld %d %lld \n", now,
RTC_OFFSET, now - RTC_OFFSET );
+printk(KERN_ERR " rtc DBG pmu_get_time2: %x %x %x %x %d \n",
req.reply[0], req.reply[1], req.reply[2], req.reply[3] ,
req.reply_len);

leads to:

[    0.000000]  rtc DBG pmu_get_time1: 14096662 2082844800 -2068748138
[    0.000000]  rtc DBG pmu_get_time2: 0 d7 19 16 4
[    0.000000] WARNING: CPU: 0 PID: 0 at
../arch/powerpc/platforms/powermac/time.c:158 pmu_get_time+0x11c/0x16c
[    0.000000] Modules linked in:
[    0.000000] CPU: 0 PID: 0 Comm: swapper Not tainted 4.17.0+ #8
[    0.000000] NIP:  c0035658 LR: c0035640 CTR: c00d4a5c
[    0.000000] REGS: c0cf7da0 TRAP: 0700   Not tainted  (4.17.0+)
[    0.000000] MSR:  00021032 <ME,IR,DR,RI>  CR: 84000848  XER: 00000000
[    0.000000]
               GPR00: c0035640 c0cf7e50 c0a41bd0 00000025 00000001
00000051 c0d56430 00000000
               GPR08: 00000051 00000001 c0aa4b38 00000004 24000842
00000000 ffbbf4c0 0013da1c
               GPR16: 0013da18 0013da20 00000000 00000000 00b81044
01696028 01697e02 40140000
               GPR24: 00000000 00d71000 c09debec dfff1120 000001a4
00d71916 ffffffff 84b16896
[    0.000000] NIP [c0035658] pmu_get_time+0x11c/0x16c
[    0.000000] LR [c0035640] pmu_get_time+0x104/0x16c
[    0.000000] Call Trace:
[    0.000000] [c0cf7e50] [c0035640] pmu_get_time+0x104/0x16c (unreliable)
[    0.000000] [c0cf7ed0] [c00113fc] read_persistent_clock64+0x78/0x1ac
[    0.000000] [c0cf7f30] [c09ad470] timekeeping_init+0x24/0x2f4
[    0.000000] [c0cf7fc0] [c09986f0] start_kernel+0x34c/0x490
[    0.000000] [c0cf7ff0] [00003444] 0x3444
[    0.000000] Instruction dump:
[    0.000000] 88e1002f 3863404c 88c1002e 88a1002d 8881002c 4809baf1
3d207c25 6129b07f
[    0.000000] 7f9d4840 39200001 409d0008 39200000 <0f090000> 4bffff20
3c60c0c1 7fe4fb78
[    0.000000] random: get_random_bytes called from
print_oops_end_marker+0x6c/0x90 with crng_init=0
[    0.000000] ---[ end trace 93a1b37c50fb7a33 ]---


and then later on :

[    5.535354]  rtc DBG pmu_get_time1: 14096667 2082844800 -2068748133
[    5.539988]  rtc DBG pmu_get_time2: 0 d7 19 1b 4
[    5.544477] WARNING: CPU: 0 PID: 1 at
../arch/powerpc/platforms/powermac/time.c:158 pmu_get_time+0x11c/0x16c
[    5.553536] Modules linked in:
[    5.558076] CPU: 0 PID: 1 Comm: swapper Tainted: G        W
4.17.0+ #8
[    5.562769] NIP:  c0035658 LR: c0035640 CTR: c00d4a5c
[    5.567437] REGS: df4e7a90 TRAP: 0700   Tainted: G        W
 (4.17.0+)
[    5.572201] MSR:  00029032 <EE,ME,IR,DR,RI>  CR: 88008288  XER: 00000000
[    5.577028]
               GPR00: c0035640 df4e7b40 df4e8000 00000025 00000001
00000344 00000400 00000000
               GPR08: 00000003 00000001 c0aa4b38 00000000 28008282
00000000 c09dfc4c c09dfc28
               GPR16: 00000690 c0aedae0 c0aee64c c0aecf50 00000054
c0ae7488 c0aea0f4 00000000
               GPR24: c0d70000 c0a85850 c086fc74 df4e7c5c df4e7ce8
00d7191b ffffffff 84b1689b
[    5.601871] NIP [c0035658] pmu_get_time+0x11c/0x16c
[    5.606952] LR [c0035640] pmu_get_time+0x104/0x16c
[    5.612042] Call Trace:
[    5.617079] [df4e7b40] [c0035640] pmu_get_time+0x104/0x16c (unreliable)
[    5.622297] [df4e7bc0] [c003572c] pmac_get_rtc_time+0x34/0x4c
[    5.627509] [df4e7bd0] [c001076c] rtc_generic_get_time+0x2c/0x40
[    5.632740] [df4e7be0] [c0611fb8] __rtc_read_time+0x70/0x13c
[    5.637977] [df4e7c00] [c06120f0] rtc_read_time+0x6c/0x130
[    5.643230] [df4e7c30] [c0613324] __rtc_read_alarm+0x34/0x684
[    5.648503] [df4e7ce0] [c060fe18] rtc_device_register+0x88/0x218
[    5.653804] [df4e7d40] [c061000c] devm_rtc_device_register+0x64/0xc4
[    5.659127] [df4e7d60] [c09d2630] generic_rtc_probe+0x50/0x78
[    5.664457] [df4e7d70] [c055eefc] platform_drv_probe+0xa8/0x128
[    5.669735] [df4e7d90] [c055a780] driver_probe_device+0x354/0x6fc
[    5.674905] [df4e7dd0] [c055acc8] __driver_attach+0x1a0/0x22c
[    5.679971] [df4e7df0] [c05565c8] bus_for_each_dev+0x84/0xdc
[    5.685017] [df4e7e20] [c0558e78] bus_add_driver+0x188/0x348
[    5.690021] [df4e7e40] [c055c20c] driver_register+0xa0/0x18c
[    5.694982] [df4e7e50] [c055f3a8] __platform_driver_probe+0x8c/0x198
[    5.699918] [df4e7e70] [c0005800] do_one_initcall+0x64/0x280
[    5.704726] [df4e7ee0] [c0998bd8] kernel_init_freeable+0x3a4/0x444
[    5.709433] [df4e7f30] [c00049f8] kernel_init+0x24/0x118
[    5.713995] [df4e7f40] [c001b1c4] ret_from_kernel_thread+0x14/0x1c
[    5.718536] Instruction dump:
[    5.723041] 88e1002f 3863404c 88c1002e 88a1002d 8881002c 4809baf1
3d207c25 6129b07f
[    5.727651] 7f9d4840 39200001 409d0008 39200000 <0f090000> 4bffff20
3c60c0c1 7fe4fb78
[    5.732196] ---[ end trace 93a1b37c50fb7a34 ]---
[    5.736768] ================================================================================
[    5.741519] UBSAN: Undefined behaviour in ../drivers/rtc/rtc-lib.c:87:22
[    5.746295] signed integer overflow:
[    5.751002] 1193026 * 3600 cannot be represented in type 'int'
[    5.755739] CPU: 0 PID: 1 Comm: swapper Tainted: G        W
4.17.0+ #8
[    5.760529] Call Trace:
[    5.765124] [df4e7b00] [c04817ec] ubsan_epilogue+0x18/0x4c (unreliable)
[    5.769871] [df4e7b10] [c048218c] handle_overflow+0xbc/0xdc
[    5.774580] [df4e7b90] [c060e27c] rtc_time64_to_tm+0x344/0x388
[    5.779264] [df4e7bd0] [c001076c] rtc_generic_get_time+0x2c/0x40
[    5.783841] [df4e7be0] [c0611fb8] __rtc_read_time+0x70/0x13c
[    5.788395] [df4e7c00] [c06120f0] rtc_read_time+0x6c/0x130
[    5.792929] [df4e7c30] [c0613324] __rtc_read_alarm+0x34/0x684
[    5.797415] [df4e7ce0] [c060fe18] rtc_device_register+0x88/0x218
[    5.801895] [df4e7d40] [c061000c] devm_rtc_device_register+0x64/0xc4
[    5.806395] [df4e7d60] [c09d2630] generic_rtc_probe+0x50/0x78
[    5.810889] [df4e7d70] [c055eefc] platform_drv_probe+0xa8/0x128
[    5.815405] [df4e7d90] [c055a780] driver_probe_device+0x354/0x6fc
[    5.819929] [df4e7dd0] [c055acc8] __driver_attach+0x1a0/0x22c
[    5.824440] [df4e7df0] [c05565c8] bus_for_each_dev+0x84/0xdc
[    5.828922] [df4e7e20] [c0558e78] bus_add_driver+0x188/0x348
[    5.833366] [df4e7e40] [c055c20c] driver_register+0xa0/0x18c
[    5.837789] [df4e7e50] [c055f3a8] __platform_driver_probe+0x8c/0x198
[    5.842244] [df4e7e70] [c0005800] do_one_initcall+0x64/0x280
[    5.846700] [df4e7ee0] [c0998bd8] kernel_init_freeable+0x3a4/0x444
[    5.851186] [df4e7f30] [c00049f8] kernel_init+0x24/0x118
[    5.855656] [df4e7f40] [c001b1c4] ret_from_kernel_thread+0x14/0x1c
[    5.860171] ================================================================================
[    5.864998] device: 'rtc0': device_add



> --

> 2.9.0

>
Andreas Schwab June 18, 2018, 8:04 p.m. UTC | #2
On Jun 18 2018, Mathieu Malaterre <malat@debian.org> wrote:

> Sadly, trying again today does not work anymore. Adding some printk

> just before WARN_ON:

>

> +printk(KERN_ERR " rtc DBG pmu_get_time1: %lld %d %lld \n", now,

> RTC_OFFSET, now - RTC_OFFSET );

> +printk(KERN_ERR " rtc DBG pmu_get_time2: %x %x %x %x %d \n",

> req.reply[0], req.reply[1], req.reply[2], req.reply[3] ,

> req.reply_len);

>

> leads to:

>

> [    0.000000]  rtc DBG pmu_get_time1: 14096662 2082844800 -2068748138

> [    0.000000]  rtc DBG pmu_get_time2: 0 d7 19 16 4


A good value would have 0xd7 in the first byte.  The problem is that
pmu_set_rtc_time is also broken, and leads to an invalid time value
stored in the RTC.  Since pmu_request is a varargs function passing
values of type time64_t without casting won't work.

You need to reset your RTC before you can continue.

I think the right fix is to change nowtime in pmu_set_rtc_time and
cuda_set_rtc_time back to unsigned int (or to u32).

Andreas.

-- 
Andreas Schwab, schwab@linux-m68k.org
GPG Key fingerprint = 7578 EB47 D4E5 4D69 2510  2552 DF73 E780 A9DA AEC1
"And now for something completely different."
Mathieu Malaterre June 19, 2018, 5:47 a.m. UTC | #3
On Mon, Jun 18, 2018 at 10:04 PM Andreas Schwab <schwab@linux-m68k.org> wrote:
>

> On Jun 18 2018, Mathieu Malaterre <malat@debian.org> wrote:

>

> > Sadly, trying again today does not work anymore. Adding some printk

> > just before WARN_ON:

> >

> > +printk(KERN_ERR " rtc DBG pmu_get_time1: %lld %d %lld \n", now,

> > RTC_OFFSET, now - RTC_OFFSET );

> > +printk(KERN_ERR " rtc DBG pmu_get_time2: %x %x %x %x %d \n",

> > req.reply[0], req.reply[1], req.reply[2], req.reply[3] ,

> > req.reply_len);

> >

> > leads to:

> >

> > [    0.000000]  rtc DBG pmu_get_time1: 14096662 2082844800 -2068748138

> > [    0.000000]  rtc DBG pmu_get_time2: 0 d7 19 16 4

>

> A good value would have 0xd7 in the first byte.  The problem is that

> pmu_set_rtc_time is also broken, and leads to an invalid time value

> stored in the RTC.  Since pmu_request is a varargs function passing

> values of type time64_t without casting won't work.

>

> You need to reset your RTC before you can continue.


Indeed that was silly, I was not paying attention. I'll try again tonight.

> I think the right fix is to change nowtime in pmu_set_rtc_time and

> cuda_set_rtc_time back to unsigned int (or to u32).

>

> Andreas.

>

> --

> Andreas Schwab, schwab@linux-m68k.org

> GPG Key fingerprint = 7578 EB47 D4E5 4D69 2510  2552 DF73 E780 A9DA AEC1

> "And now for something completely different."
Arnd Bergmann June 19, 2018, 10:18 a.m. UTC | #4
On Mon, Jun 18, 2018 at 10:04 PM, Andreas Schwab <schwab@linux-m68k.org> wrote:
> On Jun 18 2018, Mathieu Malaterre <malat@debian.org> wrote:

>

>> Sadly, trying again today does not work anymore. Adding some printk

>> just before WARN_ON:

>>

>> +printk(KERN_ERR " rtc DBG pmu_get_time1: %lld %d %lld \n", now,

>> RTC_OFFSET, now - RTC_OFFSET );

>> +printk(KERN_ERR " rtc DBG pmu_get_time2: %x %x %x %x %d \n",

>> req.reply[0], req.reply[1], req.reply[2], req.reply[3] ,

>> req.reply_len);

>>

>> leads to:

>>

>> [    0.000000]  rtc DBG pmu_get_time1: 14096662 2082844800 -2068748138

>> [    0.000000]  rtc DBG pmu_get_time2: 0 d7 19 16 4

>

> A good value would have 0xd7 in the first byte.  The problem is that

> pmu_set_rtc_time is also broken, and leads to an invalid time value

> stored in the RTC.  Since pmu_request is a varargs function passing

> values of type time64_t without casting won't work.

>

> You need to reset your RTC before you can continue.

>

> I think the right fix is to change nowtime in pmu_set_rtc_time and

> cuda_set_rtc_time back to unsigned int (or to u32).


Thanks for the additional analysis. I'll do it like you suggest and send
an updated patch.

       Arnd
diff mbox series

Patch

diff --git a/arch/powerpc/platforms/powermac/time.c b/arch/powerpc/platforms/powermac/time.c
index 7c968e46736f..173a80630169 100644
--- a/arch/powerpc/platforms/powermac/time.c
+++ b/arch/powerpc/platforms/powermac/time.c
@@ -42,7 +42,11 @@ 
 #define DBG(x...)
 #endif
 
-/* Apparently the RTC stores seconds since 1 Jan 1904 */
+/*
+ * Offset between Unix time (1970-based) and Mac time (1904-based). Cuda and PMU
+ * times wrap in 2040. If we need to handle later times, the read_time functions
+ * need to be changed to interpret wrapped times as post-2040.
+ */
 #define RTC_OFFSET	2082844800
 
 /*
@@ -97,8 +101,11 @@  static time64_t cuda_get_time(void)
 	if (req.reply_len != 7)
 		printk(KERN_ERR "cuda_get_time: got %d byte reply\n",
 		       req.reply_len);
-	now = (req.reply[3] << 24) + (req.reply[4] << 16)
-		+ (req.reply[5] << 8) + req.reply[6];
+	now = (u32)((req.reply[3] << 24) + (req.reply[4] << 16) +
+		    (req.reply[5] << 8) + req.reply[6]);
+	/* it's either after year 2040, or the RTC has gone backwards */
+	WARN_ON(now < RTC_OFFSET);
+
 	return now - RTC_OFFSET;
 }
 
@@ -140,8 +147,12 @@  static time64_t pmu_get_time(void)
 	if (req.reply_len != 4)
 		printk(KERN_ERR "pmu_get_time: got %d byte reply from PMU\n",
 		       req.reply_len);
-	now = (req.reply[0] << 24) + (req.reply[1] << 16)
-		+ (req.reply[2] << 8) + req.reply[3];
+	now = (u32)((req.reply[0] << 24) + (req.reply[1] << 16)	+
+		    (req.reply[2] << 8) + req.reply[3]);
+
+	/* it's either after year 2040, or the RTC has gone backwards */
+	WARN_ON(now < RTC_OFFSET);
+
 	return now - RTC_OFFSET;
 }