Skip to content

Commit cdedc45

Browse files
matjonalexandrebelloni
authored andcommitted
rtc: cmos: avoid UIP when reading alarm time
Some Intel chipsets disconnect the time and date RTC registers when the clock update is in progress: during this time reads may return bogus values and writes fail silently. This includes the RTC alarm registers. [1] cmos_read_alarm() did not take account for that, which caused alarm time reads to sometimes return bogus values. This can be shown with a test patch that I am attaching to this patch series. Fix this, by using mc146818_avoid_UIP(). [1] 7th Generation Intel ® Processor Family I/O for U/Y Platforms [...] Datasheet, Volume 1 of 2 (Intel's Document Number: 334658-006) Page 208 https://www.intel.com/content/dam/www/public/us/en/documents/datasheets/7th-and-8th-gen-core-family-mobile-u-y-processor-lines-i-o-datasheet-vol-1.pdf "If a RAM read from the ten time and date bytes is attempted during an update cycle, the value read do not necessarily represent the true contents of those locations. Any RAM writes under the same conditions are ignored." Signed-off-by: Mateusz Jończyk <[email protected]> Cc: Alessandro Zummo <[email protected]> Cc: Alexandre Belloni <[email protected]> Signed-off-by: Alexandre Belloni <[email protected]> Link: https://lore.kernel.org/r/[email protected]
1 parent 2c7d47a commit cdedc45

File tree

1 file changed

+49
-23
lines changed

1 file changed

+49
-23
lines changed

drivers/rtc/rtc-cmos.c

Lines changed: 49 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -249,10 +249,46 @@ static int cmos_set_time(struct device *dev, struct rtc_time *t)
249249
return mc146818_set_time(t);
250250
}
251251

252+
struct cmos_read_alarm_callback_param {
253+
struct cmos_rtc *cmos;
254+
struct rtc_time *time;
255+
unsigned char rtc_control;
256+
};
257+
258+
static void cmos_read_alarm_callback(unsigned char __always_unused seconds,
259+
void *param_in)
260+
{
261+
struct cmos_read_alarm_callback_param *p =
262+
(struct cmos_read_alarm_callback_param *)param_in;
263+
struct rtc_time *time = p->time;
264+
265+
time->tm_sec = CMOS_READ(RTC_SECONDS_ALARM);
266+
time->tm_min = CMOS_READ(RTC_MINUTES_ALARM);
267+
time->tm_hour = CMOS_READ(RTC_HOURS_ALARM);
268+
269+
if (p->cmos->day_alrm) {
270+
/* ignore upper bits on readback per ACPI spec */
271+
time->tm_mday = CMOS_READ(p->cmos->day_alrm) & 0x3f;
272+
if (!time->tm_mday)
273+
time->tm_mday = -1;
274+
275+
if (p->cmos->mon_alrm) {
276+
time->tm_mon = CMOS_READ(p->cmos->mon_alrm);
277+
if (!time->tm_mon)
278+
time->tm_mon = -1;
279+
}
280+
}
281+
282+
p->rtc_control = CMOS_READ(RTC_CONTROL);
283+
}
284+
252285
static int cmos_read_alarm(struct device *dev, struct rtc_wkalrm *t)
253286
{
254287
struct cmos_rtc *cmos = dev_get_drvdata(dev);
255-
unsigned char rtc_control;
288+
struct cmos_read_alarm_callback_param p = {
289+
.cmos = cmos,
290+
.time = &t->time,
291+
};
256292

257293
/* This not only a rtc_op, but also called directly */
258294
if (!is_valid_irq(cmos->irq))
@@ -263,28 +299,18 @@ static int cmos_read_alarm(struct device *dev, struct rtc_wkalrm *t)
263299
* the future.
264300
*/
265301

266-
spin_lock_irq(&rtc_lock);
267-
t->time.tm_sec = CMOS_READ(RTC_SECONDS_ALARM);
268-
t->time.tm_min = CMOS_READ(RTC_MINUTES_ALARM);
269-
t->time.tm_hour = CMOS_READ(RTC_HOURS_ALARM);
270-
271-
if (cmos->day_alrm) {
272-
/* ignore upper bits on readback per ACPI spec */
273-
t->time.tm_mday = CMOS_READ(cmos->day_alrm) & 0x3f;
274-
if (!t->time.tm_mday)
275-
t->time.tm_mday = -1;
276-
277-
if (cmos->mon_alrm) {
278-
t->time.tm_mon = CMOS_READ(cmos->mon_alrm);
279-
if (!t->time.tm_mon)
280-
t->time.tm_mon = -1;
281-
}
282-
}
283-
284-
rtc_control = CMOS_READ(RTC_CONTROL);
285-
spin_unlock_irq(&rtc_lock);
302+
/* Some Intel chipsets disconnect the alarm registers when the clock
303+
* update is in progress - during this time reads return bogus values
304+
* and writes may fail silently. See for example "7th Generation Intel®
305+
* Processor Family I/O for U/Y Platforms [...] Datasheet", section
306+
* 27.7.1
307+
*
308+
* Use the mc146818_avoid_UIP() function to avoid this.
309+
*/
310+
if (!mc146818_avoid_UIP(cmos_read_alarm_callback, &p))
311+
return -EIO;
286312

287-
if (!(rtc_control & RTC_DM_BINARY) || RTC_ALWAYS_BCD) {
313+
if (!(p.rtc_control & RTC_DM_BINARY) || RTC_ALWAYS_BCD) {
288314
if (((unsigned)t->time.tm_sec) < 0x60)
289315
t->time.tm_sec = bcd2bin(t->time.tm_sec);
290316
else
@@ -313,7 +339,7 @@ static int cmos_read_alarm(struct device *dev, struct rtc_wkalrm *t)
313339
}
314340
}
315341

316-
t->enabled = !!(rtc_control & RTC_AIE);
342+
t->enabled = !!(p.rtc_control & RTC_AIE);
317343
t->pending = 0;
318344

319345
return 0;

0 commit comments

Comments
 (0)