x86: Get the boot time from CMOS
authorBarret Rhoden <brho@cs.berkeley.edu>
Wed, 10 May 2017 19:46:57 +0000 (15:46 -0400)
committerBarret Rhoden <brho@cs.berkeley.edu>
Fri, 12 May 2017 20:38:12 +0000 (16:38 -0400)
We read this once at boot.  All future time checks should be based on the
walltime_ns_last.  One of these days, we'll update that field when we get
info from e.g. NTP.

Signed-off-by: Barret Rhoden <brho@cs.berkeley.edu>
kern/arch/riscv/pmap.c
kern/arch/x86/kclock.c
kern/arch/x86/pmap.c
kern/arch/x86/pmap64.c
kern/include/kclock.h [deleted file]
kern/src/init.c
kern/src/pmap.c

index f44f92b..031d12e 100644 (file)
@@ -9,7 +9,6 @@
 #include <string.h>
 #include <assert.h>
 #include <pmap.h>
-#include <kclock.h>
 #include <env.h>
 #include <stdio.h>
 #include <kmalloc.h>
index f5d3ae3..d31becb 100644 (file)
-/* See COPYRIGHT for copyright information. */
-
-/* Support for two time-related hardware gadgets: 1) the run time
- * clock with its NVRAM access functions; 2) the 8253 timer, which
- * generates interrupts on IRQ 0.
+/* Copyright (c) 2017 Google Inc
+ * Barret Rhoden <brho@cs.berkeley.edu>
+ * See LICENSE for details.
  */
 
 #include <arch/x86.h>
+#include <atomic.h>
+
+#define        CMOS_RTC_SELECT                         0x70
+#define        CMOS_RTC_DATA                           0x71
+
+#define RTC_A_UPDATE_IN_PROGRESS       (1 << 7)
+#define RTC_B_24HOUR_MODE                      (1 << 1)
+#define RTC_B_BINARY_MODE                      (1 << 2)
+#define RTC_12_HOUR_PM                         (1 << 7)
+#define CMOS_RTC_SECOND                                0x00
+#define CMOS_RTC_MINUTE                                0x02
+#define CMOS_RTC_HOUR                          0x04
+#define CMOS_RTC_WEEKDAY                       0x06
+#define CMOS_RTC_DAY                           0x07
+#define CMOS_RTC_MONTH                         0x08
+#define CMOS_RTC_YEAR                          0x09
+#define CMOS_RTC_CENTURY                       0x32
+#define CMOS_RTC_STATUS_A                      0x0A
+#define CMOS_RTC_STATUS_B                      0x0B
+
+/* If we ever disable NMIs, we'll need to make sure we don't reenable them here.
+ * (Top bit of the CMOS_RTC_SELECT selector). */
+static uint8_t cmos_read(uint8_t reg)
+{
+       outb(CMOS_RTC_SELECT, reg);
+       return inb(CMOS_RTC_DATA);
+}
+
+static void cmos_write(uint8_t reg, uint8_t datum)
+{
+       outb(CMOS_RTC_SELECT, reg);
+       outb(CMOS_RTC_DATA, datum);
+}
 
-#include <kclock.h>
+/* BCD format is a one-byte nibble of the form 0xTensDigit_OnesDigit. */
+static uint8_t bcd_to_binary(uint8_t x)
+{
+       return ((x / 16) * 10) + (x % 16);
+}
 
-unsigned
-mc146818_read(unsigned reg)
+static bool is_leap_year(int year)
 {
-       outb(IO_RTC, reg);
-       return inb(IO_RTC+1);
+       if (!(year % 400))
+               return TRUE;
+       if (!(year % 100))
+               return FALSE;
+       if (!(year % 4))
+               return TRUE;
+       return FALSE;
 }
 
-void
-mc146818_write(unsigned reg, unsigned datum)
+static uint64_t rtc_to_unix(uint8_t century, uint8_t year, uint8_t month,
+                            uint8_t day, uint8_t hour, uint8_t minute,
+                            uint8_t second)
 {
-       outb(IO_RTC, reg);
-       outb(IO_RTC+1, datum);
+       int real_year;
+       uint64_t time = 0;
+
+       real_year = century * 100 + year;
+       for (int i = 1970; i < real_year; i++) {
+               time += 86400 * 365;
+               if (is_leap_year(i))
+                       time += 86400;
+       }
+       /* Note these all fall through */
+       switch (month) {
+       case 12:
+               time += 86400 * 30;     /* november's time */
+       case 11:
+               time += 86400 * 31;
+       case 10:
+               time += 86400 * 30;
+       case 9:
+               time += 86400 * 31;
+       case 8:
+               time += 86400 * 31;
+       case 7:
+               time += 86400 * 30;
+       case 6:
+               time += 86400 * 31;
+       case 5:
+               time += 86400 * 30;
+       case 4:
+               time += 86400 * 31;
+       case 3:
+               time += 86400 * 28;
+               if (is_leap_year(real_year))
+                       time += 86400;
+       case 2:
+               time += 86400 * 31;
+       };
+       time += 86400 * (day - 1);
+       time += hour * 60 * 60;
+       time += minute * 60;
+       time += second;
+       return time;
 }
 
