From: Ben Pfaff Date: Sat, 23 Aug 2008 03:52:54 +0000 (+0000) Subject: RTC support. X-Git-Url: https://pintos-os.org/cgi-bin/gitweb.cgi?p=pintos-anon;a=commitdiff_plain;h=0fd3a243b790dd1cfc9e0a40c57dddde56cf344d RTC support. Adapted from Anthony Romano's patch with the following changes: * Style adjusted to fit rest of Pintos. * Don't modify the RTC registers. It shouldn't be necessary. We shouldn't need any initialization in fact. * Drop rtc_set_time stub. * Do handle time advancing while reading the RTC registers in rtc_get_time, using the strategy from Linux. * Fix leap year handling: adding (year / 4) * 24 * 60 * 60 is wrong for first 2 months of a leap year; the test (year & 4) should be (year % 4). * Simplify month code. * Drop enable_rtc_int, disable_rtc_int, rtc_updating that are not needed. * Update documentation. --- diff --git a/doc/bibliography.texi b/doc/bibliography.texi index a23c4c7..3597e20 100644 --- a/doc/bibliography.texi +++ b/doc/bibliography.texi @@ -67,6 +67,10 @@ Datasheet for PC timer chip. @uref{specs/8259A.pdf, , Intel 8259A Programmable Interrupt Controller (8259A/8259A-2)}. Datasheet for PC interrupt controller chip. +@bibdfn{MC146818A} +@uref{specs/mc146818a.pdf, , Motorola MC146818A Real Time Clock Plus +Ram (RTC)}. Datasheet for PC real-time clock chip. + @node Software References @section Software References diff --git a/doc/threads.texi b/doc/threads.texi index 6eec6bb..377343d 100644 --- a/doc/threads.texi +++ b/doc/threads.texi @@ -240,6 +240,12 @@ serial drivers. Interrupt queue, for managing a circular queue that both kernel threads and interrupt handlers want to access. Used by the keyboard and serial drivers. + +@item rtc.c +@itemx rtc.h +Real-time clock driver, to enable the kernel to determine the current +date and time. By default, this is only used by @file{thread/init.c} +to choose an initial seed for the random number generator. @end table @node lib files @@ -277,7 +283,11 @@ more information. @item random.c @itemx random.h -Pseudo-random number generator. +Pseudo-random number generator. The actual sequence of random values +will not vary from one Pintos run to another, unless you do one of +three things: specify a new random seed value on the @option{-rs} +kernel command-line option on each run, or use a simulator other than +Bochs, or specify the @option{-r} option to @command{pintos}. @item round.h Macros for rounding. diff --git a/specs/mc146818a.pdf b/specs/mc146818a.pdf new file mode 100644 index 0000000..0675424 Binary files /dev/null and b/specs/mc146818a.pdf differ diff --git a/src/Makefile.build b/src/Makefile.build index 277373e..0934551 100644 --- a/src/Makefile.build +++ b/src/Makefile.build @@ -30,6 +30,7 @@ devices_SRC += devices/serial.c # Serial port device. devices_SRC += devices/disk.c # IDE disk device. devices_SRC += devices/input.c # Serial and keyboard input. devices_SRC += devices/intq.c # Interrupt queue. +devices_SRC += devices/rtc.c # Real-time clock. # Library code shared between kernel and user programs. lib_SRC = lib/debug.c # Debug helpers. diff --git a/src/devices/rtc.c b/src/devices/rtc.c new file mode 100644 index 0000000..d99eb46 --- /dev/null +++ b/src/devices/rtc.c @@ -0,0 +1,112 @@ +#include "devices/rtc.h" +#include +#include "threads/io.h" + +/* This code is an interface to the MC146818A-compatible real + time clock found on PC motherboards. See [MC146818A] for + hardware details. */ + +/* I/O register addresses. */ +#define CMOS_REG_SET 0x70 /* Selects CMOS register exposed by REG_IO. */ +#define CMOS_REG_IO 0x71 /* Contains the selected data byte. */ + +/* Indexes of CMOS registers with real-time clock functions. + Note that all of these registers are in BCD format, + so that 0x59 means 59, not 89. */ +#define RTC_REG_SEC 0 /* Second: 0x00...0x59. */ +#define RTC_REG_MIN 2 /* Minute: 0x00...0x59. */ +#define RTC_REG_HOUR 4 /* Hour: 0x00...0x23. */ +#define RTC_REG_MDAY 7 /* Day of the month: 0x01...0x31. */ +#define RTC_REG_MON 8 /* Month: 0x01...0x12. */ +#define RTC_REG_YEAR 9 /* Year: 0x00...0x99. */ + +/* Indexes of CMOS control registers. */ +#define RTC_REG_A 0x0a /* Register A: update-in-progress. */ +#define RTC_REG_B 0x0b /* Register B: 24/12 hour time, irq enables. */ +#define RTC_REG_C 0x0c /* Register C: pending interrupts. */ +#define RTC_REG_D 0x0d /* Register D: valid time? */ + +/* Register A. */ +#define RTCSA_UIP 0x80 /* Set while time update in progress. */ + +/* Register B. */ +#define RTCSB_SET 0x80 /* Disables update to let time be set. */ +#define RTCSB_DM 0x04 /* 0 = BCD time format, 1 = binary format. */ +#define RTCSB_24HR 0x02 /* 0 = 12-hour format, 1 = 24-hour format. */ + +static int bcd_to_bin (uint8_t); +static uint8_t cmos_read (uint8_t index); + +/* Returns number of seconds since Unix epoch of January 1, + 1970. */ +time_t +rtc_get_time (void) +{ + static const int days_per_month[12] = + { + 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 + }; + int sec, min, hour, mday, mon, year; + time_t time; + int i; + + /* Get time components. + + We repeatedly read the time until it is stable from one read + to another, in case we start our initial read in the middle + of an update. This strategy is not recommended by the + MC146818A datasheet, but it is simpler than any of their + suggestions and, furthermore, it is also used by Linux. + + The MC146818A can be configured for BCD or binary format, + but for historical reasons everyone always uses BCD format + except on obscure non-PC platforms, so we don't bother + trying to detect the format in use. */ + do + { + sec = bcd_to_bin (cmos_read (RTC_REG_SEC)); + min = bcd_to_bin (cmos_read (RTC_REG_MIN)); + hour = bcd_to_bin (cmos_read (RTC_REG_HOUR)); + mday = bcd_to_bin (cmos_read (RTC_REG_MDAY)); + mon = bcd_to_bin (cmos_read (RTC_REG_MON)); + year = bcd_to_bin (cmos_read (RTC_REG_YEAR)); + } + while (sec != bcd_to_bin (cmos_read (RTC_REG_SEC))); + + /* Translate years-since-1900 into years-since-1970. + If it's before the epoch, assume that it has passed 2000. + This will break at 2070, but that's long after our 31-bit + time_t breaks in 2038. */ + if (year < 70) + year += 100; + year -= 70; + + /* Break down all components into seconds. */ + time = (year * 365 + (year - 1) / 4) * 24 * 60 * 60; + for (i = 1; i <= mon; i++) + time += days_per_month[i - 1] * 24 * 60 * 60; + if (mon > 2 && year % 4 == 0) + time += 24 * 60 * 60; + time += (mday - 1) * 24 * 60 * 60; + time += hour * 60 * 60; + time += min * 60; + time += sec; + + return time; +} + +/* Returns the integer value of the given BCD byte. */ +static int +bcd_to_bin (uint8_t x) +{ + return (x & 0x0f) + ((x >> 4) * 10); +} + +/* Reads a byte from the CMOS register with the given INDEX and + returns the byte read. */ +static uint8_t +cmos_read (uint8_t index) +{ + outb (CMOS_REG_SET, index); + return inb (CMOS_REG_IO); +} diff --git a/src/devices/rtc.h b/src/devices/rtc.h new file mode 100644 index 0000000..96a822f --- /dev/null +++ b/src/devices/rtc.h @@ -0,0 +1,8 @@ +#ifndef RTC_H +#define RTC_H + +typedef unsigned long time_t; + +time_t rtc_get_time (void); + +#endif diff --git a/src/threads/init.c b/src/threads/init.c index 5893ce9..b89b282 100644 --- a/src/threads/init.c +++ b/src/threads/init.c @@ -13,6 +13,7 @@ #include "devices/serial.h" #include "devices/timer.h" #include "devices/vga.h" +#include "devices/rtc.h" #include "threads/interrupt.h" #include "threads/io.h" #include "threads/loader.h" @@ -251,6 +252,16 @@ parse_options (char **argv) else PANIC ("unknown option `%s' (use -h for help)", name); } + + /* Initialize the random number generator based on the system + time. This has no effect if an "-rs" option was specified. + + When running under Bochs, this is not enough by itself to + get a good seed value, because the pintos script sets the + initial time to a predictable value, not to the local time, + for reproducibility. To fix this, give the "-r" option to + the pintos script to request real-time execution. */ + random_init (rtc_get_time ()); return argv; } diff --git a/src/utils/pintos b/src/utils/pintos index 9413205..88bd3ad 100755 --- a/src/utils/pintos +++ b/src/utils/pintos @@ -409,8 +409,11 @@ log: bochsout.txt panic: action=fatal EOF print BOCHSRC "gdbstub: enabled=1\n" if $debug eq 'gdb'; - print BOCHSRC "clock: sync=", $realtime ? 'realtime' : 'none', - ", time0=0\n"; + if ($realtime) { + print BOCHSRC "clock: sync=realtime\n"; + } else { + print BOCHSRC "clock: sync=none, time0=0\n"; + } print_bochs_disk_line ("ata0-master", 0); print_bochs_disk_line ("ata0-slave", 1); if (defined ($disks_by_iface[2]{FILE_NAME})