From aae4e7490fbb61de13f61d90092b177eeb258216 Mon Sep 17 00:00:00 2001
From: Cody Cutler <ccutler@mat.lcs.mit.edu>
Date: Fri, 12 Sep 2014 17:18:57 -0400
Subject: cmosgetdate() for system-call homework

the day of reckoning has come for the debug port "Shutdown" hack.

instead of mucking with ACPI or using a new hack, the student will now write
sys_date() using the cmosgetdate() helper.
---
 lapic.c | 71 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++---
 1 file changed, 68 insertions(+), 3 deletions(-)

(limited to 'lapic.c')

diff --git a/lapic.c b/lapic.c
index 94b484f..4da4214 100644
--- a/lapic.c
+++ b/lapic.c
@@ -3,6 +3,7 @@
 
 #include "types.h"
 #include "defs.h"
+#include "date.h"
 #include "memlayout.h"
 #include "traps.h"
 #include "mmu.h"
@@ -130,7 +131,8 @@ microdelay(int us)
 {
 }
 
-#define IO_RTC  0x70
+#define CMOS_PORT    0x70
+#define CMOS_RETURN  0x71
 
 // Start additional processor running entry code at addr.
 // See Appendix B of MultiProcessor Specification.
@@ -143,8 +145,8 @@ lapicstartap(uchar apicid, uint addr)
   // "The BSP must initialize CMOS shutdown code to 0AH
   // and the warm reset vector (DWORD based at 40:67) to point at
   // the AP startup code prior to the [universal startup algorithm]."
-  outb(IO_RTC, 0xF);  // offset 0xF is shutdown code
-  outb(IO_RTC+1, 0x0A);
+  outb(CMOS_PORT, 0xF);  // offset 0xF is shutdown code
+  outb(CMOS_PORT+1, 0x0A);
   wrv = (ushort*)P2V((0x40<<4 | 0x67));  // Warm reset vector
   wrv[0] = 0;
   wrv[1] = addr >> 4;
@@ -169,4 +171,67 @@ lapicstartap(uchar apicid, uint addr)
   }
 }
 
+#define CMOS_STATA   0x0a
+#define CMOS_STATB   0x0b
+#define CMOS_UIP    (1 << 7)        // RTC update in progress
 
+#define SECS    0x00
+#define MINS    0x02
+#define HOURS   0x04
+#define DAY     0x07
+#define MONTH   0x08
+#define YEAR    0x09
+
+static uint cmos_read(uint reg)
+{
+  outb(CMOS_PORT,  reg);
+  microdelay(200);
+
+  return inb(CMOS_RETURN);
+}
+
+static void fill_rtcdate(struct rtcdate *r)
+{
+  r->second = cmos_read(SECS);
+  r->minute = cmos_read(MINS);
+  r->hour   = cmos_read(HOURS);
+  r->day    = cmos_read(DAY);
+  r->month  = cmos_read(MONTH);
+  r->year   = cmos_read(YEAR);
+}
+
+// qemu seems to use 24-hour GWT and the values are BCD encoded
+void cmostime(struct rtcdate *r)
+{
+  struct rtcdate t1, t2;
+  int sb, bcd;
+
+  sb = cmos_read(CMOS_STATB);
+
+  bcd = (sb & (1 << 2)) == 0;
+
+  // make sure CMOS doesn't modify time while we read it
+  for (;;) {
+    fill_rtcdate(&t1);
+    if (cmos_read(CMOS_STATA) & CMOS_UIP)
+        continue;
+    fill_rtcdate(&t2);
+    if (memcmp(&t1, &t2, sizeof(t1)) == 0)
+      break;
+  }
+
+  // convert
+  if (bcd) {
+#define    CONV(x)     (t1.x = ((t1.x >> 4) * 10) + (t1.x & 0xf))
+    CONV(second);
+    CONV(minute);
+    CONV(hour  );
+    CONV(day   );
+    CONV(month );
+    CONV(year  );
+#undef     CONV
+  }
+
+  *r = t1;
+  r->year += 2000;
+}
-- 
cgit v1.2.3