This brings in support for Xen hypercalls via an MI interface
and implements functions to fetch extended version and features.
OK?
---
sys/dev/pv/xen.c | 227 ++++++++++++++++++++++++++++++++++++++++++++++++++++
sys/dev/pv/xenreg.h | 184 ++++++++++++++++++++++++++++++++++++++++++
sys/dev/pv/xenvar.h | 23 ++++++
3 files changed, 434 insertions(+)
create mode 100644 sys/dev/pv/xenreg.h
diff --git sys/dev/pv/xen.c sys/dev/pv/xen.c
index ee16d99..f1198d1 100644
--- sys/dev/pv/xen.c
+++ sys/dev/pv/xen.c
@@ -26,15 +26,19 @@
#include <machine/cpufunc.h>
#include <uvm/uvm_extern.h>
#include <dev/pv/pvvar.h>
+#include <dev/pv/xenreg.h>
#include <dev/pv/xenvar.h>
struct xen_softc *xen_sc;
void xen_find_base(struct xen_softc *);
+int xen_init_hypercall(struct xen_softc *);
+int xen_getversion(struct xen_softc *);
+int xen_getfeatures(struct xen_softc *);
int xen_match(struct device *, void *, void *);
void xen_attach(struct device *, struct device *, void *);
void xen_resume(struct device *);
int xen_activate(struct device *, int);
@@ -70,12 +74,20 @@ xen_attach(struct device *parent, struct device *self, void
*aux)
xen_find_base(sc);
printf("\n");
+ if (xen_init_hypercall(sc))
+ return;
+
/* Wire it up to the global */
xen_sc = sc;
+
+ if (xen_getversion(sc))
+ return;
+ if (xen_getfeatures(sc))
+ return;
}
void
xen_resume(struct device *self)
{
@@ -109,5 +121,220 @@ xen_find_base(struct xen_softc *sc)
}
break;
}
}
}
+
+int
+xen_init_hypercall(struct xen_softc *sc)
+{
+ extern void *xen_hypercall_page;
+ uint32_t regs[4];
+ paddr_t pa;
+
+ /* Get hypercall page configuration MSR */
+ CPUID(sc->sc_base + CPUID_OFFSET_XEN_HYPERCALL,
+ regs[0], regs[1], regs[2], regs[3]);
+
+ /* We don't support more than one hypercall page */
+ if (regs[0] != 1) {
+ printf("%s: requested %d hypercall pages\n",
+ sc->sc_dev.dv_xname, regs[0]);
+ return (-1);
+ }
+
+ sc->sc_hc = &xen_hypercall_page;
+
+ if (!pmap_extract(pmap_kernel(), (vaddr_t)sc->sc_hc, &pa)) {
+ printf("%s: hypercall page PA extraction failed\n",
+ sc->sc_dev.dv_xname);
+ return (-1);
+ }
+ wrmsr(regs[1], pa);
+
+ DPRINTF("%s: hypercall page at va %p pa %#lx\n", sc->sc_dev.dv_xname,
+ sc->sc_hc, pa);
+
+ return (0);
+}
+
+int
+xen_hypercall(struct xen_softc *sc, int op, int argc, ...)
+{
+ va_list ap;
+ ulong argv[5];
+ int i;
+
+ if (argc < 0 || argc > 5)
+ return (-1);
+ va_start(ap, argc);
+ for (i = 0; i < argc; i++)
+ argv[i] = (ulong)va_arg(ap, ulong);
+ return (xen_hypercallv(sc, op, argc, argv));
+}
+
+int
+xen_hypercallv(struct xen_softc *sc, int op, int argc, ulong *argv)
+{
+ ulong hcall;
+ int rv = 0;
+
+ hcall = (ulong)sc->sc_hc + op * 32;
+
+#if defined(XEN_DEBUG) && disabled
+ {
+ int i;
+
+ printf("hypercall %d", op);
+ if (argc > 0) {
+ printf(", args {");
+ for (i = 0; i < argc; i++)
+ printf(" %#lx", argv[i]);
+ printf(" }\n");
+ } else
+ printf("\n");
+ }
+#endif
+
+ switch (argc) {
+ case 0: {
+ HYPERCALL_RES1;
+ __asm__ volatile ( \
+ HYPERCALL_LABEL \
+ : HYPERCALL_OUT1 \
+ : HYPERCALL_PTR(hcall) \
+ : HYPERCALL_CLOBBER \
+ );
+ HYPERCALL_RET(rv);
+ break;
+ }
+ case 1: {
+ HYPERCALL_RES1; HYPERCALL_RES2;
+ HYPERCALL_ARG1(argv[0]);
+ __asm__ volatile ( \
+ HYPERCALL_LABEL \
+ : HYPERCALL_OUT1 HYPERCALL_OUT2 \
+ : HYPERCALL_IN1 \
+ , HYPERCALL_PTR(hcall) \
+ : HYPERCALL_CLOBBER \
+ );
+ HYPERCALL_RET(rv);
+ break;
+ }
+ case 2: {
+ HYPERCALL_RES1; HYPERCALL_RES2; HYPERCALL_RES3;
+ HYPERCALL_ARG1(argv[0]); HYPERCALL_ARG2(argv[1]);
+ __asm__ volatile ( \
+ HYPERCALL_LABEL \
+ : HYPERCALL_OUT1 HYPERCALL_OUT2 \
+ HYPERCALL_OUT3 \
+ : HYPERCALL_IN1 HYPERCALL_IN2 \
+ , HYPERCALL_PTR(hcall) \
+ : HYPERCALL_CLOBBER \
+ );
+ HYPERCALL_RET(rv);
+ break;
+ }
+ case 3: {
+ HYPERCALL_RES1; HYPERCALL_RES2; HYPERCALL_RES3;
+ HYPERCALL_RES4;
+ HYPERCALL_ARG1(argv[0]); HYPERCALL_ARG2(argv[1]);
+ HYPERCALL_ARG3(argv[2]);
+ __asm__ volatile ( \
+ HYPERCALL_LABEL \
+ : HYPERCALL_OUT1 HYPERCALL_OUT2 \
+ HYPERCALL_OUT3 HYPERCALL_OUT4 \
+ : HYPERCALL_IN1 HYPERCALL_IN2 \
+ HYPERCALL_IN3 \
+ , HYPERCALL_PTR(hcall) \
+ : HYPERCALL_CLOBBER \
+ );
+ HYPERCALL_RET(rv);
+ break;
+ }
+ case 4: {
+ HYPERCALL_RES1; HYPERCALL_RES2; HYPERCALL_RES3;
+ HYPERCALL_RES4; HYPERCALL_RES5;
+ HYPERCALL_ARG1(argv[0]); HYPERCALL_ARG2(argv[1]);
+ HYPERCALL_ARG3(argv[2]); HYPERCALL_ARG4(argv[3]);
+ __asm__ volatile ( \
+ HYPERCALL_LABEL \
+ : HYPERCALL_OUT1 HYPERCALL_OUT2 \
+ HYPERCALL_OUT3 HYPERCALL_OUT4 \
+ HYPERCALL_OUT5 \
+ : HYPERCALL_IN1 HYPERCALL_IN2 \
+ HYPERCALL_IN3 HYPERCALL_IN4 \
+ , HYPERCALL_PTR(hcall) \
+ : HYPERCALL_CLOBBER \
+ );
+ HYPERCALL_RET(rv);
+ break;
+ }
+ case 5: {
+ HYPERCALL_RES1; HYPERCALL_RES2; HYPERCALL_RES3;
+ HYPERCALL_RES4; HYPERCALL_RES5; HYPERCALL_RES6;
+ HYPERCALL_ARG1(argv[0]); HYPERCALL_ARG2(argv[1]);
+ HYPERCALL_ARG3(argv[2]); HYPERCALL_ARG4(argv[3]);
+ HYPERCALL_ARG5(argv[4]);
+ __asm__ volatile ( \
+ HYPERCALL_LABEL \
+ : HYPERCALL_OUT1 HYPERCALL_OUT2 \
+ HYPERCALL_OUT3 HYPERCALL_OUT4 \
+ HYPERCALL_OUT5 HYPERCALL_OUT6 \
+ : HYPERCALL_IN1 HYPERCALL_IN2 \
+ HYPERCALL_IN3 HYPERCALL_IN4 \
+ HYPERCALL_IN5 \
+ , HYPERCALL_PTR(hcall) \
+ : HYPERCALL_CLOBBER \
+ );
+ HYPERCALL_RET(rv);
+ break;
+ }
+ default:
+ DPRINTF("%s: wrong number of arguments: %d\n", __func__, argc);
+ rv = -1;
+ break;
+ }
+ return (rv);
+}
+
+int
+xen_getversion(struct xen_softc *sc)
+{
+ char buf[16];
+ int version;
+ ulong argv[2] = { XENVER_extraversion, (ulong)&buf[0] };
+ int argc = 2;
+
+ memset(buf, 0, sizeof(buf));
+ if ((version = xen_hypercall(sc, xen_version, 1, XENVER_version)) < 0) {
+ printf("%s: failed to fetch version\n", sc->sc_dev.dv_xname);
+ return (-1);
+ }
+ if (xen_hypercallv(sc, xen_version, argc, argv) < 0) {
+ printf("%s: failed to fetch extended version\n",
+ sc->sc_dev.dv_xname);
+ return (-1);
+ }
+ printf("%s: version %d.%d%s\n", sc->sc_dev.dv_xname,
+ version >> 16, version & 0xffff, buf);
+ return (0);
+}
+
+int
+xen_getfeatures(struct xen_softc *sc)
+{
+ struct xen_feature_info xfi;
+ ulong argv[2] = { XENVER_get_features, (ulong)&xfi };
+ int argc = 2;
+
+ memset(&xfi, 0, sizeof(xfi));
+ if (xen_hypercallv(sc, xen_version, argc, argv) < 0) {
+ printf("%s: failed to fetch features\n", sc->sc_dev.dv_xname);
+ return (-1);
+ }
+ sc->sc_features = xfi.submap;
+ printf("%s: features %b\n", sc->sc_dev.dv_xname, sc->sc_features,
+ "\20\014DOM0\013PIRQ\012PVCLOCK\011CBVEC\010GNTFLAGS\007HMA"
+ "\006PTUPD\005PAE4G\004SUPERVISOR\003AUTOPMAP\002WDT\001WPT");
+ return (0);
+}
diff --git sys/dev/pv/xenreg.h sys/dev/pv/xenreg.h
new file mode 100644
index 0000000..3f646d3
--- /dev/null
+++ sys/dev/pv/xenreg.h
@@ -0,0 +1,184 @@
+/*
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ *
+ * Copyright (c) 2004,2005,2006,2007, Keir Fraser <[email protected]>
+ */
+
+#ifndef _XENREG_H_
+#define _XENREG_H_
+
+/*
+ * Hypercall interface defines
+ */
+
+#if defined(__amd64__)
+# define HYPERCALL_ARG1(_i1) ulong _a1 = (ulong)(_i1)
+# define HYPERCALL_ARG2(_i2) ulong _a2 = (ulong)(_i2)
+# define HYPERCALL_ARG3(_i3) ulong _a3 = (ulong)(_i3)
+# define HYPERCALL_ARG4(_i4) register ulong _a4 __asm__("r10") = (ulong)(_i4)
+# define HYPERCALL_ARG5(_i5) register ulong _a5 __asm__("r8") = (ulong)(_i5)
+# define HYPERCALL_RES1 ulong _r1
+# define HYPERCALL_RES2 ulong _r2
+# define HYPERCALL_RES3 ulong _r3
+# define HYPERCALL_RES4 ulong _r4
+# define HYPERCALL_RES5 /* empty */
+# define HYPERCALL_RES6 /* empty */
+# define HYPERCALL_RET(_rv) (_rv) = _r1
+# define HYPERCALL_LABEL "call *%[hcall]"
+# define HYPERCALL_PTR(_ptr) [hcall] "a" (_ptr)
+# define HYPERCALL_OUT1 "=a" (_r1)
+# define HYPERCALL_OUT2 , "=D" (_r2)
+# define HYPERCALL_OUT3 , "=S" (_r3)
+# define HYPERCALL_OUT4 , "=d" (_r4)
+# define HYPERCALL_OUT5 , "+r" (_a4)
+# define HYPERCALL_OUT6 , "+r" (_a5)
+# define HYPERCALL_IN1 "1" (_a1)
+# define HYPERCALL_IN2 , "2" (_a2)
+# define HYPERCALL_IN3 , "3" (_a3)
+# define HYPERCALL_IN4 /* empty */
+# define HYPERCALL_IN5 /* empty */
+# define HYPERCALL_CLOBBER "memory"
+#elif defined(__i386__)
+# define HYPERCALL_ARG1(_i1) ulong _a1 = (ulong)(_i1)
+# define HYPERCALL_ARG2(_i2) ulong _a2 = (ulong)(_i2)
+# define HYPERCALL_ARG3(_i3) ulong _a3 = (ulong)(_i3)
+# define HYPERCALL_ARG4(_i4) ulong _a4 = (ulong)(_i4)
+# define HYPERCALL_ARG5(_i5) ulong _a5 = (ulong)(_i5)
+# define HYPERCALL_RES1 ulong _r1
+# define HYPERCALL_RES2 ulong _r2
+# define HYPERCALL_RES3 ulong _r3
+# define HYPERCALL_RES4 ulong _r4
+# define HYPERCALL_RES5 ulong _r5
+# define HYPERCALL_RES6 ulong _r6
+# define HYPERCALL_RET(_rv) (_rv) = _r1
+# define HYPERCALL_LABEL "call *%[hcall]"
+# define HYPERCALL_PTR(_ptr) [hcall] "a" (_ptr)
+# define HYPERCALL_OUT1 "=a" (_r1)
+# define HYPERCALL_OUT2 , "=b" (_r2)
+# define HYPERCALL_OUT3 , "=c" (_r3)
+# define HYPERCALL_OUT4 , "=d" (_r4)
+# define HYPERCALL_OUT5 , "=S" (_r5)
+# define HYPERCALL_OUT6 , "=D" (_r6)
+# define HYPERCALL_IN1 "1" (_a1)
+# define HYPERCALL_IN2 , "2" (_a2)
+# define HYPERCALL_IN3 , "3" (_a3)
+# define HYPERCALL_IN4 , "4" (_a4)
+# define HYPERCALL_IN5 , "5" (_a5)
+# define HYPERCALL_CLOBBER "memory"
+#else
+# error "Not implemented"
+#endif
+
+#define CPUID_OFFSET_XEN_HYPERCALL 0x2
+
+/*
+ * interface/xen.h
+ */
+
+typedef uint16_t domid_t;
+
+/* DOMID_SELF is used in certain contexts to refer to oneself. */
+#define DOMID_SELF (0x7FF0U)
+
+/*
+ * interface/features.h
+ *
+ * Feature flags, reported by XENVER_get_features.
+ */
+
+/*
+ * If set, the guest does not need to write-protect its pagetables, and can
+ * update them via direct writes.
+ */
+#define XENFEAT_writable_page_tables 0
+/*
+ * If set, the guest does not need to write-protect its segment descriptor
+ * tables, and can update them via direct writes.
+ */
+#define XENFEAT_writable_descriptor_tables 1
+/*
+ * If set, translation between the guest's 'pseudo-physical' address space
+ * and the host's machine address space are handled by the hypervisor. In this
+ * mode the guest does not need to perform phys-to/from-machine translations
+ * when performing page table operations.
+ */
+#define XENFEAT_auto_translated_physmap 2
+/* If set, the guest is running in supervisor mode (e.g., x86 ring 0). */
+#define XENFEAT_supervisor_mode_kernel 3
+/*
+ * If set, the guest does not need to allocate x86 PAE page directories
+ * below 4GB. This flag is usually implied by auto_translated_physmap.
+ */
+#define XENFEAT_pae_pgdir_above_4gb 4
+/* x86: Does this Xen host support the MMU_PT_UPDATE_PRESERVE_AD hypercall? */
+#define XENFEAT_mmu_pt_update_preserve_ad 5
+/* x86: Does this Xen host support the MMU_{CLEAR,COPY}_PAGE hypercall? */
+#define XENFEAT_highmem_assist 6
+/*
+ * If set, GNTTABOP_map_grant_ref honors flags to be placed into guest kernel
+ * available pte bits.
+ */
+#define XENFEAT_gnttab_map_avail_bits 7
+/* x86: Does this Xen host support the HVM callback vector type? */
+#define XENFEAT_hvm_callback_vector 8
+/* x86: pvclock algorithm is safe to use on HVM */
+#define XENFEAT_hvm_safe_pvclock 9
+/* x86: pirq can be used by HVM guests */
+#define XENFEAT_hvm_pirqs 10
+/* operation as Dom0 is supported */
+#define XENFEAT_dom0 11
+
+
+/*
+ * interface/version.h
+ *
+ * Xen version, type, and compile information.
+ */
+
+/* arg == NULL; returns major:minor (16:16). */
+#define XENVER_version 0
+
+/* arg == 16 bytes buffer. */
+#define XENVER_extraversion 1
+
+/* arg == xen_compile_info. */
+#define XENVER_compile_info 2
+struct xen_compile_info {
+ char compiler[64];
+ char compile_by[16];
+ char compile_domain[32];
+ char compile_date[32];
+};
+
+#define XENVER_get_features 6
+struct xen_feature_info {
+ unsigned int submap_idx; /* IN: which 32-bit submap to return */
+ uint32_t submap; /* OUT: 32-bit submap */
+};
+
+/* arg == NULL; returns host memory page size. */
+#define XENVER_pagesize 7
+
+/* arg == xen_domain_handle_t. */
+#define XENVER_guest_handle 8
+
+#define XENVER_commandline 9
+typedef char xen_commandline_t[1024];
+
+#endif /* _XENREG_H_ */
diff --git sys/dev/pv/xenvar.h sys/dev/pv/xenvar.h
index 76061ee..3fcfc96 100644
--- sys/dev/pv/xenvar.h
+++ sys/dev/pv/xenvar.h
@@ -15,11 +15,34 @@
*/
#ifndef _XENVAR_H_
#define _XENVAR_H_
+#define XEN_DEBUG
+
+#ifdef XEN_DEBUG
+#define DPRINTF(x...) printf(x)
+#else
+#define DPRINTF(x...)
+#endif
+
struct xen_softc {
struct device sc_dev;
uint32_t sc_base;
+ void *sc_hc;
+ uint32_t sc_features;
+#define XENFEAT_CBVEC (1<<8)
};
+extern struct xen_softc *xen_sc;
+
+/*
+ * Hypercalls
+ */
+#define memory_op 12
+#define xen_version 17
+#define hvm_op 34
+
+int xen_hypercall(struct xen_softc *, int, int, ...);
+int xen_hypercallv(struct xen_softc *, int, int, ulong *);
+
#endif /* _XENVAR_H_ */
--
2.6.3