Skip to content

Commit

Permalink
xen/domctl: Add XEN_DOMCTL_vmtrace_op
Browse files Browse the repository at this point in the history
Implement an interface to configure and control tracing operations.  Reuse the
existing SETDEBUGGING flask vector rather than inventing a new one.

Userspace using this interface is going to need platform specific knowledge
anyway to interpret the contents of the trace buffer.  While some operations
(e.g. enable/disable) can reasonably be generic, others cannot.  Provide an
explicitly-platform specific pair of get/set operations to reduce API churn as
new options get added/enabled.

For the VMX specific Processor Trace implementation, tolerate reading and
modifying a safe subset of bits in CTL, STATUS and OUTPUT_MASK.  This permits
userspace to control the content which gets logged, but prevents modification
of details such as the position/size of the output buffer.

Signed-off-by: Michał Leszczyński <michal.leszczynski@cert.pl>
Signed-off-by: Andrew Cooper <andrew.cooper3@citrix.com>
Reviewed-by: Roger Pau Monné <roger.pau@citrix.com>
Release-Acked-by: Ian Jackson <iwj@xenproject.org>
  • Loading branch information
icedevml authored and andyhhp committed Feb 5, 2021
1 parent 71cb03f commit 1cee4bd
Show file tree
Hide file tree
Showing 5 changed files with 289 additions and 0 deletions.
55 changes: 55 additions & 0 deletions xen/arch/x86/domctl.c
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,55 @@ void arch_get_domain_info(const struct domain *d,
info->arch_config.emulation_flags = d->arch.emulation_flags;
}

static int do_vmtrace_op(struct domain *d, struct xen_domctl_vmtrace_op *op,
XEN_GUEST_HANDLE_PARAM(xen_domctl_t) u_domctl)
{
struct vcpu *v;
int rc;

if ( !d->vmtrace_size || d == current->domain /* No vcpu_pause() */ )
return -EINVAL;

ASSERT(is_hvm_domain(d)); /* Restricted by domain creation logic. */

v = domain_vcpu(d, op->vcpu);
if ( !v )
return -ENOENT;

vcpu_pause(v);
switch ( op->cmd )
{
case XEN_DOMCTL_vmtrace_enable:
case XEN_DOMCTL_vmtrace_disable:
case XEN_DOMCTL_vmtrace_reset_and_enable:
rc = hvm_vmtrace_control(
v, op->cmd != XEN_DOMCTL_vmtrace_disable,
op->cmd == XEN_DOMCTL_vmtrace_reset_and_enable);
break;

case XEN_DOMCTL_vmtrace_output_position:
rc = hvm_vmtrace_output_position(v, &op->value);
if ( rc >= 0 )
rc = 0;
break;

case XEN_DOMCTL_vmtrace_get_option:
rc = hvm_vmtrace_get_option(v, op->key, &op->value);
break;

case XEN_DOMCTL_vmtrace_set_option:
rc = hvm_vmtrace_set_option(v, op->key, op->value);
break;

default:
rc = -EOPNOTSUPP;
break;
}
vcpu_unpause(v);

return rc;
}

#define MAX_IOPORTS 0x10000

