[PATCH v7 06/19] xen: implement an early timer for Xen PVH

From: Roger Pau Monne <roger.pau_at_citrix.com>
Date: Thu, 19 Dec 2013 19:54:43 +0100
When running as a PVH guest, there's no emulated i8254, so we need to
use the Xen PV timer as the early source for DELAY. This change allows
for different implementations of the early DELAY function and
implements a Xen variant for it.
---
 sys/amd64/amd64/machdep.c   |   15 ++++++-
 sys/amd64/include/clock.h   |    6 +++
 sys/amd64/include/sysarch.h |    2 +
 sys/conf/files.amd64        |    1 +
 sys/conf/files.i386         |    1 +
 sys/dev/xen/timer/timer.c   |   33 +++++++++++++++
 sys/i386/i386/machdep.c     |   11 +++++
 sys/i386/include/clock.h    |    6 +++
 sys/x86/isa/clock.c         |   53 +-----------------------
 sys/x86/x86/delay.c         |   95 +++++++++++++++++++++++++++++++++++++++++++
 sys/x86/xen/pv.c            |    3 +
 11 files changed, 172 insertions(+), 54 deletions(-)
 create mode 100644 sys/x86/x86/delay.c

diff --git a/sys/amd64/amd64/machdep.c b/sys/amd64/amd64/machdep.c
index 09d9a1a..a2dcb90 100644
--- a/sys/amd64/amd64/machdep.c
+++ b/sys/amd64/amd64/machdep.c
_at__at_ -180,6 +180,8 _at__at_ static caddr_t native_parse_preload_data(u_int64_t);
 /* Default init_ops implementation. */
 struct init_ops init_ops = {
 	.parse_preload_data =	native_parse_preload_data,
+	.early_delay_init =	i8254_init,
+	.early_delay =		i8254_delay,
 };
 
 /*
_at__at_ -232,6 +234,15 _at__at_ struct mem_range_softc mem_range_softc;
 
 struct mtx dt_lock;	/* lock for GDT and LDT */
 
+void
+DELAY(int n)
+{
+	if (delay_tc(n))
+		return;
+
+	init_ops.early_delay(n);
+}
+
 static void
 cpu_startup(dummy)
 	void *dummy;
_at__at_ -1900,10 +1911,10 _at__at_ hammer_time(u_int64_t modulep, u_int64_t physfree)
 	lidt(&r_idt);
 
 	/*
-	 * Initialize the i8254 before the console so that console
+	 * Initialize the early delay before the console so that console
 	 * initialization can use DELAY().
 	 */
-	i8254_init();
+	init_ops.early_delay_init();
 
 	/*
 	 * Initialize the console before we print anything out.
diff --git a/sys/amd64/include/clock.h b/sys/amd64/include/clock.h
index d7f7d82..e7817ab 100644
--- a/sys/amd64/include/clock.h
+++ b/sys/amd64/include/clock.h
_at__at_ -25,6 +25,12 _at__at_ extern int	smp_tsc;
 #endif
 
 void	i8254_init(void);
+void	i8254_delay(int);
+#ifdef XENHVM
+void	xen_delay_init(void);
+void	xen_delay(int);
+#endif
+int	delay_tc(int);
 
 /*
  * Driver to clock driver interface.
diff --git a/sys/amd64/include/sysarch.h b/sys/amd64/include/sysarch.h
index 58ac8cd..60fa635 100644
--- a/sys/amd64/include/sysarch.h
+++ b/sys/amd64/include/sysarch.h
_at__at_ -13,6 +13,8 _at__at_
  */
 struct init_ops {
 	caddr_t	(*parse_preload_data)(u_int64_t);
+	void	(*early_delay_init)(void);
+	void	(*early_delay)(int);
 };
 
 extern struct init_ops init_ops;
diff --git a/sys/conf/files.amd64 b/sys/conf/files.amd64
index b3b1319..bdc1517 100644
--- a/sys/conf/files.amd64
+++ b/sys/conf/files.amd64
_at__at_ -564,6 +564,7 _at__at_ x86/x86/mptable_pci.c		optional	mptable pci
 x86/x86/msi.c			optional	pci
 x86/x86/nexus.c			standard
 x86/x86/tsc.c			standard
+x86/x86/delay.c			standard
 x86/xen/hvm.c			optional	xenhvm
 x86/xen/xen_intr.c		optional	xen | xenhvm
 x86/xen/pv.c			optional	xenhvm
