From: "tip-bot2 for Thomas Gleixner" <tip-bot2@linutronix.de>
To: linux-tip-commits@vger.kernel.org
Cc: Thomas Gleixner <tglx@linutronix.de>,
Alexandre Belloni <alexandre.belloni@bootlin.com>,
x86@kernel.org, linux-kernel@vger.kernel.org
Subject: [tip: timers/core] rtc: mc146818: Prevent reading garbage
Date: Fri, 11 Dec 2020 10:07:49 -0000 [thread overview]
Message-ID: <160768126932.3364.17815331718961415909.tip-bot2@tip-bot2> (raw)
In-Reply-To: <20201206220541.594826678@linutronix.de>
The following commit has been merged into the timers/core branch of tip:
Commit-ID: 05a0302c35481e9b47fb90ba40922b0a4cae40d8
Gitweb: https://git.kernel.org/tip/05a0302c35481e9b47fb90ba40922b0a4cae40d8
Author: Thomas Gleixner <tglx@linutronix.de>
AuthorDate: Sun, 06 Dec 2020 22:46:14 +01:00
Committer: Thomas Gleixner <tglx@linutronix.de>
CommitterDate: Fri, 11 Dec 2020 10:40:52 +01:00
rtc: mc146818: Prevent reading garbage
The MC146818 driver is prone to read garbage from the RTC. There are
several issues all related to the update cycle of the MC146818. The chip
increments seconds obviously once per second and indicates that by a bit in
a register. The bit goes high 244us before the actual update starts. During
the update the readout of the time values is undefined.
The code just checks whether the update in progress bit (UIP) is set before
reading the clock. If it's set it waits arbitrary 20ms before retrying,
which is ample because the maximum update time is ~2ms.
But this check does not guarantee that the UIP bit goes high and the actual
update happens during the readout. So the following can happen
0.997 UIP = False
-> Interrupt/NMI/preemption
0.998 UIP -> True
0.999 Readout <- Undefined
To prevent this rework the code so it checks UIP before and after the
readout and if set after the readout try again.
But that's not enough to cover the following:
0.997 UIP = False
Readout seconds
-> NMI (or vCPU scheduled out)
0.998 UIP -> True
update completes
UIP -> False
1.000 Readout minutes,....
UIP check succeeds
That can make the readout wrong up to 59 seconds.
To prevent this, read the seconds value before the first UIP check,
validate it after checking UIP and after reading out the rest.
It's amazing that the original i386 code had this actually correct and
the generic implementation of the MC146818 driver got it wrong in 2002 and
it stayed that way until today.
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
Acked-by: Alexandre Belloni <alexandre.belloni@bootlin.com>
Link: https://lore.kernel.org/r/20201206220541.594826678@linutronix.de
---
drivers/rtc/rtc-mc146818-lib.c | 64 ++++++++++++++++++++-------------
1 file changed, 39 insertions(+), 25 deletions(-)
diff --git a/drivers/rtc/rtc-mc146818-lib.c b/drivers/rtc/rtc-mc146818-lib.c
index 2ecd875..98048bb 100644
--- a/drivers/rtc/rtc-mc146818-lib.c
+++ b/drivers/rtc/rtc-mc146818-lib.c
@@ -8,41 +8,41 @@
#include <linux/acpi.h>
#endif
-/*
- * Returns true if a clock update is in progress
- */
-static inline unsigned char mc146818_is_updating(void)
-{
- unsigned char uip;
- unsigned long flags;
-
- spin_lock_irqsave(&rtc_lock, flags);
- uip = (CMOS_READ(RTC_FREQ_SELECT) & RTC_UIP);
- spin_unlock_irqrestore(&rtc_lock, flags);
- return uip;
-}
-
unsigned int mc146818_get_time(struct rtc_time *time)
{
unsigned char ctrl;
unsigned long flags;
unsigned char century = 0;
+ bool retry;
#ifdef CONFIG_MACH_DECSTATION
unsigned int real_year;
#endif
+again:
+ spin_lock_irqsave(&rtc_lock, flags);
/*
- * read RTC once any update in progress is done. The update
- * can take just over 2ms. We wait 20ms. There is no need to
- * to poll-wait (up to 1s - eeccch) for the falling edge of RTC_UIP.
- * If you need to know *exactly* when a second has started, enable
- * periodic update complete interrupts, (via ioctl) and then
- * immediately read /dev/rtc which will block until you get the IRQ.
- * Once the read clears, read the RTC time (again via ioctl). Easy.
+ * Check whether there is an update in progress during which the
+ * readout is unspecified. The maximum update time is ~2ms. Poll
+ * every msec for completion.
+ *
+ * Store the second value before checking UIP so a long lasting NMI
+ * which happens to hit after the UIP check cannot make an update
+ * cycle invisible.
*/
- if (mc146818_is_updating())
- mdelay(20);
+ time->tm_sec = CMOS_READ(RTC_SECONDS);
+
+ if (CMOS_READ(RTC_FREQ_SELECT) & RTC_UIP) {
+ spin_unlock_irqrestore(&rtc_lock, flags);
+ mdelay(1);
+ goto again;
+ }
+
+ /* Revalidate the above readout */
+ if (time->tm_sec != CMOS_READ(RTC_SECONDS)) {
+ spin_unlock_irqrestore(&rtc_lock, flags);
+ goto again;
+ }
/*
* Only the values that we read from the RTC are set. We leave
@@ -50,8 +50,6 @@ unsigned int mc146818_get_time(struct rtc_time *time)
* RTC has RTC_DAY_OF_WEEK, we ignore it, as it is only updated
* by the RTC when initially set to a non-zero value.
*/
- spin_lock_irqsave(&rtc_lock, flags);
- time->tm_sec = CMOS_READ(RTC_SECONDS);
time->tm_min = CMOS_READ(RTC_MINUTES);
time->tm_hour = CMOS_READ(RTC_HOURS);
time->tm_mday = CMOS_READ(RTC_DAY_OF_MONTH);
@@ -66,8 +64,24 @@ unsigned int mc146818_get_time(struct rtc_time *time)
century = CMOS_READ(acpi_gbl_FADT.century);
#endif
ctrl = CMOS_READ(RTC_CONTROL);
+ /*
+ * Check for the UIP bit again. If it is set now then
+ * the above values may contain garbage.
+ */
+ retry = CMOS_READ(RTC_FREQ_SELECT) & RTC_UIP;
+ /*
+ * A NMI might have interrupted the above sequence so check whether
+ * the seconds value has changed which indicates that the NMI took
+ * longer than the UIP bit was set. Unlikely, but possible and
+ * there is also virt...
+ */
+ retry |= time->tm_sec != CMOS_READ(RTC_SECONDS);
+
spin_unlock_irqrestore(&rtc_lock, flags);
+ if (retry)
+ goto again;
+
if (!(ctrl & RTC_DM_BINARY) || RTC_ALWAYS_BCD)
{
time->tm_sec = bcd2bin(time->tm_sec);
next prev parent reply other threads:[~2020-12-11 10:09 UTC|newest]
Thread overview: 54+ messages / expand[flat|nested] mbox.gz Atom feed top
2020-12-06 21:46 [patch 0/8] ntp/rtc: Fixes and cleanups Thomas Gleixner
2020-12-06 21:46 ` [patch 1/8] rtc: mc146818: Prevent reading garbage Thomas Gleixner
2020-12-11 10:07 ` tip-bot2 for Thomas Gleixner [this message]
2021-01-25 18:40 ` [patch 1/8] rtc: mc146818: Prevent reading garbage - bug Mickaël Salaün
2021-01-26 13:26 ` Thomas Gleixner
2021-01-26 14:17 ` Mickaël Salaün
2021-01-26 15:35 ` Thomas Gleixner
2021-01-26 17:02 ` [PATCH V2] rtc: mc146818: Detect and handle broken RTCs Thomas Gleixner
2021-01-26 18:00 ` Alexandre Belloni
2021-01-27 8:41 ` [tip: timers/urgent] " tip-bot2 for Thomas Gleixner
2021-01-31 10:59 ` [PATCH V2] " Dirk Gouders
2021-02-01 13:53 ` Serge Belyshev
2021-02-01 19:09 ` [PATCH] rtc: mc146818: Dont test for bit 0-5 in Register D Thomas Gleixner
2021-02-01 19:24 ` [PATCH V2] " Thomas Gleixner
2021-02-01 19:32 ` Linus Torvalds
2021-02-01 19:40 ` Thomas Gleixner
2021-02-11 23:09 ` Maciej W. Rozycki
2021-02-01 19:38 ` Alexandre Belloni
2021-02-01 20:09 ` Borislav Petkov
2021-02-01 20:15 ` Dirk Gouders
2021-02-02 4:22 ` Len Brown
2021-02-02 19:40 ` [tip: timers/urgent] " tip-bot2 for Thomas Gleixner
2021-02-03 13:00 ` [PATCH V2] " Mickaël Salaün
2020-12-06 21:46 ` [patch 2/8] rtc: mc146818: Reduce spinlock section in mc146818_set_time() Thomas Gleixner
2020-12-11 10:07 ` [tip: timers/core] " tip-bot2 for Thomas Gleixner
2020-12-06 21:46 ` [patch 3/8] rtc: cmos: Make rtc_cmos sync offset correct Thomas Gleixner
2020-12-07 20:50 ` Jason Gunthorpe
2020-12-11 10:07 ` [tip: timers/core] " tip-bot2 for Thomas Gleixner
2020-12-06 21:46 ` [patch 4/8] rtc: core: Make the sync offset default more realistic Thomas Gleixner
2020-12-07 20:55 ` Jason Gunthorpe
2020-12-10 23:59 ` Alexandre Belloni
2020-12-11 0:23 ` Thomas Gleixner
2020-12-11 0:28 ` Alexandre Belloni
2020-12-11 10:07 ` [tip: timers/core] " tip-bot2 for Thomas Gleixner
2020-12-06 21:46 ` [patch 5/8] ntp: Make the RTC synchronization more reliable Thomas Gleixner
2020-12-07 12:47 ` Miroslav Lichvar
2020-12-07 20:59 ` Jason Gunthorpe
2020-12-11 10:07 ` [tip: timers/core] " tip-bot2 for Thomas Gleixner
2020-12-29 19:41 ` [patch 5/8] " Geert Uytterhoeven
2021-01-11 10:12 ` Thomas Gleixner
2021-01-11 10:40 ` Geert Uytterhoeven
2020-12-06 21:46 ` [patch 6/8] ntp, rtc: Move rtc_set_ntp_time() to ntp code Thomas Gleixner
2020-12-07 20:59 ` Jason Gunthorpe
2020-12-09 3:51 ` Alexandre Belloni
2020-12-11 10:07 ` [tip: timers/core] " tip-bot2 for Thomas Gleixner
2020-12-06 21:46 ` [patch 7/8] ntp: Make the RTC sync offset less obscure Thomas Gleixner
2020-12-11 10:07 ` [tip: timers/core] " tip-bot2 for Thomas Gleixner
2020-12-06 21:46 ` [patch 8/8] ntp: Consolidate the RTC update implementation Thomas Gleixner
2020-12-07 21:05 ` Jason Gunthorpe
2020-12-11 9:23 ` Thomas Gleixner
2020-12-11 10:07 ` [tip: timers/core] " tip-bot2 for Thomas Gleixner
2020-12-09 0:33 ` [patch 0/8] ntp/rtc: Fixes and cleanups Thomas Gleixner
2020-12-09 4:01 ` Alexandre Belloni
2020-12-09 12:39 ` Thomas Gleixner
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=160768126932.3364.17815331718961415909.tip-bot2@tip-bot2 \
--to=tip-bot2@linutronix.de \
--cc=alexandre.belloni@bootlin.com \
--cc=linux-kernel@vger.kernel.org \
--cc=linux-tip-commits@vger.kernel.org \
--cc=tglx@linutronix.de \
--cc=x86@kernel.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.