442 lines
		
	
	
	
		
			11 KiB
			
		
	
	
	
		
			C
		
	
	
	
	
	
		
		
			
		
	
	
			442 lines
		
	
	
	
		
			11 KiB
			
		
	
	
	
		
			C
		
	
	
	
	
	
|   | /*
 | ||
|  |  * This program is free software; you can redistribute it and/or modify | ||
|  |  * it under the terms of the GNU General Public License as published by | ||
|  |  * the Free Software Foundation; either version 2 of the License, or | ||
|  |  * (at your option) any later version. | ||
|  |  * | ||
|  |  * 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, write to the Free Software | ||
|  |  * Foundation, 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA. | ||
|  |  * | ||
|  |  *          Tristan Gingold <tristan.gingold@bull.net> | ||
|  |  * | ||
|  |  *          Copyright (c) 2007 | ||
|  |  *          Isaku Yamahata <yamahata at valinux co jp> | ||
|  |  *                          VA Linux Systems Japan K.K. | ||
|  |  *          consolidate mini and inline version. | ||
|  |  */ | ||
|  | 
 | ||
|  | #include <linux/module.h>
 | ||
|  | #include <xen/interface/xen.h>
 | ||
|  | #include <xen/interface/memory.h>
 | ||
|  | #include <xen/interface/grant_table.h>
 | ||
|  | #include <xen/interface/callback.h>
 | ||
|  | #include <xen/interface/vcpu.h>
 | ||
|  | #include <asm/xen/hypervisor.h>
 | ||
|  | #include <asm/xen/xencomm.h>
 | ||
|  | 
 | ||
|  | /* Xencomm notes:
 | ||
|  |  * This file defines hypercalls to be used by xencomm.  The hypercalls simply | ||
|  |  * create inlines or mini descriptors for pointers and then call the raw arch | ||
|  |  * hypercall xencomm_arch_hypercall_XXX | ||
|  |  * | ||
|  |  * If the arch wants to directly use these hypercalls, simply define macros | ||
|  |  * in asm/xen/hypercall.h, eg: | ||
|  |  *  #define HYPERVISOR_sched_op xencomm_hypercall_sched_op | ||
|  |  * | ||
|  |  * The arch may also define HYPERVISOR_xxx as a function and do more operations | ||
|  |  * before/after doing the hypercall. | ||
|  |  * | ||
|  |  * Note: because only inline or mini descriptors are created these functions | ||
|  |  * must only be called with in kernel memory parameters. | ||
|  |  */ | ||
|  | 
 | ||
|  | int | ||
|  | xencomm_hypercall_console_io(int cmd, int count, char *str) | ||
|  | { | ||
|  | 	/* xen early printk uses console io hypercall before
 | ||
|  | 	 * xencomm initialization. In that case, we just ignore it. | ||
|  | 	 */ | ||
|  | 	if (!xencomm_is_initialized()) | ||
|  | 		return 0; | ||
|  | 
 | ||
|  | 	return xencomm_arch_hypercall_console_io | ||
|  | 		(cmd, count, xencomm_map_no_alloc(str, count)); | ||
|  | } | ||
|  | EXPORT_SYMBOL_GPL(xencomm_hypercall_console_io); | ||
|  | 
 | ||
|  | int | ||
|  | xencomm_hypercall_event_channel_op(int cmd, void *op) | ||
|  | { | ||
|  | 	struct xencomm_handle *desc; | ||
|  | 	desc = xencomm_map_no_alloc(op, sizeof(struct evtchn_op)); | ||
|  | 	if (desc == NULL) | ||
|  | 		return -EINVAL; | ||
|  | 
 | ||
|  | 	return xencomm_arch_hypercall_event_channel_op(cmd, desc); | ||
|  | } | ||
|  | EXPORT_SYMBOL_GPL(xencomm_hypercall_event_channel_op); | ||
|  | 
 | ||
