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 <k...@xensource.com> + */ + +#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