@@ -612,6 +612,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
======
@@ -33,6 +33,9 @@ struct kvm_vcpu_introspection {
bool waiting_for_reply;
unsigned long *ev_enable_mask;
+
+ struct kvm_regs delayed_regs;
+ bool have_delayed_regs;
};
struct kvm_introspection {
@@ -29,6 +29,7 @@ enum {
KVMI_VCPU_PAUSE = 9,
KVMI_VCPU_CONTROL_EVENTS = 10,
KVMI_VCPU_GET_REGISTERS = 11,
+ KVMI_VCPU_SET_REGISTERS = 12,
KVMI_NUM_MESSAGES
};
@@ -961,6 +961,78 @@ static void test_cmd_vcpu_get_registers(struct kvm_vm *vm)
test_invalid_vcpu_get_registers(vm);
}
+static int __cmd_vcpu_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 = {};
+
+ memcpy(&req.regs, regs, sizeof(req.regs));
+
+ return __do_vcpu0_command(KVMI_VCPU_SET_REGISTERS,
+ &req.hdr, sizeof(req), NULL, 0);
+}
+
+static void test_invalid_cmd_vcpu_set_registers(struct kvm_vm *vm)
+{
+ struct vcpu_worker_data data = {.vm = vm, .vcpu_id = VCPU_ID};
+ pthread_t vcpu_thread;
+ struct kvm_regs regs;
+ int r;
+
+ vcpu_thread = start_vcpu_worker(&data);
+
+ r = __cmd_vcpu_set_registers(vm, ®s);
+
+ stop_vcpu_worker(vcpu_thread, &data);
+
+ TEST_ASSERT(r == -KVM_EOPNOTSUPP,
+ "KVMI_VCPU_SET_REGISTERS didn't failed with KVM_EOPNOTSUPP, error %d(%s)\n",
+ -r, kvm_strerror(-r));
+}
+
+static void __set_registers(struct kvm_vm *vm,
+ struct kvm_regs *regs)
+{
+ int r;
+
+ r = __cmd_vcpu_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 = {};
+
+ cmd_vcpu_get_registers(vm, ®s);
+
+ test_invalid_cmd_vcpu_set_registers(vm);
+
+ pause_vcpu();
+
+ 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));
@@ -979,6 +1051,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);
}
@@ -861,3 +861,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;
+ }
+}
@@ -40,6 +40,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,
@@ -52,6 +53,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,
@@ -437,6 +437,18 @@ static int handle_vcpu_get_registers(const struct kvmi_vcpu_msg_job *job,
return err;
}
+static int handle_vcpu_set_registers(const struct kvmi_vcpu_msg_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 functions are executed from the vCPU thread. The receiving thread
* passes the messages using a newly allocated 'struct kvmi_vcpu_msg_job'
@@ -449,6 +461,7 @@ static int(*const msg_vcpu[])(const struct kvmi_vcpu_msg_job *,
[KVMI_VCPU_CONTROL_EVENTS] = handle_vcpu_control_events,
[KVMI_VCPU_GET_INFO] = handle_vcpu_get_info,
[KVMI_VCPU_GET_REGISTERS] = handle_vcpu_get_registers,
+ [KVMI_VCPU_SET_REGISTERS] = handle_vcpu_set_registers,
};
static bool is_vcpu_command(u16 id)
@@ -743,8 +756,10 @@ static int kvmi_send_event(struct kvm_vcpu *vcpu, u32 ev_id,
err = vcpui->reply.error;
- if (!err)
+ if (!err) {
+ kvmi_post_reply(vcpu);
*action = vcpui->reply.action;
+ }
out:
if (err)