|  | int | ||
|  | xencomm_hypercall_xen_version(int cmd, void *arg) | ||
|  | { | ||
|  | 	struct xencomm_handle *desc; | ||
|  | 	unsigned int argsize; | ||
|  | 
 | ||
|  | 	switch (cmd) { | ||
|  | 	case XENVER_version: | ||
|  | 		/* do not actually pass an argument */ | ||
|  | 		return xencomm_arch_hypercall_xen_version(cmd, 0); | ||
|  | 	case XENVER_extraversion: | ||
|  | 		argsize = sizeof(struct xen_extraversion); | ||
|  | 		break; | ||
|  | 	case XENVER_compile_info: | ||
|  | 		argsize = sizeof(struct xen_compile_info); | ||
|  | 		break; | ||
|  | 	case XENVER_capabilities: | ||
|  | 		argsize = sizeof(struct xen_capabilities_info); | ||
|  | 		break; | ||
|  | 	case XENVER_changeset: | ||
|  | 		argsize = sizeof(struct xen_changeset_info); | ||
|  | 		break; | ||
|  | 	case XENVER_platform_parameters: | ||
|  | 		argsize = sizeof(struct xen_platform_parameters); | ||
|  | 		break; | ||
|  | 	case XENVER_get_features: | ||
|  | 		argsize = (arg == NULL) ? 0 : sizeof(struct xen_feature_info); | ||
|  | 		break; | ||
|  | 
 | ||
|  | 	default: | ||
|  | 		printk(KERN_DEBUG | ||
|  | 		       "%s: unknown version op %d\n", __func__, cmd); | ||
|  | 		return -ENOSYS; | ||
|  | 	} | ||
|  | 
 | ||
|  | 	desc = xencomm_map_no_alloc(arg, argsize); | ||
|  | 	if (desc == NULL) | ||
|  | 		return -EINVAL; | ||
|  | 
 | ||
|  | 	return xencomm_arch_hypercall_xen_version(cmd, desc); | ||
|  | } | ||
|  | EXPORT_SYMBOL_GPL(xencomm_hypercall_xen_version); | ||
|  | 
 | ||
|  | int | ||
|  | xencomm_hypercall_physdev_op(int cmd, void *op) | ||
|  | { | ||
|  | 	unsigned int argsize; | ||
|  | 
 | ||
|  | 	switch (cmd) { | ||
|  | 	case PHYSDEVOP_apic_read: | ||
|  | 	case PHYSDEVOP_apic_write: | ||
|  | 		argsize = sizeof(struct physdev_apic); | ||
|  | 		break; | ||
|  | 	case PHYSDEVOP_alloc_irq_vector: | ||
|  | 	case PHYSDEVOP_free_irq_vector: | ||
|  | 		argsize = sizeof(struct physdev_irq); | ||
|  | 		break; | ||
|  | 	case PHYSDEVOP_irq_status_query: | ||
|  | 		argsize = sizeof(struct physdev_irq_status_query); | ||
|  | 		break; | ||
|  | 
 | ||
|  | 	default: | ||
|  | 		printk(KERN_DEBUG | ||
|  | 		       "%s: unknown physdev op %d\n", __func__, cmd); | ||
|  | 		return -ENOSYS; | ||
|  | 	} | ||
|  | 
 | ||
|  | 	return xencomm_arch_hypercall_physdev_op | ||
|  | 		(cmd, xencomm_map_no_alloc(op, argsize)); | ||
|  | } | ||
|  | 
 | ||
