xen/ns_xen_extension.patch

4646 lines
119 KiB
Diff

%patch
Index: xen-3.2-testing/xen/arch/x86/hvm/hvm_ext/Makefile
===================================================================
--- /dev/null 1970-01-01 00:00:00.000000000 +0000
+++ xen-3.2-testing/xen/arch/x86/hvm/hvm_ext/Makefile 2008-02-26 14:01:28.000000000 -0500
@@ -0,0 +1,3 @@
+subdir-y += novell
+
+obj-y += hvm_ext.o
Index: xen-3.2-testing/xen/include/asm-x86/hvm/hvm_extensions.h
===================================================================
--- /dev/null 1970-01-01 00:00:00.000000000 +0000
+++ xen-3.2-testing/xen/include/asm-x86/hvm/hvm_extensions.h 2008-02-26 18:57:43.000000000 -0500
@@ -0,0 +1,252 @@
+/****************************************************************************
+ |
+ | Copyright (c) [2007, 2008] Novell, Inc.
+ | All Rights Reserved.
+ |
+ | This program is free software; you can redistribute it and/or
+ | modify it under the terms of version 2 of the GNU General Public License as
+ | published by the Free Software Foundation.
+ |
+ | This program is distributed in the hope that it will be useful,
+ | but WITHOUT ANY WARRANTY; without even the implied warranty of
+ | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ | GNU General Public License for more details.
+ |
+ | You should have received a copy of the GNU General Public License
+ | along with this program; if not, contact Novell, Inc.
+ |
+ | To contact Novell about this file by physical or electronic mail,
+ | you may find current contact information at www.novell.com
+ |
+ |***************************************************************************
+*/
+
+/*
+ * hvm_extensions.h
+ * This file implements a framework for extending the hypervisor
+ * functionality in a modular fashion. The framework is comprised of
+ * two components: A) A set of intercepts that will allow the extension
+ * module to implement its functionality by intercepting the corresponding
+ * code paths in Xen and B) A controlled runtime for the extension module.
+ * Initially the goal was to pacakage the extension module as a boot-time
+ * loadable module. This may not be the way we wend up packaging it.
+ *
+ * Engineering Contact: K. Y. Srinivasan
+ */
+
+#ifndef HVM_EXTENSION_H
+#define HVM_EXTENSION_H
+
+#include <xen/sched.h>
+#include <asm/domain.h>
+#include <xen/timer.h>
+#include <xen/time.h>
+#include <asm/regs.h>
+#include <asm/types.h>
+#include <asm/hvm/io.h>
+#include <asm/hvm/hvm.h>
+#include <asm/hvm/domain.h>
+
+
+
+/*
+ * Hypervisor extension hooks.
+ */
+typedef struct extension_intercept_vector {
+ /* Do not move the first field (do_continuation). Offset
+ * hardcoded in assembly files exits.S (VMX and SVM).
+ */
+ void (*do_continuation)(void);
+ int (*domain_create)(struct domain *d);
+ void (*domain_destroy)(struct domain *d);
+ int (*vcpu_initialize)(struct vcpu *v);
+ void (*vcpu_destroy)(struct vcpu *v);
+ int (*do_cpuid)(uint32_t idx, struct cpu_user_regs *regs);
+ int (*do_msr_read)(uint32_t idx, struct cpu_user_regs *regs);
+ int (*do_msr_write)(uint32_t idx, struct cpu_user_regs *regs);
+ int (*do_hypercall)(struct cpu_user_regs *pregs);
+ void (*do_migrate_timers)(struct vcpu *v);
+ void (*vcpu_up)(struct vcpu *v);
+} extension_intercept_vector_t;
+
+static inline int
+ext_intercept_domain_create(struct domain *d)
+{
+ if (d->arch.hvm_domain.ext_vector) {
+ return(d->arch.hvm_domain.ext_vector->domain_create(d));
+ }
+ return (0);
+}
+
+static inline void
+ext_intercept_domain_destroy(struct domain *d)
+{
+ if (d->arch.hvm_domain.ext_vector) {
+ d->arch.hvm_domain.ext_vector->domain_destroy(d);
+ }
+}
+
+static inline int
+ext_intercept_vcpu_initialize(struct vcpu *v)
+{
+ struct domain *d = v->domain;
+ if (d->arch.hvm_domain.ext_vector) {
+ return(d->arch.hvm_domain.ext_vector->vcpu_initialize(v));
+ }
+ return (0);
+}
+
+
+static inline void
+ext_intercept_vcpu_up(struct vcpu *v)
+{
+ struct domain *d = current->domain;
+ if (d->arch.hvm_domain.ext_vector) {
+ return(d->arch.hvm_domain.
+ ext_vector->vcpu_up(v));
+ }
+}
+
+static inline void
+ext_intercept_vcpu_destroy(struct vcpu *v)
+{
+ struct domain *d = v->domain;
+ if (d->arch.hvm_domain.ext_vector) {
+ d->arch.hvm_domain.ext_vector->vcpu_destroy(v);
+ }
+}
+
+static inline int
+ext_intercept_do_cpuid(uint32_t idx, struct cpu_user_regs *regs)
+{
+ struct domain *d = current->domain;
+ if (d->arch.hvm_domain.ext_vector) {
+ return(d->arch.hvm_domain.ext_vector->do_cpuid(
+ idx, regs));
+ }
+ return (0);
+}
+
+static inline int
+ext_intercept_do_msr_read(uint32_t idx, struct cpu_user_regs *regs)
+{
+ struct domain *d = current->domain;
+ if (d->arch.hvm_domain.ext_vector) {
+ return(d->arch.hvm_domain.
+ ext_vector->do_msr_read(idx, regs));
+ }
+ return (0);
+}
+static inline int
+ext_intercept_do_msr_write(uint32_t idx, struct cpu_user_regs *regs)
+{
+ struct domain *d = current->domain;
+ if (d->arch.hvm_domain.ext_vector) {
+ return(d->arch.hvm_domain.
+ ext_vector->do_msr_write(idx, regs));
+ }
+ return (0);
+}
+
+static inline int
+ext_intercept_do_hypercall(struct cpu_user_regs *regs)
+{
+ struct domain *d = current->domain;
+ if (d->arch.hvm_domain.ext_vector) {
+ return(d->arch.hvm_domain.
+ ext_vector->do_hypercall(regs));
+ }
+ return (0);
+}
+
+static inline void
+ext_intercept_do_migrate_timers(struct vcpu *v)
+{
+ struct domain *d = current->domain;
+ if (d->arch.hvm_domain.ext_vector) {
+ return(d->arch.hvm_domain.
+ ext_vector->do_migrate_timers(v));
+ }
+}
+
+static inline void
+ext_intercept_do_continuation(void)
+{
+ struct domain *d = current->domain;
+ if (d->arch.hvm_domain.ext_vector) {
+ d->arch.hvm_domain.
+ ext_vector->do_continuation();
+ }
+}
+
+/*
+ * Base hypervisor support available to extension modules.
+ * We may choose to do away with this level of indirection!
+ * It may still be useful to have a controlled environment for the
+ * extension modules.
+ */
+typedef struct xen_call_vector {
+ /*
+ * We may want to embed version/compiler info here to avoid mismatches
+ */
+ struct hvm_function_table *hvmFuncTable;
+ struct hvm_mmio_handler *mmIoHandler;
+ void (*extPanic)(const char *s, ...);
+ void (*extPrintk)(const char *format, ...);
+ void (*extPostInterrupt)(struct vcpu *v, int vector, int type);
+ void (*extSetTimer)(struct timer *timer, s_time_t expires);
+ s_time_t (*extGetTimeSinceBoot)(void);
+ void * (*extGetVirtFromGmfn)(struct domain *d, unsigned long gmfn);
+ unsigned long (*extGetMfnFromGmfn)(struct domain *d, unsigned long gmfn);
+ unsigned long (*extGetMfnFromGva)(unsigned long va);
+ void (*extUnmapDomainPage)(void *p);
+ void *(*extAllocMem)(size_t size);
+ void (*extFreeMem)(void *ptr);
+ enum hvm_copy_result (*extCopyToGuestPhysical)(paddr_t paddr, void *buf, int size);
+ enum hvm_copy_result (*extCopyFromGuestPhysical)(void *buf, paddr_t paddr, int size);
+ void *(*extAllocDomHeapPage)(void);
+ void (*extFreeDomHeapPage)(void *);
+ void * (*extGetVirtFromPagePtr)(void *);
+ void (*extVcpuPause)(struct vcpu *v);
+ void (*extVcpuUnPause)(struct vcpu *v);
+ void (*extArchGetDomainInfoCtxt)(struct vcpu *v,
+ struct vcpu_guest_context *);
+ int (*extArchSetDomainInfoCtxt)(struct vcpu *v,
+ struct vcpu_guest_context *);
+ int (*extCpuIsIntel)(void );
+ int (*extWrmsrHypervisorRegs)(uint32_t idx, uint32_t eax,
+ uint32_t edx);
+ void (*extKillTimer)(struct timer *timer);
+ void (*extMigrateTimer)(struct timer *timer, unsigned int new_cpu);
+} xen_call_vector_t;
+
+#define MAX_EXTENSION_ID 1
+
+/*
+ * int hvm_ext_bind(struct domain *d, int ext_id)
+ * Bind the specified domain to the specified extension module.
+ *
+ * Calling/Exit State:
+ * None.
+ *
+ * Remarks:
+ * The goal is to support per-domain extension modules. Domain
+ * creating tools will have to specify the needed extension
+ * module ID. For now it is hard coded.
+ */
+int hvm_ext_bind(struct domain *d, int ext_id);
+
+/*
+ * int hvm_ext_register(int ext_id,
+ * struct extension_intercept_vector *ext_vector,
+ * struct xen_call_vector *xen_vector)
+ * Register the extension module with the hypervisor
+ * Calling/Exit State:
+ * None.
+ */
+
+int hvm_ext_register(int ext_id, struct extension_intercept_vector *ext_vector,
+ struct xen_call_vector *xen_vector);
+
+
+#endif
Index: xen-3.2-testing/xen/arch/x86/hvm/hvm_ext/hvm_ext.c
===================================================================
--- /dev/null 1970-01-01 00:00:00.000000000 +0000
+++ xen-3.2-testing/xen/arch/x86/hvm/hvm_ext/hvm_ext.c 2008-02-26 14:01:28.000000000 -0500
@@ -0,0 +1,350 @@
+/****************************************************************************
+ |
+ | Copyright (c) [2007, 2008] Novell, Inc.
+ | All Rights Reserved.
+ |
+ | This program is free software; you can redistribute it and/or
+ | modify it under the terms of version 2 of the GNU General Public License as
+ | published by the Free Software Foundation.
+ |
+ | This program is distributed in the hope that it will be useful,
+ | but WITHOUT ANY WARRANTY; without even the implied warranty of
+ | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ | GNU General Public License for more details.
+ |
+ | You should have received a copy of the GNU General Public License
+ | along with this program; if not, contact Novell, Inc.
+ |
+ | To contact Novell about this file by physical or electronic mail,
+ | you may find current contact information at www.novell.com
+ |
+ |***************************************************************************
+*/
+
+/*
+ * hvm_ext.c
+ * Glue code for implementing the extension module.
+ *
+ * Engineering Contact: K. Y. Srinivasan
+ */
+
+#include <asm/hvm/hvm_extensions.h>
+#include <xen/lib.h>
+#include <asm/event.h>
+#include <asm/shadow.h>
+#include <asm/hvm/support.h>
+#include <xen/domain_page.h>
+#include <xen/domain.h>
+#include <xen/mm.h>
+#include <xen/event.h>
+#include <xen/sched.h>
+
+
+struct extension_intercept_vector *intercept_vector;
+
+/*
+ * static void
+ * hvm_ext_inject_interrupt(struct vcpu *v, int vector, int type)
+ * Inject the specified exception to the specified virtual cpu.
+ *
+ * Calling/Exit State:
+ * None.
+ */
+static void
+hvm_ext_inject_interrupt(struct vcpu *v, int vector, int type)
+{
+ struct vlapic *vlapic = vcpu_vlapic(v);
+
+ /*
+ * XXXKYS: Check the trigger mode.
+ */
+ if (vlapic_set_irq(vlapic, vector, 1)) {
+ vcpu_kick(v);
+ }
+}
+
+/*
+ * static void
+ * hvm_ext_set_timer(struct timer *timer, s_time_t expires)
+ * Set a timeout.
+ *
+ * Calling/Exit State:
+ * None.
+ */
+static void
+hvm_ext_set_timer(struct timer *timer, s_time_t expires)
+{
+ set_timer(timer, expires);
+}
+
+/*
+ * static void
+ * hvm_ext_kill_timer(struct timer *timer)
+ * Kill the specified timer.
+ *
+ * Calling/Exit State:
+ * None.
+ */
+static void
+hvm_ext_kill_timer(struct timer *timer)
+{
+ kill_timer(timer);
+}
+
+/*
+ * static void
+ * hvm_ext_migrate_timer(struct timer *timer, unsigned int new_cpu)
+ * Migrate the timer to the new cpu.
+ *
+ * Calling/Exit State:
+ * None.
+ */
+static void
+hvm_ext_migrate_timer(struct timer *timer, unsigned int new_cpu)
+{
+ migrate_timer(timer, new_cpu);
+}
+
+
+/*
+ * static void *
+ * hvm_ext_get_virt_from_gmfn(struct domain *d, unsigned long gmfn)
+ * Given a guest frame number return a virtual address at which
+ * the specified page can be accessed in the hypervisor.
+ *
+ * Calling/Exit State:
+ * None.
+ */
+static void *
+hvm_ext_get_virt_from_gmfn(struct domain *d, unsigned long gmfn)
+{
+ unsigned long mfn = gmfn_to_mfn(d, gmfn);
+ if (mfn == INVALID_MFN) {
+ return (NULL);
+ }
+ return (map_domain_page_global(mfn));
+}
+
+/*
+ * static unsigned long
+ * hvm_ext_get_mfn_from_gmfn(struct domain *d, unsigned long gmfn)
+ * Get the machine frame number given the guest frame number.
+ *
+ * Calling/Exit State:
+ * None.
+ */
+static unsigned long
+hvm_ext_get_mfn_from_gmfn(struct domain *d, unsigned long gmfn)
+{
+ return (gmfn_to_mfn(d, gmfn));
+}
+
+/*
+ * static unsigned long
+ * hvm_ext_get_mfn_from_gva(unsigned long va)
+ * Given the guest virtual address return the machine frame number backing the
+ * address.
+ *
+ * Calling/Exit State:
+ * None.
+ */
+static unsigned long
+hvm_ext_get_mfn_from_gva(unsigned long va)
+{
+ uint32_t pfec = PFEC_page_present;
+ unsigned long gfn;
+ gfn = paging_gva_to_gfn(current, va, &pfec);
+ return (gmfn_to_mfn((current->domain), gfn));
+}
+
+/*
+ * static void *
+ * hvm_ext_alloc_mem(size_t size)
+ * Allocate specified bytes of memory.
+ *
+ * Calling/Exit State:
+ * None.
+ */
+static void *
+hvm_ext_alloc_mem(size_t size)
+{
+ return (xmalloc_bytes(size));
+}
+
+/*
+ * static void *
+ * hvm_ext_alloc_domheap_page(void)
+ * Allocate a page from the per-domain heap.
+ *
+ * Calling/Exit State:
+ * None.
+ */
+static void *
+hvm_ext_alloc_domheap_page(void)
+{
+ return (alloc_domheap_page(NULL));
+}
+
+/*
+ * static void
+ * hvm_ext_free_domheap_page(void *p)
+ * Free a dom heap page.
+ *
+ * Calling/Exit State:
+ * None.
+ */
+static void
+hvm_ext_free_domheap_page(void *p)
+{
+ free_domheap_pages(p, 0);
+}
+
+/*
+ * static void *
+ * hvm_ext_get_virt_from_page_ptr(void *page)
+ * Map the specified page a return a hypervisor VA.
+ *
+ *
+ * Calling/Exit State:
+ * None.
+ */
+static void *
+hvm_ext_get_virt_from_page_ptr(void *page)
+{
+ struct page_info *pg = page;
+ unsigned long mfn = page_to_mfn(pg);
+ return (map_domain_page_global(mfn));
+}
+
+extern struct cpuinfo_x86 boot_cpu_data;
+
+/*
+ * static int
+ * hvm_ext_cpu_is_intel(void)
+ * Check if the CPU vendor is Intel.
+ *
+ *
+ * Calling/Exit State:
+ * None.
+ */
+static int
+hvm_ext_cpu_is_intel(void)
+{
+ if (boot_cpu_data.x86_vendor == 0) {
+ return (1);
+ }
+ return (0);
+}
+
+/*
+ * int
+ * hvm_ext_bind(struct domain *d, int ext_id)
+ * Bind the specified domain with the specified extension module.
+ *
+ *
+ * Calling/Exit State:
+ * None.
+ */
+int
+hvm_ext_bind(struct domain *d, int ext_id)
+{
+ int i;
+ /*
+ * XXXKYS: Assuming that this function will be called before the
+ * new domain begins to run. It is critical that this be the case.
+ */
+ if (ext_id == 0) {
+ /*
+ * This is the default value for this parameter.
+ */
+ return (0);
+ }
+ d->arch.hvm_domain.ext_vector = intercept_vector;
+ /*
+ * Let the extension initialize its state.
+ */
+ if (intercept_vector->domain_create(d)) {
+ return (1);
+ }
+ for (i=0; i < MAX_VIRT_CPUS; i++) {
+ if (d->vcpu[i] != NULL) {
+ if (intercept_vector->vcpu_initialize(d->vcpu[i])) {
+ int j;
+ for (j= (i-1); j >=0; j--) {
+ intercept_vector->vcpu_destroy(
+ d->vcpu[j]);
+ }
+ intercept_vector->domain_destroy(d);
+ return (1);
+ }
+ }
+ }
+ return (0);
+}
+
+
+void extPanic(const char *fmt, ...)
+{
+ domain_crash_synchronous();
+}
+
+/*
+ * For now we will support only one extension; id==1!
+ */
+
+extern struct hvm_function_table hvm_funcs;
+extern struct hvm_mmio_handler vlapic_mmio_handler;
+
+/*
+ * int
+ * hvm_ext_register(int ext_id, struct extension_intercept_vector *ext_vector,
+ *
+ * Register the invoking extension module with the hypervisor.
+ *
+ *
+ * Calling/Exit State:
+ * None.
+ */
+int
+hvm_ext_register(int ext_id, struct extension_intercept_vector *ext_vector,
+ struct xen_call_vector *xen_vector)
+{
+ ASSERT(ext_id == 1);
+ intercept_vector = ext_vector;
+ /*
+ * Populate the vector of services from the xen side; ultimately
+ * we may decide to get rid of this level of indirection; it may
+ * still be useful to limit the breadth of xen dependency here.
+ */
+ xen_vector->hvmFuncTable = &hvm_funcs;
+ xen_vector->mmIoHandler = &vlapic_mmio_handler;
+ xen_vector->extPanic = extPanic;
+ xen_vector->extPrintk = printk;
+ xen_vector->extPostInterrupt = hvm_ext_inject_interrupt;
+ xen_vector->extSetTimer = hvm_ext_set_timer;
+ xen_vector->extKillTimer = hvm_ext_kill_timer;
+ xen_vector->extMigrateTimer = hvm_ext_migrate_timer;
+ xen_vector->extGetTimeSinceBoot = get_s_time;
+ xen_vector->extGetVirtFromGmfn = hvm_ext_get_virt_from_gmfn;
+ xen_vector->extGetMfnFromGmfn = hvm_ext_get_mfn_from_gmfn;
+
+ xen_vector->extGetMfnFromGva = hvm_ext_get_mfn_from_gva;
+#ifdef CONFIG_DOMAIN_PAGE
+ xen_vector->extUnmapDomainPage = unmap_domain_page_global;
+#endif
+ xen_vector->extAllocMem = hvm_ext_alloc_mem;
+ xen_vector->extFreeMem = xfree;
+ xen_vector->extCopyToGuestPhysical = hvm_copy_to_guest_phys;
+ xen_vector->extCopyFromGuestPhysical = hvm_copy_from_guest_phys;
+ xen_vector->extAllocDomHeapPage = hvm_ext_alloc_domheap_page;
+ xen_vector->extFreeDomHeapPage = hvm_ext_free_domheap_page;
+ xen_vector->extGetVirtFromPagePtr = hvm_ext_get_virt_from_page_ptr;
+ xen_vector->extVcpuPause = vcpu_pause;
+ xen_vector->extVcpuUnPause = vcpu_unpause;
+ xen_vector->extArchGetDomainInfoCtxt = arch_get_info_guest;
+ xen_vector->extArchSetDomainInfoCtxt = arch_set_info_guest;
+ xen_vector->extCpuIsIntel = hvm_ext_cpu_is_intel;
+ xen_vector->extWrmsrHypervisorRegs = wrmsr_hypervisor_regs;
+
+ return 0;
+}
Index: xen-3.2-testing/xen/arch/x86/hvm/hvm_ext/novell/Makefile
===================================================================
--- /dev/null 1970-01-01 00:00:00.000000000 +0000
+++ xen-3.2-testing/xen/arch/x86/hvm/hvm_ext/novell/Makefile 2008-02-26 14:01:28.000000000 -0500
@@ -0,0 +1,2 @@
+obj-y += nsintercept.o
+obj-y += nshypercall.o
Index: xen-3.2-testing/xen/arch/x86/hvm/hvm_ext/novell/ns_errno.h
===================================================================
--- /dev/null 1970-01-01 00:00:00.000000000 +0000
+++ xen-3.2-testing/xen/arch/x86/hvm/hvm_ext/novell/ns_errno.h 2008-02-26 14:01:28.000000000 -0500
@@ -0,0 +1,62 @@
+/****************************************************************************
+ |
+ | Copyright (c) [2007, 2008] Novell, Inc.
+ | All Rights Reserved.
+ |
+ | This program is free software; you can redistribute it and/or
+ | modify it under the terms of version 2 of the GNU General Public License as
+ | published by the Free Software Foundation.
+ |
+ | This program is distributed in the hope that it will be useful,
+ | but WITHOUT ANY WARRANTY; without even the implied warranty of
+ | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ | GNU General Public License for more details.
+ |
+ | You should have received a copy of the GNU General Public License
+ | along with this program; if not, contact Novell, Inc.
+ |
+ | To contact Novell about this file by physical or electronic mail,
+ | you may find current contact information at www.novell.com
+ |
+ |***************************************************************************
+*/
+
+/*
+ * ns_errno.h
+ * Error codes for the Novell Shim.
+ *
+ * Engineering Contact: K. Y. Srinivasan
+ */
+
+#ifndef NS_ERRNO_H
+#define NS_ERRNO_H
+
+#define NS_STATUS_SUCCESS 0x0000
+#define NS_STATUS_INVALID_HYPERCALL_CODE 0x0002
+#define NS_STATUS_INVALID_HYPERCALL_INPUT 0x0003
+#define NS_STATUS_INVALID_ALIGNMENT 0x0004
+#define NS_STATUS_INVALID_PARAMETER 0x0005
+#define NS_STATUS_ACCESS_DENIED 0x0006
+#define NS_STATUS_INVALID_PARTITION_STATE 0x0007
+#define NS_STATUS_OPERATION_DENIED 0x0008
+#define NS_STATUS_UNKNOWN_PROPERTY 0x0009
+#define NS_STATUS_PROPERTY_VALUE_OUT_OF_RANGE 0x000A
+#define NS_STATUS_INSUFFICIENT_MEMORY 0x000B
+#define NS_STATUS_PARTITION_TOO_DEEP 0x000C
+#define NS_STATUS_INVALID_PARTITION_ID 0x000D
+#define NS_STATUS_INVALID_VP_INDEX 0x000E
+#define NS_STATUS_UNABLE_TO_RESTORE_STATE 0x000F
+#define NS_STATUS_NOT_FOUND 0x0010
+#define NS_STATUS_INVALID_PORT_ID 0x0011
+#define NS_STATUS_INVALID_CONNECTION_ID 0x0012
+#define NS_STATUS_INSUFFICIENT_BUFFERS 0x0013
+#define NS_STATUS_NOT_ACKNOWLEDGED 0x0014
+#define NS_STATUS_INVALID_VP_STATE 0x0015
+#define NS_STATUS_ACKNOWLEDGED 0x0016
+#define NS_STATUS_INVALID_SAVE_RESTORE_STATE 0x0017
+#define NS_STATUS_NO_MEMORY_4PAGES 0x0100
+#define NS_STATUS_NO_MEMORY_16PAGES 0x0101
+#define NS_STATUS_NO_MEMORY_64PAGES 0x0102
+#define NS_STATUS_NO_MEMORY_256PAGES 0x0103
+#define NS_STATUS_NO_MEMORY_1024PAGES 0x0104
+#endif
Index: xen-3.2-testing/xen/arch/x86/hvm/hvm_ext/novell/ns_shim.h
===================================================================
--- /dev/null 1970-01-01 00:00:00.000000000 +0000
+++ xen-3.2-testing/xen/arch/x86/hvm/hvm_ext/novell/ns_shim.h 2008-02-26 14:01:28.000000000 -0500
@@ -0,0 +1,480 @@
+/****************************************************************************
+ |
+ | Copyright (c) [2007, 2008] Novell, Inc.
+ | All Rights Reserved.
+ |
+ | This program is free software; you can redistribute it and/or
+ | modify it under the terms of version 2 of the GNU General Public License as
+ | published by the Free Software Foundation.
+ |
+ | This program is distributed in the hope that it will be useful,
+ | but WITHOUT ANY WARRANTY; without even the implied warranty of
+ | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ | GNU General Public License for more details.
+ |
+ | You should have received a copy of the GNU General Public License
+ | along with this program; if not, contact Novell, Inc.
+ |
+ | To contact Novell about this file by physical or electronic mail,
+ | you may find current contact information at www.novell.com
+ |
+ |***************************************************************************
+*/
+
+/*
+ * Novell Shim Implementation.
+ *
+ * Engineering Contact: K. Y. Srinivasan
+ */
+
+#ifndef NS_SHIM_H
+#define NS_SHIM_H
+
+#include <xen/sched.h>
+#include <xen/types.h>
+#include <xen/timer.h>
+#include <asm/current.h>
+#include <asm/domain.h>
+#include <asm/shadow.h>
+#include <public/xen.h>
+
+#include "nshypercall.h"
+
+/*
+ * Synthetic MSR addresses
+ */
+#define NS_MSR_GUEST_OS_ID 0x40000000
+#define NS_MSR_HYPERCALL 0x40000001
+#define NS_MSR_VP_INDEX 0x40000002
+#define NS_MSR_SYSTEM_RESET 0x40000003
+#define NS_MSR_TIME_REF_COUNT 0x40000020
+#define NS_MSR_EOI 0x40000070
+#define NS_MSR_ICR 0x40000071
+#define NS_MSR_TPR 0x40000072
+
+#define NS_MSR_SCONTROL 0x40000080
+#define NS_MSR_SVERSION 0x40000081
+#define NS_MSR_SIEFP 0x40000082
+#define NS_MSR_SIMP 0x40000083
+#define NS_MSR_SEOM 0x40000084
+#define NS_MSR_SINT0 0x40000090
+#define NS_MSR_SINT1 0x40000091
+#define NS_MSR_SINT2 0x40000092
+#define NS_MSR_SINT3 0x40000093
+#define NS_MSR_SINT4 0x40000094
+#define NS_MSR_SINT5 0x40000095
+#define NS_MSR_SINT6 0x40000096
+#define NS_MSR_SINT7 0x40000097
+#define NS_MSR_SINT8 0x40000098
+#define NS_MSR_SINT9 0x40000099
+#define NS_MSR_SINT10 0x4000009A
+#define NS_MSR_SINT11 0x4000009B
+#define NS_MSR_SINT12 0x4000009C
+#define NS_MSR_SINT13 0x4000009D
+#define NS_MSR_SINT14 0x4000009E
+#define NS_MSR_SINT15 0x4000009F
+
+#define NS_MSR_TIMER0_CONFIG 0x400000B0
+#define NS_MSR_TIMER0_COUNT 0x400000B1
+#define NS_MSR_TIMER1_CONFIG 0x400000B2
+#define NS_MSR_TIMER1_COUNT 0x400000B3
+#define NS_MSR_TIMER2_CONFIG 0x400000B4
+#define NS_MSR_TIMER2_COUNT 0x400000B5
+#define NS_MSR_TIMER3_CONFIG 0x400000B6
+#define NS_MSR_TIMER3_COUNT 0x400000B7
+
+/*
+ * MSR for supporting PV drivers on longhorn.
+ */
+#define NS_MSR_PVDRV_HCALL 0x40001000
+
+/*
+ * MSR for supporting other enlightened oses.
+ */
+#define NS_MSR_NONLH_GUEST_OS_ID 0x40001000
+
+/*
+ * Novell Shim VCPU flags.
+ * A VCPU is considered up when it is capable of invoking hypercalls.
+ */
+#define NS_VCPU_BOOT_CPU 0x00000001
+#define NS_VCPU_UP 0x00000002
+
+/*
+ * Novell shim flush flags.
+ */
+
+#define NS_FLUSH_TLB 0X01
+#define NS_FLUSH_INVLPG 0X02
+
+/*
+ * We use the following global state to manage TLB flush requests from the
+ * guest. At most only one flush can be active in the guest; we may have to
+ * revisit this if this is a bottleneck.
+ */
+typedef struct nsGlobalFlushState {
+ int cpuCount; //0 unused; else #cpus participating
+ cpumask_t waiters; //Cpus waiting for the flush block
+ struct vcpu *currentOwner;
+ u64 retVal;
+ flushVa_t *flushParam;
+ unsigned short repCount;
+} nsGlobalFlushState_t;
+
+typedef struct nsSpinLock {
+ unsigned long flags;
+ spinlock_t spinLock;
+ struct nsVcpu *owner;
+ void *retAddr;
+} nsSpinLock_t;
+
+/*
+ * Novell shim message structure.
+ */
+typedef enum {
+ /*
+ * For now we only support timer messages
+ */
+ nsMessageTypeNone = 0x00000000,
+ nsMessageTimerExpired = 0x80000010
+} nsMessageType;
+
+typedef struct nsTimerMessage {
+ nsMessageType messageType;
+ u8 pad1[3];
+ u8 messageSize;
+ u32 timerIndex;
+ u32 pad2;
+ u64 expirationTime;
+} nsTimerMessage_t;
+
+typedef struct nsMessage {
+ nsMessageType messageType;
+ uint8_t messageSize;
+ uint8_t flags;
+ uint8_t reserved[2];
+ uint32_t reserved1;
+ uint64_t payLoad[30];
+} nsMessage_t;
+
+
+typedef struct nsVcpTimerState {
+ u64 config;
+ u64 count; /*expiration time in 100ns units*/
+ int timerIndex;
+ struct nsVcpu *thisCpu;
+ struct timer vcpuTimer;
+} nsVcpTimerState_t;
+
+/*
+ * Stats structure.
+ */
+
+typedef struct {
+ u64 numSwitches;
+ u64 numFlushes;
+ u64 numFlushesPosted;
+ u64 numFlushRanges;
+ u64 numFlushRangesPosted;
+
+ u64 numTprReads;
+ u64 numIcrReads;
+ u64 numEoiWrites;
+ u64 numTprWrites;
+ u64 numIcrWrites;
+
+ u64 numGFSAcquires;
+ u64 numGFSReleases;
+ u64 numTlbFlushes;
+ u64 numInvlPages;
+ u64 numTimeOuts;
+} nsVcpuStats_t;
+
+typedef struct nsVcpu {
+ /*
+ * Per-vcpu state to support the Novell shim;
+ */
+ int nsVcplockDepth;
+ unsigned long nsVcpuFlags;
+ unsigned char nsVcpFlushRequest;
+ unsigned char nsVcpWaitingOnGFS;
+ unsigned char nsVcpFlushPending;
+ unsigned char nsVcpWaitingForCleanup;
+ unsigned short nsVcpRepCount;
+ /*
+ * Synthetic msrs.
+ */
+ u64 nsVcpSControlMsr;
+ u64 nsVcpSVersionMsr;
+ u64 nsVcpSIefpMsr;
+ u64 nsVcpSimpMsr;
+ u64 nsVcpEomMsr;
+
+ u64 nsVcpSIntMsr[16];
+ /*
+ * Timer MSRs.
+ */
+ nsVcpTimerState_t nsVcpTimers[4];
+ void *nsVcpSiefPage;
+ void *nsVcpSimPage;
+ /*
+ * Hypercall input/output processing.
+ * We keep these pages mapped in the hypervisor space.
+ */
+ void *nsVcpInputBuffer; /*input buffer virt address*/
+ void *nsVcpInputBufferPage; /*input buffer struct page */
+ void *nsVcpOutputBuffer; /*output buffer virt address*/
+ void *nsVcpOutputBufferPage; /*output buffer struct page */
+ struct vcpu *nsVcpXenVcpu; /*corresponding xen vcpu*/
+ nsVcpuStats_t nsVcpStats;
+} nsVcpu_t;
+
+/*
+ * Events of interest for gathering stats.
+ */
+#define NS_CSWITCH 1
+#define NS_FLUSH_VA_STAT 2
+#define NS_FLUSH_RANGE 3
+#define NS_FLUSH_VA_POSTED 4
+#define NS_FLUSH_RANGE_POSTED 5
+#define NS_TPR_READ 6
+#define NS_ICR_READ 7
+#define NS_TPR_WRITE 8
+#define NS_ICR_WRITE 9
+#define NS_EOI_WRITE 10
+
+#define NS_GFS_ACQUIRE 11
+#define NS_GFS_RELEASE 12
+#define NS_TLB_FLUSH 13
+#define NS_INVL_PG 14
+#define NS_TIMEOUTS 15
+
+void nsCollectStats(int event, nsVcpuStats_t *ststp);
+
+#define NS_STATS //KYS: Temporary
+
+#ifdef NS_STATS
+#define NS_STATS_COLLECT(event, statp) nsCollectStats(event, statp)
+#else
+define NS_STATS_COLLECT(event, statp)
+#endif
+
+typedef struct nsPartition {
+ /*
+ * State maintained on a per guest basis to implement
+ * the Novell shim.
+ */
+ nsSpinLock_t nsLock;
+ atomic_t nsNumVcpusActive;
+ u64 nsGuestIdMsr;
+ u64 nsHypercallMsr;
+ u64 nsPrivileges;
+ u64 nsSupportedFeatures;
+ unsigned long nsHypercallMfn;
+ int nsLongModeGuest;
+ /*
+ * Each VCPU here corresponds to the vcpu in the underlying hypervisor;
+ * they share the same ID.
+ */
+ nsVcpu_t nsVcpuState[MAX_VIRT_CPUS];
+ nsGlobalFlushState_t nsFlushState;
+} nsPartition_t;
+
+/*
+ * Max CPUID leaves supported.
+ */
+
+#define NX_MAX_CPUID_LEAVES 5
+
+/*
+ * We don't want to intercept instructions coming from the hvm bootstrap code.
+ *
+ */
+#define NS_BIOS_HIGH_ADDR
+/*
+ * Privilege flags.
+ */
+
+#define NS_ACCESS_VP_RUNTIME (1ULL << 0)
+#define NS_ACCESS_TIME_REF_CNT (1ULL << 1)
+#define NS_ACCESS_SYNC_MSRS (1ULL << 2)
+#define NS_ACCESS_SYNC_TIMERS (1ULL << 3)
+#define NS_ACCESS_APIC_MSRS (1ULL << 4)
+#define NS_ACCESS_PARTITION_ID (1ULL << 33)
+
+#define nsGetCurrentPartition() \
+((current)->domain->arch.hvm_domain.ext_handle)
+
+#define nsGetCurrentVcpuIndex() (current)->vcpu_id
+
+#define NS_PANIC(x) \
+do {\
+ nsXenVector.extPrintk("File is: %s\n", __FILE__);\
+ nsXenVector.extPrintk("Line is: %d\n", __LINE__);\
+ nsXenVector.extPanic((x));\
+} while (0);
+
+#define NS_ASSERT(x) \
+do {\
+ if (!(x)) \
+ NS_PANIC("ASSERTION FAILED\n")\
+} while (0);
+
+#define nsDebugPrint(x) \
+do { \
+ nsXenVector.extPrintk("File is: %s\n", __FILE__);\
+ nsXenVector.extPrintk("Line is: %d\n", __LINE__);\
+ nsXenVector.extPrintk((x));\
+} while (0);
+
+/* Hooks into Xen */
+extern xen_call_vector_t nsXenVector;
+
+/*
+ * static inline int
+ * nsInvalidCpuState(void)
+ * Check to see if the calling CPU is in the "correct state" to invoke
+ * the functionality implemented in the Novell Shim (Adaptor).
+ *
+ * Calling/Exit State:
+ * None.
+ */
+
+static inline int
+nsInvalidCpuState(void)
+{
+ int cpuState;
+ cpuState = nsXenVector.hvmFuncTable->guest_x86_mode(current);
+ if ((cpuState == 4) || (cpuState == 8)) {
+ return (0);
+ }
+ return (1);
+}
+
+/*
+ * inline u64
+ * nsBuildHcallRetVal(int code, int reps)
+ *
+ * Given the return code and the number of successfully completed count,
+ * compose a return value compliant with the Viridian specification.
+ *
+ * Calling/Exit State:
+ * None.
+ */
+
+static inline u64
+nsBuildHcallRetVal(int code, int reps)
+{
+ u64 retVal=0;
+ retVal |= (code & 0xff);
+ retVal |= (((long long)(reps & 0xfff)) << 32);
+ return (retVal);
+}
+
+
+/*
+ * static inline void nsSetSysCallRetVal(struct cpu_user_regs *pregs,
+ * int longModeGuest, u64 retVal)
+ * Set the return value in the saved guest registers
+ *
+ * Calling/Exit State:
+ * None.
+ */
+
+static inline void nsSetSysCallRetVal(struct cpu_user_regs *pregs,
+ int longModeGuest, u64 retVal)
+{
+ if (longModeGuest) {
+ pregs->eax = retVal;
+ } else {
+ pregs->edx = (u32)(retVal >> 32);
+ pregs->eax = (u32)(retVal);
+ }
+}
+
+/*
+ * static inline int
+ * nsPrivilegeCheck(nsPartition_t *curp, u64 flags)
+ * Check if the caller is privileged to perform the operation
+ * specified by the flags argument.
+ *
+ * Calling/Exit State:
+ * None.
+ */
+
+static inline int
+nsPrivilegeCheck(nsPartition_t *curp, u64 flags)
+{
+ return ((curp->nsPrivileges & flags)? 1: 0);
+}
+
+/* void
+ * nsHandleHyperCall(u64 opcode, u64 input, u64 output,
+ * u64 *retVal);
+ * Common entry point for handling all the extension hypercalls.
+ *
+ * Calling/Exit State:
+ * Based on the hypercall; the caller may give up the CPU while
+ * processing the hypercall. No locks should be held on entry and
+ * no locks will be held on return.
+ *
+ */
+void
+nsHandleHyperCall(u64 opcode, u64 input, u64 output,
+ u64 *retVal);
+
+/*
+ * void nsDoTlbFlush(void);
+ * Perform TLB flush on the invoking virtual CPU.
+ *
+ * Calling/Exit State:
+ * None.
+ */
+void nsDoTlbFlush(void);
+
+/*
+ * void
+ * nsLockAcquire(nsVcpu_t *vcpup, nsSpinLock_t *nsLock)
+ * Acquire the specified lock.
+ *
+ * Calling/Exit State:
+ * None.
+ */
+
+void nsLockAcquire(nsVcpu_t *vcpup, nsSpinLock_t *lock);
+
+/*
+ * void
+ * nsLockRelease(nsVcpu_t *vcpup, nsSpinLock_t *nsLock)
+ * Release the specified spin lock.
+ *
+ * Calling/Exit State:
+ * None.
+ */
+
+void nsLockRelease(nsVcpu_t *vcpup, nsSpinLock_t *lock);
+
+/*
+ * void
+ * nsLockInit(nsSpinLock_t *nsLock)
+ * Initialize the specified spin lock.
+ *
+ * Calling/Exit State:
+ * None.
+ */
+
+void nsLockInit(nsSpinLock_t *lock);
+
+/*
+ * void nsPrintStats(nsPartition_t *curp, int i)
+ * Print the per-vcpu stats for the specified partition.
+ *
+ * Calling/Exit State:
+ * None.
+ */
+
+void nsPrintStats(nsPartition_t *curp, int i);
+
+#define NS_LOCK_OWNED(v, l) \
+((l)->owner == (v))
+#endif /*NS_SHIM_H */
Index: xen-3.2-testing/xen/arch/x86/hvm/hvm_ext/novell/nshypercall.c
===================================================================
--- /dev/null 1970-01-01 00:00:00.000000000 +0000
+++ xen-3.2-testing/xen/arch/x86/hvm/hvm_ext/novell/nshypercall.c 2008-02-28 13:17:22.000000000 -0500
@@ -0,0 +1,1232 @@
+/****************************************************************************
+ |
+ | Copyright (c) [2007, 2008] Novell, Inc.
+ | All Rights Reserved.
+ |
+ | This program is free software; you can redistribute it and/or
+ | modify it under the terms of version 2 of the GNU General Public License as
+ | published by the Free Software Foundation.
+ |
+ | This program is distributed in the hope that it will be useful,
+ | but WITHOUT ANY WARRANTY; without even the implied warranty of
+ | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ | GNU General Public License for more details.
+ |
+ | You should have received a copy of the GNU General Public License
+ | along with this program; if not, contact Novell, Inc.
+ |
+ | To contact Novell about this file by physical or electronic mail,
+ | you may find current contact information at www.novell.com
+ |
+ |***************************************************************************
+*/
+
+/*
+ * nshypercall.c.
+ * This file implements the hypercall component of the Novell Shim. Hopefully
+ * we can host this component either as a driver in the guest or an extension
+ * to the Xen hypervisor.
+ *
+ * Engineering Contact: K. Y. Srinivasan
+ */
+
+#include <asm/page.h>
+#include <asm/processor.h>
+#include <asm/hvm/support.h>
+#include <xen/cpumask.h>
+#include <xen/event.h>
+
+#include <asm/hvm/hvm_extensions.h>
+#include "ns_shim.h"
+#include "ns_errno.h"
+#include "nshypercall.h"
+
+
+
+void nsDoTlbFlush(void);
+static void
+nsFlushPostProcess(nsPartition_t *curp, nsVcpu_t *curVcpup);
+
+
+
+/*
+ * void nsCollectStats(int event, nsVcpuStats_t *statsp)
+ * Collect stats.
+ *
+ * Calling/Exit State:
+ * None.
+ */
+
+void nsCollectStats(int event, nsVcpuStats_t *statsp)
+{
+ switch (event) {
+ case NS_CSWITCH:
+ statsp->numSwitches++;
+ return;
+ case NS_FLUSH_VA:
+ statsp->numFlushes++;
+ return;
+ case NS_FLUSH_RANGE:
+ statsp->numFlushRanges++;
+ return;
+ case NS_FLUSH_VA_POSTED:
+ statsp->numFlushesPosted++;
+ return;
+ case NS_FLUSH_RANGE_POSTED:
+ statsp->numFlushRangesPosted++;
+ return;
+ case NS_TPR_READ:
+ statsp->numTprReads++;
+ return;
+ case NS_ICR_READ:
+ statsp->numIcrReads++;
+ return;
+ case NS_TPR_WRITE:
+ statsp->numTprWrites++;
+ return;
+ case NS_ICR_WRITE:
+ statsp->numIcrWrites++;
+ return;
+ case NS_EOI_WRITE:
+ statsp->numEoiWrites++;
+ return;
+
+ case NS_GFS_ACQUIRE:
+ statsp->numGFSAcquires++;
+ return;
+ case NS_GFS_RELEASE:
+ statsp->numGFSReleases++;
+ return;
+ case NS_TLB_FLUSH:
+ statsp->numTlbFlushes++;
+ return;
+ case NS_INVL_PG:
+ statsp->numInvlPages++;
+ return;
+ }
+}
+
+/*
+ * void
+ * nsPrintStats(nsPartition_t *curp, int i)
+ * Print stats.
+ *
+ * Calling/Exit State:
+ * None.
+ */
+void
+nsPrintStats(nsPartition_t *curp, int i)
+{
+ nsVcpu_t *v;
+ v = &curp->nsVcpuState[i];
+ printk("Printing stats for vcpu ID: %d\n", i);
+ printk("Flush pending: %d\n", (int)v->nsVcpFlushPending);
+ printk("Flush Request: %d\n", (int)v->nsVcpFlushRequest);
+ printk("Waiting on GFS: %d\n", (int)v->nsVcpWaitingOnGFS);
+ printk("Waiting for cleanup: %d\n", (int)v->nsVcpWaitingForCleanup);
+
+ printk("Number of context switches: %lu\n", v->nsVcpStats.numSwitches);
+ printk("Number of flushes: %lu\n", v->nsVcpStats.numFlushes);
+ printk("Number of flushes posted: %lu\n", v->nsVcpStats.numFlushesPosted);
+ printk("Number of flush ranges: %lu\n", v->nsVcpStats.numFlushRanges);
+ printk("Number of flush ranges posted: %lu\n", v->nsVcpStats.numFlushRangesPosted);
+ printk("Number of TPR reads: %lu\n", v->nsVcpStats.numTprReads);
+ printk("Number of ICR reads: %lu\n", v->nsVcpStats.numIcrReads);
+ printk("Number of Eoi writes: %lu\n", v->nsVcpStats.numEoiWrites);
+ printk("Number of Tpr writes: %lu\n", v->nsVcpStats.numTprWrites);
+ printk("Number of Icr writes: %lu\n", v->nsVcpStats.numIcrWrites);
+ printk("Number of GFS acuires: %lu\n", v->nsVcpStats.numGFSAcquires);
+ printk("Number of GFS releases: %lu\n", v->nsVcpStats.numGFSReleases);
+ printk("Number of TLB flushes: %lu\n", v->nsVcpStats.numTlbFlushes);
+ printk("Number of INVLPG flushes: %lu\n", v->nsVcpStats.numInvlPages);
+ printk("Number of TIMEOUTS: %lu\n", v->nsVcpStats.numTimeOuts);
+
+}
+
+/*
+ * static inline void nsWakeupWaiters(nsPartition_t *curp)
+ * Wakeup all the VCPUs that may be blocked on the Global
+ * flush state waiting to exclusively own the global flush
+ * state.
+ *
+ * Calling/Exit State:
+ * The partition-wide spin lock nsLock is held on entry and
+ * this lock is held on exit.
+ */
+static inline void nsWakeupWaiters(nsPartition_t *curp)
+{
+ int i;
+ if (!cpus_empty(curp->nsFlushState.waiters)) {
+ /*
+ * Need to wakeup potential waiters that
+ * are waiting for the
+ * flush block to become available.
+ */
+ for (i=0; i < MAX_VIRT_CPUS; i++) {
+ struct vcpu *curVcpu;
+ if (!cpu_isset(i, curp->nsFlushState.waiters))
+ continue;
+ curVcpu =
+ curp->nsVcpuState[i].nsVcpXenVcpu;
+ NS_ASSERT(curVcpu != NULL);
+ if ( test_and_clear_bit(_VPF_blocked_in_xen,
+ &curVcpu->pause_flags) ) {
+ vcpu_wake(curVcpu);
+ }
+ }
+ cpus_clear(curp->nsFlushState.waiters);
+ }
+}
+
+/*
+ * static void nsAcquireGlobalFlushState(nsPartition_t *curp, nsVcpu_t *vcpup)
+ * Acquire the global flush state for exclusive use by the calling
+ * VCPU.
+ *
+ * Calling/Exit State:
+ * On entry nsLock is held and this lock is held on exit. If the calling
+ * VCPU is required to give up the CPU, this lock will be dropped.
+ */
+static void nsAcquireGlobalFlushState(nsPartition_t *curp, nsVcpu_t *vcpup)
+{
+acquireGFSAgain:
+ NS_ASSERT(vcpup->nsVcpWaitingOnGFS == 0);
+ NS_ASSERT(vcpup->nsVcpWaitingForCleanup == 0);
+ NS_ASSERT(NS_LOCK_OWNED(vcpup, &curp->nsLock));
+ if (curp->nsFlushState.currentOwner != NULL) {
+ /*
+ * Somebody is in the midst of flushing; deal with this
+ * situation.
+ */
+ /*
+ * We need to wait for the current flush sequence
+ * to end.
+ */
+ NS_ASSERT(curp->nsFlushState.currentOwner != current);
+ NS_ASSERT(vcpup->nsVcpWaitingForCleanup == 0);
+ if (vcpup->nsVcpFlushPending) {
+ nsLockRelease(vcpup, &curp->nsLock);
+ nsDoTlbFlush();
+ nsLockAcquire(vcpup, &curp->nsLock);
+ }
+ vcpup->nsVcpWaitingOnGFS = 1;
+ cpu_set(current->vcpu_id, curp->nsFlushState.waiters);
+ nsLockRelease(vcpup, &curp->nsLock);
+ wait_on_xen_event_channel(0,
+ ((curp->nsFlushState.currentOwner == NULL) ||
+ (vcpup->nsVcpFlushPending) ||
+ (cpus_empty(curp->nsFlushState.waiters))));
+ nsLockAcquire(vcpup, &curp->nsLock);
+ cpu_clear(current->vcpu_id, curp->nsFlushState.waiters);
+ vcpup->nsVcpWaitingOnGFS = 0;
+ goto acquireGFSAgain;
+ }
+ curp->nsFlushState.repCount = vcpup->nsVcpRepCount;
+ curp->nsFlushState.flushParam =
+ vcpup->nsVcpInputBuffer;
+ NS_STATS_COLLECT(NS_GFS_ACQUIRE, &vcpup->nsVcpStats);
+}
+
+/*
+ * static void nsReleaseGlobalFlushState(nsPartition_t *curp, nsVcpu_t *vcpup,
+ * int lockOwned)
+ * There can at most be one TLB flush event active in the system. All of the
+ * VCPUs that are part of the flush sequence need to relaese their hold
+ * on the global flush object before the global flush object can be freed.
+ * This function manages the release of the global flush object.
+ * If the "lockOwned" parameter is non-zero; on entry the nsLock is held.
+ *
+ * Calling/Exit State:
+ * The current owner of GFS may be forced to give up the CPU.
+ * On exit nsLock is held.
+ */
+static void nsReleaseGlobalFlushState(nsPartition_t *curp, nsVcpu_t *vcpup,
+ int lockOwned)
+{
+ if (!lockOwned) {
+ nsLockAcquire(vcpup, &curp->nsLock);
+ }
+ NS_ASSERT(curp->nsFlushState.cpuCount >= 0);
+ NS_ASSERT(curp->nsFlushState.currentOwner != NULL);
+
+ if (vcpup->nsVcpFlushPending) {
+ curp->nsFlushState.cpuCount--;
+ NS_ASSERT(curp->nsFlushState.cpuCount >= 0);
+ vcpup->nsVcpFlushPending = 0;
+ mb();
+ }
+
+nsReleaseGFS:
+ if (curp->nsFlushState.cpuCount > 0) {
+ if (curp->nsFlushState.currentOwner == current) {
+ /*
+ * We are the initiator; need to wait for
+ * others to complete.
+ */
+ nsWakeupWaiters(curp);
+ vcpup->nsVcpWaitingForCleanup = 1;
+ nsLockRelease(vcpup, &curp->nsLock);
+ wait_on_xen_event_channel(0,(curp->nsFlushState.cpuCount == 0));
+ nsLockAcquire(vcpup, &curp->nsLock);
+ vcpup->nsVcpWaitingForCleanup = 0;
+ goto nsReleaseGFS;
+ } else {
+ return;
+ }
+ }
+ NS_ASSERT(curp->nsFlushState.cpuCount == 0);
+ if (curp->nsFlushState.currentOwner == current) {
+ /* We are the current owner; do the final cleanup.
+ * But first set the return value. This has been stashed
+ * before we blocked.
+ */
+ NS_STATS_COLLECT(NS_GFS_RELEASE, &vcpup->nsVcpStats);
+ vcpup->nsVcpFlushRequest = 0;
+ vcpup->nsVcpFlushPending = 0;
+ vcpup->nsVcpWaitingForCleanup = 0;
+ nsSetSysCallRetVal(guest_cpu_user_regs(),
+ curp->nsLongModeGuest,
+ curp->nsFlushState.retVal);
+ curp->nsFlushState.cpuCount = 0;
+ curp->nsFlushState.currentOwner = NULL;
+ mb();
+ curp->nsFlushState.retVal = 0;
+ curp->nsFlushState.flushParam = NULL;
+ curp->nsFlushState.repCount = 0;
+ nsWakeupWaiters(curp);
+ } else {
+ /*
+ * We are not the owner; wakeup the owner.
+ */
+ if ( test_and_clear_bit(_VPF_blocked_in_xen,
+ &(curp->nsFlushState.currentOwner->pause_flags))){
+ vcpu_wake(curp->nsFlushState.currentOwner);
+ }
+ }
+}
+
+
+/*
+ * static inline int nsFlushPermitted(nsVcpu_t *vcpup)
+ * Check to see if we can execute a TLB flush on the calling vcpu.
+ *
+ * Calling/Exit State:
+ * None.
+ */
+static inline int nsFlushPermitted(nsVcpu_t *vcpup)
+{
+ if (!hvm_paging_enabled(current)) {
+ return (0);
+ }
+ if (current->arch.hvm_vmx.vmxassist_enabled) {
+ return (0);
+ }
+ if (nsInvalidCpuState()) {
+ return (0);
+ }
+
+ return (1);
+}
+
+/*
+ * void
+ * nsDoTlbFlush(void)
+ * Perform flush operations based on the state of GFS. VCPUs may be
+ * forced to relinquish the physical CPU while attempting to flush; in
+ * those events, thi is also the continuation point for execution.
+ *
+ * Calling/Exit State:
+ * None.
+ */
+void
+nsDoTlbFlush(void)
+{
+ nsPartition_t *curp = nsGetCurrentPartition();
+ nsVcpu_t *vcpup = &curp->nsVcpuState[nsGetCurrentVcpuIndex()];
+ flushVa_t *flushArgp;
+ int i,j, numPages;
+ u64 *pgList;
+ long baseVa;
+ unsigned short repCount;
+
+ NS_ASSERT(local_irq_is_enabled());
+
+ NS_ASSERT(vcpup->nsVcplockDepth == 0);
+
+ nsLockAcquire(vcpup, &curp->nsLock);
+ if (vcpup->nsVcpWaitingForCleanup) {
+ /*
+ * This is the continuation point for us; cleanup
+ * the global flush state.
+ */
+ vcpup->nsVcpWaitingForCleanup =0;
+ NS_ASSERT(curp->nsFlushState.currentOwner == current);
+ nsReleaseGlobalFlushState(curp, vcpup, 1);
+ } else if (vcpup->nsVcpWaitingOnGFS) {
+ /*
+ * This is the continuation point for us; acquire
+ * GFS and proceed with our flush operation.
+ */
+ vcpup->nsVcpWaitingOnGFS =0;
+ nsAcquireGlobalFlushState(curp, vcpup);
+ /*
+ * Now do the rest of the syscall processing
+ */
+ nsFlushPostProcess(curp, vcpup);
+ }
+ if (!vcpup->nsVcpFlushPending) {
+ nsLockRelease(vcpup, &curp->nsLock);
+ return;
+ }
+ flushArgp = curp->nsFlushState.flushParam;
+ repCount = curp->nsFlushState.repCount;
+ /*
+ * At this point a flush has been posted; see if we can perform a
+ * flush given our state.
+ */
+ if (!nsFlushPermitted(vcpup)) {
+ nsReleaseGlobalFlushState(curp, vcpup, 1);
+ nsLockRelease(vcpup, &curp->nsLock);
+ NS_ASSERT(vcpup->nsVcplockDepth == 0);
+ return;
+ }
+ nsLockRelease(vcpup, &curp->nsLock);
+ if (vcpup->nsVcpFlushPending & NS_FLUSH_TLB) {
+ NS_STATS_COLLECT(NS_TLB_FLUSH, &vcpup->nsVcpStats);
+ paging_update_cr3(current);
+ } else {
+ pgList = &flushArgp->gva;
+ NS_ASSERT(vcpup->nsVcpFlushPending == NS_FLUSH_INVLPG);
+ NS_ASSERT(pgList != NULL);
+ NS_ASSERT(repCount >=1);
+ NS_STATS_COLLECT(NS_INVL_PG, &vcpup->nsVcpStats);
+ for (i = 0; i < repCount; i++) {
+ baseVa = (long)(pgList[i] & PAGE_MASK);
+ numPages = (int)(~baseVa & pgList[i]);
+ for (j = 0; j <= numPages; j++) {
+ if (paging_invlpg(current,
+ (baseVa + (j << PAGE_SHIFT)))) {
+ flush_tlb_one_local((baseVa +
+ (j<< PAGE_SHIFT)));
+ }
+ //KYS: need to deal with ASIDS
+ }
+ }
+ }
+ /*
+ * Do post processing on the global flush state.
+ */
+ nsReleaseGlobalFlushState(curp, vcpup, 0);
+ nsLockRelease(vcpup, &curp->nsLock);
+ NS_ASSERT(vcpup->nsVcplockDepth == 0);
+}
+
+/*
+ * static int
+ * nsGetVpRegisters(paddr_t input, paddr_t output)
+ * Get the VCP register state.
+ *
+ * Calling/Exit State:
+ * None.
+ */
+
+static int
+nsGetVpRegisters(paddr_t input, paddr_t output)
+{
+ nsVcpu_t *vcpup, *targetp;
+ nsPartition_t *curp = nsGetCurrentPartition();
+ getVpRegistersInput_t *inBuf;
+ getVpRegistersOutput_t *outBuf;
+ struct vcpu_guest_context *vcpuCtx;
+ u32 *regIndexp;
+ getVpRegistersOutput_t *outRegp;
+ u32 numOutputBytes = 0;
+
+ vcpup = &curp->nsVcpuState[nsGetCurrentVcpuIndex()];
+ inBuf = vcpup->nsVcpInputBuffer;
+ outBuf = vcpup->nsVcpOutputBuffer;
+ outRegp = outBuf;
+ /*
+ * Copy the input data to the per-cpu input buffer.
+ * This may be an overkill; obviously it is better to only
+ * copy what we need. XXXKYS: Check with Mike.
+ */
+ if (nsXenVector.extCopyFromGuestPhysical(inBuf, input, PAGE_SIZE)) {
+ return (NS_STATUS_INVALID_ALIGNMENT);
+ }
+ /*
+ * If the partition ID specified does not match with the current
+ * domain return appropriate error.
+ */
+ if ((u64)current->domain->domain_id != inBuf-> partitionId) {
+ return (NS_STATUS_ACCESS_DENIED);
+ }
+ if (inBuf->vpIndex > MAX_VIRT_CPUS) {
+ return (NS_STATUS_INVALID_VP_INDEX);
+ }
+ targetp = &curp->nsVcpuState[inBuf->vpIndex];
+ if (!(targetp->nsVcpuFlags & NS_VCPU_UP)) {
+ return (NS_STATUS_INVALID_VP_STATE);
+ }
+ if ((vcpuCtx =
+ nsXenVector.extAllocMem(sizeof(struct vcpu_guest_context)))
+ == NULL) {
+ return (NS_STATUS_INSUFFICIENT_MEMORY);
+ }
+
+ /*
+ * Get the register state of the specified vcp.
+ */
+ if (current->vcpu_id != inBuf->vpIndex) {
+ nsXenVector.extVcpuPause(targetp->nsVcpXenVcpu);
+ }
+ nsXenVector.extArchGetDomainInfoCtxt(targetp->nsVcpXenVcpu, vcpuCtx);
+ if (current->vcpu_id != inBuf->vpIndex) {
+ nsXenVector.extVcpuUnPause(targetp->nsVcpXenVcpu);
+ }
+ /*
+ * Now that we have the register state; select what we want and
+ * populate the output buffer.
+ */
+ regIndexp = &inBuf->regIndex;
+ while (*regIndexp != 0) {
+ switch (*regIndexp) {
+ /*
+ * XXXKYS: need mapping code here; populate
+ * outBuf.
+ */
+ NS_PANIC("nsGetVpRegisters not supported\n");
+ }
+ regIndexp++;
+ outRegp++ ; /*128 bit registers */
+ numOutputBytes +=16;
+ if ((char *)regIndexp > ((char *)inBuf + PAGE_SIZE)) {
+ /*
+ *input list not reminated correctly; bail out.
+ */
+ NS_PANIC("nsGetVpRegisters:input list not terminated\n");
+ break;
+ }
+ }
+ if (nsXenVector.extCopyToGuestPhysical(output, outBuf,
+ numOutputBytes)) {
+ /* Some problem copying data out*/
+ NS_PANIC("nsGetVpRegisters:copyout problem\n");
+ }
+ nsXenVector.extFreeMem(vcpuCtx);
+ return (NS_STATUS_SUCCESS);
+}
+
+/*
+ * static int
+ * nsSetVpRegisters(paddr_t input, paddr_t output)
+ * Set the VCPU register state.
+ *
+ * Calling/Exit State:
+ * None.
+ */
+
+static int
+nsSetVpRegisters(paddr_t input, paddr_t output)
+{
+ nsVcpu_t *vcpup, *targetp;
+ nsPartition_t *curp = nsGetCurrentPartition();
+ setVpRegistersInput_t *inBuf;
+ struct vcpu_guest_context *vcpuCtx;
+ setVpRegisterSpec_t *regIndexp;
+ int retVal = NS_STATUS_SUCCESS;
+
+ vcpup = &curp->nsVcpuState[nsGetCurrentVcpuIndex()];
+ inBuf = vcpup->nsVcpInputBuffer;
+ /*
+ * Copy the input data to the per-cpu input buffer.
+ * This may be an overkill; obviously it is better to only
+ * copy what we need. XXXKYS: Check with Mike.
+ */
+ if (nsXenVector.extCopyFromGuestPhysical(inBuf, input, PAGE_SIZE)) {
+ return (NS_STATUS_INVALID_ALIGNMENT);
+ }
+ /*
+ * If the partition ID specified does not match with the current
+ * domain return appropriate error.
+ */
+ if ((u64)current->domain->domain_id != inBuf-> partitionId) {
+ return (NS_STATUS_ACCESS_DENIED);
+ }
+ if (inBuf->vpIndex > MAX_VIRT_CPUS) {
+ return (NS_STATUS_INVALID_VP_INDEX);
+ }
+ targetp = &curp->nsVcpuState[inBuf->vpIndex];
+ if (!(targetp->nsVcpuFlags & NS_VCPU_UP)) {
+ return (NS_STATUS_INVALID_VP_STATE);
+ }
+ if ((vcpuCtx =
+ nsXenVector.extAllocMem(sizeof(struct vcpu_guest_context)))
+ == NULL) {
+ return (NS_STATUS_INSUFFICIENT_MEMORY);
+ }
+ /*
+ * XXXKYS: Is it sufficient to just pause the target vcpu; on the
+ * xen side domain is paused for this call. CHECK.
+ */
+ if (current->vcpu_id != inBuf->vpIndex) {
+ nsXenVector.extVcpuPause(targetp->nsVcpXenVcpu);
+ }
+
+ nsXenVector.extArchGetDomainInfoCtxt(targetp->nsVcpXenVcpu, vcpuCtx);
+ /*
+ * Now that we have the register state; update the register state
+ * based on what we are given.
+ */
+ regIndexp = &inBuf->regSpec;
+ /*
+ * XXXKYS: Assuming the list is terminated by a regName that is 0.
+ * Check with Mike.
+ */
+ while (regIndexp->regName != 0) {
+ switch (regIndexp->regName) {
+ /*
+ * XXXKYS: need mapping code here; populate
+ * vcpuCtx
+ */
+ NS_PANIC("nsSetVpRegisters not supported\n");
+ }
+ regIndexp++;
+ if ((char *)regIndexp > ((char *)inBuf + PAGE_SIZE)) {
+ /*
+ *input list not reminated correctly; bail out.
+ */
+ NS_PANIC("nsSetVpRegisters:input list not terminated\n");
+ break;
+ }
+ }
+ /*
+ * Now set register state.
+ *
+ * XXXKYS: Is it sufficient to just pause the target vcpu; on the
+ * xen side domain is paused for this call. CHECK.
+ */
+
+ if (nsXenVector.extArchSetDomainInfoCtxt(targetp->nsVcpXenVcpu, vcpuCtx)) {
+ retVal = NS_STATUS_INVALID_PARAMETER;
+ }
+ if (current->vcpu_id != inBuf->vpIndex) {
+ nsXenVector.extVcpuUnPause(targetp->nsVcpXenVcpu);
+ }
+ nsXenVector.extFreeMem(vcpuCtx);
+ return (retVal);
+}
+
+/*
+ * static int
+ * nsSwitchVa(paddr_t input)
+ *
+ * Switch the page table base of the calling vcpu.
+ *
+ * Calling/Exit State:
+ * None.
+ *
+ * Remarks:
+ * The spec specifies that the input register is pointing to a guest
+ * physical that has the new page table base. However it appears that the
+ * page table base is being passed in the input register.
+ */
+static int
+nsSwitchVa(paddr_t input)
+{
+ nsPartition_t *curp = nsGetCurrentPartition();
+ nsVcpu_t *vcpup = &curp->nsVcpuState[nsGetCurrentVcpuIndex()];
+
+ /*
+ * XXXKYS: the spec sys the asID is passed via memory at offset 0 of
+ * the page whose GPA is in the input register. However, it appears
+ * the current build of longhorn (longhorn-2007-02-06-x86_64-fv-02)
+ * passes the asID in the input register instead. Need to check if
+ * future builds do this.
+ */
+ hvm_set_cr3(input);
+ NS_STATS_COLLECT(NS_CSWITCH, &vcpup->nsVcpStats);
+ return (NS_STATUS_SUCCESS);
+}
+
+/*
+ * static int
+ * nsFlushPostProcess(nsPartition_t *curp, nsVcpu_t *curVcpup)
+ *
+ * Perform the flush operation once GFS is acquired.
+ *
+ * Calling/Exit State:
+ * On entry nsLock is held; on exit this lock continues to be held.
+ */
+
+static void
+nsFlushPostProcess(nsPartition_t *curp, nsVcpu_t *curVcpup)
+{
+ int target;
+ nsVcpu_t *vcpup;
+ cpumask_t vcpuMask;
+ struct flushVa *flushArgp;
+
+ flushArgp = curVcpup->nsVcpInputBuffer;
+ vcpuMask = flushArgp->vMask;
+ /*
+ * On entry we must own the global flush state.
+ */
+ NS_ASSERT(NS_LOCK_OWNED(curVcpup, &curp->nsLock));
+ NS_ASSERT(curp->nsFlushState.cpuCount == 0);
+ NS_ASSERT(curp->nsFlushState.currentOwner == NULL);
+
+ curp->nsFlushState.retVal =
+ nsBuildHcallRetVal(NS_STATUS_SUCCESS, curVcpup->nsVcpRepCount);
+ curp->nsFlushState.currentOwner = current;
+ if (cpu_isset(current->vcpu_id, vcpuMask)) {
+ curp->nsFlushState.cpuCount = 1;
+ curVcpup->nsVcpFlushPending =
+ curVcpup->nsVcpFlushRequest;
+ mb();
+#ifdef NS_STATS
+ if (curVcpup->nsVcpFlushRequest == NS_FLUSH_TLB) {
+ NS_STATS_COLLECT(NS_FLUSH_VA_POSTED, &curVcpup->nsVcpStats);
+ } else {
+ NS_STATS_COLLECT(NS_FLUSH_RANGE_POSTED, &curVcpup->nsVcpStats);
+ }
+#endif
+
+ cpu_clear(current->vcpu_id, vcpuMask);
+ }
+ if (cpus_empty(vcpuMask)) {
+ /*
+ * We are done.
+ */
+ goto flushVaDone;
+ }
+ while (!cpus_empty(vcpuMask)) {
+ target = first_cpu(vcpuMask);
+ vcpup = &curp->nsVcpuState[target];
+ cpu_clear(target, vcpuMask);
+ if (!(vcpup->nsVcpuFlags & NS_VCPU_UP)) {
+ continue;
+ }
+ if (!nsFlushPermitted(vcpup)) {
+ continue;
+ }
+ curp->nsFlushState.cpuCount++;
+ vcpup->nsVcpFlushPending =
+ curVcpup->nsVcpFlushRequest;
+ mb();
+#ifdef NS_STATS
+ if (curVcpup->nsVcpFlushRequest == NS_FLUSH_TLB) {
+ NS_STATS_COLLECT(NS_FLUSH_VA_POSTED, &vcpup->nsVcpStats);
+ } else {
+ NS_STATS_COLLECT(NS_FLUSH_RANGE_POSTED, &vcpup->nsVcpStats);
+ }
+#endif
+
+ /*
+ * We need to force these VCPUs into the hypervisor for
+ * them to act on the pending request.
+ */
+
+ vcpu_kick(vcpup->nsVcpXenVcpu);
+ if ( test_and_clear_bit(_VPF_blocked_in_xen,
+ &vcpup->nsVcpXenVcpu->pause_flags) ) {
+ vcpu_wake(vcpup->nsVcpXenVcpu);
+ }
+
+ }
+ /*
+ * Now that we have posted the state; wait for other CPUs to perform
+ * flushes; we need to wait for all the CPUs to complete the flush
+ * before returning.
+ */
+flushVaDone:
+ /*
+ * If we are included in this round of tlb flush; we will wait for
+ * other CPUs in the tlb flush function; else we wait right here.
+ */
+ if (!curVcpup->nsVcpFlushPending) {
+ nsReleaseGlobalFlushState(curp, curVcpup, 1);
+ }
+ return;
+}
+
+/*
+ * static int
+ * nsFlushVa(paddr_t input)
+ * Perform a TLB flush on the specified set of VCPUs.
+ *
+ * Calling/Exit State:
+ * No locks can be held on entry and no locks will be held on return.
+ * The calling VCPU may relinquish the physical CPU.
+ */
+static int
+nsFlushVa(paddr_t input)
+{
+ nsPartition_t *curp = nsGetCurrentPartition();
+ int i;
+ nsVcpu_t *curVcpup;
+
+ flushVa_t *flushArgp;
+ cpumask_t vcpuMask;
+ u64 asId, inputMask, retVal;
+ int flushGlobal = 1;
+
+ curVcpup = &curp->nsVcpuState[nsGetCurrentVcpuIndex()];
+ flushArgp = curVcpup->nsVcpInputBuffer;
+
+ NS_ASSERT(curVcpup->nsVcplockDepth == 0);
+ NS_ASSERT(curVcpup->nsVcpFlushRequest == 0);
+ NS_ASSERT(curVcpup->nsVcpWaitingForCleanup == 0);
+ NS_ASSERT(curVcpup->nsVcpWaitingOnGFS == 0);
+
+ if (nsXenVector.extCopyFromGuestPhysical(flushArgp, input,
+ sizeof(*flushArgp))) {
+ return (NS_STATUS_INVALID_ALIGNMENT);
+ }
+ inputMask = flushArgp->pMask;
+ asId = flushArgp->asHandle;
+ cpus_clear(vcpuMask);
+ /*
+ * Deal with all trivial error conditions.
+ */
+ if (flushArgp->flags != 0 && (!(flushArgp->flags &
+ (NS_FLUSH_ALL_PROCESSORS |
+ NS_FLUSH_ALL_VIRTUAL_ADDRESS_SPACES |
+ NS_FLUSH_NON_GLOBAL_MAPPINGS_ONLY)))) {
+ return (NS_STATUS_INVALID_PARAMETER);
+ }
+ if (((flushArgp->pMask) == 0) &&
+ !(flushArgp->flags & NS_FLUSH_ALL_PROCESSORS)) {
+ return (NS_STATUS_INVALID_PARAMETER);
+ }
+
+ if (flushArgp->flags & NS_FLUSH_ALL_PROCESSORS) {
+ for (i=0; i< MAX_VIRT_CPUS; i++) {
+ if (current->domain->vcpu[i] != NULL) {
+ cpu_set(i, vcpuMask);
+ }
+ }
+ } else {
+ i = 0;
+ while (inputMask) {
+ if (inputMask &0x1) {
+ cpu_set(i, vcpuMask);
+ }
+ inputMask = (inputMask >> 1);
+ i++;
+ }
+ }
+
+ if (flushArgp->flags & NS_FLUSH_ALL_VIRTUAL_ADDRESS_SPACES) {
+ asId = NS_ALL_AS;
+ }
+ if (flushArgp->flags & NS_FLUSH_NON_GLOBAL_MAPPINGS_ONLY) {
+ flushGlobal = 0;
+ }
+ /*
+ * Now operate on what we are given
+ * XXXKYS: For now we are ignoring asId and fushGlobal flag.
+ * May have to revisit this. But first stash away the processed
+ * parameters for subsequent use.
+ */
+ flushArgp->asHandle = asId;
+ flushArgp->flags = flushGlobal;
+ flushArgp->vMask = vcpuMask;
+
+ curVcpup->nsVcpRepCount = 0;
+ curVcpup->nsVcpFlushRequest = NS_FLUSH_TLB;
+
+ retVal = nsBuildHcallRetVal(NS_STATUS_SUCCESS, 0);
+ nsSetSysCallRetVal(guest_cpu_user_regs(),
+ curp->nsLongModeGuest,
+ retVal);
+ NS_STATS_COLLECT(NS_FLUSH_VA_STAT, &curVcpup->nsVcpStats);
+ nsLockAcquire(curVcpup, &curp->nsLock);
+ nsAcquireGlobalFlushState(curp, curVcpup);
+ nsFlushPostProcess(curp, curVcpup);
+ nsLockRelease(curVcpup, &curp->nsLock);
+ return (NS_STATUS_SUCCESS);
+}
+
+/*
+ * static int
+ * nsFlushVaRange(paddr_t input, unsigned short startIndex,
+ * unsigned short repCount, unsigned short *repsDone)
+ * Perform a INVLPG flush on the specified set of VCPUs.
+ *
+ * Calling/Exit State:
+ * No locks can be held on entry and no locks will be held on return.
+ * The calling VCPU may relinquish the physical CPU.
+ */
+static int
+nsFlushVaRange(paddr_t input, unsigned short startIndex,
+unsigned short repCount, unsigned short *repsDone)
+{
+ nsVcpu_t *curVcpup;
+ nsPartition_t *curp = nsGetCurrentPartition();
+ flushVa_t *flushArgp;
+ cpumask_t vcpuMask;
+ u64 asId, inputMask, retVal;
+ int flushGlobal = 1;
+ int flushAllProc = 0;
+ int i;
+
+ curVcpup = &curp->nsVcpuState[nsGetCurrentVcpuIndex()];
+ flushArgp = curVcpup->nsVcpInputBuffer;
+ NS_ASSERT(curVcpup->nsVcplockDepth == 0);
+ NS_ASSERT(curVcpup->nsVcpFlushRequest == 0);
+ NS_ASSERT(curVcpup->nsVcpWaitingForCleanup == 0);
+ NS_ASSERT(curVcpup->nsVcpWaitingOnGFS == 0);
+ NS_ASSERT(repCount >=1);
+ NS_ASSERT(((sizeof(*flushArgp)) + 8*(repCount -1)) <= PAGE_SIZE);
+ if (nsXenVector.extCopyFromGuestPhysical(flushArgp, input,
+ ((sizeof(*flushArgp)) + 8*(repCount -1)))) {
+ return (NS_STATUS_INVALID_ALIGNMENT);
+ }
+ *repsDone = repCount;
+ inputMask = flushArgp->pMask;
+ asId = flushArgp->asHandle;
+ cpus_clear(vcpuMask);
+ /*
+ * Deal with all trivial error conditions.
+ */
+ if (flushArgp->flags != 0 && (!(flushArgp->flags &
+ (NS_FLUSH_ALL_PROCESSORS |
+ NS_FLUSH_ALL_VIRTUAL_ADDRESS_SPACES |
+ NS_FLUSH_NON_GLOBAL_MAPPINGS_ONLY)))) {
+ return (NS_STATUS_INVALID_PARAMETER);
+ }
+ if ((flushArgp->pMask == 0) &&
+ !(flushArgp->flags & NS_FLUSH_ALL_PROCESSORS)) {
+ return (NS_STATUS_INVALID_PARAMETER);
+ }
+
+ if (flushArgp->flags & NS_FLUSH_ALL_PROCESSORS) {
+ flushAllProc = 1;
+ for (i=0; i< MAX_VIRT_CPUS; i++) {
+ if (current->domain->vcpu[i] != NULL) {
+ cpu_set(i, vcpuMask);
+ }
+ }
+ } else {
+ i = 0;
+ /*
+ * populate the vcpu mask based on the input.
+ */
+ while (inputMask) {
+ if (inputMask & 0x1) {
+ cpu_set(i, vcpuMask);
+ }
+ inputMask = (inputMask >> 1);
+ i++;
+ }
+ }
+ if (flushArgp->flags & NS_FLUSH_ALL_VIRTUAL_ADDRESS_SPACES) {
+ asId = NS_ALL_AS;
+ }
+ if (flushArgp->flags & NS_FLUSH_NON_GLOBAL_MAPPINGS_ONLY) {
+ flushGlobal = 0;
+ }
+ /*
+ * Now operate on what we are given
+ * XXXKYS: For now we are ignoring asId and fushGlobal flag.
+ * May have to revisit this.
+ * May have to revisit this. But first stash away the processed
+ * parameters for subsequent use.
+ */
+ flushArgp->asHandle = asId;
+ flushArgp->flags = flushGlobal;
+ flushArgp->vMask = vcpuMask;
+
+ curVcpup->nsVcpRepCount = repCount;
+ curVcpup->nsVcpFlushRequest = NS_FLUSH_INVLPG;
+
+ retVal = nsBuildHcallRetVal(NS_STATUS_SUCCESS, repCount);
+ nsSetSysCallRetVal(guest_cpu_user_regs(),
+ curp->nsLongModeGuest,
+ retVal);
+
+
+ NS_STATS_COLLECT(NS_FLUSH_RANGE, &curVcpup->nsVcpStats);
+ nsLockAcquire(curVcpup, &curp->nsLock);
+ nsAcquireGlobalFlushState(curp, curVcpup);
+ nsFlushPostProcess(curp, curVcpup);
+ nsLockRelease(curVcpup, &curp->nsLock);
+ return (NS_STATUS_SUCCESS);
+}
+
+/* void
+ * nsHandleHyperCall(u64 opcode, u64 input, u64 output,
+ * u64 *retVal);
+ * Common entry point for handling all the extension hypercalls.
+ *
+ * Calling/Exit State:
+ * Based on the hypercall; the caller may give up the CPU while
+ * processing the hypercall. No locks should be held on entry and
+ * no locks will be held on return.
+ *
+ */
+
+void
+nsHandleHyperCall(u64 opcode, u64 input, u64 output,
+ u64 *retVal)
+{
+ unsigned short verb;
+ unsigned short repCount;
+ unsigned short repsDone =0;
+ unsigned short startIndex;
+ nsPartition_t *curp = nsGetCurrentPartition();
+ u64 partitionId;
+ int value;
+
+
+ verb = (short)(opcode & 0xffff);
+ repCount = (short)((opcode >>32) & 0xfff);
+ startIndex = (short)((opcode >> 48) & 0xfff);
+ switch (verb) {
+ case NS_CREATE_PARTITION:
+ /*
+ * Xen only allows dom0 to create domains.
+ */
+ *retVal = nsBuildHcallRetVal(NS_STATUS_ACCESS_DENIED, 0);
+ return;
+ case NS_INITIALIZE_PARTITION:
+ /*
+ * We don't support this.
+ */
+ *retVal = nsBuildHcallRetVal(NS_STATUS_ACCESS_DENIED, 0);
+ return;
+ case NS_DELETE_PARTITION:
+ /*
+ * We don't support this.
+ */
+ *retVal = nsBuildHcallRetVal(NS_STATUS_ACCESS_DENIED, 0);
+ return;
+ case NS_GET_PARTITION_PROPERTY:
+ /*
+ * We don't support this.
+ */
+ *retVal = nsBuildHcallRetVal(NS_STATUS_ACCESS_DENIED, 0);
+ return;
+ case NS_SET_PARTITION_PROPERTY:
+ /*
+ * We don't support this.
+ */
+ *retVal = nsBuildHcallRetVal(NS_STATUS_ACCESS_DENIED, 0);
+ return;
+ case NS_GET_PARTITION_ID:
+ if (!nsPrivilegeCheck(curp, NS_ACCESS_PARTITION_ID)) {
+ *retVal =
+ nsBuildHcallRetVal(NS_STATUS_ACCESS_DENIED, 0);
+ return;
+ }
+ partitionId = (u64)current->domain->domain_id;
+ if (nsXenVector.extCopyToGuestPhysical(output,
+ &partitionId, 8)) {
+ /*
+ * Invalid output area.
+ */
+ *retVal =
+ nsBuildHcallRetVal(NS_STATUS_ACCESS_DENIED, 0);
+ return;
+ }
+ *retVal = nsBuildHcallRetVal(NS_STATUS_SUCCESS, 0);
+ return;
+ case NS_GET_NEXT_CHILD_PARTITION:
+ /*
+ * We don't support this.
+ */
+ *retVal = nsBuildHcallRetVal(NS_STATUS_ACCESS_DENIED, 0);
+ return;
+ case NS_SET_LOGICAL_PROCESSOR_RUN_TIME_GROUP:
+ /*
+ * We don't support this.
+ */
+ *retVal = nsBuildHcallRetVal(NS_STATUS_ACCESS_DENIED, 0);
+ return;
+ case NS_CLEAR_LOGICAL_PROCESSOR_RUN_TIME_GROUP:
+ /*
+ * We don't support this.
+ */
+ *retVal = nsBuildHcallRetVal(NS_STATUS_ACCESS_DENIED, 0);
+ return;
+ case NS_NOTIFY_LOGICAL_PROCESSOR_POWER_STATE:
+ /*
+ * We don't support this.
+ */
+ *retVal = nsBuildHcallRetVal(NS_STATUS_ACCESS_DENIED, 0);
+ return;
+ case NS_GET_LOGICAL_PROCESSOR_RUN_TIME:
+ /*
+ * We don't support this.
+ */
+ *retVal = nsBuildHcallRetVal(NS_STATUS_ACCESS_DENIED, 0);
+ return;
+ case NS_DEPOSIT_MEMORY:
+ /*
+ * We don't support this.
+ */
+ *retVal = nsBuildHcallRetVal(NS_STATUS_ACCESS_DENIED, 0);
+ return;
+ case NS_WITHDRAW_MEMORY:
+ /*
+ * We don't support this.
+ */
+ *retVal = nsBuildHcallRetVal(NS_STATUS_ACCESS_DENIED, 0);
+ return;
+ case NS_GET_MEMORY_BALANCE:
+ /*
+ * We don't support this.
+ */
+ *retVal = nsBuildHcallRetVal(NS_STATUS_ACCESS_DENIED, 0);
+ return;
+ case NS_MAP_GPA_PAGES:
+ /*
+ * We don't support this.
+ */
+ *retVal = nsBuildHcallRetVal(NS_STATUS_ACCESS_DENIED, 0);
+ return;
+ case NS_UNMAP_GPA_PAGES:
+ /*
+ * We don't support this.
+ */
+ *retVal = nsBuildHcallRetVal(NS_STATUS_ACCESS_DENIED, 0);
+ return;
+ case NS_INSTALL_INTERCEPT:
+ /*
+ * We don't support this.
+ */
+ *retVal = nsBuildHcallRetVal(NS_STATUS_ACCESS_DENIED, 0);
+ return;
+ case NS_CREATE_VP:
+ /*
+ * We don't support this.
+ */
+ *retVal = nsBuildHcallRetVal(NS_STATUS_ACCESS_DENIED, 0);
+ return;
+ case NS_TERMINATE_VP:
+ /*
+ * We don't support this.
+ */
+ *retVal = nsBuildHcallRetVal(NS_STATUS_ACCESS_DENIED, 0);
+ return;
+ case NS_DELETE_VP:
+ /*
+ * We don't support this.
+ */
+ *retVal = nsBuildHcallRetVal(NS_STATUS_ACCESS_DENIED, 0);
+ return;
+ case NS_GET_NEXT_VP:
+ /*
+ * We don't support this.
+ */
+ *retVal = nsBuildHcallRetVal(NS_STATUS_ACCESS_DENIED, 0);
+ return;
+ case NS_GET_VP_REGISTERS:
+ *retVal = nsBuildHcallRetVal(
+ nsGetVpRegisters(input, output), 0);
+ return;
+ case NS_SET_VP_REGISTERS:
+ *retVal = nsBuildHcallRetVal(
+ nsSetVpRegisters(input, output), 0);
+ case NS_SWITCH_VA:
+ *retVal =
+ nsBuildHcallRetVal(nsSwitchVa(input), 0);
+ return;
+ case NS_FLUSH_VA:
+ *retVal =
+ nsBuildHcallRetVal(nsFlushVa(input), 0);
+ return;
+ case NS_FLUSH_VA_LIST:
+ value = nsFlushVaRange(input, startIndex,
+ repCount, &repsDone);
+ *retVal = nsBuildHcallRetVal(value, repsDone);
+ return;
+
+ case NS_TRASLATE_VA:
+ /*
+ * We don't support this.
+ */
+ *retVal = nsBuildHcallRetVal(NS_STATUS_ACCESS_DENIED, 0);
+ return;
+ case NS_READ_GPA:
+ /*
+ * We don't support this.
+ */
+ *retVal = nsBuildHcallRetVal(NS_STATUS_ACCESS_DENIED, 0);
+ return;
+ case NS_WRITE_GPA:
+ /*
+ * We don't support this.
+ */
+ *retVal = nsBuildHcallRetVal(NS_STATUS_ACCESS_DENIED, 0);
+ return;
+ case NS_ASSERT_VIRTUAL_INTERRUPT:
+ /*
+ * We don't support this.
+ */
+ *retVal = nsBuildHcallRetVal(NS_STATUS_ACCESS_DENIED, 0);
+ return;
+ case NS_CLEAR_VIRTUAL_INTERRUPT:
+ /*
+ * We don't support this.
+ */
+ *retVal = nsBuildHcallRetVal(NS_STATUS_ACCESS_DENIED, 0);
+ return;
+ case NS_CREATE_PORT:
+ /*
+ * We don't support this.
+ */
+ *retVal = nsBuildHcallRetVal(NS_STATUS_ACCESS_DENIED, 0);
+ return;
+ case NS_DELETE_PORT:
+ /*
+ * We don't support this.
+ */
+ *retVal = nsBuildHcallRetVal(NS_STATUS_ACCESS_DENIED, 0);
+ return;
+ case NS_CONNECT_PORT:
+ /*
+ * We don't support this.
+ */
+ *retVal = nsBuildHcallRetVal(NS_STATUS_ACCESS_DENIED, 0);
+ return;
+ case NS_GET_PORT_PROPERTY:
+ /*
+ * We don't support this.
+ */
+ *retVal = nsBuildHcallRetVal(NS_STATUS_ACCESS_DENIED, 0);
+ return;
+ case NS_DISCONNECT_PORT:
+ /*
+ * We don't support this.
+ */
+ *retVal = nsBuildHcallRetVal(NS_STATUS_ACCESS_DENIED, 0);
+ return;
+ case NS_POST_MESSAGE:
+ /*
+ * We don't support this.
+ */
+ *retVal = nsBuildHcallRetVal(NS_STATUS_ACCESS_DENIED, 0);
+ return;
+ case NS_POST_EVENT:
+ /*
+ * We don't support this.
+ */
+ *retVal = nsBuildHcallRetVal(NS_STATUS_ACCESS_DENIED, 0);
+ return;
+ case 0:
+ /*
+ * 32 bit longhorn invokes hypercall with verb == 0; need to
+ * check with Mike (XXXKYS). For now ignore it.
+ */
+ *retVal =
+ nsBuildHcallRetVal(NS_STATUS_INVALID_HYPERCALL_CODE, 0);
+ return;
+ default:
+ nsXenVector.extPrintk("Unkown hypercall: verb is: %d\n", verb);
+ *retVal =
+ nsBuildHcallRetVal(NS_STATUS_INVALID_HYPERCALL_CODE, 0);
+ return;
+ }
+}
Index: xen-3.2-testing/xen/arch/x86/hvm/hvm_ext/novell/nshypercall.h
===================================================================
--- /dev/null 1970-01-01 00:00:00.000000000 +0000
+++ xen-3.2-testing/xen/arch/x86/hvm/hvm_ext/novell/nshypercall.h 2008-02-26 14:01:28.000000000 -0500
@@ -0,0 +1,125 @@
+/****************************************************************************
+ |
+ | Copyright (c) [2007, 2008] Novell, Inc.
+ | All Rights Reserved.
+ |
+ | This program is free software; you can redistribute it and/or
+ | modify it under the terms of version 2 of the GNU General Public License as
+ | published by the Free Software Foundation.
+ |
+ | This program is distributed in the hope that it will be useful,
+ | but WITHOUT ANY WARRANTY; without even the implied warranty of
+ | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ | GNU General Public License for more details.
+ |
+ | You should have received a copy of the GNU General Public License
+ | along with this program; if not, contact Novell, Inc.
+ |
+ | To contact Novell about this file by physical or electronic mail,
+ | you may find current contact information at www.novell.com
+ |
+ |***************************************************************************
+*/
+
+/*
+ * nshypercall.h
+ * Memory layouts for the various hypercalls supported.
+ *
+ * Engineering Contact: K. Y. Srinivasan
+ */
+
+#ifndef NS_HYPERCALL_H
+#define NS_HYPERCALL_H
+
+#include <xen/cpumask.h>
+
+
+typedef struct getVpRegistersInput {
+ u64 partitionId;
+ u64 vpIndex;
+ u32 regIndex;
+} getVpRegistersInput_t;
+
+typedef struct getVpRegistersOutput {
+ u64 lowValue;
+ u64 highValue;
+} getVpRegistersOutput_t;
+
+
+
+typedef struct setVpRegisterSpec {
+ u32 regName;
+ u32 pad;
+ u64 pad1;
+ u64 lowValue;
+ u64 highValue;
+} setVpRegisterSpec_t;
+typedef struct setVpRegistersInput {
+ u64 partitionId;
+ u64 vpIndex;
+ setVpRegisterSpec_t regSpec;
+} setVpRegistersInput_t;
+
+
+typedef struct flushVa {
+ u64 asHandle;
+ u64 flags;
+ union {
+ u64 processorMask;
+ cpumask_t vcpuMask;
+ } procMask;
+#define pMask procMask.processorMask
+#define vMask procMask.vcpuMask
+ u64 gva;
+} flushVa_t;
+
+#define NS_FLUSH_ALL_PROCESSORS 0x00000001
+#define NS_FLUSH_ALL_VIRTUAL_ADDRESS_SPACES 0x00000002
+#define NS_FLUSH_NON_GLOBAL_MAPPINGS_ONLY 0x00000004
+
+#define NS_ALL_AS (-1)
+
+/*
+ * Hypercall verbs.
+ */
+
+#define NS_CREATE_PARTITION 0x0010
+#define NS_INITIALIZE_PARTITION 0x0011
+#define NS_DELETE_PARTITION 0x0014
+#define NS_GET_PARTITION_PROPERTY 0x0017
+#define NS_SET_PARTITION_PROPERTY 0x0018
+#define NS_GET_PARTITION_ID 0x0015
+#define NS_GET_NEXT_CHILD_PARTITION 0x0016
+#define NS_SET_LOGICAL_PROCESSOR_RUN_TIME_GROUP 0x0005
+#define NS_CLEAR_LOGICAL_PROCESSOR_RUN_TIME_GROUP 0x0006
+#define NS_NOTIFY_LOGICAL_PROCESSOR_POWER_STATE 0x0007
+#define NS_GET_LOGICAL_PROCESSOR_RUN_TIME 0x0004
+#define NS_DEPOSIT_MEMORY 0x001C
+#define NS_WITHDRAW_MEMORY 0x001D
+#define NS_GET_MEMORY_BALANCE 0x001E
+#define NS_MAP_GPA_PAGES 0x001A
+#define NS_UNMAP_GPA_PAGES 0x001B
+#define NS_INSTALL_INTERCEPT 0x0019
+#define NS_CREATE_VP 0x001F
+#define NS_TERMINATE_VP 0x0020
+#define NS_DELETE_VP 0x0021
+#define NS_GET_NEXT_VP 0x0027
+#define NS_GET_VP_REGISTERS 0x0022
+#define NS_SET_VP_REGISTERS 0x0023
+#define NS_SWITCH_VA 0x0001
+#define NS_FLUSH_VA 0x0002
+#define NS_FLUSH_VA_LIST 0x0003
+#define NS_TRASLATE_VA 0x0024
+#define NS_READ_GPA 0x0025
+#define NS_WRITE_GPA 0x0026
+#define NS_ASSERT_VIRTUAL_INTERRUPT 0x002A
+#define NS_CLEAR_VIRTUAL_INTERRUPT 0x002C
+#define NS_CREATE_PORT 0x002D
+#define NS_DELETE_PORT 0x002E
+#define NS_CONNECT_PORT 0x002F
+#define NS_GET_PORT_PROPERTY 0x0031
+#define NS_DISCONNECT_PORT 0x0030
+#define NS_POST_MESSAGE 0x0032
+#define NS_POST_EVENT 0x0034
+
+#endif /* NS_HYPERCALL_H */
Index: xen-3.2-testing/xen/arch/x86/hvm/hvm_ext/novell/nsintercept.c
===================================================================
--- /dev/null 1970-01-01 00:00:00.000000000 +0000
+++ xen-3.2-testing/xen/arch/x86/hvm/hvm_ext/novell/nsintercept.c 2008-02-28 13:18:54.000000000 -0500
@@ -0,0 +1,2093 @@
+/****************************************************************************
+ |
+ | Copyright (c) [2007, 2008] Novell, Inc.
+ | All Rights Reserved.
+ |
+ | This program is free software; you can redistribute it and/or
+ | modify it under the terms of version 2 of the GNU General Public License as
+ | published by the Free Software Foundation.
+ |
+ | This program is distributed in the hope that it will be useful,
+ | but WITHOUT ANY WARRANTY; without even the implied warranty of
+ | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ | GNU General Public License for more details.
+ |
+ | You should have received a copy of the GNU General Public License
+ | along with this program; if not, contact Novell, Inc.
+ |
+ | To contact Novell about this file by physical or electronic mail,
+ | you may find current contact information at www.novell.com
+ |
+ |***************************************************************************
+*/
+
+/*
+ * nsintercept.c.
+ * This file implements the intercepts to support the Novell Shim.
+ *
+ * Engineering Contact: K. Y. Srinivasan
+ */
+
+#include <asm/hvm/hvm_extensions.h>
+
+
+#include <asm/config.h>
+#include <asm/hvm/io.h>
+#include <asm/processor.h>
+#include <asm/page.h>
+#include <asm/apicdef.h>
+#include <asm/regs.h>
+#include <asm/msr.h>
+
+#include <xen/string.h>
+#include <xen/init.h>
+#include <xen/compile.h>
+#include <xen/hvm/save.h>
+#include <public/sched.h>
+
+
+/*
+ * Local includes; extension specific.
+ */
+#include "ns_errno.h"
+#include "ns_shim.h"
+
+
+/*
+ * Implement Novell Shim.
+ */
+
+
+/*
+ * Hypervisor intercept vector.
+ */
+static int
+nsDomainCreate(struct domain *d);
+static void
+nsDomainDestroy(struct domain *d);
+static int
+nsVcpuInitialize(struct vcpu *v);
+static void
+nsVcpuUp(struct vcpu *v);
+static void
+nsVcpuDestroy(struct vcpu *v);
+static int
+nsDoCpuId(uint32_t input, struct cpu_user_regs *regs);
+static int
+nsDoRdMsr(uint32_t idx, struct cpu_user_regs *regs);
+static int
+nsDoWrMsr(uint32_t idx, struct cpu_user_regs *regs);
+static int
+nsDoHyperCall(struct cpu_user_regs *pregs);
+static void
+nsDoMigrateTimers(struct vcpu *v);
+
+extension_intercept_vector_t nsExtensionVector = {
+ .domain_create = nsDomainCreate,
+ .domain_destroy = nsDomainDestroy,
+ .vcpu_initialize = nsVcpuInitialize,
+ .vcpu_destroy = nsVcpuDestroy,
+ .do_cpuid = nsDoCpuId,
+ .do_msr_read = nsDoRdMsr,
+ .do_msr_write = nsDoWrMsr,
+ .do_hypercall = nsDoHyperCall,
+ .do_continuation = nsDoTlbFlush,
+ .do_migrate_timers = nsDoMigrateTimers,
+ .vcpu_up = nsVcpuUp
+};
+
+/*
+ * Hooks into xen services; to be populated by our proxy in xen.
+ */
+
+xen_call_vector_t nsXenVector;
+
+static inline void
+nsInjectException(int trap);
+
+static inline void
+nsHypercallPageInitialize(void *hypercallPage, nsPartition_t *curp);
+
+static inline void
+nsInitEventPage(void *siefPage);
+
+static inline void
+nsInitMessagePage(void *simPage);
+
+/*
+ * static int __init nsExtensionInit(void)
+ * Initialize the extensiom module.
+ *
+ * Calling/Exit State:
+ * None.
+ */
+static int __init nsExtensionInit(void)
+{
+ int retVal;
+ retVal = hvm_ext_register(1, &nsExtensionVector, &nsXenVector);
+ NS_ASSERT(retVal == 0);
+ nsXenVector.extPrintk("NS Extension Initialized\n");
+ return 0;
+}
+__initcall(nsExtensionInit);
+
+/*
+ * Our lock primitives.
+ */
+/*
+ * void
+ * nsLockAcquire(nsVcpu_t *vcpup, nsSpinLock_t *nsLock)
+ * Acquire the specified lock.
+ *
+ * Calling/Exit State:
+ * None.
+ */
+void
+nsLockAcquire(nsVcpu_t *vcpup, nsSpinLock_t *nsLock)
+{
+ NS_ASSERT(nsLock->owner != vcpup);
+ spin_lock_irqsave(&nsLock->spinLock, nsLock->flags);
+ nsLock->owner = vcpup;
+ nsLock->retAddr = __builtin_return_address(0);
+ vcpup->nsVcplockDepth++;
+}
+
+/*
+ * void
+ * nsLockRelease(nsVcpu_t *vcpup, nsSpinLock_t *nsLock)
+ * Release the specified spin lock.
+ *
+ * Calling/Exit State:
+ * None.
+ */
+void
+nsLockRelease(nsVcpu_t *vcpup, nsSpinLock_t *nsLock)
+{
+ NS_ASSERT((nsLock->owner == vcpup));
+ nsLock->owner = NULL;
+ vcpup->nsVcplockDepth--;
+ NS_ASSERT(vcpup->nsVcplockDepth >= 0);
+ spin_unlock_irqrestore(&nsLock->spinLock, nsLock->flags);
+}
+
+/*
+ * void
+ * nsLockInit(nsSpinLock_t *nsLock)
+ * Initialize the specified spin lock.
+ *
+ * Calling/Exit State:
+ * None.
+ */
+void
+nsLockInit(nsSpinLock_t *nsLock)
+{
+ spin_lock_init(&nsLock->spinLock);
+ nsLock->owner = NULL;
+ nsLock->retAddr = NULL;
+}
+
+/*
+ * static inline void nsWriteGuestIdMsr(nsPartition_t *curp,
+ * nsVcpu_t *curVcpu,
+ * u64 msrContent)
+ * Write the guest ID.
+ *
+ * Calling/Exit State:
+ * None.
+ */
+static inline void
+nsWriteGuestIdMsr(nsPartition_t *curp, nsVcpu_t *curVcpu, u64 msrContent)
+{
+ curp->nsGuestIdMsr = msrContent;
+ if (curp->nsGuestIdMsr == 0) {
+ /*
+ * Guest has cleared the guest ID;
+ * clear the hypercall page.
+ */
+ if (curp->nsHypercallMsr) {
+ curVcpu->nsVcpuFlags &= ~NS_VCPU_UP;
+ }
+ }
+}
+
+/*
+ * static inline void nsWriteHypercallMsr(nsPartition_t *curp,
+ * nsVcpu_t *curVcpu,
+ * u64 msrContent)
+ * Write hypercall msr.
+ *
+ * Calling/Exit State:
+ * None.
+ */
+
+static inline void
+nsWriteHypercallMsr(nsPartition_t *curp,
+ nsVcpu_t *curVcpu,
+ u64 msrContent)
+{
+ unsigned long gmfn;
+ void *hypercallPage;
+ struct domain *d = curVcpu->nsVcpXenVcpu->domain;
+
+ nsLockAcquire(curVcpu, &curp->nsLock);
+ gmfn = (msrContent >> 12);
+ if (curp->nsGuestIdMsr == 0) {
+ /* Nothing to do if the guest is not registered*/
+ nsLockRelease(curVcpu, &curp->nsLock);
+ return;
+ }
+ /*
+ * Guest is registered; see if we can turn-on the
+ * hypercall page.
+ * XXXKYS: Can the guest write the GPA in one call and
+ * subsequently enable it? Check. For now assume that all the
+ * info is specified in one call.
+ */
+ if (((u32)msrContent & (0x00000001)) == 0) {
+ /*
+ * The client is not enabling the hypercall; just
+ * ignore everything.
+ */
+ nsLockRelease(curVcpu, &curp->nsLock);
+ return;
+ }
+ hypercallPage = nsXenVector.extGetVirtFromGmfn(d,gmfn);
+ if (hypercallPage == NULL) {
+ /*
+ * The guest specified a bogus GPA; inject a GP fault
+ * into the guest.
+ */
+ nsInjectException(TRAP_gp_fault);
+ nsLockRelease(curVcpu, &curp->nsLock);
+ return;
+ }
+ nsHypercallPageInitialize(hypercallPage, curp);
+ curp->nsHypercallMfn = nsXenVector.extGetMfnFromGmfn(d, gmfn);
+#ifdef CONFIG_DOMAIN_PAGE
+ nsXenVector.extUnmapDomainPage(hypercallPage);
+#endif
+ curp->nsHypercallMsr = msrContent;
+ nsLockRelease(curVcpu, &curp->nsLock);
+ curVcpu->nsVcpuFlags |= NS_VCPU_UP;
+}
+
+/*
+ * static inline void nsWriteSxMsr(uint32_t idx, nsPartition_t *curp,
+ * nsVcpu_t *curVcpu,
+ * u64 msrContent)
+ * Write SIEFP or SIMP msr.
+ *
+ * Calling/Exit State:
+ * None.
+ */
+
+static inline void nsWriteSxMsr(uint32_t idx, nsPartition_t *curp,
+ nsVcpu_t *curVcpu,
+ u64 msrContent)
+{
+ unsigned long gmfn;
+ void *sxPage;
+ struct domain *d = curVcpu->nsVcpXenVcpu->domain;
+ gmfn = (msrContent >> 12);
+ /*
+ * Can the client enable the siefp and specify
+ * the base address in two
+ * different calls? XXXKYS: For now assume
+ * that it is done in one call.
+ */
+ if (!((u32)msrContent & (0x00000001))) {
+ /*
+ * The client is not enabling the sx page; just
+ * ignore everything.
+ */
+ return;
+ }
+ sxPage = nsXenVector.extGetVirtFromGmfn(d, gmfn);
+ if (sxPage == NULL) {
+ /*
+ * The guest specified a bogus GPA; inject a GP fault
+ * into the guest.
+ */
+ nsInjectException(TRAP_gp_fault);
+ return;
+ }
+ switch (idx) {
+ case NS_MSR_SIEFP:
+ nsInitEventPage(sxPage);
+ curVcpu->nsVcpSIefpMsr = msrContent;
+ curVcpu->nsVcpSiefPage = sxPage;
+ break;
+ case NS_MSR_SIMP:
+ nsInitMessagePage(sxPage);
+ curVcpu->nsVcpSimpMsr = msrContent;
+ curVcpu->nsVcpSimPage = sxPage;
+ break;
+ }
+
+}
+
+/*
+ * Time this domain booted.
+ */
+s_time_t nsDomainBootTime;
+
+/*
+ * static inline u64
+ * nsGetTimeSinceDomainBoot(void)
+ * Retrieve the time since boot in 100ns units.
+ *
+ * Calling/Exit State:
+ * None.
+ */
+
+static inline u64
+nsGetTimeSinceDomainBoot(void)
+{
+ u64 curTime = nsXenVector.extGetTimeSinceBoot();
+ return ((curTime - nsDomainBootTime)/100) ;
+}
+
+/*
+ * static inline int
+ * nsCallFromBios(struct cpu_user_regs *regs)
+ * Check if the caller is in the right state to consumE the services of the
+ * extension module.
+ *
+ * Calling/Exit State:
+ * None.
+ */
+
+static inline int
+nsCallFromBios(struct cpu_user_regs *regs)
+{
+ if (hvm_paging_enabled(current)) {
+ return (0);
+ } else {
+ return (1);
+ }
+}
+
+/*
+ * static inline void
+ * nsInjectException(int trap)
+ * Injecct the specified exception into the invoking virtual CPU.
+ *
+ * Calling/Exit State:
+ * None.
+ */
+
+static inline void
+nsInjectException(int trap)
+{
+ nsXenVector.hvmFuncTable->inject_exception(trap, 0, 0);
+}
+
+
+/*
+ * static inline int
+ * nsOsRegistered(void)
+ * Check to see if the guest has registered itself with the Novell Shim.
+ *
+ * Calling/Exit State:
+ * None.
+ */
+
+static inline int
+nsOsRegistered(void)
+{
+ nsPartition_t *curp = nsGetCurrentPartition();
+ return (curp->nsGuestIdMsr != 0?1:0);
+}
+
+
+/*
+ * static inline void
+ * nsSetPartitionPrivileges(nsPartition_t *nspp)
+ * Set the partitionwide privileges. Currently it is harcoded.
+ * We could perhaps make this an attribute of the domain and have the
+ * configuration tools manage it.
+ *
+ * Calling/Exit State:
+ * None.
+ */
+
+static inline void
+nsSetPartitionPrivileges(nsPartition_t *nspp)
+{
+ /*
+ * This is based on the hypervisor spec under section 5.2.3.
+ */
+ nspp->nsPrivileges = 0x000000020000007f;
+}
+
+/*
+ * static inline u32
+ * nsGetRecommendations(void)
+ * Get the recommendations.
+ *
+ * Calling/Exit State:
+ * None.
+ */
+static inline u32
+nsGetRecommendations(void)
+{
+ /*
+ *For now we recommend all the features. Need to validate.
+ */
+ if ( paging_mode_hap(current->domain)) {
+ /*
+ * If HAP is enabled; the guest should not use TLB flush
+ * related enlightenments.
+ */
+ return (0x19);
+ } else {
+ return (0x1f);
+ }
+}
+
+/*
+ * static inline void
+ * nsSetPartitionFeatures(nsPartition_t *nspp)
+ * Set the partitionwide features. Currently it is harcoded.
+ * We could perhaps make this an attribute of the domain and have the
+ * configuration tools manage it.
+ *
+ * Calling/Exit State:
+ * None.
+ */
+
+static inline void
+nsSetPartitionFeatures(nsPartition_t *nspp)
+{
+ nspp->nsSupportedFeatures = 0x1f;
+}
+
+static inline u16
+nsGetGuestMajor(void)
+{
+ return (0);
+}
+static inline u16
+nsGetGuestMinor(void)
+{
+ return (0);
+}
+static inline u32
+nsGetGuestServicePack(void)
+{
+ return (0);
+}
+
+static inline u8
+nsGetGuestServiceBranchInfo(void)
+{
+ return (0);
+}
+static inline u32
+nsGetGuestServiceNumber(void)
+{
+ return (0);
+}
+
+/*
+ * static inline u32
+ * nsGetSupportedSyntheticMsrs(void)
+ * Get the synthetic MSRs supported by the Novell Shim. Currently
+ * it is hardcoded.
+ *
+ * Calling/Exit State:
+ * None.
+ */
+static inline u32
+nsGetSupportedSyntheticMsrs(void)
+{
+ /*
+ * All MSRS in the spec version 0.83 including RESET MSR.
+ */
+ return (0xff);
+}
+
+
+/*
+ * static inline u32
+ * nsGetMaxVcpusSupported(void)
+ * Retrieve the maximum vcpus supported.
+ *
+ * Calling/Exit State:
+ * None.
+ */
+
+static inline u32
+nsGetMaxVcpusSupported(void)
+{
+ return MAX_VIRT_CPUS;
+}
+
+/*
+ * static inline u32
+ * nsGetMaxLcpusSupported(void)
+ * Retrieve the maximum physical cpus supported.
+ *
+ * Calling/Exit State:
+ * None.
+ */
+static inline u32
+nsGetMaxLcpusSupported(void)
+{
+ return NR_CPUS;
+}
+
+
+/*
+ * static inline void
+ * nsReadIcr(u64 *icrContent)
+ * Read the ICR of the local APIC of the calling VCPU.
+ *
+ * Calling/Exit State:
+ * None.
+ */
+static inline void
+nsReadIcr(u64 *icrContent)
+{
+ u32 icrLow, icrHigh;
+ u64 retVal;
+
+
+ icrLow = nsXenVector.mmIoHandler->read_handler(current,
+ (vlapic_base_address(vcpu_vlapic(current)) + 0x300), 4);
+ icrHigh = nsXenVector.mmIoHandler->read_handler(current,
+ (vlapic_base_address(vcpu_vlapic(current)) + 0x310), 4);
+ retVal = icrHigh;
+ *icrContent = ((retVal << 32) | icrLow);
+
+}
+
+/*
+ * static inline void
+ * nsReadTpr(u64 *tprContent)
+ * Read the TPR of the local APIC of the calling VCPU.
+ *
+ * Calling/Exit State:
+ * None.
+ */
+static inline void
+nsReadTpr(u64 *tprContent)
+{
+ u32 tprLow;
+
+
+ tprLow = nsXenVector.mmIoHandler->read_handler(current,
+ (vlapic_base_address(vcpu_vlapic(current)) + 0x80), 4);
+ *tprContent = (u64)tprLow;
+
+}
+
+/*
+ * static inline void
+ * nsWriteEoi(u64 msrContent)
+ * Write the EOI register of the local APIC of the calling VCPU.
+ *
+ * Calling/Exit State:
+ * None.
+ */
+static inline void
+nsWriteEoi(u64 msrContent)
+{
+ u32 eoi = (u32)msrContent;
+
+ nsXenVector.mmIoHandler->write_handler(current,
+ (vlapic_base_address(vcpu_vlapic(current)) + 0xb0), 4, eoi);
+
+}
+
+/*
+ * static inline void
+ * nsWriteIcr(u64 msrContent)
+ * Write the ICR register of the local APIC of the calling VCPU.
+ *
+ * Calling/Exit State:
+ * None.
+ */
+static inline void
+nsWriteIcr(u64 msrContent)
+{
+ u32 icrLow, icrHigh;
+ icrLow = (u32)msrContent;
+ icrHigh = (u32)(msrContent >> 32);
+
+ if (icrHigh != 0) {
+ nsXenVector.mmIoHandler->write_handler(current,
+ (vlapic_base_address(vcpu_vlapic(current)) + 0x310), 4,
+ icrHigh);
+ }
+ if (icrLow != 0) {
+ nsXenVector.mmIoHandler->write_handler(current,
+ (vlapic_base_address(vcpu_vlapic(current)) + 0x300), 4,
+ icrLow);
+ }
+
+}
+
+/*
+ * static inline void
+ * nsWriteTpr(u64 msrContent)
+ * Write the TPR register of the local APIC of the calling VCPU.
+ *
+ * Calling/Exit State:
+ * None.
+ */
+static inline void
+nsWriteTpr(u64 msrContent)
+{
+ u32 tpr = (u32)msrContent;
+
+
+ nsXenVector.mmIoHandler->write_handler(current,
+ (vlapic_base_address(vcpu_vlapic(current)) + 0x80), 4, tpr);
+
+}
+
+/*
+ * static inline void
+ * nsHypercallPageInitialize(void *hypercallPage, nsPartition_t *curp)
+ * Initialize the hypercall page to support the Novell Shim Hypercalls.
+ *
+ * Calling/Exit State:
+ * None.
+ */
+static inline void
+nsHypercallPageInitialize(void *hypercallPage, nsPartition_t *curp)
+{
+ char *p;
+
+ if (nsXenVector.hvmFuncTable->guest_x86_mode(current) == 8) {
+ curp->nsLongModeGuest = 1;
+ } else {
+ curp->nsLongModeGuest = 0;
+ }
+
+ memset(hypercallPage, 0, PAGE_SIZE);
+ p = (char *)(hypercallPage) ;
+ *(u8 *)(p + 0) = 0x0f; /* vmcall */
+ *(u8 *)(p + 1) = 0x01;
+ if (nsXenVector.extCpuIsIntel()) {
+ *(u8 *)(p + 2) = 0xc1;
+ } else {
+ *(u8 *)(p + 2) = 0xd9;
+ }
+ *(u8 *)(p + 3) = 0xc3; /* ret */
+}
+
+/*
+ * static inline void
+ * nsInitEventPage(void *siefPage)
+ * Initialize the per-vcpu event page.
+ *
+ * Calling/Exit State:
+ * None.
+ */
+static inline void
+nsInitEventPage(void *siefPage)
+{
+ memset(siefPage, 0, PAGE_SIZE);
+}
+
+/*
+ * static inline void
+ * nsInitMessagePage(void *siefPage)
+ * Initialize the per-vcpu message page.
+ *
+ * Calling/Exit State:
+ * None.
+ */
+static inline void
+nsInitMessagePage(void *simPage)
+{
+ memset(simPage, 0, PAGE_SIZE);
+}
+
+
+/*
+ * static inline void
+ * nsProcessMessageQ(nsPartition_t *curp, nsVcpu_t *curVcpu)
+ * Process the message queue.
+ *
+ * Calling/Exit State:
+ * None.
+ */
+static inline void
+nsProcessMessageQ(nsPartition_t *curp, nsVcpu_t *curVcpu)
+{
+ /*
+ * XXXKYS: we currently do not support queued messages.
+ */
+}
+
+/*
+ * static inline void
+ * nsScheduleTimeOut(nsVcpTimerState_t *timer)
+ * Schedule a timeout based on the specified timer.
+ *
+ *
+ * Calling/Exit State:
+ * None.
+ */
+static inline void
+nsScheduleTimeOut(nsVcpTimerState_t *timer)
+{
+ /*
+ * We maintain the count in the units of 100ns. Furthermore,
+ * this is not relative to NOW() but rather absolute.
+ */
+ nsXenVector.extSetTimer(&timer->vcpuTimer, (timer->count * 100));
+}
+
+/*
+ * static void
+ * nsTimeOutHandler(void *arg)
+ * The timeout handler for Novell Shim/Adaptor.
+ *
+ * Calling/Exit State:
+ * None.
+ */
+
+static void
+nsTimeOutHandler(void *arg)
+{
+ nsVcpTimerState_t *timerData = arg;
+ nsVcpu_t *curVcpu = timerData->thisCpu;
+ int sIntNum;
+ int vector;
+ if (!(curVcpu->nsVcpSControlMsr & 0x9)) {
+ goto nsToPostProcess;
+ }
+ /*
+ * SynIC is enabled; do further processing. Timeouts are posted as
+ * messages; verify if the message page is enabled.
+ */
+ if (!(curVcpu->nsVcpSimpMsr & 0x1)) {
+ goto nsToPostProcess;
+ }
+ sIntNum = (((u32)(timerData->config >> 16)) & 0x0000000f);
+ /*
+ * First post the message and then optionally deal with the
+ * interrupt notification.
+ */
+ if (curVcpu->nsVcpSimPage == NULL) {
+ NS_PANIC("Novell Shim: Sim page not setup\n");
+ }
+ if ((((nsMessage_t *)curVcpu->nsVcpSimPage)[sIntNum]).messageType !=
+ nsMessageTypeNone) {
+ /*
+ * The message slot is not empty just silently return.
+ */
+ goto nsToPostProcess;
+ }
+ /*
+ * The slot is available; post the message.
+ */
+ (((nsTimerMessage_t *)curVcpu->nsVcpSimPage)[sIntNum]).messageType =
+ nsMessageTimerExpired;
+ (((nsTimerMessage_t *)curVcpu->nsVcpSimPage)[sIntNum]).messageSize =
+ sizeof(nsTimerMessage_t);
+ (((nsTimerMessage_t *)curVcpu->nsVcpSimPage)[sIntNum]).timerIndex =
+ timerData->timerIndex;
+ (((nsTimerMessage_t *)curVcpu->nsVcpSimPage)[sIntNum]).expirationTime =
+ timerData->count;
+ if ((curVcpu->nsVcpSIntMsr[sIntNum] >> 16) &0x1) {
+ /*
+ * The designated sintx register is masked; just return.
+ */
+ goto nsToPostProcess;
+ }
+ vector = ((u32)curVcpu->nsVcpSIntMsr[sIntNum] &0xff);
+
+ /*
+ * Now post the interrupt to the VCPU.
+ * XXXKYS: What is the delivery mode for interrupts delivered here.
+ * Check with Mike?
+ */
+ nsXenVector.extPostInterrupt(current, vector, APIC_DM_FIXED);
+
+ /*
+ * If auto eoi is set; deal with that.
+ */
+ if (((u32)(curVcpu->nsVcpSIntMsr[sIntNum] >> 16)) & 0x1) {
+ nsWriteEoi(0);
+ }
+
+nsToPostProcess:
+ /*
+ * Prior to returning, deal with all the post timeout issues.
+ */
+ if (((u32)(timerData->config)) & 0x00000002) {
+ NS_STATS_COLLECT(NS_TIMEOUTS, &curVcpu->nsVcpStats);
+ nsScheduleTimeOut(timerData);
+ }
+}
+
+/*
+ * static inline void
+ * nsTimerInit(nsVcpu_t *vcpup, int timer)
+ * Initialize the specified timer structure.
+ *
+ * Calling/Exit State:
+ * None.
+ */
+
+static inline void
+nsTimerInit(nsVcpu_t *vcpup, int timer)
+{
+ vcpup->nsVcpTimers[timer].config = 0;
+ vcpup->nsVcpTimers[timer].count = 0;
+ vcpup->nsVcpTimers[timer].thisCpu = vcpup;
+ vcpup->nsVcpTimers[timer].timerIndex = timer;
+ init_timer(&vcpup->nsVcpTimers[timer].vcpuTimer, nsTimeOutHandler,
+ &vcpup->nsVcpTimers[timer], current->processor);
+}
+
+/*
+ * static inline int
+ * nsAccessTimeRefCnt(nsPartition_t *curp, u64 *msrContent)
+ * Read the per-partition time base.
+ *
+ * Calling/Exit State:
+ * None.
+ */
+
+static inline int
+nsAccessTimeRefCnt(nsPartition_t *curp, u64 *msrContent)
+{
+ if (!nsPrivilegeCheck(curp, NS_ACCESS_TIME_REF_CNT)) {
+ /*
+ * The partition does not have the privilege to
+ * read this; return error.
+ */
+ return (0);
+ }
+ *msrContent = nsGetTimeSinceDomainBoot();
+ return (1);
+}
+
+/*
+ * static void
+ * nsDoMigrateTimers(struct vcpu *v)
+ * The binding between this vcpu and the physical cpu has changed; migrate
+ * the timers for this vcpu.
+ *
+ * Calling/Exit State:
+ * The new binding is already in place.
+ */
+
+static void
+nsDoMigrateTimers(struct vcpu *v)
+{
+ nsPartition_t *curp = nsGetCurrentPartition();
+ nsVcpu_t *vcpup;
+ int i;
+ vcpup = &curp->nsVcpuState[nsGetCurrentVcpuIndex()];
+
+ for (i=0; i<4; i++) {
+ nsXenVector.extMigrateTimer(&vcpup->nsVcpTimers[i].vcpuTimer,
+ v->processor);
+ }
+}
+
+/*
+ * static void
+ * nsVcpuUp(struct vcpu *v)
+ * A secondary processor has come on line; mark the processor as up.
+ *
+ * Calling/Exit State:
+ * None.
+ */
+
+static void
+nsVcpuUp(struct vcpu *v)
+{
+ nsPartition_t *curp = nsGetCurrentPartition();
+ nsVcpu_t *vcpup;
+ vcpup = &curp->nsVcpuState[v->vcpu_id];
+ vcpup->nsVcpuFlags |= NS_VCPU_UP;
+}
+
+/*
+ * static int
+ * nsDoHyperCall(struct cpu_user_regs *pregs)
+ * Intercept for implementing Extension hypercalls.
+ *
+ * Calling/Exit State:
+ * Based on the hypercall; the caller may give up the CPU while
+ * processing the hypercall. No locks should be held on entry and
+ * no locks will be held on return.
+ *
+ *
+ */
+
+static int
+nsDoHyperCall(struct cpu_user_regs *pregs)
+{
+ nsPartition_t *curp = nsGetCurrentPartition();
+ nsVcpu_t *vcpup;
+ int longModeGuest = curp->nsLongModeGuest;
+ unsigned long hypercallMfn;
+ unsigned long gmfn;
+ gmfn = (curp->nsHypercallMsr >> 12);
+
+ hypercallMfn = nsXenVector.extGetMfnFromGva(pregs->eip);
+
+ if (hypercallMfn == curp->nsHypercallMfn) {
+ u64 opcode, input, output, retVal;
+ vcpup = &curp->nsVcpuState[nsGetCurrentVcpuIndex()];
+
+ /*
+ * This is an extension hypercall; process it; but first make
+ * sure that the CPU is in the right state for invoking
+ * the hypercall - protected mode at CPL 0.
+ */
+ if (nsInvalidCpuState()) {
+ nsInjectException(TRAP_gp_fault);
+ retVal = nsBuildHcallRetVal(NS_STATUS_INVALID_VP_STATE,
+ 0);
+ nsSetSysCallRetVal(pregs, longModeGuest, retVal);
+ return (1);
+ }
+ if (longModeGuest) {
+ opcode = pregs->ecx;
+ input = pregs->edx;
+ output = pregs->r8;
+ } else {
+ opcode =
+ ((((u64)pregs->edx) << 32) | ((u64)pregs->eax));
+ input =
+ ((((u64)pregs->ebx) << 32) | ((u64)pregs->ecx));
+ output =
+ ((((u64)pregs->edi) << 32) | ((u64)pregs->esi));
+ }
+ NS_ASSERT(vcpup->nsVcplockDepth == 0);
+ nsHandleHyperCall(opcode, input, output, &retVal);
+ nsSetSysCallRetVal(pregs, longModeGuest, retVal);
+ NS_ASSERT(vcpup->nsVcplockDepth == 0);
+ return (1);
+ }
+ /*
+ * This hypercall page is not the page for extension.
+ */
+ return (0);
+}
+
+/*
+ * static int
+ * nsDomainCreate(struct domain *d)
+ * NS intercept for domain creation.
+ *
+ * Calling/Exit State:
+ * None.
+ */
+
+
+static int
+nsDomainCreate(struct domain *d)
+{
+ nsPartition_t *nspp;
+ nspp = nsXenVector.extAllocMem(sizeof(nsPartition_t));
+ if (nspp == NULL) {
+ nsDebugPrint("Memory allocation failed\n");
+ return (1);
+ }
+ memset(nspp, 0, sizeof(*nspp));
+ nsLockInit(&nspp->nsLock);
+ /*
+ * Set the partition wide privilege; We can start with no privileges
+ * and progressively turn on fancier hypervisor features.
+ */
+ nsSetPartitionPrivileges(nspp);
+ nsSetPartitionFeatures(nspp);
+ /*
+ * Stash away pointer to our state in the hvm domain structure.
+ */
+ d->arch.hvm_domain.ext_handle = nspp;
+ nsDomainBootTime = nsXenVector.extGetTimeSinceBoot();
+ return (0);
+}
+
+
+
+/*
+ * static void
+ * nsDomainDestroy(struct domain *d)
+ * NS intercept for the domain destruction.
+ *
+ * Calling/Exit State:
+ * None.
+ */
+static void
+nsDomainDestroy(struct domain *d)
+{
+ int i;
+ nsPartition_t *curp = d->arch.hvm_domain.ext_handle;
+ nsXenVector.extPrintk("NS Domain Being Destroyed\n");
+ NS_ASSERT(curp != NULL);
+ nsXenVector.extPrintk("DUMP STATS\n");
+ nsXenVector.extPrintk("GFS cpucount is %d\n", curp->nsFlushState.cpuCount);
+ if (curp->nsFlushState.currentOwner != NULL) {
+ nsXenVector.extPrintk("GFS owner is %d\n", curp->nsFlushState.currentOwner->vcpu_id);
+ } else {
+ nsXenVector.extPrintk("GFS is free\n");
+ }
+ if (!cpus_empty(curp->nsFlushState.waiters)) {
+ nsXenVector.extPrintk("GFS: waiters not empty\n");
+ } else {
+ nsXenVector.extPrintk("GFS: waiters empty\n");
+ }
+ for (i=0; i < MAX_VIRT_CPUS; i++) {
+ if (d->vcpu[i] != NULL) {
+ nsPrintStats(curp, i);
+ }
+ }
+
+ nsXenVector.extFreeMem(d->arch.hvm_domain.ext_handle);
+ d->arch.hvm_domain.ext_handle = NULL;
+}
+
+/*
+ * static int
+ * nsVcpuInitialize(struct vcpu *v)
+ * NS intercept for vcpu creation.
+ *
+ * Calling/Exit State:
+ * None.
+ */
+
+static int
+nsVcpuInitialize(struct vcpu *v)
+{
+ nsVcpu_t *vcpup;
+ nsPartition_t *curp = v->domain->arch.hvm_domain.ext_handle;
+ int i;
+ vcpup = &curp->nsVcpuState[v->vcpu_id];
+ atomic_inc(&curp->nsNumVcpusActive);
+ if (v->vcpu_id == 0) {
+ vcpup->nsVcpuFlags |= NS_VCPU_BOOT_CPU;
+ }
+ /*
+ * Initialize all the synthetic MSRs corresponding to this VCPU.
+ * Note that all state is set to 0 to begin
+ * with.
+ */
+ vcpup->nsVcpSVersionMsr = 0x00000001;
+ /*
+ * Initialize the synthetic timet structures.
+ */
+ for (i=0; i < 4; i++) {
+ nsTimerInit(vcpup, i);
+ }
+ /*
+ * Setup the input page for handling hypercalls.
+ *
+ */
+ vcpup->nsVcpInputBufferPage =
+ nsXenVector.extAllocDomHeapPage();
+ if (vcpup->nsVcpInputBufferPage == NULL) {
+ nsDebugPrint("Memory allocation failed\n");
+ return (1);
+ }
+ vcpup->nsVcpInputBuffer =
+ nsXenVector.extGetVirtFromPagePtr(vcpup->nsVcpInputBufferPage);
+ if (vcpup->nsVcpInputBuffer == NULL) {
+ nsDebugPrint("Coud not get VA\n");
+ nsXenVector.extFreeDomHeapPage(vcpup->nsVcpInputBufferPage);
+ return (1);
+ }
+ memset(vcpup->nsVcpInputBuffer, 0, PAGE_SIZE);
+ vcpup->nsVcpOutputBufferPage =
+ nsXenVector.extAllocDomHeapPage();
+ if (vcpup->nsVcpOutputBufferPage == NULL) {
+ nsDebugPrint("Memory allocation failed\n");
+#ifdef CONFIG_DOMAIN_PAGE
+ nsXenVector.extUnmapDomainPage(vcpup->nsVcpInputBuffer);
+#endif
+ nsXenVector.extFreeDomHeapPage(vcpup->nsVcpInputBufferPage);
+ return (1);
+ }
+ vcpup->nsVcpOutputBuffer =
+ nsXenVector.extGetVirtFromPagePtr(vcpup->nsVcpOutputBufferPage);
+ if (vcpup->nsVcpOutputBuffer == NULL) {
+ nsDebugPrint("Coud not get VA\n");
+ nsXenVector.extFreeDomHeapPage(vcpup->nsVcpOutputBufferPage);
+#ifdef CONFIG_DOMAIN_PAGE
+ nsXenVector.extUnmapDomainPage(vcpup->nsVcpInputBuffer);
+#endif
+ nsXenVector.extFreeDomHeapPage(vcpup->nsVcpInputBufferPage);
+ return (1);
+ }
+ vcpup->nsVcpXenVcpu = v;
+ vcpup->nsVcpFlushRequest = 0;
+
+ return (0);
+}
+
+/*
+ * static void
+ * nsVcpuDestroy(struct vcpu *v)
+ * NS intercept for domain destruction.
+ *
+ * Calling/Exit State:
+ * None.
+ */
+static void
+nsVcpuDestroy(struct vcpu *v)
+{
+ nsVcpu_t *vcpup;
+ nsPartition_t *curp = v->domain->arch.hvm_domain.ext_handle;
+ int i;
+
+ vcpup = &curp->nsVcpuState[v->vcpu_id];
+ atomic_dec(&curp->nsNumVcpusActive);
+ vcpup->nsVcpuFlags &= ~NS_VCPU_UP;
+ /*
+ * Get rid of the pages we have allocated for this VCPU.
+ */
+#ifdef CONFIG_DOMAIN_PAGE
+ nsXenVector.extUnmapDomainPage(vcpup->nsVcpSiefPage);
+ nsXenVector.extUnmapDomainPage(vcpup->nsVcpSimPage);
+ nsXenVector.extUnmapDomainPage(vcpup->nsVcpInputBuffer);
+ nsXenVector.extUnmapDomainPage(vcpup->nsVcpOutputBuffer);
+#endif
+
+ nsXenVector.extFreeDomHeapPage(vcpup->nsVcpInputBufferPage);
+ nsXenVector.extFreeDomHeapPage(vcpup->nsVcpOutputBufferPage);
+ /*
+ * Kill the timers
+ */
+ for (i=0; i < 4; i++) {
+ nsXenVector.extKillTimer(&vcpup->nsVcpTimers[i].vcpuTimer);
+ }
+ return;
+}
+
+/*
+ * static int nsVcpuSave(struct domain *d, hvm_domain_context_t *h)
+ * Save per-cpu shim state to support either migration or domain save.
+ *
+ * Calling exit state:
+ * None.
+ */
+static int
+nsVcpuSave(struct domain *d, hvm_domain_context_t *h)
+{
+ struct vcpu *v;
+ struct hvm_ns_veridian_cpu ctxt;
+
+ nsVcpu_t *vcpup;
+ nsPartition_t *curp = d->arch.hvm_domain.ext_handle;
+ int i;
+
+ if (curp == NULL) {
+ return 0;
+ }
+ for_each_vcpu(d, v) {
+ vcpup = &curp->nsVcpuState[v->vcpu_id];
+
+ NS_ASSERT(vcpup->nsVcplockDepth == 0);
+ NS_ASSERT(vcpup->nsVcpFlushRequest == 0);
+ NS_ASSERT(vcpup->nsVcpWaitingOnGFS == 0);
+ NS_ASSERT(vcpup->nsVcpFlushPending == 0);
+ NS_ASSERT(vcpup->nsVcpWaitingForCleanup == 0);
+ /*
+ * We don't need to save state for a
+ * vcpu that is down; the restore
+ * code will leave it down if there is nothing saved.
+ */
+ if ( test_bit(_VPF_down, &v->pause_flags) )
+ continue;
+ ctxt.control_msr = vcpup->nsVcpSControlMsr;
+ ctxt.version_msr = vcpup->nsVcpSVersionMsr;
+ ctxt.sief_msr = vcpup->nsVcpSIefpMsr;
+ ctxt.simp_msr = vcpup->nsVcpSimpMsr;
+ ctxt.eom_msr = vcpup->nsVcpEomMsr;
+ for (i=0; i < 16; i++)
+ ctxt.int_msr[i] = vcpup->nsVcpSIntMsr[i];
+ for (i=0; i < 4; i++) {
+ ctxt.timers[i].config = vcpup->nsVcpTimers[i].config;
+ /*
+ * Save the count in units of 100ns relative to NOW()
+ * When we restore we will add NOW() to properly
+ * account for the elapsed time when the timer was
+ * active.
+ */
+ if (vcpup->nsVcpTimers[i].count > ((NOW())/100)) {
+ ctxt.timers[i].count =
+ (vcpup->nsVcpTimers[i].count - ((NOW())/100));
+ } else {
+ ctxt.timers[i].count = 0;
+ }
+ }
+ if ( hvm_save_entry(NS_VERIDIAN_CPU,
+ v->vcpu_id, h, &ctxt) != 0 )
+ return 1;
+ }
+
+ return 0;
+}
+
+/*
+ * static int nsVcpuRestore(struct domain *d, hvm_domain_context_t *h)
+ * Restore per-cpu shim state to support either migration or domain save.
+ *
+ * Calling exit state:
+ * None.
+ */
+static int
+nsVcpuRestore(struct domain *d, hvm_domain_context_t *h)
+{
+ int vcpuid, i;
+ struct hvm_ns_veridian_cpu ctxt;
+
+ nsVcpu_t *vcpup;
+ nsPartition_t *curp = d->arch.hvm_domain.ext_handle;
+
+ if (curp == NULL) {
+ return 0;
+ }
+ /* Which vcpu is this? */
+ vcpuid = hvm_load_instance(h);
+ vcpup = &curp->nsVcpuState[vcpuid];
+ NS_ASSERT(vcpup != NULL);
+ if ( hvm_load_entry(NS_VERIDIAN_CPU, h, &ctxt) != 0 )
+ return -22;
+
+ vcpup->nsVcpSControlMsr = ctxt.control_msr;
+ vcpup->nsVcpSVersionMsr = ctxt.version_msr;
+
+ nsWriteSxMsr(NS_MSR_SIEFP, curp, vcpup, ctxt.sief_msr);
+ nsWriteSxMsr(NS_MSR_SIMP, curp, vcpup, ctxt.simp_msr);
+
+ vcpup->nsVcpEomMsr = ctxt.eom_msr;
+ for (i=0; i<16; i++)
+ vcpup->nsVcpSIntMsr[i] = ctxt.int_msr[i];
+ for (i=0; i < 4; i++) {
+ vcpup->nsVcpTimers[i].config = ctxt.timers[i].config;
+ vcpup->nsVcpTimers[i].count =
+ (ctxt.timers[i].count + ((NOW())/100));
+ if ((vcpup->nsVcpTimers[i].config | 0x9)) {
+ /*
+ * XXXKYS: Some issues with regards to time
+ * management here:
+ * 1) We will ignore the elapsed wall clock time
+ * when the domain was not running.
+ * 2) Clearly we should account fot the time that
+ * has elapsed when the domain was running with
+ * respect to the timeouts that were scheduled
+ * prior to saving the domain.
+ * We will deal with on the save side.
+ */
+ nsScheduleTimeOut(&vcpup->nsVcpTimers[i]);
+ NS_STATS_COLLECT(NS_TIMEOUTS, &vcpup->nsVcpStats);
+ }
+ }
+
+ vcpup->nsVcpuFlags |= NS_VCPU_UP;
+ return 0;
+}
+
+
+
+/*
+ * static int nsDomSave(struct domain *d, hvm_domain_context_t *h)
+ * Save per-domain shim state to support either migration or domain save.
+ *
+ * Calling exit state:
+ * None.
+ */
+
+static int
+nsDomSave(struct domain *d, hvm_domain_context_t *h)
+{
+ struct hvm_ns_veridian_dom ctxt;
+ nsPartition_t *curp = d->arch.hvm_domain.ext_handle;
+
+ if (curp == NULL) {
+ return 0;
+ }
+
+ ctxt.guestid_msr = curp->nsGuestIdMsr;
+ ctxt.hypercall_msr = curp->nsHypercallMsr;
+ ctxt.long_mode = curp->nsLongModeGuest;
+ ctxt.pad0 = 0;
+ return (hvm_save_entry(NS_VERIDIAN_DOM, 0, h, &ctxt));
+}
+
+/*
+ * static int nsDomRestore(struct domain *d, hvm_domain_context_t *h)
+ * Restore per-domain shim state to support either migration or domain save.
+ *
+ * Calling exit state:
+ * None.
+ */
+
+static int
+nsDomRestore(struct domain *d, hvm_domain_context_t *h)
+{
+ struct hvm_ns_veridian_dom ctxt;
+ nsPartition_t *curp = d->arch.hvm_domain.ext_handle;
+
+ if (curp == NULL) {
+ return 0;
+ }
+
+ if ( hvm_load_entry(NS_VERIDIAN_DOM, h, &ctxt) != 0 )
+ return -22;
+ curp->nsGuestIdMsr = ctxt.guestid_msr;
+ curp->nsHypercallMsr = ctxt.hypercall_msr;
+ curp->nsLongModeGuest = ctxt.long_mode;
+ curp->nsHypercallMfn =
+ nsXenVector.extGetMfnFromGmfn(d, (ctxt.hypercall_msr >> 12));
+
+ return 0;
+}
+
+HVM_REGISTER_SAVE_RESTORE(NS_VERIDIAN_DOM, nsDomSave, nsDomRestore,
+ 1, HVMSR_PER_DOM);
+
+
+HVM_REGISTER_SAVE_RESTORE(NS_VERIDIAN_CPU, nsVcpuSave , nsVcpuRestore,
+ 1, HVMSR_PER_VCPU);
+
+
+/*
+ * static int
+ * nsPreProcessCpuIdLeaves(unsigned int input, struct cpu_user_regs *regs)
+ *
+ * Preprocess cpuid leaves. Both xen and Veridian use identical cpuid
+ * leaves for getting info from the hypervisor.
+ *
+ * Calling exit state:
+ * None.
+ */
+static int
+nsPreProcessCpuIdLeaves(unsigned int input, struct cpu_user_regs *regs)
+{
+ uint32_t idx;
+ struct domain *d = current->domain;
+ int extid = d->arch.hvm_domain.params[HVM_PARAM_EXTEND_HYPERVISOR];
+
+ if (extid == 1) {
+ /*
+ * Enlightened Windows guest; need to remap and handle
+ * leaves used by PV front-end drivers.
+ */
+ if ((input >= 0x40000000) && (input <= 0x40000005)) {
+ return (0);
+ }
+ /*
+ * PV drivers use cpuid to query the hypervisor for details. On
+ * Windows we will use the following leaves for this:
+ *
+ * 4096: VMM Sinature (corresponds to 0x40000000 on Linux)
+ * 4097: VMM Version (corresponds to 0x40000001 on Linux)
+ * 4098: Hypercall details (corresponds to 0x40000002 on Linux)
+ */
+ if ((input >= 0x40001000) && (input <= 0x40001002)) {
+ idx = (input - 0x40001000);
+ switch (idx) {
+ case 0:
+ regs->eax = 0x40000002; /* Largest leaf */
+ regs->ebx = 0x566e6558;/*Signature 1: "XenV" */
+ regs->ecx = 0x65584d4d; /*Signature 2: "MMXe" */
+ regs->edx = 0x4d4d566e; /*Signature 3: "nVMM"*/
+ break;
+ case 1:
+ regs->eax =
+ (XEN_VERSION << 16) |
+ XEN_SUBVERSION;
+ regs->ebx = 0; /* Reserved */
+ regs->ecx = 0; /* Reserved */
+ regs->edx = 0; /* Reserved */
+ break;
+
+ case 2:
+ regs->eax = 1; /*Number of hypercall-transfer pages*/
+ /*In linux this is 0x40000000 */
+ regs->ebx = 0x40001000; /* MSR base address */
+ regs->ecx = 0; /* Features 1 */
+ regs->edx = 0; /* Features 2 */
+ break;
+ }
+ }
+ return (1);
+ } else {
+ /*
+ * For now this is all other "enlightened guests"
+ */
+ if ((input >= 0x40000000) && (input <= 0x40000002)) {
+ /*
+ * These leaves have already been correctly
+ * processed; just return.
+ */
+ return (1);
+ }
+ return (0);
+ }
+}
+
+/*
+ * static int
+ * nsDoCpuId(unsigned int input, struct cpu_user_regs *regs)
+ * NS intercept for cpuid instruction
+ *
+ * Calling/Exit State:
+ * None.
+ */
+
+static int
+nsDoCpuId(unsigned int input, struct cpu_user_regs *regs)
+{
+ uint32_t idx;
+
+ /*
+ * hvmloader uses cpuid to set up a hypercall page; we don't want to
+ * intercept calls coming from the bootstrap (bios) code in the HVM
+ * guest; we discriminate based on the instruction pointer.
+ */
+ if (nsCallFromBios(regs)) {
+ /*
+ * We don't intercept this.
+ */
+ return (0);
+ }
+
+ if (input == 0x00000001) {
+ regs->ecx = (regs->ecx | 0x80000000);
+ return (1);
+ }
+
+ if (nsPreProcessCpuIdLeaves(input, regs)) {
+ return (0);
+ }
+ idx = (input - 0x40000000);
+
+ switch (idx) {
+ case 0:
+ /*
+ * 0x40000000: Hypervisor identification.
+ */
+ regs->eax = 0x40000005; /* For now clamp this */
+ regs->ebx = 0x65766f4e; /* "Nove" */
+ regs->ecx = 0x68536c6c; /* "llSh" */
+ regs->edx = 0x76486d69; /* "imHv" */
+ break;
+
+ case 1:
+ /*
+ * 0x40000001: Hypervisor identification.
+ */
+ regs->eax = 0x31237648; /* "Hv#1*/
+ regs->ebx = 0; /* Reserved */
+ regs->ecx = 0; /* Reserved */
+ regs->edx = 0; /* Reserved */
+ break;
+ case 2:
+ /*
+ * 0x40000002: Guest Info
+ */
+ if (nsOsRegistered()) {
+ regs->eax = nsGetGuestMajor();
+ regs->ebx =
+ (nsGetGuestMajor() << 16) | nsGetGuestMinor();
+ regs->ecx = nsGetGuestServicePack();
+ regs->edx =
+ (nsGetGuestServiceBranchInfo() << 24) |
+ nsGetGuestServiceNumber();
+ } else {
+ regs->eax = 0;
+ regs->ebx = 0;
+ regs->ecx = 0;
+ regs->edx = 0;
+ }
+ break;
+ case 3:
+ /*
+ * 0x40000003: Feature identification.
+ */
+ regs->eax = nsGetSupportedSyntheticMsrs();
+ /* We only support AcessSelfPartitionId bit 1 */
+ regs->ebx = 0x2;
+ regs->ecx = 0; /* Reserved */
+ regs->edx = 0; /*No MWAIT (bit 0), No debugging (bit 1)*/
+ break;
+ case 4:
+ /*
+ * 0x40000004: Imlementation recommendations.
+ */
+ regs->eax = nsGetRecommendations();
+ regs->ebx = 0; /* Reserved */
+ regs->ecx = 0; /* Reserved */
+ regs->edx = 0; /* Reserved */
+ break;
+ case 5:
+ /*
+ * 0x40000005: Implementation limits.
+ * Currently we retrieve maximum number of vcpus and
+ * logical processors (hardware threads) supported.
+ */
+ regs->eax = nsGetMaxVcpusSupported();
+ regs->ebx = nsGetMaxLcpusSupported();
+ regs->ecx = 0; /* Reserved */
+ regs->edx = 0; /* Reserved */
+ break;
+
+ default:
+ /*
+ * We don't handle this leaf.
+ */
+ return (0);
+
+ }
+ return (1);
+}
+
+/*
+ * static int
+ * nsDoRdMsr(uint32_t idx, struct cpu_user_regs *regs)
+ * NS intercept for reading MSRS.
+ *
+ * Calling/Exit State:
+ * None.
+ */
+
+static int
+nsDoRdMsr(uint32_t idx, struct cpu_user_regs *regs)
+{
+ nsPartition_t *curp = nsGetCurrentPartition();
+ unsigned int vcpuIndex = nsGetCurrentVcpuIndex();
+ u64 msrContent = 0;
+ nsVcpu_t *curVcpu = &curp->nsVcpuState[vcpuIndex];
+ int synInt, timer;
+ struct domain *d = current->domain;
+ int extid = d->arch.hvm_domain.params[HVM_PARAM_EXTEND_HYPERVISOR];
+ u64 timerCount;
+
+ /*
+ * hvmloader uses rdmsr; we don't want to
+ * intercept calls coming from the bootstrap (bios) code in the HVM
+ * guest; we descriminate based on the instruction pointer.
+ */
+ if (nsCallFromBios(regs)) {
+ /*
+ * We don't intercept this.
+ */
+ return (0);
+ }
+ if (extid > 1) {
+ /*
+ * For now this is all other "Enlightened" operating systems
+ * other than Longhorn.
+ */
+ if (idx == 0x40000000) {
+ /*
+ * PV driver hypercall setup. Let xen handle this.
+ */
+ return (0);
+ }
+ if (idx == 0x40001000) {
+ idx = 0x40000000;
+ }
+ }
+ switch (idx) {
+ case NS_MSR_GUEST_OS_ID:
+ nsLockAcquire(curVcpu, &curp->nsLock);
+ regs->eax = (u32)(curp->nsGuestIdMsr & 0xFFFFFFFF);
+ regs->edx = (u32)(curp->nsGuestIdMsr >> 32);
+ nsLockRelease(curVcpu, &curp->nsLock);
+ break;
+ case NS_MSR_HYPERCALL:
+ nsLockAcquire(curVcpu, &curp->nsLock);
+ regs->eax = (u32)(curp->nsHypercallMsr & 0xFFFFFFFF);
+ regs->edx = (u32)(curp->nsHypercallMsr >> 32);
+ nsLockRelease(curVcpu, &curp->nsLock);
+ if ((((u32)curp->nsHypercallMsr) & (0x00000001)) != 0) {
+ curVcpu->nsVcpuFlags |= NS_VCPU_UP;
+ }
+ break;
+ case NS_MSR_VP_INDEX:
+ regs->eax = (u32)(vcpuIndex);
+ regs->edx = (u32)(0x0);
+ break;
+ case NS_MSR_ICR:
+ if (!nsPrivilegeCheck(curp, NS_ACCESS_APIC_MSRS)) {
+ goto msrReadError;
+ }
+ nsReadIcr(&msrContent);
+ NS_STATS_COLLECT(NS_ICR_READ, &curVcpu->nsVcpStats);
+ regs->eax = (u32)(msrContent & 0xFFFFFFFF);
+ regs->edx = (u32)(msrContent >> 32);
+ break;
+ case NS_MSR_TPR:
+ if (!nsPrivilegeCheck(curp, NS_ACCESS_APIC_MSRS)) {
+ goto msrReadError;
+ }
+ nsReadTpr(&msrContent);
+ NS_STATS_COLLECT(NS_TPR_READ, &curVcpu->nsVcpStats);
+ regs->eax = (u32)(msrContent & 0xFFFFFFFF);
+ regs->edx = (u32)(msrContent >> 32);
+ break;
+ /*
+ * The following synthetic MSRs are implemented in the Novell Shim.
+ */
+ case NS_MSR_SCONTROL:
+ if (!nsPrivilegeCheck(curp, NS_ACCESS_SYNC_MSRS)) {
+ goto msrReadError;
+ }
+ regs->eax = (u32)(curVcpu->nsVcpSControlMsr & 0xFFFFFFFF);
+ regs->edx = (u32)(curVcpu->nsVcpSControlMsr >> 32);
+ break;
+ case NS_MSR_SVERSION:
+ if (!nsPrivilegeCheck(curp, NS_ACCESS_SYNC_MSRS)) {
+ goto msrReadError;
+ }
+ regs->eax = (u32)(curVcpu->nsVcpSVersionMsr & 0xFFFFFFFF);
+ regs->edx = (u32)(curVcpu->nsVcpSVersionMsr >> 32);
+ break;
+ case NS_MSR_SIEFP:
+ if (!nsPrivilegeCheck(curp, NS_ACCESS_SYNC_MSRS)) {
+ goto msrReadError;
+ }
+ regs->eax = (u32)(curVcpu->nsVcpSIefpMsr & 0xFFFFFFFF);
+ regs->edx = (u32)(curVcpu->nsVcpSIefpMsr >> 32);
+ break;
+ case NS_MSR_SIMP:
+ if (!nsPrivilegeCheck(curp, NS_ACCESS_SYNC_MSRS)) {
+ goto msrReadError;
+ }
+ regs->eax = (u32)(curVcpu->nsVcpSimpMsr & 0xFFFFFFFF);
+ regs->edx = (u32)(curVcpu->nsVcpSimpMsr >> 32);
+ break;
+ case NS_MSR_SINT0:
+ synInt = 0;
+ goto synIntReadProcess;
+ case NS_MSR_SINT1:
+ synInt = 1;
+ goto synIntReadProcess;
+ case NS_MSR_SINT2:
+ synInt = 2;
+ goto synIntReadProcess;
+ case NS_MSR_SINT3:
+ synInt = 3;
+ goto synIntReadProcess;
+ case NS_MSR_SINT4:
+ synInt = 4;
+ goto synIntReadProcess;
+ case NS_MSR_SINT5:
+ synInt = 5;
+ goto synIntReadProcess;
+ case NS_MSR_SINT6:
+ synInt = 6;
+ goto synIntReadProcess;
+ case NS_MSR_SINT7:
+ synInt = 7;
+ goto synIntReadProcess;
+ case NS_MSR_SINT8:
+ synInt = 8;
+ goto synIntReadProcess;
+ case NS_MSR_SINT9:
+ synInt = 9;
+ goto synIntReadProcess;
+ case NS_MSR_SINT10:
+ synInt = 10;
+ goto synIntReadProcess;
+ case NS_MSR_SINT11:
+ synInt = 11;
+ goto synIntReadProcess;
+ case NS_MSR_SINT12:
+ synInt = 12;
+ goto synIntReadProcess;
+ case NS_MSR_SINT13:
+ synInt = 13;
+ goto synIntReadProcess;
+ case NS_MSR_SINT14:
+ synInt = 14;
+ goto synIntReadProcess;
+ case NS_MSR_SINT15:
+ synInt = 15;
+synIntReadProcess:
+ if (!nsPrivilegeCheck(curp, NS_ACCESS_SYNC_MSRS)) {
+ goto msrReadError;
+ }
+ regs->eax = (u32)(curVcpu->nsVcpSIntMsr[synInt] & 0xFFFFFFFF);
+ regs->edx = (u32)(curVcpu->nsVcpSIntMsr[synInt] >> 32);
+ break;
+
+ case NS_MSR_SEOM:
+ /*
+ * This is a write only register; reads return 0.
+ */
+ regs->eax = 0;
+ regs->edx = 0;
+ break;
+ case NS_MSR_TIME_REF_COUNT:
+ if (!nsAccessTimeRefCnt(curp, &msrContent)) {
+ goto msrReadError;
+ }
+ regs->eax = (u32)(msrContent & 0xFFFFFFFF);
+ regs->edx = (u32)(msrContent >> 32);
+ break;
+ /*
+ * Synthetic timer MSRs.
+ */
+ case NS_MSR_TIMER0_CONFIG:
+ timer = 0;
+ goto processTimerConfigRead;
+ case NS_MSR_TIMER1_CONFIG:
+ timer = 1;
+ goto processTimerConfigRead;
+ case NS_MSR_TIMER2_CONFIG:
+ timer = 2;
+ goto processTimerConfigRead;
+ case NS_MSR_TIMER3_CONFIG:
+ timer = 3;
+processTimerConfigRead:
+ if (!nsPrivilegeCheck(curp, NS_ACCESS_SYNC_TIMERS)) {
+ goto msrReadError;
+ }
+ regs->eax =
+ (u32)(curVcpu->nsVcpTimers[timer].config & 0xFFFFFFFF);
+ regs->edx =
+ (u32)(curVcpu->nsVcpTimers[timer].config >> 32);
+ break;
+ case NS_MSR_TIMER0_COUNT:
+ timer = 0;
+ goto processTimerCountRead;
+ case NS_MSR_TIMER1_COUNT:
+ timer = 1;
+ goto processTimerCountRead;
+ case NS_MSR_TIMER2_COUNT:
+ timer = 2;
+ goto processTimerCountRead;
+ case NS_MSR_TIMER3_COUNT:
+ timer = 3;
+processTimerCountRead:
+ if (!nsPrivilegeCheck(curp, NS_ACCESS_SYNC_TIMERS)) {
+ goto msrReadError;
+ }
+ timerCount = curVcpu->nsVcpTimers[timer].count;
+ if (timerCount > ((NOW())/100)) {
+ timerCount -= ((NOW())/100);
+ } else {
+ timerCount = 0;
+ }
+ regs->eax =
+ (u32)(timerCount & 0xFFFFFFFF);
+ regs->edx =
+ (u32)(timerCount >> 32);
+ break;
+ case NS_MSR_PVDRV_HCALL:
+ regs->eax = 0;
+ regs->edx = 0;
+ break;
+ case NS_MSR_SYSTEM_RESET:
+ regs->eax = 0;
+ regs->edx = 0;
+ break;
+ default:
+ /*
+ * We did not handle the MSR address specified;
+ * let the caller figure out
+ * What to do.
+ */
+ return (0);
+ }
+ return (1);
+msrReadError:
+ /*
+ * Have to inject #GP fault.
+ */
+ nsInjectException(TRAP_gp_fault);
+ return (1);
+}
+
+/*
+ * static int
+ * nsDoWrMsr(uint32_t idx, struct cpu_user_regs *regs)
+ * NS intercept for writing MSRS.
+ *
+ * Calling/Exit State:
+ * None.
+ */
+
+static int
+nsDoWrMsr(uint32_t idx, struct cpu_user_regs *regs)
+{
+ nsPartition_t *curp = nsGetCurrentPartition();
+ unsigned int vcpuIndex = nsGetCurrentVcpuIndex();
+ u64 msrContent = 0;
+ nsVcpu_t *curVcpu = &curp->nsVcpuState[vcpuIndex];
+ int synInt, timer;
+ struct domain *d = current->domain;
+ int extid = d->arch.hvm_domain.params[HVM_PARAM_EXTEND_HYPERVISOR];
+
+ /*
+ * hvmloader uses wrmsr; we don't want to
+ * intercept calls coming from the bootstrap (bios) code in the HVM
+ * guest; we descriminate based on the instruction pointer.
+ */
+ if (nsCallFromBios(regs)) {
+ /*
+ * We don't intercept this.
+ */
+ return (0);
+ }
+ msrContent =
+ (u32)regs->eax | ((u64)regs->edx << 32);
+ if (extid > 1) {
+ /*
+ * For now this is all other "Enlightened" operating systems
+ * other than Longhorn.
+ */
+ if (idx == 0x40000000) {
+ /*
+ * PV driver hypercall setup. Let xen handle this.
+ */
+ return (0);
+ }
+ if (idx == 0x40001000) {
+ idx = 0x40000000;
+ }
+ }
+ switch (idx) {
+ case NS_MSR_GUEST_OS_ID:
+ nsWriteGuestIdMsr(curp, curVcpu, msrContent);
+ break;
+ case NS_MSR_HYPERCALL:
+ nsWriteHypercallMsr(curp, curVcpu, msrContent);
+ break;
+
+ case NS_MSR_VP_INDEX:
+ goto msrWriteError;
+
+ case NS_MSR_EOI:
+ if (!nsPrivilegeCheck(curp, NS_ACCESS_APIC_MSRS)) {
+ goto msrWriteError;
+ }
+ nsWriteEoi(msrContent);
+ NS_STATS_COLLECT(NS_EOI_WRITE, &curVcpu->nsVcpStats);
+ break;
+ case NS_MSR_ICR:
+ if (!nsPrivilegeCheck(curp, NS_ACCESS_APIC_MSRS)) {
+ goto msrWriteError;
+ }
+ nsWriteIcr(msrContent);
+ NS_STATS_COLLECT(NS_ICR_WRITE, &curVcpu->nsVcpStats);
+ break;
+ case NS_MSR_TPR:
+ if (!nsPrivilegeCheck(curp, NS_ACCESS_APIC_MSRS)) {
+ goto msrWriteError;
+ }
+ nsWriteTpr(msrContent);
+ NS_STATS_COLLECT(NS_TPR_WRITE, &curVcpu->nsVcpStats);
+ break;
+
+ /*
+ * The following MSRs are synthetic MSRs supported in the Novell Shim.
+ */
+ case NS_MSR_SCONTROL:
+ if (!nsPrivilegeCheck(curp, NS_ACCESS_SYNC_MSRS)) {
+ goto msrWriteError;
+ }
+ curVcpu->nsVcpSControlMsr = msrContent;
+ break;
+ case NS_MSR_SVERSION:
+ if (!nsPrivilegeCheck(curp, NS_ACCESS_SYNC_MSRS)) {
+ goto msrWriteError;
+ }
+ /*
+ * This is a read-only MSR; generate #GP
+ */
+ nsInjectException(TRAP_gp_fault);
+ break;
+ case NS_MSR_SIEFP:
+ case NS_MSR_SIMP:
+ if (!nsPrivilegeCheck(curp, NS_ACCESS_SYNC_MSRS)) {
+ goto msrWriteError;
+ }
+ nsWriteSxMsr(idx, curp, curVcpu, msrContent);
+ break;
+ case NS_MSR_SINT0:
+ synInt = 0;
+ goto synIntWrProcess;
+ case NS_MSR_SINT1:
+ synInt = 1;
+ goto synIntWrProcess;
+ case NS_MSR_SINT2:
+ synInt = 2;
+ goto synIntWrProcess;
+ case NS_MSR_SINT3:
+ synInt = 3;
+ goto synIntWrProcess;
+ case NS_MSR_SINT4:
+ synInt = 4;
+ goto synIntWrProcess;
+ case NS_MSR_SINT5:
+ synInt = 5;
+ goto synIntWrProcess;
+ case NS_MSR_SINT6:
+ synInt = 6;
+ goto synIntWrProcess;
+ case NS_MSR_SINT7:
+ synInt = 7;
+ goto synIntWrProcess;
+ case NS_MSR_SINT8:
+ synInt = 8;
+ goto synIntWrProcess;
+ case NS_MSR_SINT9:
+ synInt = 9;
+ goto synIntWrProcess;
+ case NS_MSR_SINT10:
+ synInt = 10;
+ goto synIntWrProcess;
+ case NS_MSR_SINT11:
+ synInt = 11;
+ goto synIntWrProcess;
+ case NS_MSR_SINT12:
+ synInt = 12;
+ goto synIntWrProcess;
+ case NS_MSR_SINT13:
+ synInt = 13;
+ goto synIntWrProcess;
+ case NS_MSR_SINT14:
+ synInt = 14;
+ goto synIntWrProcess;
+ case NS_MSR_SINT15:
+ synInt = 15;
+synIntWrProcess:
+ if (!nsPrivilegeCheck(curp, NS_ACCESS_SYNC_MSRS)) {
+ goto msrWriteError;
+ }
+ /*
+ * XXXKYS: We assume that the synInt registers will be
+ * first written before the interrupt generation can occur.
+ * Specifically if SINT is masked all interrupts that may have
+ * been generated will be lost. Also when SINT is disabled;
+ * its effects will be only felt for subsequent interrupts that
+ * may be posted. XXXKYS: CHECK
+ */
+ curVcpu->nsVcpSIntMsr[synInt] = msrContent;
+ break;
+
+ case NS_MSR_SEOM:
+ if (!nsPrivilegeCheck(curp, NS_ACCESS_SYNC_MSRS)) {
+ goto msrWriteError;
+ }
+ curVcpu->nsVcpEomMsr = msrContent;
+ nsProcessMessageQ(curp, curVcpu);
+ break;
+ case NS_MSR_TIME_REF_COUNT:
+ /*
+ * This is a read-only msr.
+ */
+ goto msrWriteError;
+
+ /*
+ * Synthetic timer MSRs.
+ */
+ case NS_MSR_TIMER0_CONFIG:
+ timer = 0;
+ goto processTimerConfig;
+ case NS_MSR_TIMER1_CONFIG:
+ timer = 1;
+ goto processTimerConfig;
+ case NS_MSR_TIMER2_CONFIG:
+ timer = 2;
+ goto processTimerConfig;
+ case NS_MSR_TIMER3_CONFIG:
+ timer = 3;
+processTimerConfig:
+ if (!nsPrivilegeCheck(curp, NS_ACCESS_SYNC_TIMERS)) {
+ goto msrWriteError;
+ }
+ /*
+ * Assume that the client is going to write the whole msr.
+ */
+ if (!(msrContent & 0x9)) {
+ /*
+ * We are neither setting Auto Enable or Enable;
+ * silently exit.
+ * Should this be considered to turn off a
+ * timer that may be currently
+ * active; XXXKYS: Check. For now we are
+ * not doing anything here.
+ */
+ break;
+ }
+ if (!(((u32)(msrContent >> 16)) & 0x0000000f)) {
+ /*
+ * sintx is 0; clear the enable bit(s).
+ */
+ msrContent &= ~(0x1);
+ }
+ curVcpu->nsVcpTimers[timer].config = msrContent;
+ /*
+ * XXXKYS: Can any order be assumed here;
+ * should we just act on whatever is in the
+ * count register. For now act as if the count
+ * register is valid and act on it.
+ */
+ if (msrContent & 0x1) {
+ nsScheduleTimeOut(&curVcpu->nsVcpTimers[timer]);
+ NS_STATS_COLLECT(NS_TIMEOUTS, &curVcpu->nsVcpStats);
+ }
+ break;
+ case NS_MSR_TIMER0_COUNT:
+ timer = 0;
+ goto processTimerCount;
+ case NS_MSR_TIMER1_COUNT:
+ timer = 1;
+ goto processTimerCount;
+ case NS_MSR_TIMER2_COUNT:
+ timer = 2;
+ goto processTimerCount;
+ case NS_MSR_TIMER3_COUNT:
+ timer = 3;
+processTimerCount:
+ if (!nsPrivilegeCheck(curp, NS_ACCESS_SYNC_TIMERS)) {
+ goto msrWriteError;
+ }
+ curVcpu->nsVcpTimers[timer].count =
+ (msrContent + ((NOW())/100));
+ if ((curVcpu->nsVcpTimers[timer].config | 0x9)) {
+ nsScheduleTimeOut(&curVcpu->nsVcpTimers[timer]);
+ NS_STATS_COLLECT(NS_TIMEOUTS, &curVcpu->nsVcpStats);
+ }
+
+ break;
+ case NS_MSR_PVDRV_HCALL:
+ /*
+ * Establish the hypercall page for PV drivers.
+ */
+ nsXenVector.extWrmsrHypervisorRegs(0x40000000, regs->eax,
+ regs->edx);
+ break;
+ case NS_MSR_SYSTEM_RESET:
+ /*
+ * Shutdown the domain/partition.
+ */
+ if (msrContent & 0x1) {
+ domain_shutdown(d, SHUTDOWN_reboot);
+ }
+ break;
+
+ default:
+ /*
+ * We did not handle the MSR address;
+ * let the caller deal with this.
+ */
+ return (0);
+ }
+ return (1);
+msrWriteError:
+ /*
+ * Have to inject #GP fault.
+ */
+ nsInjectException(TRAP_gp_fault);
+ return (1);
+}