x86/hvm: Split the hypercall dispatching infrastructure out of hvm.c

Into a new hypercall.c.  This is purely code motion.

Signed-off-by: Andrew Cooper <andrew.cooper3@citrix.com>
Reviewed-by: Wei Liu <wei.liu2@citrix.com>
Acked-by: Jan Beulich <jbeulich@suse.com>
This commit is contained in:
Andrew Cooper committed 2017-02-14 14:34:26 +00:00
1 parent 55fa23d907
commit e637e8d12e
3 files changed
+333 -298

No files matched your search

+1
View File
@@ -6,6 +6,7 @@ obj-y += dm.o
obj-y += emulate.o
obj-y += hpet.o
obj-y += hvm.o
obj-y += hypercall.o
obj-y += i8254.o
obj-y += intercept.o
obj-y += io.o
-298
View File
@@ -3719,304 +3719,6 @@ enum hvm_intblk hvm_interrupt_blocked(struct vcpu *v, struct hvm_intack intack)
return hvm_intblk_none;
}
static int grant_table_op_is_allowed(unsigned int cmd)
{
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:
return 1;
default:
/* all other commands need auditing */
return 0;
}
}
static long hvm_grant_table_op(
unsigned int cmd, XEN_GUEST_HANDLE_PARAM(void) uop, unsigned int count)
{
if ( !grant_table_op_is_allowed(cmd) )
return -ENOSYS; /* all other commands need auditing */
return do_grant_table_op(cmd, uop, count);
}
static long hvm_memory_op(int cmd, XEN_GUEST_HANDLE_PARAM(void) arg)
{
long rc;
switch ( cmd & MEMOP_CMD_MASK )
{
case XENMEM_machine_memory_map:
case XENMEM_machphys_mapping:
return -ENOSYS;
case XENMEM_decrease_reservation:
rc = do_memory_op(cmd, arg);
current->domain->arch.hvm_domain.qemu_mapcache_invalidate = 1;
return rc;
}
return do_memory_op(cmd, arg);
}
static long hvm_physdev_op(int cmd, XEN_GUEST_HANDLE_PARAM(void) arg)
{
switch ( cmd )
{
default:
if ( !is_pvh_vcpu(current) || !is_hardware_domain(current->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:
return do_physdev_op(cmd, arg);
}
}
static long hvm_grant_table_op_compat32(unsigned int cmd,
XEN_GUEST_HANDLE_PARAM(void) uop,
unsigned int count)
{
if ( !grant_table_op_is_allowed(cmd) )
return -ENOSYS;
return compat_grant_table_op(cmd, uop, count);
}
static long hvm_memory_op_compat32(int cmd, XEN_GUEST_HANDLE_PARAM(void) arg)
{
int rc;
switch ( cmd & MEMOP_CMD_MASK )
{
case XENMEM_machine_memory_map:
case XENMEM_machphys_mapping:
return -ENOSYS;
case XENMEM_decrease_reservation:
rc = compat_memory_op(cmd, arg);
current->domain->arch.hvm_domain.qemu_mapcache_invalidate = 1;
return rc;
}
return compat_memory_op(cmd, arg);
}
static long hvm_physdev_op_compat32(
int cmd, XEN_GUEST_HANDLE_PARAM(void) arg)
{
switch ( cmd )
{
case PHYSDEVOP_map_pirq:
case PHYSDEVOP_unmap_pirq:
case PHYSDEVOP_eoi:
case PHYSDEVOP_irq_status_query:
case PHYSDEVOP_get_free_pirq:
return compat_physdev_op(cmd, arg);
break;
default:
return -ENOSYS;
break;
}
}
#define HYPERCALL(x) \
[ __HYPERVISOR_ ## x ] = { (hypercall_fn_t *) do_ ## x, \
(hypercall_fn_t *) do_ ## x }
#define COMPAT_CALL(x) \
[ __HYPERVISOR_ ## x ] = { (hypercall_fn_t *) do_ ## x, \
(hypercall_fn_t *) compat_ ## x }
#define do_memory_op hvm_memory_op
#define compat_memory_op hvm_memory_op_compat32
#define do_physdev_op hvm_physdev_op
#define compat_physdev_op hvm_physdev_op_compat32
#define do_grant_table_op hvm_grant_table_op
#define compat_grant_table_op hvm_grant_table_op_compat32
#define do_arch_1 paging_domctl_continuation
static const hypercall_table_t hvm_hypercall_table[] = {
COMPAT_CALL(memory_op),
COMPAT_CALL(grant_table_op),
COMPAT_CALL(vcpu_op),
COMPAT_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_memory_op
#undef compat_memory_op
#undef do_physdev_op
#undef compat_physdev_op
#undef do_grant_table_op
#undef compat_grant_table_op
#undef do_arch_1
#undef HYPERCALL
#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 ( 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->arch.hvm_vcpu.hcall_preempted = 0;
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
curr->arch.hvm_vcpu.hcall_64bit = 1;
regs->rax = hvm_hypercall_table[eax].native(rdi, rsi, rdx, r10, r8,
r9);
curr->arch.hvm_vcpu.hcall_64bit = 0;
#ifndef NDEBUG
if ( !curr->arch.hvm_vcpu.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
regs->rax = hvm_hypercall_table[eax].compat(ebx, ecx, edx, esi, edi,
ebp);
#ifndef NDEBUG
if ( !curr->arch.hvm_vcpu.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->arch.hvm_vcpu.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;
}
static void hvm_latch_shinfo_size(struct domain *d)
{
/*
+332
View File
@@ -0,0 +1,332 @@
/******************************************************************************
* 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 <http://www.gnu.org/licenses/>.
*
* Copyright (c) 2017 Citrix Systems Ltd.
*/
#include <xen/lib.h>
#include <xen/hypercall.h>
#include <asm/hvm/support.h>
static int grant_table_op_is_allowed(unsigned int cmd)
{
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:
return 1;
default:
/* all other commands need auditing */
return 0;
}
}
static long hvm_grant_table_op(
unsigned int cmd, XEN_GUEST_HANDLE_PARAM(void) uop, unsigned int count)
{
if ( !grant_table_op_is_allowed(cmd) )
return -ENOSYS; /* all other commands need auditing */
return do_grant_table_op(cmd, uop, count);
}
static long hvm_memory_op(int cmd, XEN_GUEST_HANDLE_PARAM(void) arg)
{
long rc;
switch ( cmd & MEMOP_CMD_MASK )
{
case XENMEM_machine_memory_map:
case XENMEM_machphys_mapping:
return -ENOSYS;
case XENMEM_decrease_reservation:
rc = do_memory_op(cmd, arg);
current->domain->arch.hvm_domain.qemu_mapcache_invalidate = 1;
return rc;
}
return do_memory_op(cmd, arg);
}
static long hvm_physdev_op(int cmd, XEN_GUEST_HANDLE_PARAM(void) arg)
{
switch ( cmd )
{
default:
if ( !is_pvh_vcpu(current) || !is_hardware_domain(current->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:
return do_physdev_op(cmd, arg);
}
}
static long hvm_grant_table_op_compat32(unsigned int cmd,
XEN_GUEST_HANDLE_PARAM(void) uop,
unsigned int count)
{
if ( !grant_table_op_is_allowed(cmd) )
return -ENOSYS;
return compat_grant_table_op(cmd, uop, count);
}
static long hvm_memory_op_compat32(int cmd, XEN_GUEST_HANDLE_PARAM(void) arg)
{
int rc;
switch ( cmd & MEMOP_CMD_MASK )
{
case XENMEM_machine_memory_map:
case XENMEM_machphys_mapping:
return -ENOSYS;
case XENMEM_decrease_reservation:
rc = compat_memory_op(cmd, arg);
current->domain->arch.hvm_domain.qemu_mapcache_invalidate = 1;
return rc;
}
return compat_memory_op(cmd, arg);
}
static long hvm_physdev_op_compat32(
int cmd, XEN_GUEST_HANDLE_PARAM(void) arg)
{
switch ( cmd )
{
case PHYSDEVOP_map_pirq:
case PHYSDEVOP_unmap_pirq:
case PHYSDEVOP_eoi:
case PHYSDEVOP_irq_status_query:
case PHYSDEVOP_get_free_pirq:
return compat_physdev_op(cmd, arg);
break;
default:
return -ENOSYS;
break;
}
}
#define HYPERCALL(x) \
[ __HYPERVISOR_ ## x ] = { (hypercall_fn_t *) do_ ## x, \
(hypercall_fn_t *) do_ ## x }
#define COMPAT_CALL(x) \
[ __HYPERVISOR_ ## x ] = { (hypercall_fn_t *) do_ ## x, \
(hypercall_fn_t *) compat_ ## x }
#define do_memory_op hvm_memory_op
#define compat_memory_op hvm_memory_op_compat32
#define do_physdev_op hvm_physdev_op
#define compat_physdev_op hvm_physdev_op_compat32
#define do_grant_table_op hvm_grant_table_op
#define compat_grant_table_op hvm_grant_table_op_compat32
#define do_arch_1 paging_domctl_continuation
static const hypercall_table_t hvm_hypercall_table[] = {
COMPAT_CALL(memory_op),
COMPAT_CALL(grant_table_op),
COMPAT_CALL(vcpu_op),
COMPAT_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_memory_op
#undef compat_memory_op
#undef do_physdev_op
#undef compat_physdev_op
#undef do_grant_table_op
#undef compat_grant_table_op
#undef do_arch_1
#undef HYPERCALL
#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 ( 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->arch.hvm_vcpu.hcall_preempted = 0;
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
curr->arch.hvm_vcpu.hcall_64bit = 1;
regs->rax = hvm_hypercall_table[eax].native(rdi, rsi, rdx, r10, r8,
r9);
curr->arch.hvm_vcpu.hcall_64bit = 0;
#ifndef NDEBUG
if ( !curr->arch.hvm_vcpu.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
regs->rax = hvm_hypercall_table[eax].compat(ebx, ecx, edx, esi, edi,
ebp);
#ifndef NDEBUG
if ( !curr->arch.hvm_vcpu.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->arch.hvm_vcpu.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:
*/
Reference in new issue
Block a user
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.