Files
xen/tools/libs/ctrl/xc_monitor.c
T
Tamas K Lengyel 540645212a x86/monitor: Add new monitor event to catch all vmexits
Add monitor event that hooks the vmexit handler allowing for both sync and
async monitoring of events. With async monitoring an event is placed on the
monitor ring for each exit and the rest of the vmexit handler resumes normally.
If there are additional monitor events configured those will also place their
respective events on the monitor ring.

With the sync version an event is placed on the monitor ring but the handler
does not get resumed, thus the sync version is only useful when the VM is not
expected to resume normally after the vmexit. Our use-case is primarily with
the sync version with VM forks where the fork gets reset after sync vmexit
event, thus the rest of the vmexit handler can be safely skipped. This is
very useful when we want to avoid Xen crashing the VM under any circumstance,
for example during fuzzing. Collecting all vmexit information regardless of
the root cause makes it easier to reason about the state of the VM on the
monitor side, hence we opt to receive all events, even for external interrupt
and NMI exits and let the monitor agent decide how to proceed.

Signed-off-by: Tamas K Lengyel <tamas.lengyel@intel.com>
Reviewed-by: Roger Pau Monné <roger.pau@citrix.com>
2022-05-18 18:31:11 +01:00

273 lines
8.7 KiB
C

/******************************************************************************
*
* xc_monitor.c
*
* Interface to VM event monitor
*
* Copyright (c) 2015 Tamas K Lengyel (tamas@tklengyel.com)
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; If not, see <http://www.gnu.org/licenses/>.
*/
#include "xc_private.h"
void *xc_monitor_enable(xc_interface *xch, uint32_t domain_id, uint32_t *port)
{
return xc_vm_event_enable(xch, domain_id, HVM_PARAM_MONITOR_RING_PFN,
port);
}
int xc_monitor_disable(xc_interface *xch, uint32_t domain_id)
{
return xc_vm_event_control(xch, domain_id,
XEN_VM_EVENT_DISABLE,
XEN_DOMCTL_VM_EVENT_OP_MONITOR,
NULL);
}
int xc_monitor_resume(xc_interface *xch, uint32_t domain_id)
{
return xc_vm_event_control(xch, domain_id,
XEN_VM_EVENT_RESUME,
XEN_DOMCTL_VM_EVENT_OP_MONITOR,
NULL);
}
int xc_monitor_get_capabilities(xc_interface *xch, uint32_t domain_id,
uint32_t *capabilities)
{
int rc;
DECLARE_DOMCTL;
if ( !capabilities )
{
errno = EINVAL;
return -1;
}
domctl.cmd = XEN_DOMCTL_monitor_op;
domctl.domain = domain_id;
domctl.u.monitor_op.op = XEN_DOMCTL_MONITOR_OP_GET_CAPABILITIES;
rc = do_domctl(xch, &domctl);
if ( rc )
return rc;
*capabilities = domctl.u.monitor_op.event;
return 0;
}
int xc_monitor_write_ctrlreg(xc_interface *xch, uint32_t domain_id,
uint16_t index, bool enable, bool sync,
uint64_t bitmask, bool onchangeonly)
{
DECLARE_DOMCTL;
domctl.cmd = XEN_DOMCTL_monitor_op;
domctl.domain = domain_id;
domctl.u.monitor_op.op = enable ? XEN_DOMCTL_MONITOR_OP_ENABLE
: XEN_DOMCTL_MONITOR_OP_DISABLE;
domctl.u.monitor_op.event = XEN_DOMCTL_MONITOR_EVENT_WRITE_CTRLREG;
domctl.u.monitor_op.u.mov_to_cr.index = index;
domctl.u.monitor_op.u.mov_to_cr.sync = sync;
domctl.u.monitor_op.u.mov_to_cr.onchangeonly = onchangeonly;
domctl.u.monitor_op.u.mov_to_cr.bitmask = bitmask;
domctl.u.monitor_op.u.mov_to_cr.pad1 = 0;
domctl.u.monitor_op.u.mov_to_cr.pad2 = 0;
return do_domctl(xch, &domctl);
}
int xc_monitor_mov_to_msr(xc_interface *xch, uint32_t domain_id, uint32_t msr,
bool enable, bool onchangeonly)
{
DECLARE_DOMCTL;
domctl.cmd = XEN_DOMCTL_monitor_op;
domctl.domain = domain_id;
domctl.u.monitor_op.op = enable ? XEN_DOMCTL_MONITOR_OP_ENABLE
: XEN_DOMCTL_MONITOR_OP_DISABLE;
domctl.u.monitor_op.event = XEN_DOMCTL_MONITOR_EVENT_MOV_TO_MSR;
domctl.u.monitor_op.u.mov_to_msr.msr = msr;
domctl.u.monitor_op.u.mov_to_msr.onchangeonly = onchangeonly;
return do_domctl(xch, &domctl);
}
int xc_monitor_software_breakpoint(xc_interface *xch, uint32_t domain_id,
bool enable)
{
DECLARE_DOMCTL;
domctl.cmd = XEN_DOMCTL_monitor_op;
domctl.domain = domain_id;
domctl.u.monitor_op.op = enable ? XEN_DOMCTL_MONITOR_OP_ENABLE
: XEN_DOMCTL_MONITOR_OP_DISABLE;
domctl.u.monitor_op.event = XEN_DOMCTL_MONITOR_EVENT_SOFTWARE_BREAKPOINT;
return do_domctl(xch, &domctl);
}
int xc_monitor_singlestep(xc_interface *xch, uint32_t domain_id,
bool enable)
{
DECLARE_DOMCTL;
domctl.cmd = XEN_DOMCTL_monitor_op;
domctl.domain = domain_id;
domctl.u.monitor_op.op = enable ? XEN_DOMCTL_MONITOR_OP_ENABLE
: XEN_DOMCTL_MONITOR_OP_DISABLE;
domctl.u.monitor_op.event = XEN_DOMCTL_MONITOR_EVENT_SINGLESTEP;
return do_domctl(xch, &domctl);
}
int xc_monitor_descriptor_access(xc_interface *xch, uint32_t domain_id,
bool enable)
{
DECLARE_DOMCTL;
domctl.cmd = XEN_DOMCTL_monitor_op;
domctl.domain = domain_id;
domctl.u.monitor_op.op = enable ? XEN_DOMCTL_MONITOR_OP_ENABLE
: XEN_DOMCTL_MONITOR_OP_DISABLE;
domctl.u.monitor_op.event = XEN_DOMCTL_MONITOR_EVENT_DESC_ACCESS;
return do_domctl(xch, &domctl);
}
int xc_monitor_guest_request(xc_interface *xch, uint32_t domain_id, bool enable,
bool sync, bool allow_userspace)
{
DECLARE_DOMCTL;
domctl.cmd = XEN_DOMCTL_monitor_op;
domctl.domain = domain_id;
domctl.u.monitor_op.op = enable ? XEN_DOMCTL_MONITOR_OP_ENABLE
: XEN_DOMCTL_MONITOR_OP_DISABLE;
domctl.u.monitor_op.event = XEN_DOMCTL_MONITOR_EVENT_GUEST_REQUEST;
domctl.u.monitor_op.u.guest_request.sync = sync;
domctl.u.monitor_op.u.guest_request.allow_userspace = enable ? allow_userspace : false;
return do_domctl(xch, &domctl);
}
int xc_monitor_inguest_pagefault(xc_interface *xch, uint32_t domain_id,
bool disable)
{
DECLARE_DOMCTL;
domctl.cmd = XEN_DOMCTL_monitor_op;
domctl.domain = domain_id;
domctl.u.monitor_op.op = disable ? XEN_DOMCTL_MONITOR_OP_ENABLE
: XEN_DOMCTL_MONITOR_OP_DISABLE;
domctl.u.monitor_op.event = XEN_DOMCTL_MONITOR_EVENT_INGUEST_PAGEFAULT;
return do_domctl(xch, &domctl);
}
int xc_monitor_emulate_each_rep(xc_interface *xch, uint32_t domain_id,
bool enable)
{
DECLARE_DOMCTL;
domctl.cmd = XEN_DOMCTL_monitor_op;
domctl.domain = domain_id;
domctl.u.monitor_op.op = XEN_DOMCTL_MONITOR_OP_EMULATE_EACH_REP;
domctl.u.monitor_op.event = enable;
return do_domctl(xch, &domctl);
}
int xc_monitor_debug_exceptions(xc_interface *xch, uint32_t domain_id,
bool enable, bool sync)
{
DECLARE_DOMCTL;
domctl.cmd = XEN_DOMCTL_monitor_op;
domctl.domain = domain_id;
domctl.u.monitor_op.op = enable ? XEN_DOMCTL_MONITOR_OP_ENABLE
: XEN_DOMCTL_MONITOR_OP_DISABLE;
domctl.u.monitor_op.event = XEN_DOMCTL_MONITOR_EVENT_DEBUG_EXCEPTION;
domctl.u.monitor_op.u.debug_exception.sync = sync;
return do_domctl(xch, &domctl);
}
int xc_monitor_cpuid(xc_interface *xch, uint32_t domain_id, bool enable)
{
DECLARE_DOMCTL;
domctl.cmd = XEN_DOMCTL_monitor_op;
domctl.domain = domain_id;
domctl.u.monitor_op.op = enable ? XEN_DOMCTL_MONITOR_OP_ENABLE
: XEN_DOMCTL_MONITOR_OP_DISABLE;
domctl.u.monitor_op.event = XEN_DOMCTL_MONITOR_EVENT_CPUID;
return do_domctl(xch, &domctl);
}
int xc_monitor_privileged_call(xc_interface *xch, uint32_t domain_id,
bool enable)
{
DECLARE_DOMCTL;
domctl.cmd = XEN_DOMCTL_monitor_op;
domctl.domain = domain_id;
domctl.u.monitor_op.op = enable ? XEN_DOMCTL_MONITOR_OP_ENABLE
: XEN_DOMCTL_MONITOR_OP_DISABLE;
domctl.u.monitor_op.event = XEN_DOMCTL_MONITOR_EVENT_PRIVILEGED_CALL;
return do_domctl(xch, &domctl);
}
int xc_monitor_emul_unimplemented(xc_interface *xch, uint32_t domain_id,
bool enable)
{
DECLARE_DOMCTL;
domctl.cmd = XEN_DOMCTL_monitor_op;
domctl.domain = domain_id;
domctl.u.monitor_op.op = enable ? XEN_DOMCTL_MONITOR_OP_ENABLE
: XEN_DOMCTL_MONITOR_OP_DISABLE;
domctl.u.monitor_op.event = XEN_DOMCTL_MONITOR_EVENT_EMUL_UNIMPLEMENTED;
return do_domctl(xch, &domctl);
}
int xc_monitor_vmexit(xc_interface *xch, uint32_t domain_id, bool enable,
bool sync)
{
DECLARE_DOMCTL;
domctl.cmd = XEN_DOMCTL_monitor_op;
domctl.domain = domain_id;
domctl.u.monitor_op.op = enable ? XEN_DOMCTL_MONITOR_OP_ENABLE
: XEN_DOMCTL_MONITOR_OP_DISABLE;
domctl.u.monitor_op.event = XEN_DOMCTL_MONITOR_EVENT_VMEXIT;
domctl.u.monitor_op.u.vmexit.sync = sync;
return do_domctl(xch, &domctl);
}
/*
* Local variables:
* mode: C
* c-file-style: "BSD"
* c-basic-offset: 4
* tab-width: 4
* indent-tabs-mode: nil
* End:
*/