Skip to content

Commit

Permalink
KVM: introspection: add KVMI_VCPU_INJECT_EXCEPTION + KVMI_VCPU_EVENT_…
Browse files Browse the repository at this point in the history
…TRAP

The KVMI_VCPU_INJECT_EXCEPTION command is used by the introspection tool
to inject exceptions, for example, to get a page from swap.

The exception is injected right before entering in guest unless there is
already an exception pending. The introspection tool is notified with
an KVMI_VCPU_EVENT_TRAP event about the success of the injection. In
case of failure, the introspection tool is expected to try again later.

Signed-off-by: Mihai Donțu <mdontu@bitdefender.com>
Co-developed-by: Nicușor Cîțu <nicu.citu@icloud.com>
Signed-off-by: Nicușor Cîțu <nicu.citu@icloud.com>
Co-developed-by: Adalbert Lazăr <alazar@bitdefender.com>
Signed-off-by: Adalbert Lazăr <alazar@bitdefender.com>
  • Loading branch information
mdontu-bd authored and intel-lab-lkp committed Nov 25, 2020
1 parent 7c9678b commit 248beb9
Show file tree
Hide file tree
Showing 12 changed files with 419 additions and 14 deletions.
76 changes: 76 additions & 0 deletions Documentation/virt/kvm/kvmi.rst
Original file line number Diff line number Diff line change
Expand Up @@ -550,6 +550,7 @@ because these are sent as a result of certain commands (but they can be
disallowed by the device manager) ::

KVMI_VCPU_EVENT_PAUSE
KVMI_VCPU_EVENT_TRAP

The VM events (e.g. *KVMI_VM_EVENT_UNHOOK*) are controlled with
the *KVMI_VM_CONTROL_EVENTS* command.
Expand Down Expand Up @@ -736,6 +737,46 @@ ID set.
* -KVM_EINVAL - the padding is not zero
* -KVM_EAGAIN - the selected vCPU can't be introspected yet

16. KVMI_VCPU_INJECT_EXCEPTION
------------------------------

:Architectures: x86
:Versions: >= 1
:Parameters:

::

struct kvmi_vcpu_hdr;
struct kvmi_vcpu_inject_exception {
__u8 nr;
__u8 padding1;
__u16 padding2;
__u32 error_code;
__u64 address;
};

:Returns:

::

struct kvmi_error_code

Injects a vCPU exception (``nr``) with or without an error code (``error_code``).
For page fault exceptions, the guest virtual address (``address``)
has to be specified too.

The *KVMI_VCPU_EVENT_TRAP* event will be sent with the effective injected
exception.

:Errors:

* -KVM_EPERM - the *KVMI_VCPU_EVENT_TRAP* event is disallowed
* -KVM_EINVAL - the selected vCPU is invalid
* -KVM_EINVAL - the padding is not zero
* -KVM_EAGAIN - the selected vCPU can't be introspected yet
* -KVM_EBUSY - another *KVMI_VCPU_INJECT_EXCEPTION*-*KVMI_VCPU_EVENT_TRAP*
pair is in progress

Events
======

Expand Down Expand Up @@ -966,3 +1007,38 @@ register (see **KVMI_VCPU_CONTROL_EVENTS**).
(``cr``), the old value (``old_value``) and the new value (``new_value``)
are sent to the introspection tool. The *CONTINUE* action will set the
``new_val``.

6. KVMI_VCPU_EVENT_TRAP
-----------------------

:Architectures: x86
:Versions: >= 1
:Actions: CONTINUE, CRASH
:Parameters:

::

struct kvmi_vcpu_event;
struct kvmi_vcpu_event_trap {
__u8 nr;
__u8 padding1;
__u16 padding2;
__u32 error_code;
__u64 address;
};

:Returns:

::

struct kvmi_vcpu_hdr;
struct kvmi_vcpu_event_reply;

This event is sent if a previous *KVMI_VCPU_INJECT_EXCEPTION* command
took place. Because it has a high priority, it will be sent before any
other vCPU introspection event.

``kvmi_vcpu_event`` (with the vCPU state), exception/interrupt number
(``nr``), exception code (``error_code``) and ``address`` are sent to
the introspection tool, which should check if its exception has been
injected or overridden.
11 changes: 11 additions & 0 deletions arch/x86/include/asm/kvmi_host.h
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,15 @@ struct kvm_vcpu_arch_introspection {
bool have_delayed_regs;

DECLARE_BITMAP(cr_mask, KVMI_NUM_CR);

struct {
u8 nr;
u32 error_code;
bool error_code_valid;
u64 address;
bool pending;
bool send_event;
} exception;
};

