On Tue, Jan 15, 2008 at 08:57:45AM +0100, Alexander Graf wrote:
> Dan Kenigsberg wrote:
> > On Mon, Jan 14, 2008 at 02:49:31PM +0100, Alexander Graf wrote:
> >
> >> Hi,
> >>
> >> Currently CPUID function 4 is broken. This function's values rely on the
> >> value of ECX.
> >> To solve the issue cleanly, there is already a new API for cpuid
> >> settings, which is not used yet.
> >> Using the current interface, the function 4 can be easily passed
> >> through, by giving multiple function 4 outputs and increasing the
> >> index-identifier on the fly. This does not break compatibility.
> >>
> >> This fix is really important for Mac OS X, as it requires cache
> >> information. Please also see my previous patches for Mac OS X (or rather
> >> core duo target) compatibility.
> >>
> >> Regards,
> >>
> >> Alex
> >>
> >
> >
> >> diff --git a/kernel/x86.c b/kernel/x86.c
> >> index b55c177..73312e9 100644
> >> --- a/kernel/x86.c
> >> +++ b/kernel/x86.c
> >> @@ -783,7 +783,7 @@ static int kvm_vcpu_ioctl_set_cpuid(struct kvm_vcpu
> >> *vcpu,
> >> struct kvm_cpuid *cpuid,
> >> struct kvm_cpuid_entry __user *entries)
> >> {
> >> - int r, i;
> >> + int r, i, n = 0;
> >> struct kvm_cpuid_entry *cpuid_entries;
> >>
> >> r = -E2BIG;
> >> @@ -803,8 +803,17 @@ static int kvm_vcpu_ioctl_set_cpuid(struct kvm_vcpu
> >> *vcpu,
> >> vcpu->arch.cpuid_entries[i].ebx = cpuid_entries[i].ebx;
> >> vcpu->arch.cpuid_entries[i].ecx = cpuid_entries[i].ecx;
> >> vcpu->arch.cpuid_entries[i].edx = cpuid_entries[i].edx;
> >> - vcpu->arch.cpuid_entries[i].index = 0;
> >> - vcpu->arch.cpuid_entries[i].flags = 0;
> >> + switch(vcpu->arch.cpuid_entries[i].function) {
> >> + case 4:
> >> + vcpu->arch.cpuid_entries[i].index = n;
> >> + vcpu->arch.cpuid_entries[i].flags =
> >> KVM_CPUID_FLAG_SIGNIFCANT_INDEX;
> >> + n++;
> >> + break;
> >> + default:
> >> + vcpu->arch.cpuid_entries[i].index = 0;
> >> + vcpu->arch.cpuid_entries[i].flags = 0;
> >> + break;
> >> + }
> >>
> >
> > I will not mention the whitespace damage here :-). Instead, I'd ask you
> >
> Oh well, after having been into qemu source, I just got used to use
> spaces instead of tabs ;-).
>
> > to review, comment, and even try, the patch that I posted here not long
> > ago, exposing all safe host cpuid functions to guests.
> >
> Sure.
> Basically your patch targets at a completely different use case than
> mine though. You want to expose the host features on the virtual CPU,
> whereas my goal is to have a virtual Core Duo/Solo CPU, even if your
> host CPU is actually an SVM capable one.
>
> So my CoreDuo CPU definition still fails to populate a proper CPUID
> function 4. With the -cpu host option, Linux works (as it's bright
> enough to know that some values are just plain wrong), but Darwin
> crashes. I am not exactly sure why it is, but I guess it's due to the
> function 4 values exposing a 2-core CPU, which kvm simply doesn't emulate.
What I wanted to say is that the fact that the usermode support is not
used, is not IMHO a good-enough reason to change the kernel:
kvm_vcpu_ioctl_set_cpuid() was ment to be a stupid function, to be used
only with old usermode. I hate to teach it the true complex logic of Intel's
CPUID.
What I would like to see is something that uses the cpuid2 API, and not
circumvene it... For this to happen, I need a deep review of my code.
How about the (untested) attched kvm-cpuid.patch, on top of the attached
cpuid-user patch?
Regards,
Dan.
>From a8b795151e225452745dcd9831b828c06dff438c Mon Sep 17 00:00:00 2001
From: Dan Kenigsberg <[EMAIL PROTECTED]>
Date: Tue, 15 Jan 2008 15:31:19 +0200
Subject: [PATCH] Add -cpu host option. Expose to guest the CPUID that is
supported by the
host (or specifically requested in the command line).
Note that the default cpu is now `host' instead of `qemu64'
Signed-off-by: Dan Kenigsberg <[EMAIL PROTECTED]>
---
libkvm/libkvm-x86.c | 97 +++++++++++++++++++++++++++++++++++++++--
libkvm/libkvm.h | 44 ++++++++++++++++++-
qemu/hw/pc.c | 4 ++
qemu/qemu-kvm-x86.c | 103 ++++++++++++++++++++++++++++++++++++-------
qemu/target-i386/cpu.h | 3 +
qemu/target-i386/helper2.c | 35 +++++++++++++++
6 files changed, 264 insertions(+), 22 deletions(-)
diff --git a/libkvm/libkvm-x86.c b/libkvm/libkvm-x86.c
index 2fa8146..e9a8113 100644
--- a/libkvm/libkvm-x86.c
+++ b/libkvm/libkvm-x86.c
@@ -485,20 +485,109 @@ __u64 kvm_get_cr8(kvm_context_t kvm, int vcpu)
}
int kvm_setup_cpuid(kvm_context_t kvm, int vcpu, int nent,
- struct kvm_cpuid_entry *entries)
+ struct kvm_cpuid_entry2 *entries)
{
- struct kvm_cpuid *cpuid;
- int r;
+ int r = -ENOSYS;
+#ifdef KVM_CAP_EXT_CPUID
+ r = ioctl(kvm->fd, KVM_CHECK_EXTENSION, KVM_CAP_EXT_CPUID);
+ if (r <= 0)
+#endif
+ { /* kernel has no KVM_SET_CPUID2 */
+ int i;
+ struct kvm_cpuid *cpuid;
+
+ cpuid = malloc(sizeof(*cpuid) +
+ nent * sizeof(struct kvm_cpuid_entry));
+ if (!cpuid)
+ return -ENOMEM;
+
+ cpuid->nent = nent;
+ for (i = 0; i < nent; ++i) {
+ cpuid->entries[i].function = entries[i].function;
+ cpuid->entries[i].eax = entries[i].eax;
+ cpuid->entries[i].ebx = entries[i].ebx;
+ cpuid->entries[i].ecx = entries[i].ecx;
+ cpuid->entries[i].edx = entries[i].edx;
+ }
+ r = ioctl(kvm->vcpu_fd[vcpu], KVM_SET_CPUID, cpuid);
+ if (r)
+ r = -errno;
+
+ free(cpuid);
+ }
+#ifdef KVM_CAP_EXT_CPUID
+ else {
+ struct kvm_cpuid2 *cpuid;
cpuid = malloc(sizeof(*cpuid) + nent * sizeof(*entries));
if (!cpuid)
return -ENOMEM;
cpuid->nent = nent;
memcpy(cpuid->entries, entries, nent * sizeof(*entries));
- r = ioctl(kvm->vcpu_fd[vcpu], KVM_SET_CPUID, cpuid);
+ r = ioctl(kvm->vcpu_fd[vcpu], KVM_SET_CPUID2, cpuid);
+ if (r)
+ r = -errno;
+
+ free(cpuid);
+ }
+#endif
+ return r;
+}
+
+int kvm_get_supported_cpuid(kvm_context_t kvm, int *nent,
+ struct kvm_cpuid_entry2 *entries)
+{
+ struct kvm_cpuid2 *cpuid;
+ int r = -ENOSYS;
+
+#ifdef KVM_CAP_EXT_CPUID
+ r = ioctl(kvm->fd, KVM_CHECK_EXTENSION, KVM_CAP_EXT_CPUID);
+ if (r <= 0)
+ return -ENOSYS;
+
+ cpuid = malloc(sizeof(*cpuid) + *nent * sizeof(*entries));
+ if (!cpuid)
+ return -ENOMEM;
+ cpuid->nent = *nent;
+ r = ioctl(kvm->vm_fd, KVM_GET_SUPPORTED_CPUID, cpuid);
+ if (r)
+ r = -errno;
+ else {
+ memcpy(entries, cpuid->entries, *nent * sizeof(*entries));
+ *nent = cpuid->nent;
+ }
free(cpuid);
+#endif
+ return r;
+}
+
+int kvm_get_cpuid(kvm_context_t kvm, int vcpu, int *nent,
+ struct kvm_cpuid_entry2 *entries)
+{
+ struct kvm_cpuid2 *cpuid;
+ int r = -ENOSYS;
+
+#ifdef KVM_CAP_EXT_CPUID
+ r = ioctl(kvm->fd, KVM_CHECK_EXTENSION, KVM_CAP_EXT_CPUID);
+ if (r <= 0)
+ return -ENOSYS;
+
+ cpuid = malloc(sizeof(*cpuid) + *nent * sizeof(*entries));
+ if (!cpuid)
+ return -ENOMEM;
+ cpuid->nent = *nent;
+
+ r = ioctl(kvm->vcpu_fd[vcpu], KVM_GET_CPUID2, cpuid);
+ if (r)
+ r = -errno;
+ else {
+ memcpy(entries, cpuid->entries, *nent * sizeof(*entries));
+ *nent = cpuid->nent;
+ }
+ free(cpuid);
+#endif
return r;
}
diff --git a/libkvm/libkvm.h b/libkvm/libkvm.h
index 2574abe..920c4b1 100644
--- a/libkvm/libkvm.h
+++ b/libkvm/libkvm.h
@@ -305,6 +305,19 @@ int kvm_inject_irq(kvm_context_t kvm, int vcpu, unsigned
irq);
int kvm_guest_debug(kvm_context_t, int vcpu, struct kvm_debug_guest *dbg);
#if defined(__i386__) || defined(__x86_64__)
+#ifndef KVM_CAP_EXT_CPUID
+/* for compilation against old kernels */
+struct kvm_cpuid_entry2 {
+ __u32 function;
+ __u32 index;
+ __u32 flags;
+ __u32 eax;
+ __u32 ebx;
+ __u32 ecx;
+ __u32 edx;
+ __u32 padding[3];
+};
+#endif
/*!
* \brief Setup a vcpu's cpuid instruction emulation
*
@@ -317,7 +330,36 @@ int kvm_guest_debug(kvm_context_t, int vcpu, struct
kvm_debug_guest *dbg);
* \return 0 on success, or -errno on error
*/
int kvm_setup_cpuid(kvm_context_t kvm, int vcpu, int nent,
- struct kvm_cpuid_entry *entries);
+ struct kvm_cpuid_entry2 *entries);
+
+/*!
+ * \brief Obtain vcpu's cpuid table
+ *
+ * Get the currently-held table of cpuid functions and their states.\n
+ *
+ * \param kvm Pointer to the current kvm_context
+ * \param vcpu Which virtual CPU should be initialized
+ * \param nent Pointer to the number of entries to be obtained.
+ * On return, the number that was actually obtained.
+ * \param entries cpuid function entries table
+ * \return 0 on success, or -errno on error
+ */
+int kvm_get_cpuid(kvm_context_t kvm, int vcpu, int *nent,
+ struct kvm_cpuid_entry2 *entries);
+
+/*!
+ * \brief Obtain cpuid supported by kvm
+ *
+ * Obtain a table of cpuid function supported by kvm.\n
+ *
+ * \param kvm Pointer to the current kvm_context
+ * \param nent Pointer to the number of entries to be obtained.
+ * On return, the number that was actually obtained.
+ * \param entries cpuid function entries table
+ * \return 0 on success, or -errno on error
+ */
+int kvm_get_supported_cpuid(kvm_context_t kvm, int *nent,
+ struct kvm_cpuid_entry2 *entries);
/*!
* \brief Setting the number of shadow pages to be allocated to the vm
diff --git a/qemu/hw/pc.c b/qemu/hw/pc.c
index 3972ab4..228b3ad 100644
--- a/qemu/hw/pc.c
+++ b/qemu/hw/pc.c
@@ -783,6 +783,10 @@ static void pc_init1(ram_addr_t ram_size, int vga_ram_size,
#else
cpu_model = "qemu32";
#endif
+#ifdef USE_KVM
+ if (kvm_allowed)
+ cpu_model = "host";
+#endif
}
for(i = 0; i < smp_cpus; i++) {
diff --git a/qemu/qemu-kvm-x86.c b/qemu/qemu-kvm-x86.c
index c79ca36..aa9120c 100644
--- a/qemu/qemu-kvm-x86.c
+++ b/qemu/qemu-kvm-x86.c
@@ -461,7 +461,7 @@ static void host_cpuid(uint32_t function, uint32_t *eax,
uint32_t *ebx,
}
-static void do_cpuid_ent(struct kvm_cpuid_entry *e, uint32_t function,
+static void do_cpuid_ent(struct kvm_cpuid_entry2 *e, uint32_t function,
CPUState *env)
{
env->regs[R_EAX] = function;
@@ -506,11 +506,28 @@ static void do_cpuid_ent(struct kvm_cpuid_entry *e,
uint32_t function,
}
}
+static struct kvm_cpuid_entry2 *cpuid_entry_lookup(struct kvm_cpuid_entry2
*entries,
+ int nent, uint32_t function, uint32_t index)
+{
+ int i;
+
+ for (i = 0; i < nent; ++i) {
+ struct kvm_cpuid_entry2 *e = &entries[i];
+ if (e->function == function &&
+ (!(e->flags & KVM_CPUID_FLAG_SIGNIFCANT_INDEX)
+ || e->index == index)) {
+ return e;
+ }
+ }
+ fprintf(stderr, "failed to find cpuid func %x index %x\n", function,
index);
+ return 0;
+}
+
int kvm_arch_qemu_init_env(CPUState *cenv)
{
- struct kvm_cpuid_entry cpuid_ent[100];
+ struct kvm_cpuid_entry2 cpuid_ent[100];
#ifdef KVM_CPUID_SIGNATURE
- struct kvm_cpuid_entry *pv_ent;
+ struct kvm_cpuid_entry2 *pv_ent;
uint32_t signature[3];
#endif
int cpuid_nent = 0;
@@ -519,6 +536,72 @@ int kvm_arch_qemu_init_env(CPUState *cenv)
copy = *cenv;
+ if (cenv->cpuid_hostlike) {
+ struct kvm_cpuid_entry2 *e;
+ int r;
+ extern struct hostlike_user_disable {
+ uint32_t features;
+ uint32_t ext_features;
+ uint32_t ext2_features;
+ uint32_t ext3_features;
+ } hostlike_user_disable;
+
+ cpuid_nent = sizeof(cpuid_ent) / sizeof(struct kvm_cpuid_entry2);
+ r = kvm_get_supported_cpuid(kvm_context, &cpuid_nent, cpuid_ent);
+ if (r) {
+ printf("failed to obtain supported cpuid from kvm module (%d)\n",
r);
+ exit(1);
+ }
+
+ e = cpuid_entry_lookup(cpuid_ent, cpuid_nent, 0, 0);
+ copy.cpuid_level = cenv->cpuid_level = e->eax;
+ e = cpuid_entry_lookup(cpuid_ent, cpuid_nent, 0x80000000, 0);
+ copy.cpuid_xlevel = cenv->cpuid_xlevel = e->eax;
+
+ /* override some of the advertized data with qemu's */
+ /* family-model-stepping */
+ e = cpuid_entry_lookup(cpuid_ent, cpuid_nent, 1, 0);
+ if (cenv->cpuid_version >> 8 == 0)
+ cenv->cpuid_version |= e->eax & 0xFF00;
+ if ((cenv->cpuid_version >> 4 & 0xF) == 0)
+ cenv->cpuid_version |= e->eax & 0xF0;
+ if ((cenv->cpuid_version & 0xF) == 0)
+ cenv->cpuid_version |= e->eax & 0xF;
+ e->eax = cenv->cpuid_version;
+ e->ecx = (e->ecx | cenv->cpuid_ext_features) &
~hostlike_user_disable.ext_features;
+ e->edx = (e->edx | cenv->cpuid_features) &
~hostlike_user_disable.features;
+
+ /* extended features */
+ e = cpuid_entry_lookup(cpuid_ent, cpuid_nent, 0x80000001, 0);
+ e->eax = (e->eax | cenv->cpuid_features) &
~hostlike_user_disable.features;
+ e->ecx = (e->ecx | cenv->cpuid_ext3_features) &
~hostlike_user_disable.ext3_features;
+ e->edx = (e->edx | cenv->cpuid_ext2_features) &
~hostlike_user_disable.ext2_features;
+
+ /* cpuid_model */
+ {
+ extern const char *cpu_vendor_string;
+ if (cpu_vendor_string)
+ for (i = 0x80000002; i <= 0x80000004; ++i) {
+ e = cpuid_entry_lookup(cpuid_ent, cpuid_nent, i, 0);
+ do_cpuid_ent(e, i, ©);
+ }
+ }
+ } else {
+ copy.regs[R_EAX] = 0;
+ qemu_kvm_cpuid_on_env(©);
+ limit = copy.regs[R_EAX];
+
+ for (i = 0; i <= limit; ++i)
+ do_cpuid_ent(&cpuid_ent[cpuid_nent++], i, ©);
+
+ copy.regs[R_EAX] = 0x80000000;
+ qemu_kvm_cpuid_on_env(©);
+ limit = copy.regs[R_EAX];
+
+ for (i = 0x80000000; i <= limit; ++i)
+ do_cpuid_ent(&cpuid_ent[cpuid_nent++], i, ©);
+ }
+
#ifdef KVM_CPUID_SIGNATURE
/* Paravirtualization CPUIDs */
memcpy(signature, "KVMKVMKVM", 12);
@@ -536,20 +619,6 @@ int kvm_arch_qemu_init_env(CPUState *cenv)
pv_ent->eax = 0;
#endif
- copy.regs[R_EAX] = 0;
- qemu_kvm_cpuid_on_env(©);
- limit = copy.regs[R_EAX];
-
- for (i = 0; i <= limit; ++i)
- do_cpuid_ent(&cpuid_ent[cpuid_nent++], i, ©);
-
- copy.regs[R_EAX] = 0x80000000;
- qemu_kvm_cpuid_on_env(©);
- limit = copy.regs[R_EAX];
-
- for (i = 0x80000000; i <= limit; ++i)
- do_cpuid_ent(&cpuid_ent[cpuid_nent++], i, ©);
-
kvm_setup_cpuid(kvm_context, cenv->cpu_index, cpuid_nent, cpuid_ent);
return 0;
}
diff --git a/qemu/target-i386/cpu.h b/qemu/target-i386/cpu.h
index 7143ab3..2034381 100644
--- a/qemu/target-i386/cpu.h
+++ b/qemu/target-i386/cpu.h
@@ -588,6 +588,9 @@ typedef struct CPUX86State {
uint32_t cpuid_ext2_features;
uint32_t cpuid_ext3_features;
uint32_t cpuid_apic_id;
+#ifdef USE_KVM
+ int cpuid_hostlike;
+#endif
#ifdef USE_KQEMU
int kqemu_enabled;
diff --git a/qemu/target-i386/helper2.c b/qemu/target-i386/helper2.c
index ac663aa..f637d82 100644
--- a/qemu/target-i386/helper2.c
+++ b/qemu/target-i386/helper2.c
@@ -123,6 +123,15 @@ CPUX86State *cpu_x86_init(const char *cpu_model)
return env;
}
+#ifdef USE_KVM
+struct hostlike_user_disable {
+ uint32_t features;
+ uint32_t ext_features;
+ uint32_t ext2_features;
+ uint32_t ext3_features;
+} hostlike_user_disable;
+#endif
+
typedef struct x86_def_t {
const char *name;
uint32_t level;
@@ -131,6 +140,9 @@ typedef struct x86_def_t {
int model;
int stepping;
uint32_t features, ext_features, ext2_features, ext3_features;
+#ifdef USE_KVM
+ int hostlike;
+#endif
uint32_t xlevel;
} x86_def_t;
@@ -207,6 +219,12 @@ static x86_def_t x86_defs[] = {
.features = 0x0383F9FF,
.xlevel = 0,
},
+#ifdef USE_KVM
+ {
+ .name = "host",
+ .hostlike = 1,
+ },
+#endif
};
static int cpu_x86_find_by_name(x86_def_t *x86_cpu_def, const char *cpu_model)
@@ -229,6 +247,12 @@ static int cpu_x86_find_by_name(x86_def_t *x86_cpu_def,
const char *cpu_model)
}
if (!def)
goto error;
+#ifdef USE_KVM
+ if (!kvm_allowed && def->hostlike) {
+ fprintf(stderr, "hostlike cpu requires KVM.\n");
+ goto error;
+ }
+#endif
memcpy(x86_cpu_def, def, sizeof(*def));
featurestr = strtok(NULL, ",");
@@ -288,6 +312,14 @@ static int cpu_x86_find_by_name(x86_def_t *x86_cpu_def,
const char *cpu_model)
x86_cpu_def->ext_features &= ~minus_ext_features;
x86_cpu_def->ext2_features &= ~minus_ext2_features;
x86_cpu_def->ext3_features &= ~minus_ext3_features;
+#ifdef USE_KVM
+ if (kvm_allowed) {
+ hostlike_user_disable.features = minus_features;
+ hostlike_user_disable.ext_features = minus_ext_features;
+ hostlike_user_disable.ext2_features = minus_ext2_features;
+ hostlike_user_disable.ext3_features = minus_ext3_features;
+ }
+#endif
free(s);
return 0;
@@ -341,6 +373,9 @@ static int cpu_x86_register (CPUX86State *env, const char
*cpu_model)
env->cpuid_model[i >> 2] |= c << (8 * (i & 3));
}
}
+#ifdef USE_KVM
+ env->cpuid_hostlike = def->hostlike;
+#endif
return 0;
}
--
1.5.3.7
diff --git a/qemu/qemu-kvm-x86.c b/qemu/qemu-kvm-x86.c
index aa9120c..765a590 100644
--- a/qemu/qemu-kvm-x86.c
+++ b/qemu/qemu-kvm-x86.c
@@ -462,9 +462,10 @@ static void host_cpuid(uint32_t function, uint32_t *eax,
uint32_t *ebx,
static void do_cpuid_ent(struct kvm_cpuid_entry2 *e, uint32_t function,
- CPUState *env)
+ uint32_t index, CPUState *env)
{
env->regs[R_EAX] = function;
+ env->regs[R_ECX] = index;
qemu_kvm_cpuid_on_env(env);
e->function = function;
e->eax = env->regs[R_EAX];
@@ -532,7 +533,7 @@ int kvm_arch_qemu_init_env(CPUState *cenv)
#endif
int cpuid_nent = 0;
CPUState copy;
- uint32_t i, limit;
+ uint32_t i, j, limit;
copy = *cenv;
@@ -583,7 +584,7 @@ int kvm_arch_qemu_init_env(CPUState *cenv)
if (cpu_vendor_string)
for (i = 0x80000002; i <= 0x80000004; ++i) {
e = cpuid_entry_lookup(cpuid_ent, cpuid_nent, i, 0);
- do_cpuid_ent(e, i, ©);
+ do_cpuid_ent(e, i, 0, ©);
}
}
} else {
@@ -591,15 +592,32 @@ int kvm_arch_qemu_init_env(CPUState *cenv)
qemu_kvm_cpuid_on_env(©);
limit = copy.regs[R_EAX];
+ for (i = 0; i <= limit; ++i) {
+ switch(i) {
+ case 4:
+ for(j = 0; ; j++) {
+ struct kvm_cpuid_entry2 *e = &cpuid_ent[cpuid_nent++];
+
+ do_cpuid_ent(e, i, j, ©);
+ e->flags |= KVM_CPUID_FLAG_SIGNIFCANT_INDEX;
+ if (!copy.regs[R_EAX])
+ break;
+ }
+ break;
+ default:
+ do_cpuid_ent(&cpuid_ent[cpuid_nent++], i, 0, ©);
+ break;
+ }
+ }
for (i = 0; i <= limit; ++i)
- do_cpuid_ent(&cpuid_ent[cpuid_nent++], i, ©);
+ do_cpuid_ent(&cpuid_ent[cpuid_nent++], i, 0, ©);
copy.regs[R_EAX] = 0x80000000;
qemu_kvm_cpuid_on_env(©);
limit = copy.regs[R_EAX];
for (i = 0x80000000; i <= limit; ++i)
- do_cpuid_ent(&cpuid_ent[cpuid_nent++], i, ©);
+ do_cpuid_ent(&cpuid_ent[cpuid_nent++], i, 0, ©);
}
#ifdef KVM_CPUID_SIGNATURE
-------------------------------------------------------------------------
This SF.net email is sponsored by: Microsoft
Defy all challenges. Microsoft(R) Visual Studio 2008.
http://clk.atdmt.com/MRT/go/vse0120000070mrt/direct/01/
_______________________________________________
kvm-devel mailing list
kvm-devel@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/kvm-devel