@@ -601,6 +601,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
======
@@ -5,6 +5,8 @@
#include <asm/kvmi.h>
struct kvm_vcpu_arch_introspection {
+ struct kvm_regs delayed_regs;
+ bool have_delayed_regs;
};
struct kvm_arch_introspection {
@@ -118,3 +118,25 @@ int kvmi_arch_cmd_vcpu_get_registers(struct kvm_vcpu *vcpu,
return err ? -KVM_EINVAL : 0;
}
+
+void kvmi_arch_cmd_vcpu_set_registers(struct kvm_vcpu *vcpu,
+ const struct kvm_regs *regs)
+{
+ struct kvm_vcpu_introspection *vcpui = VCPUI(vcpu);
+ struct kvm_regs *dest = &vcpui->arch.delayed_regs;
+
+ memcpy(dest, regs, sizeof(*dest));
+
+ vcpui->arch.have_delayed_regs = true;
+}
+
+void kvmi_arch_post_reply(struct kvm_vcpu *vcpu)
+{
+ struct kvm_vcpu_introspection *vcpui = VCPUI(vcpu);
+
+ if (!vcpui->arch.have_delayed_regs)
+ return;
+
+ kvm_arch_vcpu_set_regs(vcpu, &vcpui->arch.delayed_regs, false);
+ vcpui->arch.have_delayed_regs = false;
+}
@@ -5,5 +5,7 @@
int kvmi_arch_cmd_vcpu_get_registers(struct kvm_vcpu *vcpu,
const struct kvmi_vcpu_get_registers *req,
struct kvmi_vcpu_get_registers_reply *rpl);
+void kvmi_arch_cmd_vcpu_set_registers(struct kvm_vcpu *vcpu,
+ const struct kvm_regs *regs);
#endif
@@ -90,9 +90,27 @@ 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 = 0;
+
+ if (msg->size < sizeof(*regs))
+ ec = -KVM_EINVAL;
+ else if (!VCPUI(job->vcpu)->waiting_for_reply)
+ ec = -KVM_EOPNOTSUPP;
+ else
+ kvmi_arch_cmd_vcpu_set_registers(job->vcpu, regs);
+
+ return kvmi_msg_vcpu_reply(job, msg, ec, NULL, 0);
+}
+
static kvmi_vcpu_msg_job_fct const msg_vcpu[] = {
[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_msg_job_fct kvmi_arch_vcpu_msg_handler(u16 id)
@@ -38,6 +38,7 @@ enum {
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_NEXT_VCPU_MESSAGE
};
@@ -638,6 +638,16 @@ static int do_vcpu_command(struct kvm_vm *vm, int cmd_id,
return r;
}
+static int __do_vcpu0_command(int cmd_id, struct kvmi_msg_hdr *req,
+ size_t req_size, void *rpl, size_t rpl_size)
+{
+ struct kvmi_vcpu_hdr *vcpu_hdr = (struct kvmi_vcpu_hdr *)(req + 1);
+
+ vcpu_hdr->vcpu = 0;
+
+ return do_command(cmd_id, req, req_size, rpl, rpl_size);
+}
+
static int do_vcpu0_command(struct kvm_vm *vm, int cmd_id,
struct kvmi_msg_hdr *req, size_t req_size,
void *rpl, size_t rpl_size)
@@ -866,6 +876,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);
+
+ wait_vcpu_worker(vcpu_thread);
+
+ 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_VCPU_EVENT_PAUSE;
+ struct kvmi_msg_hdr hdr;
+ pthread_t vcpu_thread;
+ struct vcpu_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_vcpu_event(&hdr, &ev, sizeof(ev), event_id);
+
+ __set_registers(vm, &ev.common.arch.regs);
+
+ reply_to_event(&hdr, &ev, KVMI_EVENT_ACTION_CONTINUE,
+ &rpl, sizeof(rpl));
+
+ wait_vcpu_worker(vcpu_thread);
+}
+
static void test_introspection(struct kvm_vm *vm)
{
srandom(time(0));
@@ -884,6 +966,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);
}
@@ -70,5 +70,6 @@ void kvmi_arch_init_vcpu_events_mask(unsigned long *supported);
kvmi_vcpu_msg_job_fct kvmi_arch_vcpu_msg_handler(u16 id);
void kvmi_arch_setup_vcpu_event(struct kvm_vcpu *vcpu,
struct kvmi_vcpu_event *ev);
+void kvmi_arch_post_reply(struct kvm_vcpu *vcpu);
#endif
@@ -717,10 +717,12 @@ int kvmi_send_vcpu_event(struct kvm_vcpu *vcpu, u32 ev_id,
err = vcpui->reply.error;
out:
- if (err)
+ if (err) {
kvmi_sock_shutdown(kvmi);
- else
+ } else {
+ kvmi_arch_post_reply(vcpu);
*action = vcpui->reply.action;
+ }
return err;
}