]> hydra-www.ietfng.org Git - xv6-public/commitdiff
Disentangled CMOS from LAPIC.
authorPeter H. Froehlich <peter.hans.froehlich@gmail.com>
Thu, 5 Nov 2015 21:02:23 +0000 (16:02 -0500)
committerPeter H. Froehlich <peter.hans.froehlich@gmail.com>
Thu, 5 Nov 2015 21:02:23 +0000 (16:02 -0500)
Makefile
cmos.c [new file with mode: 0644]
cmos.h [new file with mode: 0644]
defs.h
lapic.c

index 4a2d7ff2649c17090b3c08f298e04905ee6a6728..ca7b586001b040bce1186b75ac0a02c3460b562e 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -1,5 +1,6 @@
 OBJS = \
        bio.o\
+       cmos.o\
        console.o\
        debug.o\
        exec.o\
diff --git a/cmos.c b/cmos.c
new file mode 100644 (file)
index 0000000..6036031
--- /dev/null
+++ b/cmos.c
@@ -0,0 +1,73 @@
+#include "types.h"
+#include "defs.h"
+#include "date.h"
+#include "x86.h"
+#include "cmos.h"
+
+uint cmosread(uint reg)
+{
+  if(reg >= CMOS_NMI_BIT)
+    panic("cmosread: invalid register");
+
+  outb(CMOS_PORT, reg);
+  microdelay(200);
+  return inb(CMOS_RETURN);
+}
+
+void cmoswrite(uint reg, uint data)
+{
+  if(reg >= CMOS_NMI_BIT)
+    panic("cmoswrite: invalid register");
+  if(data > 0xff)
+    panic("cmoswrite: invalid data");
+
+  outb(CMOS_PORT,  reg);
+  microdelay(200);
+  outb(CMOS_RETURN, data);
+}
+
+static void fill_rtcdate(struct rtcdate *r)
+{
+  r->second = cmosread(CMOS_SECS);
+  r->minute = cmosread(CMOS_MINS);
+  r->hour   = cmosread(CMOS_HOURS);
+  r->day    = cmosread(CMOS_DAY);
+  r->month  = cmosread(CMOS_MONTH);
+  r->year   = cmosread(CMOS_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 = cmosread(CMOS_STATB);
+
+  bcd = (sb & CMOS_BINARY_BIT) == 0;
+
+  // make sure CMOS doesn't modify time while we read it
+  for (;;) {
+    fill_rtcdate(&t1);
+    if (cmosread(CMOS_STATA) & CMOS_UIP_BIT)
+        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;
+}
diff --git a/cmos.h b/cmos.h
new file mode 100644 (file)
index 0000000..378f152
--- /dev/null
+++ b/cmos.h
@@ -0,0 +1,28 @@
+// Access is through these two I/O addresses. Select a register by writing to
+// CMOS_PORT, then read/write data from/to that register through CMOS_RETURN.
+//
+// Note that due to general IBM insanity bit 7 of CMOS_PORT controls whether
+// NMIs get to "the" CPU or not. All of xv6 has ignored this issue for years,
+// so we do the same. But this technically makes CMOS_PORT a 7-bit address
+// and the CMOS cannot have more than 128 registers. The original Motorola
+// MC146818 in fact had 64 registers (only AD0-AD5 were used for addresses).
+#define CMOS_PORT    0x70
+  #define CMOS_NMI_BIT    (1 << 7)  // NMI enabled
+#define CMOS_RETURN  0x71
+
+// The CMOS registers for the real-time clock.
+#define CMOS_SECS    0x00
+#define CMOS_MINS    0x02
+#define CMOS_HOURS   0x04
+#define CMOS_DAY     0x07
+#define CMOS_MONTH   0x08
+#define CMOS_YEAR    0x09
+#define CMOS_STATA   0x0a
+  #define CMOS_UIP_BIT    (1 << 7)  // RTC update in progress
+#define CMOS_STATB   0x0b
+  #define CMOS_24H_BIT    (1 << 1)  // RTC 24/12 hour format
+  #define CMOS_BINARY_BIT (1 << 2)  // RTC binary/bcd format
+
+// The shutdown status register is used in lapic.c to bring up APs.
+#define CMOS_SHUTDOWN_STAT 0x0f
+  #define CMOS_JMP_DWORD_NO_EOI 0x0a
diff --git a/defs.h b/defs.h
index b0406dff311b2a89e7ed83b4d8097df1a9d15677..e43788ed6065c0d57397e57eb28ee60c89fbb3f1 100644 (file)
--- a/defs.h
+++ b/defs.h
@@ -16,6 +16,11 @@ struct buf*     bread(uint, uint);
 void            brelse(struct buf*);
 void            bwrite(struct buf*);
 
+// cmos.c
+uint            cmosread(uint reg);
+void            cmoswrite(uint reg, uint data);
+void            cmostime(struct rtcdate *r);
+
 // console.c
 void            consoleinit(void);
 void            cprintf(char*, ...);
@@ -78,7 +83,6 @@ void            kinit2(void*, void*);
 void            kbdintr(void);
 
 // lapic.c
-void            cmostime(struct rtcdate *r);
 int             cpunum(void);
 extern volatile uint*    lapic;
 void            lapiceoi(void);
diff --git a/lapic.c b/lapic.c
index 4da4214878bc43dd90bf83b48b0cb7e1caadb546..230c2de117f412b78c5c918828be5f2137a48d93 100644 (file)
--- a/lapic.c
+++ b/lapic.c
@@ -3,11 +3,11 @@
 
 #include "types.h"
 #include "defs.h"
-#include "date.h"
 #include "memlayout.h"
 #include "traps.h"
 #include "mmu.h"
 #include "x86.h"
+#include "cmos.h"
 
 // Local APIC registers, divided by 4 for use as uint[] indices.
 #define ID      (0x0020/4)   // ID
@@ -53,19 +53,19 @@ lapicw(int index, int value)
 void
 lapicinit(void)
 {
-  if(!lapic) 
+  if(!lapic)
     return;
 
   // Enable local APIC; set spurious interrupt vector.
   lapicw(SVR, ENABLE | (T_IRQ0 + IRQ_SPURIOUS));
 
   // The timer repeatedly counts down at bus frequency
-  // from lapic[TICR] and then issues an interrupt.  
+  // from lapic[TICR] and then issues an interrupt.
   // If xv6 cared more about precise timekeeping,
   // TICR would be calibrated using an external time source.
   lapicw(TDCR, X1);
   lapicw(TIMER, PERIODIC | (T_IRQ0 + IRQ_TIMER));
-  lapicw(TICR, 10000000); 
+  lapicw(TICR, 10000000);
 
   // Disable logical interrupt lines.
   lapicw(LINT0, MASKED);
@@ -131,9 +131,6 @@ microdelay(int us)
 {
 }
 
-#define CMOS_PORT    0x70
-#define CMOS_RETURN  0x71
-
 // Start additional processor running entry code at addr.
 // See Appendix B of MultiProcessor Specification.
 void
@@ -141,12 +138,11 @@ lapicstartap(uchar apicid, uint addr)
 {
   int i;
   ushort *wrv;
-  
+
   // "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(CMOS_PORT, 0xF);  // offset 0xF is shutdown code
-  outb(CMOS_PORT+1, 0x0A);
+  cmoswrite(CMOS_SHUTDOWN_STAT, CMOS_JMP_DWORD_NO_EOI);
   wrv = (ushort*)P2V((0x40<<4 | 0x67));  // Warm reset vector
   wrv[0] = 0;
   wrv[1] = addr >> 4;
@@ -158,7 +154,7 @@ lapicstartap(uchar apicid, uint addr)
   microdelay(200);
   lapicw(ICRLO, INIT | LEVEL);
   microdelay(100);    // should be 10ms, but too slow in Bochs!
-  
+
   // Send startup IPI (twice!) to enter code.
   // Regular hardware is supposed to only accept a STARTUP
   // when it is in the halted state due to an INIT.  So the second
@@ -171,67 +167,3 @@ 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;
-}