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.