@@ -608,6 +608,35 @@ registers, the special registers and the requested set of MSRs.
* -KVM_EAGAIN - the selected vCPU can't be introspected yet
* -KVM_ENOMEM - there is not enough memory to allocate the reply
+12. KVMI_VCPU_SET_REGISTERS
+---------------------------
+
+:Architectures: x86
+:Versions: >= 1
+:Parameters:
+
+::
+
+ struct kvmi_vcpu_hdr;
+ struct kvm_regs;
+
+:Returns:
+
+::
+
+ struct kvmi_error_code
+
+Sets the general purpose registers for the given vCPU. The changes become
+visible to other threads accessing the KVM vCPU structure after the event
+currently being handled is replied to.
+
+:Errors:
+
+* -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_EOPNOTSUPP - the command hasn't been received during an introspection event
+
Events
======
@@ -38,6 +38,9 @@ struct kvm_vcpu_introspection {
bool waiting_for_reply;
DECLARE_BITMAP(ev_enable_mask, KVMI_NUM_EVENTS);
+
+ struct kvm_regs delayed_regs;
+ bool have_delayed_regs;
};
struct kvm_introspection {
@@ -29,6 +29,7 @@ enum {
KVMI_VCPU_PAUSE = 10,
KVMI_VCPU_CONTROL_EVENTS = 11,
KVMI_VCPU_GET_REGISTERS = 12,
+ KVMI_VCPU_SET_REGISTERS = 13,
KVMI_NUM_MESSAGES
};
@@ -832,6 +832,83 @@ static void test_cmd_vcpu_get_registers(struct kvm_vm *vm)
DEBUG("get_registers rip 0x%llx\n", regs.rip);
}
+static int __cmd_set_registers(struct kvm_vm *vm,
+ struct kvm_regs *regs)
+{
+ struct {
+ struct kvmi_msg_hdr hdr;
+ struct kvmi_vcpu_hdr vcpu_hdr;
+ struct kvm_regs regs;
+ } req = {};
+ __u16 vcpu_index = 0;
+
+ req.vcpu_hdr.vcpu = vcpu_index;
+
+ memcpy(&req.regs, regs, sizeof(req.regs));
+
+ return do_command(KVMI_VCPU_SET_REGISTERS,
+ &req.hdr, sizeof(req), NULL, 0);
+}
+
+static int cmd_set_registers(struct kvm_vm *vm,
+ struct kvm_regs *regs)
+{
+ struct vcpu_worker_data data = {.vm = vm, .vcpu_id = VCPU_ID};
+ pthread_t vcpu_thread;
+ int r;
+
+ vcpu_thread = start_vcpu_worker(&data);
+
+ r = __cmd_set_registers(vm, regs);
+
+ stop_vcpu_worker(vcpu_thread, &data);
+
+ return r;
+}
+
+static void __set_registers(struct kvm_vm *vm,
+ struct kvm_regs *regs)
+{
+ int r;
+
+ r = __cmd_set_registers(vm, regs);
+ TEST_ASSERT(r == 0,
+ "KVMI_VCPU_SET_REGISTERS failed, error %d(%s)\n",
+ -r, kvm_strerror(-r));
+}
+
+static void test_cmd_vcpu_set_registers(struct kvm_vm *vm)
+{
+ struct vcpu_worker_data data = {.vm = vm, .vcpu_id = VCPU_ID};
+ __u16 event_id = KVMI_EVENT_PAUSE_VCPU;
+ struct kvmi_msg_hdr hdr;
+ pthread_t vcpu_thread;
+ struct kvmi_event ev;
+ struct vcpu_reply rpl = {};
+ struct kvm_regs regs = {};
+ int r;
+
+ get_vcpu_registers(vm, ®s);
+
+ r = cmd_set_registers(vm, ®s);
+ TEST_ASSERT(r == -KVM_EOPNOTSUPP,
+ "KVMI_VCPU_SET_REGISTERS didn't failed with KVM_EOPNOTSUPP, error %d(%s)\n",
+ -r, kvm_strerror(-r));
+
+ pause_vcpu(vm);
+
+ vcpu_thread = start_vcpu_worker(&data);
+
+ receive_event(&hdr, &ev, sizeof(ev), event_id);
+
+ __set_registers(vm, &ev.arch.regs);
+
+ reply_to_event(&hdr, &ev, KVMI_EVENT_ACTION_CONTINUE,
+ &rpl, sizeof(rpl));
+
+ stop_vcpu_worker(vcpu_thread, &data);
+}
+
static void test_introspection(struct kvm_vm *vm)
{
srandom(time(0));
@@ -850,6 +927,7 @@ static void test_introspection(struct kvm_vm *vm)
test_pause(vm);
test_cmd_vcpu_control_events(vm);
test_cmd_vcpu_get_registers(vm);
+ test_cmd_vcpu_set_registers(vm);
unhook_introspection(vm);
}
@@ -815,3 +815,27 @@ int kvmi_cmd_vcpu_pause(struct kvm_vcpu *vcpu, bool wait)
return 0;
}
+
+int kvmi_cmd_vcpu_set_registers(struct kvm_vcpu *vcpu,
+ const struct kvm_regs *regs)
+{
+ struct kvm_vcpu_introspection *vcpui = VCPUI(vcpu);
+
+ if (!vcpui->waiting_for_reply)
+ return -KVM_EOPNOTSUPP;
+
+ memcpy(&vcpui->delayed_regs, regs, sizeof(vcpui->delayed_regs));
+ vcpui->have_delayed_regs = true;
+
+ return 0;
+}
+
+void kvmi_post_reply(struct kvm_vcpu *vcpu)
+{
+ struct kvm_vcpu_introspection *vcpui = VCPUI(vcpu);
+
+ if (vcpui->have_delayed_regs) {
+ kvm_arch_vcpu_set_regs(vcpu, &vcpui->delayed_regs, false);
+ vcpui->have_delayed_regs = false;
+ }
+}
@@ -39,6 +39,7 @@ int kvmi_add_job(struct kvm_vcpu *vcpu,
void (*fct)(struct kvm_vcpu *vcpu, void *ctx),
void *ctx, void (*free_fct)(void *ctx));
void kvmi_run_jobs(struct kvm_vcpu *vcpu);
+void kvmi_post_reply(struct kvm_vcpu *vcpu);
int kvmi_cmd_vm_control_events(struct kvm_introspection *kvmi,
unsigned int event_id, bool enable);
int kvmi_cmd_vcpu_control_events(struct kvm_vcpu *vcpu,
@@ -51,6 +52,8 @@ int kvmi_cmd_read_physical(struct kvm *kvm, u64 gpa, size_t size,
int kvmi_cmd_write_physical(struct kvm *kvm, u64 gpa, size_t size,
const void *buf);
int kvmi_cmd_vcpu_pause(struct kvm_vcpu *vcpu, bool wait);
+int kvmi_cmd_vcpu_set_registers(struct kvm_vcpu *vcpu,
+ const struct kvm_regs *regs);
/* arch */
int kvmi_arch_cmd_vcpu_get_info(struct kvm_vcpu *vcpu,
@@ -32,6 +32,7 @@ static const char *const msg_IDs[] = {
[KVMI_VCPU_GET_INFO] = "KVMI_VCPU_GET_INFO",
[KVMI_VCPU_GET_REGISTERS] = "KVMI_VCPU_GET_REGISTERS",
[KVMI_VCPU_PAUSE] = "KVMI_VCPU_PAUSE",
+ [KVMI_VCPU_SET_REGISTERS] = "KVMI_VCPU_SET_REGISTERS",
};
static const char *id2str(u16 id)
@@ -486,6 +487,18 @@ static int handle_get_registers(const struct kvmi_vcpu_cmd_job *job,
return err;
}
+static int handle_set_registers(const struct kvmi_vcpu_cmd_job *job,
+ const struct kvmi_msg_hdr *msg,
+ const void *_req)
+{
+ const struct kvm_regs *regs = _req;
+ int ec;
+
+ ec = kvmi_cmd_vcpu_set_registers(job->vcpu, regs);
+
+ return kvmi_msg_vcpu_reply(job, msg, ec, NULL, 0);
+}
+
/*
* These commands are executed from the vCPU thread. The receiving thread
* passes the messages using a newly allocated 'struct kvmi_vcpu_cmd_job'
@@ -498,6 +511,7 @@ static int(*const msg_vcpu[])(const struct kvmi_vcpu_cmd_job *,
[KVMI_VCPU_CONTROL_EVENTS] = handle_vcpu_control_events,
[KVMI_VCPU_GET_INFO] = handle_get_vcpu_info,
[KVMI_VCPU_GET_REGISTERS] = handle_get_registers,
+ [KVMI_VCPU_SET_REGISTERS] = handle_set_registers,
};
static bool is_vcpu_command(u16 id)
@@ -787,6 +801,7 @@ static int kvmi_send_event(struct kvm_vcpu *vcpu, u32 ev_id,
if (err)
goto out;
+ kvmi_post_reply(vcpu);
*action = vcpui->reply.action;
out: