From: Waldemar Kozaczuk <jwkozac...@gmail.com>
Committer: Nadav Har'El <n...@scylladb.com>
Branch: master

Added ability to OSv boot logic to detect if running of Hyper/V and implemented Hyper/V specific clock.

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>
Message-Id: <1499985434-3665-1-git-send-email-jwkozac...@gmail.com>

---
diff --git a/Makefile b/Makefile
--- 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
--- 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
--- 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
--- 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
--- 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
--- 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 @@ class hpetclock : public clock {
     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
--- a/drivers/hypervclock.cc
+++ 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
--- a/drivers/rtc.cc
+++ 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
--- a/drivers/rtc.hh
+++ 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

--
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