long arch_do_domctl(
Expand Down Expand Up @@ -1320,6 +1369,12 @@ long arch_do_domctl(
domain_unpause(d);
break;

case XEN_DOMCTL_vmtrace_op:
ret = do_vmtrace_op(d, &domctl->u.vmtrace_op, u_domctl);
if ( !ret )
copyback = true;
break;

default:
ret = iommu_do_domctl(domctl, d, u_domctl);
break;
Expand Down
135 changes: 135 additions & 0 deletions xen/arch/x86/hvm/vmx/vmx.c
Original file line number Diff line number Diff line change
Expand Up @@ -2261,6 +2261,137 @@ static bool vmx_get_pending_event(struct vcpu *v, struct x86_event *info)
return true;
}

/*
* We only let vmtrace agents see and modify a subset of bits in MSR_RTIT_CTL.
* These all pertain to data-emitted into the trace buffer(s). Must not
* include controls pertaining to the structure/position of the trace
* buffer(s).
*/
#define RTIT_CTL_MASK \
(RTIT_CTL_TRACE_EN | RTIT_CTL_OS | RTIT_CTL_USR | RTIT_CTL_TSC_EN | \
RTIT_CTL_DIS_RETC | RTIT_CTL_BRANCH_EN)

/*
* Status bits restricted to the first-gen subset (i.e. no further CPUID
* requirements.)
*/
#define RTIT_STATUS_MASK \
(RTIT_STATUS_FILTER_EN | RTIT_STATUS_CONTEXT_EN | RTIT_STATUS_TRIGGER_EN | \
RTIT_STATUS_ERROR | RTIT_STATUS_STOPPED)

static int vmtrace_get_option(struct vcpu *v, uint64_t key, uint64_t *output)
{
const struct vcpu_msrs *msrs = v->arch.msrs;

switch ( key )
{
case MSR_RTIT_CTL:
*output = msrs->rtit.ctl & RTIT_CTL_MASK;
break;

case MSR_RTIT_STATUS:
*output = msrs->rtit.status & RTIT_STATUS_MASK;
break;

default:
*output = 0;
return -EINVAL;
}
return 0;
}

static int vmtrace_set_option(struct vcpu *v, uint64_t key, uint64_t value)
{
struct vcpu_msrs *msrs = v->arch.msrs;
bool new_en, old_en = msrs->rtit.ctl & RTIT_CTL_TRACE_EN;

switch ( key )
{
case MSR_RTIT_CTL:
if ( value & ~RTIT_CTL_MASK )
return -EINVAL;

msrs->rtit.ctl &= ~RTIT_CTL_MASK;
msrs->rtit.ctl |= (value & RTIT_CTL_MASK);
break;

case MSR_RTIT_STATUS:
if ( value & ~RTIT_STATUS_MASK )
return -EINVAL;

msrs->rtit.status &= ~RTIT_STATUS_MASK;
msrs->rtit.status |= (value & RTIT_STATUS_MASK);
break;

default:
return -EINVAL;
}

new_en = msrs->rtit.ctl & RTIT_CTL_TRACE_EN;

/* ctl.trace_en changed => update MSR load/save lists appropriately. */
if ( !old_en && new_en )
{
if ( vmx_add_guest_msr(v, MSR_RTIT_CTL, msrs->rtit.ctl) ||
vmx_add_host_load_msr(v, MSR_RTIT_CTL, 0) )
{
/*
* The only failure cases here are failing the
* singleton-per-domain memory allocation, or exceeding the space
* in the allocation. We could unwind in principle, but there is
* nothing userspace can usefully do to continue using this VM.
*/
domain_crash(v->domain);
return -ENXIO;
}
}
else if ( old_en && !new_en )
{
vmx_del_msr(v, MSR_RTIT_CTL, VMX_MSR_GUEST);
vmx_del_msr(v, MSR_RTIT_CTL, VMX_MSR_HOST);
}

return 0;
}

static int vmtrace_control(struct vcpu *v, bool enable, bool reset)
{
struct vcpu_msrs *msrs = v->arch.msrs;
uint64_t new_ctl;
int rc;

/*
* Absolutely nothing good will come of Xen's and userspace's idea of
* whether ipt is enabled getting out of sync.
*/
if ( v->arch.hvm.vmx.ipt_active == enable )
return -EINVAL;

if ( reset )
{
msrs->rtit.status = 0;
msrs->rtit.output_offset = 0;
}

new_ctl = msrs->rtit.ctl & ~RTIT_CTL_TRACE_EN;
if ( enable )
new_ctl |= RTIT_CTL_TRACE_EN;

rc = vmtrace_set_option(v, MSR_RTIT_CTL, new_ctl);
if ( rc )
return rc;

v->arch.hvm.vmx.ipt_active = enable;

return 0;
}

static int vmtrace_output_position(struct vcpu *v, uint64_t *pos)
{
*pos = v->arch.msrs->rtit.output_offset;
return v->arch.hvm.vmx.ipt_active;
}

static struct hvm_function_table __initdata vmx_function_table = {
.name = "VMX",
.cpu_up_prepare = vmx_cpu_up_prepare,
Expand Down Expand Up @@ -2316,6 +2447,10 @@ static struct hvm_function_table __initdata vmx_function_table = {
.altp2m_vcpu_update_vmfunc_ve = vmx_vcpu_update_vmfunc_ve,
.altp2m_vcpu_emulate_ve = vmx_vcpu_emulate_ve,
.altp2m_vcpu_emulate_vmfunc = vmx_vcpu_emulate_vmfunc,
.vmtrace_control = vmtrace_control,
.vmtrace_output_position = vmtrace_output_position,
.vmtrace_set_option = vmtrace_set_option,
.vmtrace_get_option = vmtrace_get_option,
.tsc_scaling = {
.max_ratio = VMX_TSC_MULTIPLIER_MAX,
},
Expand Down
63 changes: 63 additions & 0 deletions xen/include/asm-x86/hvm/hvm.h
Original file line number Diff line number Diff line change
Expand Up @@ -214,6 +214,12 @@ struct hvm_function_table {
bool_t (*altp2m_vcpu_emulate_ve)(struct vcpu *v);
int (*altp2m_vcpu_emulate_vmfunc)(const struct cpu_user_regs *regs);

/* vmtrace */
int (*vmtrace_control)(struct vcpu *v, bool enable, bool reset);
int (*vmtrace_output_position)(struct vcpu *v, uint64_t *pos);
int (*vmtrace_set_option)(struct vcpu *v, uint64_t key, uint64_t value);
int (*vmtrace_get_option)(struct vcpu *v, uint64_t key, uint64_t *value);

/*
* Parameters and callbacks for hardware-assisted TSC scaling,
* which are valid only when the hardware feature is available.
Expand Down Expand Up @@ -655,6 +661,41 @@ static inline bool altp2m_vcpu_emulate_ve(struct vcpu *v)
return false;
}

static inline int hvm_vmtrace_control(struct vcpu *v, bool enable, bool reset)
{
if ( hvm_funcs.vmtrace_control )
return hvm_funcs.vmtrace_control(v, enable, reset);

return -EOPNOTSUPP;
}

/* Returns -errno, or a boolean of whether tracing is currently active. */
static inline int hvm_vmtrace_output_position(struct vcpu *v, uint64_t *pos)
{
if ( hvm_funcs.vmtrace_output_position )
return hvm_funcs.vmtrace_output_position(v, pos);

return -EOPNOTSUPP;
}

static inline int hvm_vmtrace_set_option(
struct vcpu *v, uint64_t key, uint64_t value)
{
if ( hvm_funcs.vmtrace_set_option )
return hvm_funcs.vmtrace_set_option(v, key, value);

return -EOPNOTSUPP;
}

static inline int hvm_vmtrace_get_option(
struct vcpu *v, uint64_t key, uint64_t *value)
{
if ( hvm_funcs.vmtrace_get_option )
return hvm_funcs.vmtrace_get_option(v, key, value);

return -EOPNOTSUPP;
}

/*
* This must be defined as a macro instead of an inline function,
* because it uses 'struct vcpu' and 'struct domain' which have
Expand Down Expand Up @@ -751,6 +792,28 @@ static inline bool hvm_has_set_descriptor_access_exiting(void)
return false;
}

static inline int hvm_vmtrace_control(struct vcpu *v, bool enable, bool reset)
{
return -EOPNOTSUPP;
}

static inline int hvm_vmtrace_output_position(struct vcpu *v, uint64_t *pos)
{
return -EOPNOTSUPP;
}

static inline int hvm_vmtrace_set_option(
struct vcpu *v, uint64_t key, uint64_t value)
{
return -EOPNOTSUPP;
}

static inline int hvm_vmtrace_get_option(
struct vcpu *v, uint64_t key, uint64_t *value)
{
return -EOPNOTSUPP;
}

#define is_viridian_domain(d) ((void)(d), false)
#define is_viridian_vcpu(v) ((void)(v), false)
#define has_viridian_time_ref_count(d) ((void)(d), false)
Expand Down
35 changes: 35 additions & 0 deletions xen/include/public/domctl.h
Original file line number Diff line number Diff line change
Expand Up @@ -1135,6 +1135,39 @@ struct xen_domctl_vuart_op {
*/
};

/* XEN_DOMCTL_vmtrace_op: Perform VM tracing operations. */
struct xen_domctl_vmtrace_op {
uint32_t cmd; /* IN */
uint32_t vcpu; /* IN */
uint64_aligned_t key; /* IN - @cmd specific data. */
uint64_aligned_t value; /* IN/OUT - @cmd specific data. */

/*
* General enable/disable of tracing.
*
* XEN_DOMCTL_vmtrace_reset_and_enable is provided as optimisation for
* common usecases, which want to reset status and position information
* when turning tracing back on.
*/
#define XEN_DOMCTL_vmtrace_enable 1
#define XEN_DOMCTL_vmtrace_disable 2
#define XEN_DOMCTL_vmtrace_reset_and_enable 3

/* Obtain the current output position within the buffer. Fills @value. */
#define XEN_DOMCTL_vmtrace_output_position 4

/*
* Get/Set platform specific configuration.
*
* For Intel Processor Trace, @key/@value are interpreted as MSR
* reads/writes to MSR_RTIT_*, filtered to a safe subset.
*/
#define XEN_DOMCTL_vmtrace_get_option 5
#define XEN_DOMCTL_vmtrace_set_option 6
};
typedef struct xen_domctl_vmtrace_op xen_domctl_vmtrace_op_t;
DEFINE_XEN_GUEST_HANDLE(xen_domctl_vmtrace_op_t);

struct xen_domctl {
uint32_t cmd;
#define XEN_DOMCTL_createdomain 1
Expand Down Expand Up @@ -1219,6 +1252,7 @@ struct xen_domctl {
#define XEN_DOMCTL_vuart_op 81
#define XEN_DOMCTL_get_cpu_policy 82
#define XEN_DOMCTL_set_cpu_policy 83
#define XEN_DOMCTL_vmtrace_op 84
#define XEN_DOMCTL_gdbsx_guestmemio 1000
#define XEN_DOMCTL_gdbsx_pausevcpu 1001
#define XEN_DOMCTL_gdbsx_unpausevcpu 1002
Expand Down Expand Up @@ -1279,6 +1313,7 @@ struct xen_domctl {
struct xen_domctl_monitor_op monitor_op;
struct xen_domctl_psr_alloc psr_alloc;
struct xen_domctl_vuart_op vuart_op;
struct xen_domctl_vmtrace_op vmtrace_op;
uint8_t pad[128];
} u;
};
Expand Down
1 change: 1 addition & 0 deletions xen/xsm/flask/hooks.c
Original file line number Diff line number Diff line change
Expand Up @@ -703,6 +703,7 @@ static int flask_domctl(struct domain *d, int cmd)
return current_has_perm(d, SECCLASS_DOMAIN2, DOMAIN2__VM_EVENT);

case XEN_DOMCTL_debug_op:
case XEN_DOMCTL_vmtrace_op:
case XEN_DOMCTL_gdbsx_guestmemio:
case XEN_DOMCTL_gdbsx_pausevcpu:
case XEN_DOMCTL_gdbsx_unpausevcpu:
Expand Down

0 comments on commit 1cee4bd

Please sign in to comment.