RTC support.
authorBen Pfaff <blp@cs.stanford.edu>
Sat, 23 Aug 2008 03:52:54 +0000 (03:52 +0000)
committerBen Pfaff <blp@cs.stanford.edu>
Sat, 23 Aug 2008 03:52:54 +0000 (03:52 +0000)
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.

doc/bibliography.texi
doc/threads.texi
specs/mc146818a.pdf [new file with mode: 0644]
src/Makefile.build
src/devices/rtc.c [new file with mode: 0644]
src/devices/rtc.h [new file with mode: 0644]
src/threads/init.c
src/utils/pintos

index a23c4c74e997ac4b1c4a5e0eb1a8cd282bb2e481..3597e200018825d0256706283a788c0cbb063247 100644 (file)
@@ -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
 
index 6eec6bb1ea5a44d1620b04faf8108b879d7ad5f1..377343d77323a114becf54644c9e011857e78d00 100644 (file)
@@ -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 (file)
index 0000000..0675424
Binary files /dev/null and b/specs/mc146818a.pdf differ
index 277373effe4910e6818671581ba51b9223599cfc..0934551144d38614c9c262e5b5acab01ed3843f1 100644 (file)
@@ -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 (file)
index 0000000..d99eb46
--- /dev/null
@@ -0,0 +1,112 @@
+#include "devices/rtc.h"
+#include <stdio.h>
+#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 (file)
index 0000000..96a822f
--- /dev/null
@@ -0,0 +1,8 @@
+#ifndef RTC_H
+#define RTC_H
+
+typedef unsigned long time_t;
+
+time_t rtc_get_time (void);
+
+#endif
index 5893ce970b21fe12f707e4c3be55767e31c0034f..b89b282435ffa4fd284c776049dcc4c45a60c66c 100644 (file)
@@ -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;
 }
index 94132052b30e4b22feba396b0f58b8b57b928a2c..88bd3adc1c12a662dff6a7e0430581f0f1d6da76 100755 (executable)
@@ -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})