/* * 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 * * Copyright (c) 2007 * Isaku Yamahata * VA Linux Systems Japan K.K. * consolidate mini and inline version. */ #include #include #include #include #include #include #include #include /* 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))); }