diff --git a/sys/conf/files.i386 b/sys/conf/files.i386
index eb8697c..790296d 100644
--- a/sys/conf/files.i386
+++ b/sys/conf/files.i386
_at__at_ -600,5 +600,6 _at__at_ x86/x86/mptable_pci.c		optional apic native pci
 x86/x86/msi.c			optional apic pci
 x86/x86/nexus.c			standard
 x86/x86/tsc.c			standard
+x86/x86/delay.c			standard
 x86/xen/hvm.c			optional xenhvm
 x86/xen/xen_intr.c		optional xen | xenhvm
diff --git a/sys/dev/xen/timer/timer.c b/sys/dev/xen/timer/timer.c
index b2f6bcd..96372ab 100644
--- a/sys/dev/xen/timer/timer.c
+++ b/sys/dev/xen/timer/timer.c
_at__at_ -59,6 +59,9 _at__at_ __FBSDID("$FreeBSD$");
 #include <machine/_inttypes.h>
 #include <machine/smp.h>
 
+/* For the declaration of clock_lock */
+#include <isa/rtc.h>
+
 #include "clock_if.h"
 
 static devclass_t xentimer_devclass;
_at__at_ -584,6 +587,36 _at__at_ xentimer_suspend(device_t dev)
 	return (0);
 }
 
