These changes are enough to allow execution of simple OSv apps that do NOT need
networking nor storage access. The image built using the command below should
successfully boot and execute on Hyper/V:

scripts/build image=native-example fs=ramfs

Here is the list of specific changes:
 - added hyperv_clocksource to features_type to indicate that Hyper/V reference 
time count MSR is available
 - modified cpuid.cc to alllow detection availability of Hyper/V reference time 
count MSR
 - moved rtc class from hpet file into separate header and implementation file 
so that it can be shared between hpet and hy
pervclock
 - implemented hypervclock class to provide time source when running on Hyper/V

Fixes #874

Signed-off-by: Waldemar Kozaczuk <jwkozac...@gmail.com>
---
 Makefile                            |  3 +-
 arch/x64/cpuid.cc                   |  8 +++++
 arch/x64/cpuid.hh                   |  1 +
 bsd/sys/dev/hyperv/include/hyperv.h |  3 ++
 bsd/sys/dev/hyperv/vmbus/hyperv.cc  |  2 --
 drivers/hpet.cc                     | 67 +-----------------------------------
 drivers/hypervclock.cc              | 68 +++++++++++++++++++++++++++++++++++++
 drivers/rtc.cc                      | 64 ++++++++++++++++++++++++++++++++++
 drivers/rtc.hh                      | 23 +++++++++++++
 9 files changed, 170 insertions(+), 69 deletions(-)
 create mode 100644 drivers/hypervclock.cc
 create mode 100644 drivers/rtc.cc
 create mode 100644 drivers/rtc.hh

diff --git a/Makefile b/Makefile
index b261409..049ab57 100644
--- a/Makefile
+++ b/Makefile
@@ -820,9 +820,10 @@ drivers += drivers/vmxnet3-queues.o
 drivers += drivers/virtio-blk.o
 drivers += drivers/virtio-scsi.o
 drivers += drivers/virtio-rng.o
-drivers += drivers/kvmclock.o drivers/xenclock.o
+drivers += drivers/kvmclock.o drivers/xenclock.o drivers/hypervclock.o
 drivers += drivers/acpi.o
 drivers += drivers/hpet.o
+drivers += drivers/rtc.o
 drivers += drivers/xenfront.o drivers/xenfront-xenbus.o drivers/xenfront-blk.o
 drivers += drivers/pvpanic.o
 drivers += drivers/ahci.o
diff --git a/arch/x64/cpuid.cc b/arch/x64/cpuid.cc
index f1c979c..45c0fea 100644
--- a/arch/x64/cpuid.cc
+++ b/arch/x64/cpuid.cc
@@ -8,6 +8,7 @@
 #include "cpuid.hh"
 #include "processor.hh"
 #include "xen.hh"
+#include <dev/hyperv/include/hyperv.h>
 
 namespace processor {
 
@@ -109,12 +110,19 @@ void process_xen_bits(features_type &features)
     }
 }
 
+void process_hyperv_bits(features_type &features) {
+    if(hyperv_identify() && hyperv_is_timecount_available()) {
+        features.hyperv_clocksource = true;
+    }
+}
+
 void process_cpuid(features_type& features)
 {
     for (unsigned i = 0; i < nr_cpuid_bits; ++i) {
         process_cpuid_bit(features, cpuid_bits[i]);
     }
     process_xen_bits(features);
+    process_hyperv_bits(features);
 }
 
 }
diff --git a/arch/x64/cpuid.hh b/arch/x64/cpuid.hh
index cd85e47..e84e2b0 100644
--- a/arch/x64/cpuid.hh
+++ b/arch/x64/cpuid.hh
@@ -37,6 +37,7 @@ struct features_type {
     bool xen_clocksource;
     bool xen_vector_callback;
     bool xen_pci;
+    bool hyperv_clocksource;
 };
 
 extern const features_type& features();
diff --git a/bsd/sys/dev/hyperv/include/hyperv.h 
b/bsd/sys/dev/hyperv/include/hyperv.h
index 0fb18f9..47d1409 100644
--- a/bsd/sys/dev/hyperv/include/hyperv.h
+++ b/bsd/sys/dev/hyperv/include/hyperv.h
@@ -31,6 +31,8 @@
 #ifndef _HYPERV_H_
 #define _HYPERV_H_
 
+#include <bsd/porting/netport.h>
+
 #ifdef _KERNEL
 
 #include <sys/param.h>
@@ -93,6 +95,7 @@ extern u_int            hyperv_features;        /* 
CPUID_HV_MSR_ */
 
 bool                    hyperv_identify();
 bool                    hyperv_is_timecount_available();
+uint64_t                hyperv_tc64_rdmsr();
 
 #endif  /* _KERNEL */
 