+/* Returns the current unix time in nanoseconds. */
 uint64_t read_persistent_clock(void)
 {
-       return 1242129600 * 1000000000UL; /* nanwan's birthday */
+       static spinlock_t lock = SPINLOCK_INITIALIZER_IRQSAVE;
+       uint8_t century, year, month, day, hour, minute, second;
+       bool is_pm = FALSE;
+
+       spin_lock_irqsave(&lock);
+retry:
+       while (cmos_read(CMOS_RTC_STATUS_A) & RTC_A_UPDATE_IN_PROGRESS)
+               cpu_relax();
+
+       /* Even QEMU has a century register. */
+       century = cmos_read(CMOS_RTC_CENTURY);
+       year    = cmos_read(CMOS_RTC_YEAR);
+       month   = cmos_read(CMOS_RTC_MONTH);
+       day     = cmos_read(CMOS_RTC_DAY);
+       hour    = cmos_read(CMOS_RTC_HOUR);
+       minute  = cmos_read(CMOS_RTC_MINUTE);
+       second  = cmos_read(CMOS_RTC_SECOND);
+
+       while (cmos_read(CMOS_RTC_STATUS_A) & RTC_A_UPDATE_IN_PROGRESS)
+               cpu_relax();
+
+       if ((century != cmos_read(CMOS_RTC_CENTURY)) ||
+           (year    != cmos_read(CMOS_RTC_YEAR))    ||
+           (month   != cmos_read(CMOS_RTC_MONTH))   ||
+           (day     != cmos_read(CMOS_RTC_DAY))     ||
+           (hour    != cmos_read(CMOS_RTC_HOUR))    ||
+           (minute  != cmos_read(CMOS_RTC_MINUTE))  ||
+           (second  != cmos_read(CMOS_RTC_SECOND)))
+               goto retry;
+       spin_unlock_irqsave(&lock);
+
+       if (!(cmos_read(CMOS_RTC_STATUS_B) & RTC_B_24HOUR_MODE)) {
+               /* need to clear the bit before doing the BCD conversions */
+               is_pm = hour & RTC_12_HOUR_PM;
+               hour &= ~RTC_12_HOUR_PM;
+       }
+       if (!(cmos_read(CMOS_RTC_STATUS_B) & RTC_B_BINARY_MODE)) {
+               century = bcd_to_binary(century);
+               year    = bcd_to_binary(year);
+               month   = bcd_to_binary(month);
+               day     = bcd_to_binary(day);
+               hour    = bcd_to_binary(hour);
+               minute  = bcd_to_binary(minute);
+               second  = bcd_to_binary(second);
+       }
+       if (is_pm) {
+               /* midnight appears as 12 and is_pm is set.  we want 0. */
+               hour = (hour + 12) % 24;
+       }
+
+       /* Always remember 1242129600, Nanwan's birthday! */
+       return rtc_to_unix(century, year, month, day, hour, minute, second)
+              * 1000000000UL;
 }
index b0c0e69..5cc1efe 100644 (file)
 #include <string.h>
 #include <assert.h>
 #include <pmap.h>
-#include <kclock.h>
 #include <env.h>
 #include <stdio.h>
 #include <kmalloc.h>
 #include <page_alloc.h>
 
-static int nvram_read(int r)
-{
-       return mc146818_read(r) | (mc146818_read(r + 1) << 8);
-}
-
 bool enable_pse(void)
 {
        uint32_t edx, cr4;
index 55c6329..1e2c1e4 100644 (file)
@@ -25,7 +25,6 @@
 #include <string.h>
 #include <assert.h>
 #include <pmap.h>
-#include <kclock.h>
 #include <env.h>
 #include <stdio.h>
 #include <kmalloc.h>
diff --git a/kern/include/kclock.h b/kern/include/kclock.h
deleted file mode 100644 (file)
index cdb90f6..0000000
+++ /dev/null
@@ -1,30 +0,0 @@
-/* See COPYRIGHT for copyright information. */
-
-#pragma once
-#ifndef ROS_KERNEL
-# error "This is a ROS kernel header; user programs should not #include it"
-#endif
-
-#define        IO_RTC          0x070           /* RTC port */
-
-#define        MC_NVRAM_START  0xe     /* start of NVRAM: offset 14 */
-#define        MC_NVRAM_SIZE   50      /* 50 bytes of NVRAM */
-
-/* NVRAM bytes 7 & 8: base memory size */
-#define NVRAM_BASELO   (MC_NVRAM_START + 7)    /* low byte; RTC off. 0x15 */
-#define NVRAM_BASEHI   (MC_NVRAM_START + 8)    /* high byte; RTC off. 0x16 */
-
-/* NVRAM bytes 9 & 10: extended memory size */
-#define NVRAM_EXTLO    (MC_NVRAM_START + 9)    /* low byte; RTC off. 0x17 */
-#define NVRAM_EXTHI    (MC_NVRAM_START + 10)   /* high byte; RTC off. 0x18 */
-
-/* NVRAM bytes 34 and 35: extended memory POSTed size */
-#define NVRAM_PEXTLO   (MC_NVRAM_START + 34)   /* low byte; RTC off. 0x30 */
-#define NVRAM_PEXTHI   (MC_NVRAM_START + 35)   /* high byte; RTC off. 0x31 */
-
-/* NVRAM byte 36: current century.  (please increment in Dec99!) */
-#define NVRAM_CENTURY  (MC_NVRAM_START + 36)   /* RTC offset 0x32 */
-
-unsigned mc146818_read(unsigned reg);
-void mc146818_write(unsigned reg, unsigned datum);
-void kclock_init(void);
index ec18d95..8e3a70b 100644 (file)
@@ -21,7 +21,6 @@
 #include <process.h>
 #include <trap.h>
 #include <syscall.h>
-#include <kclock.h>
 #include <manager.h>
 #include <testing.h>
 #include <kmalloc.h>
index fa0fb0b..6b4d167 100644 (file)
@@ -16,7 +16,6 @@
 #include <string.h>
 #include <assert.h>
 #include <pmap.h>
-#include <kclock.h>
 #include <process.h>
 #include <stdio.h>
 #include <mm.h>