|  | static int | ||
|  | xencommize_grant_table_op(struct xencomm_mini **xc_area, | ||
|  | 			  unsigned int cmd, void *op, unsigned int count, | ||
|  | 			  struct xencomm_handle **desc) | ||
|  | { | ||
|  | 	struct xencomm_handle *desc1; | ||
|  | 	unsigned int argsize; | ||
|  | 
 | ||
|  | 	switch (cmd) { | ||
|  | 	case GNTTABOP_map_grant_ref: | ||
|  | 		argsize = sizeof(struct gnttab_map_grant_ref); | ||
|  | 		break; | ||
|  | 	case GNTTABOP_unmap_grant_ref: | ||
|  | 		argsize = sizeof(struct gnttab_unmap_grant_ref); | ||
|  | 		break; | ||
|  | 	case GNTTABOP_setup_table: | ||
|  | 	{ | ||
|  | 		struct gnttab_setup_table *setup = op; | ||
|  | 
 | ||
|  | 		argsize = sizeof(*setup); | ||
|  | 
 | ||
|  | 		if (count != 1) | ||
|  | 			return -EINVAL; | ||
|  | 		desc1 = __xencomm_map_no_alloc | ||
|  | 			(xen_guest_handle(setup->frame_list), | ||
|  | 			 setup->nr_frames * | ||
|  | 			 sizeof(*xen_guest_handle(setup->frame_list)), | ||
|  | 			 *xc_area); | ||
|  | 		if (desc1 == NULL) | ||
|  | 			return -EINVAL; | ||
|  | 		(*xc_area)++; | ||
|  | 		set_xen_guest_handle(setup->frame_list, (void *)desc1); | ||
|  | 		break; | ||
|  | 	} | ||
|  | 	case GNTTABOP_dump_table: | ||
|  | 		argsize = sizeof(struct gnttab_dump_table); | ||
|  | 		break; | ||
|  | 	case GNTTABOP_transfer: | ||
|  | 		argsize = sizeof(struct gnttab_transfer); | ||
|  | 		break; | ||
|  | 	case GNTTABOP_copy: | ||
|  | 		argsize = sizeof(struct gnttab_copy); | ||
|  | 		break; | ||
|  | 	case GNTTABOP_query_size: | ||
|  | 		argsize = sizeof(struct gnttab_query_size); | ||
|  | 		break; | ||
|  | 	default: | ||
|  | 		printk(KERN_DEBUG "%s: unknown hypercall grant table op %d\n", | ||
|  | 		       __func__, cmd); | ||
|  | 		BUG(); | ||
|  | 	} | ||
|  | 
 | ||
|  | 	*desc = __xencomm_map_no_alloc(op, count * argsize, *xc_area); | ||
|  | 	if (*desc == NULL) | ||
|  | 		return -EINVAL; | ||
|  | 	(*xc_area)++; | ||
|  | 
 | ||
|  | 	return 0; | ||
|  | } | ||
|  | 
 | ||
|  | int | ||
|  | xencomm_hypercall_grant_table_op(unsigned int cmd, void *op, | ||
|  | 				 unsigned int count) | ||
|  | { | ||
|  | 	int rc; | ||
|  | 	struct xencomm_handle *desc; | ||
|  | 	XENCOMM_MINI_ALIGNED(xc_area, 2); | ||
|  | 
 | ||
|  | 	rc = xencommize_grant_table_op(&xc_area, cmd, op, count, &desc); | ||
|  | 	if (rc) | ||
|  | 		return rc; | ||
|  | 
 | ||
|  | 	return xencomm_arch_hypercall_grant_table_op(cmd, desc, count); | ||
|  | } | ||
|  | EXPORT_SYMBOL_GPL(xencomm_hypercall_grant_table_op); | ||
|  | 
 | ||
|  | int | ||
|  | xencomm_hypercall_sched_op(int cmd, void *arg) | ||
|  | { | ||
|  | 	struct xencomm_handle *desc; | ||
|  | 	unsigned int argsize; | ||
|  | 
 | ||
|  | 	switch (cmd) { | ||
|  | 	case SCHEDOP_yield: | ||
|  | 	case SCHEDOP_block: | ||
|  | 		argsize = 0; | ||
|  | 		break; | ||
|  | 	case SCHEDOP_shutdown: | ||
|  | 		argsize = sizeof(struct sched_shutdown); | ||
|  | 		break; | ||
|  | 	case SCHEDOP_poll: | ||
|  | 	{ | ||
|  | 		struct sched_poll *poll = arg; | ||
|  | 		struct xencomm_handle *ports; | ||
|  | 
 | ||
|  | 		argsize = sizeof(struct sched_poll); | ||
|  | 		ports = xencomm_map_no_alloc(xen_guest_handle(poll->ports), | ||
|  | 				     sizeof(*xen_guest_handle(poll->ports))); | ||
|  | 
 | ||
|  | 		set_xen_guest_handle(poll->ports, (void *)ports); | ||
|  | 		break; | ||
|  | 	} | ||
|  | 	default: | ||
|  | 		printk(KERN_DEBUG "%s: unknown sched op %d\n", __func__, cmd); | ||
|  | 		return -ENOSYS; | ||
|  | 	} | ||
|  | 
 | ||
|  | 	desc = xencomm_map_no_alloc(arg, argsize); | ||
|  | 	if (desc == NULL) | ||
|  | 		return -EINVAL; | ||
|  | 
 | ||
|  | 	return xencomm_arch_hypercall_sched_op(cmd, desc); | ||
|  | } | ||
|  | EXPORT_SYMBOL_GPL(xencomm_hypercall_sched_op); | ||
|  | 
 | ||
