From b68c18d69fcaf2204eff83522d53c17853d42783 Mon Sep 17 00:00:00 2001 From: bde Date: Sat, 25 Dec 1999 15:30:31 +0000 Subject: [PATCH] Fixed races accessing the RTC. The races apparently caused apm_default_resume() to sometimes set a very wrong time. (1) Accesses to the RTC index and data registers were not atomic enough. Interrupts were not masked. This was only good enough until an interrupt handler (rtcintr()) started accessing the RTC in FreeBSD-2.0. (2) Access to the block of time registers in inittodr() was not atomic enough. inittodr() has 244us to read the time registers. Interrupts were not masked. This was only good enough until something (apm) started calling inittodr() after boot time in FreeBSD-2.0. The fix for (2) also makes the timecounter update more atomic, although this is currently unimportant due to the low resolution of the RTC. Problem reported by: mckay --- sys/amd64/amd64/tsc.c | 20 ++++++++++++++++---- sys/amd64/isa/clock.c | 20 ++++++++++++++++---- sys/i386/i386/tsc.c | 20 ++++++++++++++++---- sys/i386/isa/clock.c | 20 ++++++++++++++++---- sys/isa/atrtc.c | 20 ++++++++++++++++---- 5 files changed, 80 insertions(+), 20 deletions(-) diff --git a/sys/amd64/amd64/tsc.c b/sys/amd64/amd64/tsc.c index e421fb1f67f0..11b142689145 100644 --- a/sys/amd64/amd64/tsc.c +++ b/sys/amd64/amd64/tsc.c @@ -550,23 +550,30 @@ int rtcin(reg) int reg; { + int s; u_char val; + s = splhigh(); outb(IO_RTC, reg); inb(0x84); val = inb(IO_RTC + 1); inb(0x84); + splx(s); return (val); } static __inline void writertc(u_char reg, u_char val) { + int s; + + s = splhigh(); inb(0x84); outb(IO_RTC, reg); inb(0x84); outb(IO_RTC + 1, val); inb(0x84); /* XXX work around wrong order in rtcin() */ + splx(s); } static __inline int @@ -847,7 +854,11 @@ inittodr(time_t base) /* wait for time update to complete */ /* If RTCSA_TUP is zero, we have at least 244us before next update */ - while (rtcin(RTC_STATUSA) & RTCSA_TUP); + s = splhigh(); + while (rtcin(RTC_STATUSA) & RTCSA_TUP) { + splx(s); + s = splhigh(); + } days = 0; #ifdef USE_RTC_CENTURY @@ -857,8 +868,10 @@ inittodr(time_t base) if (year < 1970) year += 100; #endif - if (year < 1970) + if (year < 1970) { + splx(s); goto wrong_time; + } month = readrtc(RTC_MONTH); for (m = 1; m < month; m++) days += daysinmonth[m-1]; @@ -880,12 +893,11 @@ inittodr(time_t base) y = time_second - sec; if (y <= -2 || y >= 2) { /* badly off, adjust it */ - s = splclock(); ts.tv_sec = sec; ts.tv_nsec = 0; set_timecounter(&ts); - splx(s); } + splx(s); return; wrong_time: diff --git a/sys/amd64/isa/clock.c b/sys/amd64/isa/clock.c index e421fb1f67f0..11b142689145 100644 --- a/sys/amd64/isa/clock.c +++ b/sys/amd64/isa/clock.c @@ -550,23 +550,30 @@ int rtcin(reg) int reg; { + int s; u_char val; + s = splhigh(); outb(IO_RTC, reg); inb(0x84); val = inb(IO_RTC + 1); inb(0x84); + splx(s); return (val); } static __inline void writertc(u_char reg, u_char val) { + int s; + + s = splhigh(); inb(0x84); outb(IO_RTC, reg); inb(0x84); outb(IO_RTC + 1, val); inb(0x84); /* XXX work around wrong order in rtcin() */ + splx(s); } static __inline int @@ -847,7 +854,11 @@ inittodr(time_t base) /* wait for time update to complete */ /* If RTCSA_TUP is zero, we have at least 244us before next update */ - while (rtcin(RTC_STATUSA) & RTCSA_TUP); + s = splhigh(); + while (rtcin(RTC_STATUSA) & RTCSA_TUP) { + splx(s); + s = splhigh(); + } days = 0; #ifdef USE_RTC_CENTURY @@ -857,8 +868,10 @@ inittodr(time_t base) if (year < 1970) year += 100; #endif - if (year < 1970) + if (year < 1970) { + splx(s); goto wrong_time; + } month = readrtc(RTC_MONTH); for (m = 1; m < month; m++) days += daysinmonth[m-1]; @@ -880,12 +893,11 @@ inittodr(time_t base) y = time_second - sec; if (y <= -2 || y >= 2) { /* badly off, adjust it */ - s = splclock(); ts.tv_sec = sec; ts.tv_nsec = 0; set_timecounter(&ts); - splx(s); } + splx(s); return; wrong_time: diff --git a/sys/i386/i386/tsc.c b/sys/i386/i386/tsc.c index e421fb1f67f0..11b142689145 100644 --- a/sys/i386/i386/tsc.c +++ b/sys/i386/i386/tsc.c @@ -550,23 +550,30 @@ int rtcin(reg) int reg; { + int s; u_char val; + s = splhigh(); outb(IO_RTC, reg); inb(0x84); val = inb(IO_RTC + 1); inb(0x84); + splx(s); return (val); } static __inline void writertc(u_char reg, u_char val) { + int s; + + s = splhigh(); inb(0x84); outb(IO_RTC, reg); inb(0x84); outb(IO_RTC + 1, val); inb(0x84); /* XXX work around wrong order in rtcin() */ + splx(s); } static __inline int @@ -847,7 +854,11 @@ inittodr(time_t base) /* wait for time update to complete */ /* If RTCSA_TUP is zero, we have at least 244us before next update */ - while (rtcin(RTC_STATUSA) & RTCSA_TUP); + s = splhigh(); + while (rtcin(RTC_STATUSA) & RTCSA_TUP) { + splx(s); + s = splhigh(); + } days = 0; #ifdef USE_RTC_CENTURY @@ -857,8 +868,10 @@ inittodr(time_t base) if (year < 1970) year += 100; #endif - if (year < 1970) + if (year < 1970) { + splx(s); goto wrong_time; + } month = readrtc(RTC_MONTH); for (m = 1; m < month; m++) days += daysinmonth[m-1]; @@ -880,12 +893,11 @@ inittodr(time_t base) y = time_second - sec; if (y <= -2 || y >= 2) { /* badly off, adjust it */ - s = splclock(); ts.tv_sec = sec; ts.tv_nsec = 0; set_timecounter(&ts); - splx(s); } + splx(s); return; wrong_time: diff --git a/sys/i386/isa/clock.c b/sys/i386/isa/clock.c index e421fb1f67f0..11b142689145 100644 --- a/sys/i386/isa/clock.c +++ b/sys/i386/isa/clock.c @@ -550,23 +550,30 @@ int rtcin(reg) int reg; { + int s; u_char val; + s = splhigh(); outb(IO_RTC, reg); inb(0x84); val = inb(IO_RTC + 1); inb(0x84); + splx(s); return (val); } static __inline void writertc(u_char reg, u_char val) { + int s; + + s = splhigh(); inb(0x84); outb(IO_RTC, reg); inb(0x84); outb(IO_RTC + 1, val); inb(0x84); /* XXX work around wrong order in rtcin() */ + splx(s); } static __inline int @@ -847,7 +854,11 @@ inittodr(time_t base) /* wait for time update to complete */ /* If RTCSA_TUP is zero, we have at least 244us before next update */ - while (rtcin(RTC_STATUSA) & RTCSA_TUP); + s = splhigh(); + while (rtcin(RTC_STATUSA) & RTCSA_TUP) { + splx(s); + s = splhigh(); + } days = 0; #ifdef USE_RTC_CENTURY @@ -857,8 +868,10 @@ inittodr(time_t base) if (year < 1970) year += 100; #endif - if (year < 1970) + if (year < 1970) { + splx(s); goto wrong_time; + } month = readrtc(RTC_MONTH); for (m = 1; m < month; m++) days += daysinmonth[m-1]; @@ -880,12 +893,11 @@ inittodr(time_t base) y = time_second - sec; if (y <= -2 || y >= 2) { /* badly off, adjust it */ - s = splclock(); ts.tv_sec = sec; ts.tv_nsec = 0; set_timecounter(&ts); - splx(s); } + splx(s); return; wrong_time: diff --git a/sys/isa/atrtc.c b/sys/isa/atrtc.c index e421fb1f67f0..11b142689145 100644 --- a/sys/isa/atrtc.c +++ b/sys/isa/atrtc.c @@ -550,23 +550,30 @@ int rtcin(reg) int reg; { + int s; u_char val; + s = splhigh(); outb(IO_RTC, reg); inb(0x84); val = inb(IO_RTC + 1); inb(0x84); + splx(s); return (val); } static __inline void writertc(u_char reg, u_char val) { + int s; + + s = splhigh(); inb(0x84); outb(IO_RTC, reg); inb(0x84); outb(IO_RTC + 1, val); inb(0x84); /* XXX work around wrong order in rtcin() */ + splx(s); } static __inline int @@ -847,7 +854,11 @@ inittodr(time_t base) /* wait for time update to complete */ /* If RTCSA_TUP is zero, we have at least 244us before next update */ - while (rtcin(RTC_STATUSA) & RTCSA_TUP); + s = splhigh(); + while (rtcin(RTC_STATUSA) & RTCSA_TUP) { + splx(s); + s = splhigh(); + } days = 0; #ifdef USE_RTC_CENTURY @@ -857,8 +868,10 @@ inittodr(time_t base) if (year < 1970) year += 100; #endif - if (year < 1970) + if (year < 1970) { + splx(s); goto wrong_time; + } month = readrtc(RTC_MONTH); for (m = 1; m < month; m++) days += daysinmonth[m-1]; @@ -880,12 +893,11 @@ inittodr(time_t base) y = time_second - sec; if (y <= -2 || y >= 2) { /* badly off, adjust it */ - s = splclock(); ts.tv_sec = sec; ts.tv_nsec = 0; set_timecounter(&ts); - splx(s); } + splx(s); return; wrong_time: