/****************************************************************************** * arch/hvm/hypercall.c * * HVM hypercall dispatching routines * * 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, see . * * Copyright (c) 2017 Citrix Systems Ltd. */ #include #include #include static long hvm_memory_op(int cmd, XEN_GUEST_HANDLE_PARAM(void) arg) { const struct vcpu *curr = current; long rc; switch ( cmd & MEMOP_CMD_MASK ) { case XENMEM_machine_memory_map: case XENMEM_machphys_mapping: return -ENOSYS; } if ( !curr->hcall_compat ) rc = do_memory_op(cmd, arg); else rc = compat_memory_op(cmd, arg); if ( (cmd & MEMOP_CMD_MASK) == XENMEM_decrease_reservation ) curr->domain->arch.hvm_domain.qemu_mapcache_invalidate = true; return rc; } static long hvm_grant_table_op( unsigned int cmd, XEN_GUEST_HANDLE_PARAM(void) uop, unsigned int count) { switch ( cmd ) { case GNTTABOP_query_size: case GNTTABOP_setup_table: case GNTTABOP_set_version: case GNTTABOP_get_version: case GNTTABOP_copy: case GNTTABOP_map_grant_ref: case GNTTABOP_unmap_grant_ref: case GNTTABOP_swap_grant_ref: break; default: /* All other commands need auditing. */ return -ENOSYS; } if ( !current->hcall_compat ) return do_grant_table_op(cmd, uop, count); else return compat_grant_table_op(cmd, uop, count); } static long hvm_physdev_op(int cmd, XEN_GUEST_HANDLE_PARAM(void) arg) { const struct vcpu *curr = current; switch ( cmd ) { default: if ( !is_hardware_domain(curr->domain) ) return -ENOSYS; /* fall through */ case PHYSDEVOP_map_pirq: case PHYSDEVOP_unmap_pirq: case PHYSDEVOP_eoi: case PHYSDEVOP_irq_status_query: case PHYSDEVOP_get_free_pirq: if ( !has_pirq(curr->domain) ) return -ENOSYS; break; } if ( !curr->hcall_compat ) return do_physdev_op(cmd, arg); else return compat_physdev_op(cmd, arg); } #define HYPERCALL(x) \ [ __HYPERVISOR_ ## x ] = { (hypercall_fn_t *) do_ ## x, \ (hypercall_fn_t *) do_ ## x } #define HVM_CALL(x) \ [ __HYPERVISOR_ ## x ] = { (hypercall_fn_t *) hvm_ ## x, \ (hypercall_fn_t *) hvm_ ## x } #define COMPAT_CALL(x) \ [ __HYPERVISOR_ ## x ] = { (hypercall_fn_t *) do_ ## x, \ (hypercall_fn_t *) compat_ ## x } #define do_arch_1 paging_domctl_continuation static const hypercall_table_t hvm_hypercall_table[] = { HVM_CALL(memory_op), HVM_CALL(grant_table_op), COMPAT_CALL(vcpu_op), HVM_CALL(physdev_op), COMPAT_CALL(xen_version), HYPERCALL(console_io), HYPERCALL(event_channel_op), COMPAT_CALL(sched_op), COMPAT_CALL(set_timer_op), HYPERCALL(xsm_op), HYPERCALL(hvm_op), HYPERCALL(sysctl), HYPERCALL(domctl), #ifdef CONFIG_TMEM HYPERCALL(tmem_op), #endif COMPAT_CALL(platform_op), COMPAT_CALL(mmuext_op), HYPERCALL(xenpmu_op), COMPAT_CALL(dm_op), HYPERCALL(arch_1) }; #undef do_arch_1 #undef HYPERCALL #undef HVM_CALL #undef COMPAT_CALL int hvm_hypercall(struct cpu_user_regs *regs) { struct vcpu *curr = current; struct domain *currd = curr->domain; int mode = hvm_guest_x86_mode(curr); unsigned long eax = regs->eax; switch ( mode ) { case 8: eax = regs->rax; /* Fallthrough to permission check. */ case 4: case 2: if ( currd->arch.monitor.guest_request_userspace_enabled && eax == __HYPERVISOR_hvm_op && (mode == 8 ? regs->rdi : regs->ebx) == HVMOP_guest_request_vm_event ) break; if ( unlikely(hvm_get_cpl(curr)) ) { default: regs->rax = -EPERM; return HVM_HCALL_completed; } case 0: break; } if ( (eax & 0x80000000) && is_viridian_domain(currd) ) return viridian_hypercall(regs); BUILD_BUG_ON(ARRAY_SIZE(hvm_hypercall_table) > ARRAY_SIZE(hypercall_args_table)); if ( (eax >= ARRAY_SIZE(hvm_hypercall_table)) || !hvm_hypercall_table[eax].native ) { regs->rax = -ENOSYS; return HVM_HCALL_completed; } curr->hcall_preempted = false; if ( mode == 8 ) { unsigned long rdi = regs->rdi; unsigned long rsi = regs->rsi; unsigned long rdx = regs->rdx; unsigned long r10 = regs->r10; unsigned long r8 = regs->r8; unsigned long r9 = regs->r9; HVM_DBG_LOG(DBG_LEVEL_HCALL, "hcall%lu(%lx, %lx, %lx, %lx, %lx, %lx)", eax, rdi, rsi, rdx, r10, r8, r9); #ifndef NDEBUG /* Deliberately corrupt parameter regs not used by this hypercall. */ switch ( hypercall_args_table[eax].native ) { case 0: rdi = 0xdeadbeefdeadf00dUL; case 1: rsi = 0xdeadbeefdeadf00dUL; case 2: rdx = 0xdeadbeefdeadf00dUL; case 3: r10 = 0xdeadbeefdeadf00dUL; case 4: r8 = 0xdeadbeefdeadf00dUL; case 5: r9 = 0xdeadbeefdeadf00dUL; } #endif regs->rax = hvm_hypercall_table[eax].native(rdi, rsi, rdx, r10, r8, r9); #ifndef NDEBUG if ( !curr->hcall_preempted ) { /* Deliberately corrupt parameter regs used by this hypercall. */ switch ( hypercall_args_table[eax].native ) { case 6: regs->r9 = 0xdeadbeefdeadf00dUL; case 5: regs->r8 = 0xdeadbeefdeadf00dUL; case 4: regs->r10 = 0xdeadbeefdeadf00dUL; case 3: regs->rdx = 0xdeadbeefdeadf00dUL; case 2: regs->rsi = 0xdeadbeefdeadf00dUL; case 1: regs->rdi = 0xdeadbeefdeadf00dUL; } } #endif } else { unsigned int ebx = regs->ebx; unsigned int ecx = regs->ecx; unsigned int edx = regs->edx; unsigned int esi = regs->esi; unsigned int edi = regs->edi; unsigned int ebp = regs->ebp; HVM_DBG_LOG(DBG_LEVEL_HCALL, "hcall%lu(%x, %x, %x, %x, %x, %x)", eax, ebx, ecx, edx, esi, edi, ebp); #ifndef NDEBUG /* Deliberately corrupt parameter regs not used by this hypercall. */ switch ( hypercall_args_table[eax].compat ) { case 0: ebx = 0xdeadf00d; case 1: ecx = 0xdeadf00d; case 2: edx = 0xdeadf00d; case 3: esi = 0xdeadf00d; case 4: edi = 0xdeadf00d; case 5: ebp = 0xdeadf00d; } #endif curr->hcall_compat = true; regs->rax = hvm_hypercall_table[eax].compat(ebx, ecx, edx, esi, edi, ebp); curr->hcall_compat = false; #ifndef NDEBUG if ( !curr->hcall_preempted ) { /* Deliberately corrupt parameter regs used by this hypercall. */ switch ( hypercall_args_table[eax].compat ) { case 6: regs->rbp = 0xdeadf00d; case 5: regs->rdi = 0xdeadf00d; case 4: regs->rsi = 0xdeadf00d; case 3: regs->rdx = 0xdeadf00d; case 2: regs->rcx = 0xdeadf00d; case 1: regs->rbx = 0xdeadf00d; } } #endif } HVM_DBG_LOG(DBG_LEVEL_HCALL, "hcall%lu -> %lx", eax, regs->rax); if ( curr->hcall_preempted ) return HVM_HCALL_preempted; if ( unlikely(currd->arch.hvm_domain.qemu_mapcache_invalidate) && test_and_clear_bool(currd->arch.hvm_domain.qemu_mapcache_invalidate) ) send_invalidate_req(); return HVM_HCALL_completed; } /* * Local variables: * mode: C * c-file-style: "BSD" * c-basic-offset: 4 * tab-width: 4 * indent-tabs-mode: nil * End: */