|  | int | ||
|  | xencomm_hypercall_multicall(void *call_list, int nr_calls) | ||
|  | { | ||
|  | 	int rc; | ||
|  | 	int i; | ||
|  | 	struct multicall_entry *mce; | ||
|  | 	struct xencomm_handle *desc; | ||
|  | 	XENCOMM_MINI_ALIGNED(xc_area, nr_calls * 2); | ||
|  | 
 | ||
|  | 	for (i = 0; i < nr_calls; i++) { | ||
|  | 		mce = (struct multicall_entry *)call_list + i; | ||
|  | 
 | ||
|  | 		switch (mce->op) { | ||
|  | 		case __HYPERVISOR_update_va_mapping: | ||
|  | 		case __HYPERVISOR_mmu_update: | ||
|  | 			/* No-op on ia64.  */ | ||
|  | 			break; | ||
|  | 		case __HYPERVISOR_grant_table_op: | ||
|  | 			rc = xencommize_grant_table_op | ||
|  | 				(&xc_area, | ||
|  | 				 mce->args[0], (void *)mce->args[1], | ||
|  | 				 mce->args[2], &desc); | ||
|  | 			if (rc) | ||
|  | 				return rc; | ||
|  | 			mce->args[1] = (unsigned long)desc; | ||
|  | 			break; | ||
|  | 		case __HYPERVISOR_memory_op: | ||
|  | 		default: | ||
|  | 			printk(KERN_DEBUG | ||
|  | 			       "%s: unhandled multicall op entry op %lu\n", | ||
|  | 			       __func__, mce->op); | ||
|  | 			return -ENOSYS; | ||
|  | 		} | ||
|  | 	} | ||
|  | 
 | ||
|  | 	desc = xencomm_map_no_alloc(call_list, | ||
|  | 				    nr_calls * sizeof(struct multicall_entry)); | ||
|  | 	if (desc == NULL) | ||
|  | 		return -EINVAL; | ||
|  | 
 | ||
|  | 	return xencomm_arch_hypercall_multicall(desc, nr_calls); | ||
|  | } | ||
|  | EXPORT_SYMBOL_GPL(xencomm_hypercall_multicall); | ||
|  | 
 | ||
|  | int | ||
|  | xencomm_hypercall_callback_op(int cmd, void *arg) | ||
|  | { | ||
|  | 	unsigned int argsize; | ||
|  | 	switch (cmd) { | ||
|  | 	case CALLBACKOP_register: | ||
|  | 		argsize = sizeof(struct callback_register); | ||
|  | 		break; | ||
|  | 	case CALLBACKOP_unregister: | ||
|  | 		argsize = sizeof(struct callback_unregister); | ||
|  | 		break; | ||
|  | 	default: | ||
|  | 		printk(KERN_DEBUG | ||
|  | 		       "%s: unknown callback op %d\n", __func__, cmd); | ||
|  | 		return -ENOSYS; | ||
|  | 	} | ||
|  | 
 | ||
|  | 	return xencomm_arch_hypercall_callback_op | ||
|  | 		(cmd, xencomm_map_no_alloc(arg, argsize)); | ||
|  | } | ||
|  | 
 | ||
|  | static int | ||
|  | xencommize_memory_reservation(struct xencomm_mini *xc_area, | ||
|  | 			      struct xen_memory_reservation *mop) | ||
|  | { | ||
|  | 	struct xencomm_handle *desc; | ||
|  | 
 | ||
|  | 	desc = __xencomm_map_no_alloc(xen_guest_handle(mop->extent_start), | ||
|  | 			mop->nr_extents * | ||
|  | 			sizeof(*xen_guest_handle(mop->extent_start)), | ||
|  | 			xc_area); | ||
|  | 	if (desc == NULL) | ||
|  | 		return -EINVAL; | ||
|  | 
 | ||
|  | 	set_xen_guest_handle(mop->extent_start, (void *)desc); | ||
|  | 	return 0; | ||
|  | } | ||
|  | 
 | ||
