%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-15 18:28:11.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-15 18:28:11.000000000 -0500 @@ -0,0 +1,239 @@ +/**************************************************************************** + | + | 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 +#include +#include +#include +#include +#include +#include +#include +#include + + + +/* + * 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); +} 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_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-15 18:28:11.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 +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +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-15 18:28:11.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-15 18:28:11.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-15 18:28:11.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 +#include +#include +#include +#include +#include +#include + +#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-15 18:28:11.000000000 -0500 @@ -0,0 +1,1220 @@ +/**************************************************************************** + | + | 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 +#include +#include +#include +#include + +#include +#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. + */ + vcpup->nsVcpWaitingOnGFS = 0; + 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)); + nsLockAcquire(vcpup, &curp->nsLock); + vcpup->nsVcpWaitingOnGFS = 0; + NS_ASSERT(curp->nsFlushState.currentOwner != current); + NS_ASSERT(vcpup->nsVcpWaitingForCleanup == 0); + if (vcpup->nsVcpFlushPending) { + nsLockRelease(vcpup, &curp->nsLock); + nsDoTlbFlush(); + nsLockAcquire(vcpup, &curp->nsLock); + } + goto acquireGFSAgain; + } + vcpup->nsVcpWaitingOnGFS = 0; + 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; + } + +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; + 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; +#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; +#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); + } + /* + * 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; + 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-15 18:28:11.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 + + +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-15 18:28:34.000000000 -0500 @@ -0,0 +1,2077 @@ +/**************************************************************************** + | + | 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 + + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + + +/* + * 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 +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 +}; + +/* + * 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; + /* + * XXXKYS: if the binding between vcpu and physical processor + * changes what is done about pending timeouts? + */ +//KYS: Need to migrate timers when the vcpu->physical CPU binding changes. + 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 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); +}