+/*
+ * Xen delay early init
+ */
+void xen_delay_init(void)
+{
+	/* Init the clock lock */
+	mtx_init(&clock_lock, "clk", NULL, MTX_SPIN | MTX_NOPROFILE);
+}
+/*
+ * Xen PV DELAY function
+ *
+ * When running on PVH mode we don't have an emulated i8524, so
+ * make use of the Xen time info in order to code a simple DELAY
+ * function that can be used during early boot.
+ */
+void xen_delay(int n)
+{
+	uint64_t end_ns;
+	uint64_t current;
+
+	end_ns = xen_fetch_vcpu_time(&HYPERVISOR_shared_info->vcpu_info[0]);
+	end_ns += n * NSEC_IN_USEC;
+
+	for (;;) {
+		current = xen_fetch_vcpu_time(&HYPERVISOR_shared_info->vcpu_info[0]);
+		if (current >= end_ns)
+			break;
+	}
+}
+
 static device_method_t xentimer_methods[] = {
 	DEVMETHOD(device_identify, xentimer_identify),
 	DEVMETHOD(device_probe, xentimer_probe),
diff --git a/sys/i386/i386/machdep.c b/sys/i386/i386/machdep.c
index d43abbf..ad2734f 100644
--- a/sys/i386/i386/machdep.c
+++ b/sys/i386/i386/machdep.c
_at__at_ -253,6 +253,17 _at__at_ struct mtx icu_lock;
 
 struct mem_range_softc mem_range_softc;
 
+#ifndef XEN
+void
+DELAY(int n)
+{
+	if (delay_tc(n))
+		return;
+
+	i8254_delay(n);
+}
+#endif
+
 static void
 cpu_startup(dummy)
 	void *dummy;
diff --git a/sys/i386/include/clock.h b/sys/i386/include/clock.h
index d980ec7..287b2c8 100644
--- a/sys/i386/include/clock.h
+++ b/sys/i386/include/clock.h
_at__at_ -22,6 +22,12 _at__at_ extern int	tsc_is_invariant;
 extern int	tsc_perf_stat;
 
 void	i8254_init(void);
+void	i8254_delay(int);
+#ifdef XENHVM
+void	xen_delay_init(void);
+void	xen_delay(int);
+#endif
+int	delay_tc(int);
 
 /*
  * Driver to clock driver interface.
diff --git a/sys/x86/isa/clock.c b/sys/x86/isa/clock.c
index a12e175..a5aed1c 100644
--- a/sys/x86/isa/clock.c
+++ b/sys/x86/isa/clock.c
_at__at_ -247,61 +247,13 _at__at_ getit(void)
 	return ((high << 8) | low);
 }
 
-#ifndef DELAYDEBUG
-static u_int
-get_tsc(__unused struct timecounter *tc)
-{
-
-	return (rdtsc32());
-}
-
-static __inline int
-delay_tc(int n)
-{
-	struct timecounter *tc;
-	timecounter_get_t *func;
-	uint64_t end, freq, now;
-	u_int last, mask, u;
-
-	tc = timecounter;
-	freq = atomic_load_acq_64(&tsc_freq);
-	if (tsc_is_invariant && freq != 0) {
-		func = get_tsc;
-		mask = ~0u;
-	} else {
-		if (tc->tc_quality <= 0)
-			return (0);
-		func = tc->tc_get_timecount;
-		mask = tc->tc_counter_mask;
-		freq = tc->tc_frequency;
-	}
-	now = 0;
-	end = freq * n / 1000000;
-	if (func == get_tsc)
-		sched_pin();
-	last = func(tc) & mask;
-	do {
-		cpu_spinwait();
-		u = func(tc) & mask;
-		if (u < last)
-			now += mask - last + u + 1;
-		else
-			now += u - last;
-		last = u;
-	} while (now < end);
-	if (func == get_tsc)
-		sched_unpin();
-	return (1);
-}
-#endif
-
 /*
  * Wait "n" microseconds.
  * Relies on timer 1 counting down from (i8254_freq / hz)
  * Note: timer had better have been programmed before this is first used!
  */
 void
-DELAY(int n)
+i8254_delay(int n)
 {
 	int delta, prev_tick, tick, ticks_left;
 #ifdef DELAYDEBUG
_at__at_ -317,9 +269,6 _at__at_ DELAY(int n)
 	}
 	if (state == 1)
 		printf("DELAY(%d)...", n);
-#else
-	if (delay_tc(n))
-		return;
 #endif
 	/*
 	 * Read the counter first, so that the rest of the setup overhead is
diff --git a/sys/x86/x86/delay.c b/sys/x86/x86/delay.c
new file mode 100644
index 0000000..7ea70b1
--- /dev/null
+++ b/sys/x86/x86/delay.c
_at__at_ -0,0 +1,95 _at__at_
+/*-
+ * Copyright (c) 1990 The Regents of the University of California.
+ * Copyright (c) 2010 Alexander Motin <mav_at_FreeBSD.org>
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * William Jolitz and Don Ahn.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 4. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ *	from: _at_(#)clock.c	7.2 (Berkeley) 5/12/91
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+/* Generic x86 routines to handle delay */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/timetc.h>
+#include <sys/proc.h>
+#include <sys/kernel.h>
+#include <sys/sched.h>
+
+#include <machine/clock.h>
+#include <machine/cpu.h>
+
+static u_int
+get_tsc(__unused struct timecounter *tc)
+{
+
+	return (rdtsc32());
+}
+
+int
+delay_tc(int n)
+{
+	struct timecounter *tc;
+	timecounter_get_t *func;
+	uint64_t end, freq, now;
+	u_int last, mask, u;
+
+	tc = timecounter;
+	freq = atomic_load_acq_64(&tsc_freq);
+	if (tsc_is_invariant && freq != 0) {
+		func = get_tsc;
+		mask = ~0u;
+	} else {
+		if (tc->tc_quality <= 0)
+			return (0);
+		func = tc->tc_get_timecount;
+		mask = tc->tc_counter_mask;
+		freq = tc->tc_frequency;
+	}
+	now = 0;
+	end = freq * n / 1000000;
+	if (func == get_tsc)
+		sched_pin();
+	last = func(tc) & mask;
+	do {
+		cpu_spinwait();
+		u = func(tc) & mask;
+		if (u < last)
+			now += mask - last + u + 1;
+		else
+			now += u - last;
+		last = u;
+	} while (now < end);
+	if (func == get_tsc)
+		sched_unpin();
+	return (1);
+}
diff --git a/sys/x86/xen/pv.c b/sys/x86/xen/pv.c
index bbaca32..f8d8cfa 100644
--- a/sys/x86/xen/pv.c
+++ b/sys/x86/xen/pv.c
_at__at_ -36,6 +36,7 _at__at_ __FBSDID("$FreeBSD$");
 #include <sys/systm.h>
 
 #include <machine/sysarch.h>
+#include <machine/clock.h>
 
 #include <xen/xen-os.h>
 #include <xen/pv.h>
_at__at_ -47,6 +48,8 _at__at_ static caddr_t xen_pv_parse_preload_data(u_int64_t);
 /* Xen init_ops implementation. */
 struct init_ops xen_init_ops = {
 	.parse_preload_data =	xen_pv_parse_preload_data,
+	.early_delay_init =	xen_delay_init,
+	.early_delay =		xen_delay,
 };
 
 static struct
-- 
1.7.7.5 (Apple Git-26)
Received on Thu Dec 19 2013 - 17:55:28 UTC

This archive was generated by hypermail 2.4.0 : Wed May 19 2021 - 11:40:45 UTC