diff --git a/bsd/sys/dev/hyperv/vmbus/hyperv.cc 
b/bsd/sys/dev/hyperv/vmbus/hyperv.cc
index 7b8fac5..446477b 100644
--- a/bsd/sys/dev/hyperv/vmbus/hyperv.cc
+++ b/bsd/sys/dev/hyperv/vmbus/hyperv.cc
@@ -32,8 +32,6 @@
 #include <sys/cdefs.h>
 __FBSDID("$FreeBSD$");
 
-#include <bsd/porting/netport.h>
-
 #include <sys/param.h>
 #include <sys/kernel.h>
 #include <sys/systm.h>
diff --git a/drivers/hpet.cc b/drivers/hpet.cc
index 60387c3..a051eb9 100644
--- a/drivers/hpet.cc
+++ b/drivers/hpet.cc
@@ -9,7 +9,6 @@ extern "C" {
 #include "acpi.h"
 }
 #include <boost/intrusive/parent_from_member.hpp>
-#include <boost/date_time.hpp>
 #include <osv/prio.hh>
 #include "processor.hh"
 #include "clock.hh"
@@ -18,6 +17,7 @@ extern "C" {
 #include "arch.hh"
 #include <osv/xen.hh>
 #include <osv/irqlock.hh>
+#include "rtc.hh"
 
 using boost::intrusive::get_parent_from_member;
 
@@ -33,71 +33,6 @@ private:
     uint64_t _period;
 };
 
-class rtc {
-public:
-    rtc();
-    uint64_t wallclock_ns();
-private:
-    bool _is_bcd;
-    uint8_t cmos_read(uint8_t val);
-    uint8_t cmos_read_date(uint8_t val);
-};
-
-
-#define RTC_PORT(x) (0x70 + (x))
-#define RTC_BINARY_DATE 0x4
-
-rtc::rtc()
-{
-    auto status = cmos_read(0xB);
-    _is_bcd = !(status & RTC_BINARY_DATE);
-}
-
-uint8_t rtc::cmos_read(uint8_t addr)
-{
-    processor::outb(addr, RTC_PORT(0));
-    return processor::inb(RTC_PORT(1));
-}
-
-uint8_t rtc::cmos_read_date(uint8_t addr)
-{
-    uint8_t val = cmos_read(addr);
-    if (!_is_bcd)
-        return val;
-    return (val & 0x0f) + (val >> 4) * 10;
-}
-
-uint64_t rtc::wallclock_ns()
-{
-    // 0x80 : Update in progress. Wait for it.
-    while ((cmos_read(0xA) & 0x80));
-
-    uint8_t year = cmos_read_date(9);
-    uint8_t month = cmos_read_date(8);
-    uint8_t day = cmos_read_date(7);
-    uint8_t hours = cmos_read_date(4);
-    uint8_t mins = cmos_read_date(2);
-    uint8_t secs = cmos_read_date(0);
-
-    // FIXME: Get century from FADT.
-    auto gdate = boost::gregorian::date(2000 + year, month, day);
-
-    // My understanding from boost's documentation is that this handles leap
-    // seconds correctly. They don't mention it explicitly, but they say that
-    // one of the motivations for writing the library is that: " most libraries
-    // do not correctly handle leap seconds, provide concepts such as infinity,
-    // or provide the ability to use high resolution or network time sources"
-    auto now = boost::posix_time::ptime(gdate, 
-                    boost::posix_time::hours(hours) +
-                    boost::posix_time::minutes(mins) +
-                    boost::posix_time::seconds(secs));
-
-    auto base = boost::posix_time::ptime(boost::gregorian::date(1970, 1, 1));
-    auto dur = now - base;
-
-    return dur.total_nanoseconds();
-}
-
 #define HPET_CAP        0x000
 #define HPET_CAP_COUNT_SIZE (1<<13)
 #define HPET_PERIOD     0x004
