@@ -618,6 +618,42 @@ currently being handled is replied to.
* -KVM_EAGAIN - the selected vCPU can't be introspected yet
* -KVM_EOPNOTSUPP - the command hasn't been received during an introspection event
+13. KVMI_VCPU_GET_CPUID
+-----------------------
+
+:Architectures: x86
+:Versions: >= 1
+:Parameters:
+
+::
+
+ struct kvmi_vcpu_hdr;
+ struct kvmi_vcpu_get_cpuid {
+ __u32 function;
+ __u32 index;
+ };
+
+:Returns:
+
+::
+
+ struct kvmi_error_code;
+ struct kvmi_vcpu_get_cpuid_reply {
+ __u32 eax;
+ __u32 ebx;
+ __u32 ecx;
+ __u32 edx;
+ };
+
+Returns a CPUID leaf (as seen by the guest OS).
+
+:Errors:
+
+* -KVM_EINVAL - the selected vCPU is invalid
+* -KVM_EINVAL - padding is not zero
+* -KVM_EAGAIN - the selected vCPU can't be introspected yet
+* -KVM_ENOENT - the selected leaf is not present or is invalid
+
Events
======
@@ -45,4 +45,16 @@ struct kvmi_vcpu_get_registers_reply {
struct kvm_msrs msrs;
};
+struct kvmi_vcpu_get_cpuid {
+ __u32 function;
+ __u32 index;
+};
+
+struct kvmi_vcpu_get_cpuid_reply {
+ __u32 eax;
+ __u32 ebx;
+ __u32 ecx;
+ __u32 edx;
+};
+
#endif /* _UAPI_ASM_X86_KVMI_H */
@@ -7,6 +7,7 @@
#include "linux/kvm_host.h"
#include "x86.h"
+#include "cpuid.h"
#include "../../../virt/kvm/introspection/kvmi_int.h"
static unsigned int kvmi_vcpu_mode(const struct kvm_vcpu *vcpu,
@@ -140,3 +141,21 @@ int kvmi_arch_cmd_vcpu_get_registers(struct kvm_vcpu *vcpu,
return err;
}
+
+int kvmi_arch_cmd_vcpu_get_cpuid(struct kvm_vcpu *vcpu,
+ const struct kvmi_vcpu_get_cpuid *req,
+ struct kvmi_vcpu_get_cpuid_reply *rpl)
+{
+ struct kvm_cpuid_entry2 *e;
+
+ e = kvm_find_cpuid_entry(vcpu, req->function, req->index);
+ if (!e)
+ return -KVM_ENOENT;
+
+ rpl->eax = e->eax;
+ rpl->ebx = e->ebx;
+ rpl->ecx = e->ecx;
+ rpl->edx = e->edx;
+
+ return 0;
+}
@@ -31,6 +31,7 @@ enum {
KVMI_VCPU_CONTROL_EVENTS = 11,
KVMI_VCPU_GET_REGISTERS = 12,
KVMI_VCPU_SET_REGISTERS = 13,
+ KVMI_VCPU_GET_CPUID = 14,
KVMI_NUM_MESSAGES
};
@@ -909,6 +909,39 @@ static void test_cmd_vcpu_set_registers(struct kvm_vm *vm)
disable_vcpu_event(vm, event_id);
}
+static int cmd_get_cpuid(struct kvm_vm *vm,
+ __u32 function, __u32 index,
+ struct kvmi_vcpu_get_cpuid_reply *rpl)
+{
+ struct {
+ struct kvmi_msg_hdr hdr;
+ struct kvmi_vcpu_hdr vcpu_hdr;
+ struct kvmi_vcpu_get_cpuid cmd;
+ } req = {};
+
+ req.cmd.function = function;
+ req.cmd.index = index;
+
+ return do_vcpu0_command(vm, KVMI_VCPU_GET_CPUID, &req.hdr, sizeof(req),
+ rpl, sizeof(*rpl));
+}
+
+static void test_cmd_vcpu_get_cpuid(struct kvm_vm *vm)
+{
+ struct kvmi_vcpu_get_cpuid_reply rpl = {};
+ __u32 function = 0;
+ __u32 index = 0;
+ int r;
+
+ r = cmd_get_cpuid(vm, function, index, &rpl);
+ TEST_ASSERT(r == 0,
+ "KVMI_VCPU_GET_CPUID failed, error %d(%s)\n",
+ -r, kvm_strerror(-r));
+
+ DEBUG("cpuid(%u, %u) => eax 0x%.8x, ebx 0x%.8x, ecx 0x%.8x, edx 0x%.8x\n",
+ function, index, rpl.eax, rpl.ebx, rpl.ecx, rpl.edx);
+}
+
static void test_introspection(struct kvm_vm *vm)
{
setup_socket();
@@ -927,6 +960,7 @@ static void test_introspection(struct kvm_vm *vm)
test_cmd_vcpu_control_events(vm);
test_cmd_vcpu_get_registers(vm);
test_cmd_vcpu_set_registers(vm);
+ test_cmd_vcpu_get_cpuid(vm);
unhook_introspection(vm);
}
@@ -38,6 +38,7 @@
| BIT(KVMI_VCPU_GET_INFO) \
| BIT(KVMI_VCPU_PAUSE) \
| BIT(KVMI_VCPU_CONTROL_EVENTS) \
+ | BIT(KVMI_VCPU_GET_CPUID) \
| BIT(KVMI_VCPU_GET_REGISTERS) \
| BIT(KVMI_VCPU_SET_REGISTERS) \
)
@@ -92,5 +93,8 @@ int kvmi_arch_cmd_vcpu_get_registers(struct kvm_vcpu *vcpu,
const struct kvmi_vcpu_get_registers *req,
struct kvmi_vcpu_get_registers_reply **dest,
size_t *dest_size);
+int kvmi_arch_cmd_vcpu_get_cpuid(struct kvm_vcpu *vcpu,
+ const struct kvmi_vcpu_get_cpuid *req,
+ struct kvmi_vcpu_get_cpuid_reply *rpl);
#endif
@@ -26,6 +26,7 @@ static const char *const msg_IDs[] = {
[KVMI_VM_READ_PHYSICAL] = "KVMI_VM_READ_PHYSICAL",
[KVMI_VM_WRITE_PHYSICAL] = "KVMI_VM_WRITE_PHYSICAL",
[KVMI_VCPU_CONTROL_EVENTS] = "KVMI_VCPU_CONTROL_EVENTS",
+ [KVMI_VCPU_GET_CPUID] = "KVMI_VCPU_GET_CPUID",
[KVMI_VCPU_GET_INFO] = "KVMI_VCPU_GET_INFO",
[KVMI_VCPU_GET_REGISTERS] = "KVMI_VCPU_GET_REGISTERS",
[KVMI_VCPU_PAUSE] = "KVMI_VCPU_PAUSE",
@@ -464,6 +465,20 @@ static int handle_set_registers(const struct kvmi_vcpu_cmd_job *job,
return kvmi_msg_vcpu_reply(job, msg, ec, NULL, 0);
}
+static int handle_get_cpuid(const struct kvmi_vcpu_cmd_job *job,
+ const struct kvmi_msg_hdr *msg,
+ const void *req)
+{
+ struct kvmi_vcpu_get_cpuid_reply rpl;
+ int ec;
+
+ memset(&rpl, 0, sizeof(rpl));
+
+ ec = kvmi_arch_cmd_vcpu_get_cpuid(job->vcpu, req, &rpl);
+
+ return kvmi_msg_vcpu_reply(job, msg, ec, &rpl, sizeof(rpl));
+}
+
/*
* These commands are executed on the vCPU thread. The receiving thread
* passes the messages using a newly allocated 'struct kvmi_vcpu_cmd_job'
@@ -474,6 +489,7 @@ static int(*const msg_vcpu[])(const struct kvmi_vcpu_cmd_job *,
const struct kvmi_msg_hdr *, const void *) = {
[KVMI_EVENT_REPLY] = handle_event_reply,
[KVMI_VCPU_CONTROL_EVENTS] = handle_vcpu_control_events,
+ [KVMI_VCPU_GET_CPUID] = handle_get_cpuid,
[KVMI_VCPU_GET_INFO] = handle_get_vcpu_info,
[KVMI_VCPU_GET_REGISTERS] = handle_get_registers,
[KVMI_VCPU_SET_REGISTERS] = handle_set_registers,