struct kvm_arch_introspection {
Expand All @@ -36,6 +45,7 @@ bool kvmi_cr_event(struct kvm_vcpu *vcpu, unsigned int cr,
unsigned long old_value, unsigned long *new_value);
bool kvmi_cr3_intercepted(struct kvm_vcpu *vcpu);
bool kvmi_monitor_cr3w_intercept(struct kvm_vcpu *vcpu, bool enable);
void kvmi_enter_guest(struct kvm_vcpu *vcpu);

#else /* CONFIG_KVM_INTROSPECTION */

Expand All @@ -48,6 +58,7 @@ static inline bool kvmi_cr_event(struct kvm_vcpu *vcpu, unsigned int cr,
static inline bool kvmi_cr3_intercepted(struct kvm_vcpu *vcpu) { return false; }
static inline bool kvmi_monitor_cr3w_intercept(struct kvm_vcpu *vcpu,
bool enable) { return false; }
static inline void kvmi_enter_guest(struct kvm_vcpu *vcpu) { }

#endif /* CONFIG_KVM_INTROSPECTION */

Expand Down
16 changes: 16 additions & 0 deletions arch/x86/include/uapi/asm/kvmi.h
Original file line number Diff line number Diff line change
Expand Up @@ -79,4 +79,20 @@ struct kvmi_vcpu_event_cr_reply {
__u64 new_val;
};

struct kvmi_vcpu_event_trap {
__u8 nr;
__u8 padding1;
__u16 padding2;
__u32 error_code;
__u64 address;
};

struct kvmi_vcpu_inject_exception {
__u8 nr;
__u8 padding1;
__u16 padding2;
__u32 error_code;
__u64 address;
};

#endif /* _UAPI_ASM_X86_KVMI_H */
110 changes: 110 additions & 0 deletions arch/x86/kvm/kvmi.c
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ void kvmi_arch_init_vcpu_events_mask(unsigned long *supported)
set_bit(KVMI_VCPU_EVENT_BREAKPOINT, supported);
set_bit(KVMI_VCPU_EVENT_CR, supported);
set_bit(KVMI_VCPU_EVENT_HYPERCALL, supported);
set_bit(KVMI_VCPU_EVENT_TRAP, supported);
}

static unsigned int kvmi_vcpu_mode(const struct kvm_vcpu *vcpu,
Expand Down Expand Up @@ -457,3 +458,112 @@ bool kvmi_cr3_intercepted(struct kvm_vcpu *vcpu)
return ret;
}
EXPORT_SYMBOL(kvmi_cr3_intercepted);

int kvmi_arch_cmd_vcpu_inject_exception(struct kvm_vcpu *vcpu,
const struct kvmi_vcpu_inject_exception *req)
{
struct kvm_vcpu_arch_introspection *arch = &VCPUI(vcpu)->arch;
bool has_error;

arch->exception.pending = true;

has_error = x86_exception_has_error_code(req->nr);

arch->exception.nr = req->nr;
arch->exception.error_code = has_error ? req->error_code : 0;
arch->exception.error_code_valid = has_error;
arch->exception.address = req->address;

return 0;
}

static void kvmi_queue_exception(struct kvm_vcpu *vcpu)
{
struct kvm_vcpu_arch_introspection *arch = &VCPUI(vcpu)->arch;
struct x86_exception e = {
.vector = arch->exception.nr,
.error_code_valid = arch->exception.error_code_valid,
.error_code = arch->exception.error_code,
.address = arch->exception.address,
};

if (e.vector == PF_VECTOR)
kvm_inject_page_fault(vcpu, &e);
else if (e.error_code_valid)
kvm_queue_exception_e(vcpu, e.vector, e.error_code);
else
kvm_queue_exception(vcpu, e.vector);
}

static void kvmi_save_injected_event(struct kvm_vcpu *vcpu)
{
struct kvm_vcpu_introspection *vcpui = VCPUI(vcpu);

vcpui->arch.exception.error_code = 0;
vcpui->arch.exception.error_code_valid = false;

vcpui->arch.exception.address = vcpu->arch.cr2;
if (vcpu->arch.exception.injected) {
vcpui->arch.exception.nr = vcpu->arch.exception.nr;
vcpui->arch.exception.error_code_valid =
x86_exception_has_error_code(vcpu->arch.exception.nr);
vcpui->arch.exception.error_code = vcpu->arch.exception.error_code;
} else if (vcpu->arch.interrupt.injected) {
vcpui->arch.exception.nr = vcpu->arch.interrupt.nr;
}
}

static void kvmi_inject_pending_exception(struct kvm_vcpu *vcpu)
{
struct kvm_vcpu_introspection *vcpui = VCPUI(vcpu);

if (!kvm_event_needs_reinjection(vcpu)) {
kvmi_queue_exception(vcpu);
kvm_inject_pending_exception(vcpu);
}

kvmi_save_injected_event(vcpu);

vcpui->arch.exception.pending = false;
vcpui->arch.exception.send_event = true;
kvm_make_request(KVM_REQ_INTROSPECTION, vcpu);
}

void kvmi_enter_guest(struct kvm_vcpu *vcpu)
{
struct kvm_vcpu_introspection *vcpui;
struct kvm_introspection *kvmi;

kvmi = kvmi_get(vcpu->kvm);
if (kvmi) {
vcpui = VCPUI(vcpu);

if (vcpui->arch.exception.pending)
kvmi_inject_pending_exception(vcpu);

kvmi_put(vcpu->kvm);
}
}

static void kvmi_send_trap_event(struct kvm_vcpu *vcpu)
{
u32 action;

action = kvmi_msg_send_vcpu_trap(vcpu);
switch (action) {
case KVMI_EVENT_ACTION_CONTINUE:
break;
default:
kvmi_handle_common_event_actions(vcpu, action);
}
}

void kvmi_arch_send_pending_event(struct kvm_vcpu *vcpu)
{
struct kvm_vcpu_introspection *vcpui = VCPUI(vcpu);

if (vcpui->arch.exception.send_event) {
vcpui->arch.exception.send_event = false;
kvmi_send_trap_event(vcpu);
}
}
3 changes: 3 additions & 0 deletions arch/x86/kvm/kvmi.h
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,11 @@ int kvmi_arch_cmd_vcpu_get_registers(struct kvm_vcpu *vcpu,
void kvmi_arch_cmd_vcpu_set_registers(struct kvm_vcpu *vcpu,
const struct kvm_regs *regs);
int kvmi_arch_cmd_vcpu_control_cr(struct kvm_vcpu *vcpu, int cr, bool enable);
int kvmi_arch_cmd_vcpu_inject_exception(struct kvm_vcpu *vcpu,
const struct kvmi_vcpu_inject_exception *req);

u32 kvmi_msg_send_vcpu_cr(struct kvm_vcpu *vcpu, u32 cr, u64 old_value,
u64 new_value, u64 *ret_value);
u32 kvmi_msg_send_vcpu_trap(struct kvm_vcpu *vcpu);

#endif
55 changes: 50 additions & 5 deletions arch/x86/kvm/kvmi_msg.c
Original file line number Diff line number Diff line change
Expand Up @@ -150,12 +150,37 @@ static int handle_vcpu_control_cr(const struct kvmi_vcpu_msg_job *job,
return kvmi_msg_vcpu_reply(job, msg, ec, NULL, 0);
}

static int handle_vcpu_inject_exception(const struct kvmi_vcpu_msg_job *job,
const struct kvmi_msg_hdr *msg,
const void *_req)
{
const struct kvmi_vcpu_inject_exception *req = _req;
struct kvm_vcpu_arch_introspection *arch;
struct kvm_vcpu *vcpu = job->vcpu;
int ec;

arch = &VCPUI(vcpu)->arch;

if (!kvmi_is_event_allowed(KVMI(vcpu->kvm), KVMI_VCPU_EVENT_TRAP))
ec = -KVM_EPERM;
else if (req->padding1 || req->padding2)
ec = -KVM_EINVAL;
else if (VCPUI(vcpu)->arch.exception.pending ||
VCPUI(vcpu)->arch.exception.send_event)
ec = -KVM_EBUSY;
else
ec = kvmi_arch_cmd_vcpu_inject_exception(vcpu, req);

return kvmi_msg_vcpu_reply(job, msg, ec, NULL, 0);
}

static kvmi_vcpu_msg_job_fct const msg_vcpu[] = {
[KVMI_VCPU_CONTROL_CR] = handle_vcpu_control_cr,
[KVMI_VCPU_GET_CPUID] = handle_vcpu_get_cpuid,
[KVMI_VCPU_GET_INFO] = handle_vcpu_get_info,
[KVMI_VCPU_GET_REGISTERS] = handle_vcpu_get_registers,
[KVMI_VCPU_SET_REGISTERS] = handle_vcpu_set_registers,
[KVMI_VCPU_CONTROL_CR] = handle_vcpu_control_cr,
[KVMI_VCPU_GET_CPUID] = handle_vcpu_get_cpuid,
[KVMI_VCPU_GET_INFO] = handle_vcpu_get_info,
[KVMI_VCPU_GET_REGISTERS] = handle_vcpu_get_registers,
[KVMI_VCPU_INJECT_EXCEPTION] = handle_vcpu_inject_exception,
[KVMI_VCPU_SET_REGISTERS] = handle_vcpu_set_registers,
};

kvmi_vcpu_msg_job_fct kvmi_arch_vcpu_msg_handler(u16 id)
Expand Down Expand Up @@ -187,3 +212,23 @@ u32 kvmi_msg_send_vcpu_cr(struct kvm_vcpu *vcpu, u32 cr, u64 old_value,

return action;
}

u32 kvmi_msg_send_vcpu_trap(struct kvm_vcpu *vcpu)
{
struct kvm_vcpu_introspection *vcpui = VCPUI(vcpu);
struct kvmi_vcpu_event_trap e;
u32 action;
int err;

memset(&e, 0, sizeof(e));
e.nr = vcpui->arch.exception.nr;
e.error_code = vcpui->arch.exception.error_code;
e.address = vcpui->arch.exception.address;

err = __kvmi_send_vcpu_event(vcpu, KVMI_VCPU_EVENT_TRAP,
&e, sizeof(e), NULL, 0, &action);
if (err)
action = KVMI_EVENT_ACTION_CONTINUE;

return action;
}
2 changes: 2 additions & 0 deletions arch/x86/kvm/x86.c
Original file line number Diff line number Diff line change
Expand Up @@ -9008,6 +9008,8 @@ static int vcpu_enter_guest(struct kvm_vcpu *vcpu)
goto cancel_injection;
}

kvmi_enter_guest(vcpu);

if (req_immediate_exit) {
kvm_make_request(KVM_REQ_EVENT, vcpu);
kvm_x86_ops.request_immediate_exit(vcpu);
Expand Down
14 changes: 8 additions & 6 deletions include/uapi/linux/kvmi.h
Original file line number Diff line number Diff line change
Expand Up @@ -36,12 +36,13 @@ enum {
enum {
KVMI_VCPU_EVENT = KVMI_VCPU_MESSAGE_ID(0),

KVMI_VCPU_GET_INFO = KVMI_VCPU_MESSAGE_ID(1),
KVMI_VCPU_CONTROL_EVENTS = KVMI_VCPU_MESSAGE_ID(2),
KVMI_VCPU_GET_REGISTERS = KVMI_VCPU_MESSAGE_ID(3),
KVMI_VCPU_SET_REGISTERS = KVMI_VCPU_MESSAGE_ID(4),
KVMI_VCPU_GET_CPUID = KVMI_VCPU_MESSAGE_ID(5),
KVMI_VCPU_CONTROL_CR = KVMI_VCPU_MESSAGE_ID(6),
KVMI_VCPU_GET_INFO = KVMI_VCPU_MESSAGE_ID(1),
KVMI_VCPU_CONTROL_EVENTS = KVMI_VCPU_MESSAGE_ID(2),
KVMI_VCPU_GET_REGISTERS = KVMI_VCPU_MESSAGE_ID(3),
KVMI_VCPU_SET_REGISTERS = KVMI_VCPU_MESSAGE_ID(4),
KVMI_VCPU_GET_CPUID = KVMI_VCPU_MESSAGE_ID(5),
KVMI_VCPU_CONTROL_CR = KVMI_VCPU_MESSAGE_ID(6),
KVMI_VCPU_INJECT_EXCEPTION = KVMI_VCPU_MESSAGE_ID(7),

KVMI_NEXT_VCPU_MESSAGE
};
Expand All @@ -60,6 +61,7 @@ enum {
KVMI_VCPU_EVENT_HYPERCALL = KVMI_VCPU_EVENT_ID(1),
KVMI_VCPU_EVENT_BREAKPOINT = KVMI_VCPU_EVENT_ID(2),
KVMI_VCPU_EVENT_CR = KVMI_VCPU_EVENT_ID(3),
KVMI_VCPU_EVENT_TRAP = KVMI_VCPU_EVENT_ID(4),

KVMI_NEXT_VCPU_EVENT
};
Expand Down

0 comments on commit 248beb9

Please sign in to comment.