diff --git a/drivers/hypervclock.cc b/drivers/hypervclock.cc
new file mode 100644
index 0000000..17c5110
--- /dev/null
+++ b/drivers/hypervclock.cc
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2017 Waldemar Kozaczuk
+ *
+ * This work is open source software, licensed under the terms of the
+ * BSD license as described in the LICENSE file in the top-level directory.
+ */
+
+#include <osv/prio.hh>
+#include <osv/irqlock.hh>
+#include <osv/mutex.h>
+#include "clock.hh"
+#include "rtc.hh"
+#include <dev/hyperv/include/hyperv.h>
+
+/**
+ * This clock uses uses RTC to grab wall clock time and Partition Reference 
Counter MSR
+ * (section 12.4 from 
https://github.com/Microsoft/Virtualization-Documentation/raw/master/tlfs/Hypervisor%20Top%20Level%20Functional%20Specification%20v5.0b.pdf)
+ * as a TSC source.
+ * TODO: The MSR is simulated so the call to read its value is quite 
expensive. Therefore
+ * eventually we should implement more efficient clock using Hyper/V Reference 
TSC page
+ * (see section 12.6 in the same document reference above).
+ */
+class hypervclock : public clock {
+public:
+    hypervclock();
+    virtual s64 time() __attribute__((no_instrument_function));
+    virtual s64 uptime() override __attribute__((no_instrument_function));
+    virtual s64 boot_time() override __attribute__((no_instrument_function));
+private:
+    uint64_t _boot_wall;
+    uint64_t _boot_count; // Single reference count represents 100 ns
+};
+
+hypervclock::hypervclock()
+{
+    auto r = new rtc();
+
+    // In theory we should disable NMIs, but on virtual hardware, we can
+    // relax that (This is specially true given our current NMI handler,
+    // which will just halt us forever.
+    irq_save_lock_type irq_lock;
+    WITH_LOCK(irq_lock) {
+        _boot_wall = r->wallclock_ns();
+        _boot_count = hyperv_tc64_rdmsr();
+    };
+}
+
+s64 hypervclock::time()
+{
+    return _boot_wall + (hyperv_tc64_rdmsr() - _boot_count) * 100;
+}
+
+s64 hypervclock::uptime()
+{
+    return (hyperv_tc64_rdmsr() - _boot_count) * 100;
+}
+
+s64 hypervclock::boot_time()
+{
+    return _boot_wall;
+}
+
+void __attribute__((constructor(init_prio::clock))) hyperv_init()
+{
+    if (processor::features().hyperv_clocksource) {
+        clock::register_clock(new hypervclock);
+    }
+}
diff --git a/drivers/rtc.cc b/drivers/rtc.cc
new file mode 100644
index 0000000..77066d2
--- /dev/null
+++ b/drivers/rtc.cc
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2013 Cloudius Systems, Ltd.
+ *
+ * This work is open source software, licensed under the terms of the
+ * BSD license as described in the LICENSE file in the top-level directory.
+ */
+
+#include <boost/date_time.hpp>
+#include "rtc.hh"
+#include "processor.hh"
+
+#define RTC_PORT(x) (0x70 + (x))
+#define RTC_BINARY_DATE 0x4
+
+rtc::rtc()
+{
+    auto status = cmos_read(0xB);
+    _is_bcd = !(status & RTC_BINARY_DATE);
+}
+
+uint8_t rtc::cmos_read(uint8_t addr)
+{
+    processor::outb(addr, RTC_PORT(0));
+    return processor::inb(RTC_PORT(1));
+}
+
+uint8_t rtc::cmos_read_date(uint8_t addr)
+{
+    uint8_t val = cmos_read(addr);
+    if (!_is_bcd)
+        return val;
+    return (val & 0x0f) + (val >> 4) * 10;
+}
+
+uint64_t rtc::wallclock_ns()
+{
+    // 0x80 : Update in progress. Wait for it.
+    while ((cmos_read(0xA) & 0x80));
+
+    uint8_t year = cmos_read_date(9);
+    uint8_t month = cmos_read_date(8);
+    uint8_t day = cmos_read_date(7);
+    uint8_t hours = cmos_read_date(4);
+    uint8_t mins = cmos_read_date(2);
+    uint8_t secs = cmos_read_date(0);
+
+    // FIXME: Get century from FADT.
+    auto gdate = boost::gregorian::date(2000 + year, month, day);
+
+    // My understanding from boost's documentation is that this handles leap
+    // seconds correctly. They don't mention it explicitly, but they say that
+    // one of the motivations for writing the library is that: " most libraries
+    // do not correctly handle leap seconds, provide concepts such as infinity,
+    // or provide the ability to use high resolution or network time sources"
+    auto now = boost::posix_time::ptime(gdate,
+                                        boost::posix_time::hours(hours) +
+                                        boost::posix_time::minutes(mins) +
+                                        boost::posix_time::seconds(secs));
+
+    auto base = boost::posix_time::ptime(boost::gregorian::date(1970, 1, 1));
+    auto dur = now - base;
+
+    return dur.total_nanoseconds();
+}
diff --git a/drivers/rtc.hh b/drivers/rtc.hh
new file mode 100644
index 0000000..27cd01c
--- /dev/null
+++ b/drivers/rtc.hh
@@ -0,0 +1,23 @@
+/*
+ * Copyright (C) 2013 Cloudius Systems, Ltd.
+ *
+ * This work is open source software, licensed under the terms of the
+ * BSD license as described in the LICENSE file in the top-level directory.
+ */
+
+#ifndef OSV_RTC_HH
+#define OSV_RTC_HH
+
+#include <osv/types.h>
+
+class rtc {
+public:
+    rtc();
+    uint64_t wallclock_ns();
+private:
+    bool _is_bcd;
+    uint8_t cmos_read(uint8_t val);
+    uint8_t cmos_read_date(uint8_t val);
+};
+
+#endif //OSV_RTC_HH
-- 
2.7.4

-- 
You received this message because you are subscribed to the Google Groups "OSv 
Development" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to osv-dev+unsubscr...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Reply via email to