From patchwork Mon Nov 1 15:01:23 2010 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Alexander Graf X-Patchwork-Id: 69781 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from lists.gnu.org (lists.gnu.org [199.232.76.165]) (using TLSv1 with cipher DHE-RSA-AES256-SHA (256/256 bits)) (Client did not present a certificate) by ozlabs.org (Postfix) with ESMTPS id 3ABB91007D3 for ; Tue, 2 Nov 2010 02:20:07 +1100 (EST) Received: from localhost ([127.0.0.1]:56901 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.43) id 1PCwAD-0005bN-N8 for incoming@patchwork.ozlabs.org; Mon, 01 Nov 2010 11:19:13 -0400 Received: from [140.186.70.92] (port=52897 helo=eggs.gnu.org) by lists.gnu.org with esmtp (Exim 4.43) id 1PCvtw-0003k8-9G for qemu-devel@nongnu.org; Mon, 01 Nov 2010 11:02:27 -0400 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1PCvtW-0000fU-By for qemu-devel@nongnu.org; Mon, 01 Nov 2010 11:02:01 -0400 Received: from cantor.suse.de ([195.135.220.2]:60260 helo=mx1.suse.de) by eggs.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1PCvtV-0000eh-V4 for qemu-devel@nongnu.org; Mon, 01 Nov 2010 11:01:58 -0400 Received: from relay2.suse.de (charybdis-ext.suse.de [195.135.221.2]) (using TLSv1 with cipher DHE-RSA-AES256-SHA (256/256 bits)) (No client certificate requested) by mx1.suse.de (Postfix) with ESMTP id 5477B94608; Mon, 1 Nov 2010 16:01:54 +0100 (CET) From: Alexander Graf To: qemu-devel Developers Date: Mon, 1 Nov 2010 16:01:23 +0100 Message-Id: <1288623713-28062-11-git-send-email-agraf@suse.de> X-Mailer: git-send-email 1.6.0.2 In-Reply-To: <1288623713-28062-1-git-send-email-agraf@suse.de> References: <1288623713-28062-1-git-send-email-agraf@suse.de> X-detected-operating-system: by eggs.gnu.org: GNU/Linux 2.4-2.6 Cc: Gerd Hoffmann Subject: [Qemu-devel] [PATCH 10/40] xenner: kernel: Hypercall handler (i386) X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.5 Precedence: list List-Id: qemu-devel.nongnu.org List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Sender: qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org Errors-To: qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org Xenner handles guest hypercalls itself. This patch adds all the handling code that is i386 specific. Signed-off-by: Alexander Graf --- pc-bios/xenner/xenner-hcall32.c | 299 +++++++++++++++++++++++++++++++++++++++ 1 files changed, 299 insertions(+), 0 deletions(-) create mode 100644 pc-bios/xenner/xenner-hcall32.c diff --git a/pc-bios/xenner/xenner-hcall32.c b/pc-bios/xenner/xenner-hcall32.c new file mode 100644 index 0000000..45a3046 --- /dev/null +++ b/pc-bios/xenner/xenner-hcall32.c @@ -0,0 +1,299 @@ +/* + * Copyright (C) Red Hat 2007 + * Copyright (C) Novell Inc. 2010 + * + * Author(s): Gerd Hoffmann + * Alexander Graf + * + * Xenner 32 bit hypercall handlers + * + * 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; under version 2 of the License. + * + * 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, see . + */ + +#include + +#include "xenner.h" + +/* --------------------------------------------------------------------- */ + +typedef int32_t (*xen_hcall)(struct xen_cpu *cpu, uint32_t *args); +static int32_t multicall(struct xen_cpu *cpu, uint32_t *args); + +/* --------------------------------------------------------------------- */ + +static int32_t update_va_mapping(struct xen_cpu *cpu, uint32_t *args) +{ + uint32_t va = args[0]; + uint64_t val = args[1] | ((uint64_t)args[2] << 32); + uint32_t flags = args[3]; + void *map = NULL; + pte_t *ptr = find_pte_lpt(va); + pte_t pte; + + printk(5, "%s: va %" PRIx32 " val %" PRIx64 " pte %p\n", + __FUNCTION__, va, val, ptr); + + pgtable_walk(cpu, (uint32_t)ptr); + + if (va >= XEN_M2P) { + goto inval; + } + + if (copypt_pf(&pte, ptr) < 0) { + printk(2, "%s: lpt rd fault: va %" PRIx32 " val %" PRIx64 "\n", + __FUNCTION__, va, val); + goto inval; + } + + if ((pte & ~_PAGE_RW) == val && !(val & _PAGE_USER)) { + /* ignore make_readonly */ + vminfo.faults[XEN_FAULT_UPDATE_VA_FIX_RO]++; + goto doflags; + } + + pte = val; + if (!copypt_pf(ptr, &pte)) { + goto doflags; + } + + printk(1, "%s: lpt wr fault: va %" PRIx32 " val %" PRIx64 "\n", + __FUNCTION__, va, val); + + ptr = find_pte_map(cpu, va); + if (!ptr) { + goto inval; + } + + map = ptr; + if (!copypt_pf(ptr, &pte)) { + goto doflags; + } + + printk(1, "%s: map wr fault: va %" PRIx32 " val %" PRIx64 "\n", + __FUNCTION__, va, val); + goto inval; + +doflags: + if (map) { + free_page(map); + } + switch (flags & UVMF_FLUSHTYPE_MASK) { + case UVMF_NONE: + break; + case UVMF_TLB_FLUSH: + flush_tlb(); + break; + case UVMF_INVLPG: + flush_tlb_addr(va); + break; + } + return 0; + +inval: + if (map) { + free_page(map); + } + return -EINVAL; +} + +static int32_t mmu_update(struct xen_cpu *cpu, uint32_t *args) +{ + uint64_t *reqs = (void*)args[0]; + uint32_t count = args[1]; + uint32_t *done = (void*)args[2]; + uint32_t dom = args[3]; + uint32_t i; + + if (dom != DOMID_SELF) { + printk(1, "%s: foreigndom not supported (domid %d)\n", + __FUNCTION__, dom); + return -ENOSYS; + } + + for (i = 0; i < count; i++) { + switch (reqs[0] & 3) { + case MMU_NORMAL_PT_UPDATE: + { + pte_t *pte = map_page(reqs[0]); + *pte = reqs[1]; + free_page(pte); +#ifdef CONFIG_PAE + if (read_cr3_mfn(cpu) == addr_to_frame(reqs[0])) { + update_emu_mappings(read_cr3_mfn(cpu)); + flush_tlb(); + } +#endif + break; + } + case MMU_MACHPHYS_UPDATE: + { + xen_pfn_t gmfn = reqs[0] >> PAGE_SHIFT; + xen_pfn_t gpfn = reqs[1]; + if (gmfn < vmconf.mfn_guest) + panic("suspious m2p update", NULL); + m2p[gmfn] = gpfn; + break; + } + default: + return -ENOSYS; + } + reqs += 2; + } + if (done) { + *done = i; + } + + return 0; +} + +static int32_t iret(struct xen_cpu *cpu, uint32_t *args) +{ + struct regs_32 *regs = (void*)cpu->stack_high - sizeof(*regs); + uint32_t eflags; + uint32_t *stack; + + stack = (void*)regs->esp; + + regs->eax = stack[0]; + regs->eip = stack[1]; + regs->cs = stack[2]; + eflags = stack[3]; + regs->eflags = (eflags & ~X86_EFLAGS_IOPL) | X86_EFLAGS_IF; + + if (context_is_emu(regs)) { + /* not allowed */ + panic("guest tried iret to ring0", regs); + + } else if (context_is_kernel(cpu, regs)) { + /* kernel -> kernel -- just move stack pointer */ + regs->esp += 4*4; + + } else { + /* kernel -> user -- switch back stack */ + regs->esp = stack[4]; + regs->ss = stack[5]; + } + + /* Restore upcall mask from supplied EFLAGS.IF. */ + if (eflags & X86_EFLAGS_IF) { + guest_sti(cpu); + } else { + guest_cli(cpu); + } + + return -EINTR; +} + +/* --------------------------------------------------------------------- */ + +static xen_hcall hcalls[XEN_HCALL_MAX] = { + [ __HYPERVISOR_update_va_mapping ] = update_va_mapping, + [ __HYPERVISOR_mmu_update ] = mmu_update, + [ __HYPERVISOR_mmuext_op ] = mmuext_op, + [ __HYPERVISOR_update_descriptor ] = update_descriptor, + [ __HYPERVISOR_stack_switch ] = stack_switch, + [ __HYPERVISOR_multicall ] = multicall, + [ __HYPERVISOR_iret ] = iret, + [ __HYPERVISOR_fpu_taskswitch ] = fpu_taskswitch, + [ __HYPERVISOR_grant_table_op ] = grant_table_op, + [ __HYPERVISOR_xen_version ] = xen_version, + [ __HYPERVISOR_vm_assist ] = vm_assist, + [ __HYPERVISOR_sched_op ] = sched_op, + [ __HYPERVISOR_sched_op_compat ] = sched_op_compat, + [ __HYPERVISOR_memory_op ] = memory_op, + [ __HYPERVISOR_set_trap_table ] = set_trap_table, + [ __HYPERVISOR_set_callbacks ] = set_callbacks, + [ __HYPERVISOR_callback_op ] = callback_op, + [ __HYPERVISOR_set_gdt ] = set_gdt, + [ __HYPERVISOR_vcpu_op ] = vcpu_op, + [ __HYPERVISOR_event_channel_op ] = event_channel_op, + [ __HYPERVISOR_event_channel_op_compat ] = event_channel_op_compat, + [ __HYPERVISOR_set_timer_op ] = set_timer_op, + [ __HYPERVISOR_physdev_op ] = physdev_op, + [ __HYPERVISOR_get_debugreg ] = get_debugreg, + [ __HYPERVISOR_set_debugreg ] = set_debugreg, + [ __HYPERVISOR_console_io ] = console_io, + + [ __HYPERVISOR_physdev_op_compat ] = error_noperm, + [ __HYPERVISOR_platform_op ] = error_noperm, + [ __HYPERVISOR_set_debugreg ] = error_noop, +}; + +static int32_t multicall(struct xen_cpu *cpu, uint32_t *args) +{ + struct multicall_entry *calls = (void*)args[0]; + uint32_t i, count = args[1]; + uint32_t margs[6]; + + for (i = 0; i < count; i++) { + if (!hcalls[calls[i].op]) + panic("unknown hypercall in multicall list", NULL); + vminfo.hcalls[calls[i].op]++; + margs[0] = calls[i].args[0]; + margs[1] = calls[i].args[1]; + margs[2] = calls[i].args[2]; + margs[3] = calls[i].args[3]; + margs[4] = calls[i].args[4]; + margs[5] = calls[i].args[5]; + calls[i].result = hcalls[calls[i].op](cpu, margs); + } + return 0; +} + +asmlinkage void do_hypercall(struct regs_32 *regs) +{ + uint32_t args[6]; + uint32_t retval = -ENOSYS; + struct xen_cpu *cpu =get_cpu(); + + printk(3, "%s: %s #%d\n", __FUNCTION__, + __hypervisor_name(regs->eax), regs->eax); + + if (regs->eax >= XEN_HCALL_MAX) { + /* invalid hypercall number */ + goto handled; + } + if (!hcalls[regs->eax]) { + /* no hypercall handler */ + goto handled; + } + + /* do call */ + vminfo.hcalls[regs->eax]++; + args[0] = regs->ebx; + args[1] = regs->ecx; + args[2] = regs->edx; + args[3] = regs->esi; + args[4] = regs->edi; + args[5] = regs->ebp; + retval = hcalls[regs->eax](cpu, args); + + if (-EINTR == retval) { + goto iret; + } + +handled: + if (-ENOSYS == retval) { + printk(0, "hypercall %s (#%d) | arg0 0x%x arg1 0x%x -> -ENOSYS\n", + __hypervisor_name(regs->eax), regs->eax, args[0], args[1]); + } + regs->eax = retval; + regs->error = HCALL_HANDLED; + evtchn_try_forward(cpu, regs); + return; + +iret: + regs->error = HCALL_HANDLED; + evtchn_try_forward(cpu, regs); + return; +}