summaryrefslogtreecommitdiffstats
path: root/sys/boot
diff options
context:
space:
mode:
authorolli <olli@FreeBSD.org>2008-06-16 17:04:04 +0000
committerolli <olli@FreeBSD.org>2008-06-16 17:04:04 +0000
commit992f3401a70ae7b9b81e88aaf3cf81ee833e38e8 (patch)
treea0de29cec6795e8433864a2ad0ce4bece0b24909 /sys/boot
parentf629d3e257a428b9f2137c0e00a090161da64d27 (diff)
downloadFreeBSD-src-992f3401a70ae7b9b81e88aaf3cf81ee833e38e8.zip
FreeBSD-src-992f3401a70ae7b9b81e88aaf3cf81ee833e38e8.tar.gz
Implement a workaround for a long-standing problem in
libi386's time(), caused by a qemu bug. The bug might be present in other BIOSes, too. qemu either does not simulate the AT RTC correctly or has a broken BIOS 1A/02 implementation, and will return an incorrect value if the RTC is read while it is being updated. The effect is worsened by the fact that qemu's INT 15/86 function ("wait" a.k.a. usleep) is non-implmeneted or broken and returns immediately, causing beastie.4th to spin in a tight loop calling the "read RTC" function millions of times, triggering the problem quickly. Therefore, we keep reading the BIOS value until we get the same result twice. This change fixes beastie.4th's countdown under qemu. Approved by: des (mentor)
Diffstat (limited to 'sys/boot')
-rw-r--r--sys/boot/i386/libi386/time.c40
1 files changed, 32 insertions, 8 deletions
diff --git a/sys/boot/i386/libi386/time.c b/sys/boot/i386/libi386/time.c
index 1db9a4a..183ac3c 100644
--- a/sys/boot/i386/libi386/time.c
+++ b/sys/boot/i386/libi386/time.c
@@ -32,18 +32,16 @@ __FBSDID("$FreeBSD$");
#include "bootstrap.h"
#include "libi386.h"
+static int bios_seconds(void);
+
/*
- * Return the time in seconds since the beginning of the day.
- *
- * If we pass midnight, don't wrap back to 0.
+ * Return the BIOS time-of-day value.
*
* XXX uses undocumented BCD support from libstand.
*/
-
-time_t
-time(time_t *t)
+static int
+bios_seconds(void)
{
- static time_t lasttime, now;
int hr, minute, sec;
v86.ctl = 0;
@@ -55,7 +53,33 @@ time(time_t *t)
minute = bcd2bin(v86.ecx & 0xff); /* minute in %cl */
sec = bcd2bin((v86.edx & 0xff00) >> 8); /* second in %dh */
- now = hr * 3600 + minute * 60 + sec;
+ return (hr * 3600 + minute * 60 + sec);
+}
+
+/*
+ * Return the time in seconds since the beginning of the day.
+ *
+ * Some BIOSes (notably qemu) don't correctly read the RTC
+ * registers in an atomic way, sometimes returning bogus values.
+ * Therefore we "debounce" the reading by accepting it only when
+ * we got two identical values in succession.
+ *
+ * If we pass midnight, don't wrap back to 0.
+ */
+time_t
+time(time_t *t)
+{
+ static time_t lasttime;
+ time_t now, check;
+ int try;
+
+ try = 0;
+ check = bios_seconds();
+ do {
+ now = check;
+ check = bios_seconds();
+ } while (now != check && ++try < 1000);
+
if (now < lasttime)
now += 24 * 3600;
lasttime = now;
OpenPOWER on IntegriCloud