|  | int | ||
|  | xencomm_hypercall_memory_op(unsigned int cmd, void *arg) | ||
|  | { | ||
|  | 	GUEST_HANDLE(xen_pfn_t) extent_start_va[2] = { {NULL}, {NULL} }; | ||
|  | 	struct xen_memory_reservation *xmr = NULL; | ||
|  | 	int rc; | ||
|  | 	struct xencomm_handle *desc; | ||
|  | 	unsigned int argsize; | ||
|  | 	XENCOMM_MINI_ALIGNED(xc_area, 2); | ||
|  | 
 | ||
|  | 	switch (cmd) { | ||
|  | 	case XENMEM_increase_reservation: | ||
|  | 	case XENMEM_decrease_reservation: | ||
|  | 	case XENMEM_populate_physmap: | ||
|  | 		xmr = (struct xen_memory_reservation *)arg; | ||
|  | 		set_xen_guest_handle(extent_start_va[0], | ||
|  | 				     xen_guest_handle(xmr->extent_start)); | ||
|  | 
 | ||
|  | 		argsize = sizeof(*xmr); | ||
|  | 		rc = xencommize_memory_reservation(xc_area, xmr); | ||
|  | 		if (rc) | ||
|  | 			return rc; | ||
|  | 		xc_area++; | ||
|  | 		break; | ||
|  | 
 | ||
|  | 	case XENMEM_maximum_ram_page: | ||
|  | 		argsize = 0; | ||
|  | 		break; | ||
|  | 
 | ||
|  | 	case XENMEM_add_to_physmap: | ||
|  | 		argsize = sizeof(struct xen_add_to_physmap); | ||
|  | 		break; | ||
|  | 
 | ||
|  | 	default: | ||
|  | 		printk(KERN_DEBUG "%s: unknown memory op %d\n", __func__, cmd); | ||
|  | 		return -ENOSYS; | ||
|  | 	} | ||
|  | 
 | ||
|  | 	desc = xencomm_map_no_alloc(arg, argsize); | ||
|  | 	if (desc == NULL) | ||
|  | 		return -EINVAL; | ||
|  | 
 | ||
|  | 	rc = xencomm_arch_hypercall_memory_op(cmd, desc); | ||
|  | 
 | ||
|  | 	switch (cmd) { | ||
|  | 	case XENMEM_increase_reservation: | ||
|  | 	case XENMEM_decrease_reservation: | ||
|  | 	case XENMEM_populate_physmap: | ||
|  | 		set_xen_guest_handle(xmr->extent_start, | ||
|  | 				     xen_guest_handle(extent_start_va[0])); | ||
|  | 		break; | ||
|  | 	} | ||
|  | 
 | ||
|  | 	return rc; | ||
|  | } | ||
|  | EXPORT_SYMBOL_GPL(xencomm_hypercall_memory_op); | ||
|  | 
 | ||
|  | int | ||
|  | xencomm_hypercall_suspend(unsigned long srec) | ||
|  | { | ||
|  | 	struct sched_shutdown arg; | ||
|  | 
 | ||
|  | 	arg.reason = SHUTDOWN_suspend; | ||
|  | 
 | ||
|  | 	return xencomm_arch_hypercall_sched_op( | ||
|  | 		SCHEDOP_shutdown, xencomm_map_no_alloc(&arg, sizeof(arg))); | ||
|  | } | ||
|  | 
 | ||
|  | long | ||
|  | xencomm_hypercall_vcpu_op(int cmd, int cpu, void *arg) | ||
|  | { | ||
|  | 	unsigned int argsize; | ||
|  | 	switch (cmd) { | ||
|  | 	case VCPUOP_register_runstate_memory_area: { | ||
|  | 		struct vcpu_register_runstate_memory_area *area = | ||
|  | 			(struct vcpu_register_runstate_memory_area *)arg; | ||
|  | 		argsize = sizeof(*arg); | ||
|  | 		set_xen_guest_handle(area->addr.h, | ||
|  | 		     (void *)xencomm_map_no_alloc(area->addr.v, | ||
|  | 						  sizeof(area->addr.v))); | ||
|  | 		break; | ||
|  | 	} | ||
|  | 
 | ||
|  | 	default: | ||
|  | 		printk(KERN_DEBUG "%s: unknown vcpu op %d\n", __func__, cmd); | ||
|  | 		return -ENOSYS; | ||
|  | 	} | ||
|  | 
 | ||
|  | 	return xencomm_arch_hypercall_vcpu_op(cmd, cpu, | ||
|  | 					xencomm_map_no_alloc(arg, argsize)); | ||
|  | } | ||
|  | 
 | ||
|  | long | ||
|  | xencomm_hypercall_opt_feature(void *arg) | ||
|  | { | ||
|  | 	return xencomm_arch_hypercall_opt_feature( | ||
|  | 		xencomm_map_no_alloc(arg, | ||
|  | 				     sizeof(struct xen_ia64_opt_feature))); | ||
|  | } |