|
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index] [Xen-devel] [PATCH 02/27] x86/cpuid: Introduce guest_cpuid() and struct cpuid_leaf
Longterm, pv_cpuid() and hvm_cpuid() will be merged into a single
guest_cpuid(), which is also capable of working outside of current context.
To aid this transtion, introduce guest_cpuid() with the intended API, which
simply defers back to pv_cpuid() or hvm_cpuid() as appropriate.
Introduce struct cpuid_leaf which is used to represent the results of a CPUID
query in a more efficient mannor than passing four pointers through the
calltree.
Update all codepaths which should use the new guest_cpuid() API. These are
the codepaths which have variable inputs, and (other than some specific
x86_emulate() cases) all pertain to servicing a CPUID instruction from a
guest.
The other codepaths using {pv,hvm}_cpuid() with fixed inputs will later be
adjusted to read their data straight from the policy block.
No intended functional change.
Signed-off-by: Andrew Cooper <andrew.cooper3@xxxxxxxxxx>
---
CC: Jan Beulich <JBeulich@xxxxxxxx>
CC: Paul Durrant <paul.durrant@xxxxxxxxxx>
CC: Jun Nakajima <jun.nakajima@xxxxxxxxx>
CC: Kevin Tian <kevin.tian@xxxxxxxxx>
CC: Boris Ostrovsky <boris.ostrovsky@xxxxxxxxxx>
CC: Suravee Suthikulpanit <suravee.suthikulpanit@xxxxxxx>
Jan: I note that this patch texturally conflicts with your register renaming
series.
---
tools/tests/x86_emulator/x86_emulate.c | 15 ++++-----
tools/tests/x86_emulator/x86_emulate.h | 60 ++++++++++++++++------------------
xen/arch/x86/cpuid.c | 33 +++++++++++++++++++
xen/arch/x86/hvm/emulate.c | 10 ++----
xen/arch/x86/hvm/svm/svm.c | 23 ++++++-------
xen/arch/x86/hvm/vmx/vmx.c | 35 ++++++--------------
xen/arch/x86/traps.c | 26 +++++++--------
xen/arch/x86/x86_emulate/x86_emulate.c | 31 +++++++++---------
xen/arch/x86/x86_emulate/x86_emulate.h | 12 ++++---
xen/include/asm-x86/cpuid.h | 4 +++
xen/include/asm-x86/hvm/emulate.h | 8 ++---
xen/include/asm-x86/mm.h | 4 +--
12 files changed, 135 insertions(+), 126 deletions(-)
diff --git a/tools/tests/x86_emulator/x86_emulate.c
b/tools/tests/x86_emulator/x86_emulate.c
index 7f644d381..165f98a 100644
--- a/tools/tests/x86_emulator/x86_emulate.c
+++ b/tools/tests/x86_emulator/x86_emulate.c
@@ -39,22 +39,21 @@ bool emul_test_make_stack_executable(void)
}
int emul_test_cpuid(
- unsigned int *eax,
- unsigned int *ebx,
- unsigned int *ecx,
- unsigned int *edx,
+ unsigned int leaf,
+ unsigned int subleaf,
+ struct cpuid_leaf *res,
struct x86_emulate_ctxt *ctxt)
{
- unsigned int leaf = *eax;
-
- asm ("cpuid" : "+a" (*eax), "+c" (*ecx), "=d" (*edx), "=b" (*ebx));
+ asm ("cpuid"
+ : "=a" (res->a), "=b" (res->b), "=c" (res->c), "=d" (res->d)
+ : "a" (leaf), "c" (subleaf));
/*
* The emulator doesn't itself use MOVBE, so we can always run the
* respective tests.
*/
if ( leaf == 1 )
- *ecx |= 1U << 22;
+ res->c |= 1U << 22;
return X86EMUL_OKAY;
}
diff --git a/tools/tests/x86_emulator/x86_emulate.h
b/tools/tests/x86_emulator/x86_emulate.h
index c14c613..ed9951a 100644
--- a/tools/tests/x86_emulator/x86_emulate.h
+++ b/tools/tests/x86_emulator/x86_emulate.h
@@ -58,61 +58,59 @@ static inline uint64_t xgetbv(uint32_t xcr)
}
#define cache_line_size() ({ \
- unsigned int eax = 1, ebx, ecx = 0, edx; \
- emul_test_cpuid(&eax, &ebx, &ecx, &edx, NULL); \
- edx & (1U << 19) ? (ebx >> 5) & 0x7f8 : 0; \
+ struct cpuid_leaf res; \
+ emul_test_cpuid(1, 0, &res, NULL); \
+ res.d & (1U << 19) ? (res.b >> 5) & 0x7f8 : 0; \
})
#define cpu_has_mmx ({ \
- unsigned int eax = 1, ecx = 0, edx; \
- emul_test_cpuid(&eax, &ecx, &ecx, &edx, NULL); \
- (edx & (1U << 23)) != 0; \
+ struct cpuid_leaf res; \
+ emul_test_cpuid(1, 0, &res, NULL); \
+ (res.d & (1U << 23)) != 0; \
})
#define cpu_has_sse ({ \
- unsigned int eax = 1, ecx = 0, edx; \
- emul_test_cpuid(&eax, &ecx, &ecx, &edx, NULL); \
- (edx & (1U << 25)) != 0; \
+ struct cpuid_leaf res; \
+ emul_test_cpuid(1, 0, &res, NULL); \
+ (res.d & (1U << 25)) != 0; \
})
#define cpu_has_sse2 ({ \
- unsigned int eax = 1, ecx = 0, edx; \
- emul_test_cpuid(&eax, &ecx, &ecx, &edx, NULL); \
- (edx & (1U << 26)) != 0; \
+ struct cpuid_leaf res; \
+ emul_test_cpuid(1, 0, &res, NULL); \
+ (res.d & (1U << 26)) != 0; \
})
#define cpu_has_xsave ({ \
- unsigned int eax = 1, ecx = 0; \
- emul_test_cpuid(&eax, &eax, &ecx, &eax, NULL); \
+ struct cpuid_leaf res; \
+ emul_test_cpuid(1, 0, &res, NULL); \
/* Intentionally checking OSXSAVE here. */ \
- (ecx & (1U << 27)) != 0; \
+ (res.c & (1U << 27)) != 0; \
})
#define cpu_has_avx ({ \
- unsigned int eax = 1, ecx = 0; \
- emul_test_cpuid(&eax, &eax, &ecx, &eax, NULL); \
- if ( !(ecx & (1U << 27)) || ((xgetbv(0) & 6) != 6) ) \
- ecx = 0; \
- (ecx & (1U << 28)) != 0; \
+ struct cpuid_leaf res; \
+ emul_test_cpuid(1, 0, &res, NULL); \
+ if ( !(res.c & (1U << 27)) || ((xgetbv(0) & 6) != 6) ) \
+ res.c = 0; \
+ (res.c & (1U << 28)) != 0; \
})
#define cpu_has_avx2 ({ \
- unsigned int eax = 1, ebx, ecx = 0; \
- emul_test_cpuid(&eax, &ebx, &ecx, &eax, NULL); \
- if ( !(ecx & (1U << 27)) || ((xgetbv(0) & 6) != 6) ) \
- ebx = 0; \
+ struct cpuid_leaf res; \
+ emul_test_cpuid(1, 0, &res, NULL); \
+ if ( !(res.c & (1U << 27)) || ((xgetbv(0) & 6) != 6) ) \
+ res.b = 0; \
else { \
- eax = 7, ecx = 0; \
- emul_test_cpuid(&eax, &ebx, &ecx, &eax, NULL); \
+ emul_test_cpuid(7, 0, &res, NULL); \
} \
- (ebx & (1U << 5)) != 0; \
+ (res.b & (1U << 5)) != 0; \
})
int emul_test_cpuid(
- unsigned int *eax,
- unsigned int *ebx,
- unsigned int *ecx,
- unsigned int *edx,
+ unsigned int leaf,
+ unsigned int subleaf,
+ struct cpuid_leaf *res,
struct x86_emulate_ctxt *ctxt);
int emul_test_read_cr(
diff --git a/xen/arch/x86/cpuid.c b/xen/arch/x86/cpuid.c
index 3e85a63..7796df6 100644
--- a/xen/arch/x86/cpuid.c
+++ b/xen/arch/x86/cpuid.c
@@ -1,5 +1,6 @@
#include <xen/init.h>
#include <xen/lib.h>
+#include <xen/sched.h>
#include <asm/cpuid.h>
#include <asm/hvm/hvm.h>
#include <asm/hvm/vmx/vmcs.h>
@@ -17,6 +18,8 @@ uint32_t __read_mostly raw_featureset[FSCAPINTS];
uint32_t __read_mostly pv_featureset[FSCAPINTS];
uint32_t __read_mostly hvm_featureset[FSCAPINTS];
+#define EMPTY_LEAF (struct cpuid_leaf){}
+
static void __init sanitise_featureset(uint32_t *fs)
{
/* for_each_set_bit() uses unsigned longs. Extend with zeroes. */
@@ -215,6 +218,36 @@ const uint32_t * __init lookup_deep_deps(uint32_t feature)
return NULL;
}
+void guest_cpuid(const struct vcpu *v, unsigned int leaf,
+ unsigned int subleaf, struct cpuid_leaf *res)
+{
+ *res = EMPTY_LEAF;
+
+ /* {pv,hvm}_cpuid() have this expectation. */
+ ASSERT(v == current);
+
+ if ( is_pv_vcpu(v) || is_pvh_vcpu(v) )
+ {
+ struct cpu_user_regs regs = *guest_cpu_user_regs();
+
+ regs.rax = leaf;
+ regs.rcx = subleaf;
+
+ pv_cpuid(®s);
+
+ res->a = regs._eax;
+ res->b = regs._ebx;
+ res->c = regs._ecx;
+ res->d = regs._edx;
+ }
+ else
+ {
+ res->c = subleaf;
+
+ hvm_cpuid(leaf, &res->a, &res->b, &res->c, &res->d);
+ }
+}
+
static void __init __maybe_unused build_assertions(void)
{
BUILD_BUG_ON(ARRAY_SIZE(known_features) != FSCAPINTS);
diff --git a/xen/arch/x86/hvm/emulate.c b/xen/arch/x86/hvm/emulate.c
index 41bd4f5..8d1bf51 100644
--- a/xen/arch/x86/hvm/emulate.c
+++ b/xen/arch/x86/hvm/emulate.c
@@ -1552,12 +1552,8 @@ static int hvmemul_wbinvd(
return X86EMUL_OKAY;
}
-int hvmemul_cpuid(
- unsigned int *eax,
- unsigned int *ebx,
- unsigned int *ecx,
- unsigned int *edx,
- struct x86_emulate_ctxt *ctxt)
+int hvmemul_cpuid(unsigned int leaf, unsigned int subleaf,
+ struct cpuid_leaf *res, struct x86_emulate_ctxt *ctxt)
{
/*
* x86_emulate uses this function to query CPU features for its own
internal
@@ -1568,7 +1564,7 @@ int hvmemul_cpuid(
hvm_check_cpuid_faulting(current) )
return X86EMUL_EXCEPTION;
- hvm_cpuid(*eax, eax, ebx, ecx, edx);
+ guest_cpuid(current, leaf, subleaf, res);
return X86EMUL_OKAY;
}
diff --git a/xen/arch/x86/hvm/svm/svm.c b/xen/arch/x86/hvm/svm/svm.c
index 89daa39..de20f64 100644
--- a/xen/arch/x86/hvm/svm/svm.c
+++ b/xen/arch/x86/hvm/svm/svm.c
@@ -1572,23 +1572,20 @@ static void svm_fpu_dirty_intercept(void)
static void svm_vmexit_do_cpuid(struct cpu_user_regs *regs)
{
- unsigned int eax, ebx, ecx, edx, inst_len;
+ struct vcpu *curr = current;
+ unsigned int inst_len;
+ struct cpuid_leaf res;
- if ( (inst_len = __get_instruction_length(current, INSTR_CPUID)) == 0 )
+ if ( (inst_len = __get_instruction_length(curr, INSTR_CPUID)) == 0 )
return;
- eax = regs->_eax;
- ebx = regs->_ebx;
- ecx = regs->_ecx;
- edx = regs->_edx;
-
- hvm_cpuid(regs->_eax, &eax, &ebx, &ecx, &edx);
- HVMTRACE_5D(CPUID, regs->_eax, eax, ebx, ecx, edx);
+ guest_cpuid(curr, regs->_eax, regs->_ecx, &res);
+ HVMTRACE_5D(CPUID, regs->_eax, res.a, res.b, res.c, res.d);
- regs->rax = eax;
- regs->rbx = ebx;
- regs->rcx = ecx;
- regs->rdx = edx;
+ regs->rax = res.a;
+ regs->rbx = res.b;
+ regs->rcx = res.c;
+ regs->rdx = res.d;
__update_guest_eip(regs, inst_len);
}
diff --git a/xen/arch/x86/hvm/vmx/vmx.c b/xen/arch/x86/hvm/vmx/vmx.c
index 15d66a2..ada5358 100644
--- a/xen/arch/x86/hvm/vmx/vmx.c
+++ b/xen/arch/x86/hvm/vmx/vmx.c
@@ -2361,8 +2361,9 @@ static void vmx_fpu_dirty_intercept(void)
static int vmx_do_cpuid(struct cpu_user_regs *regs)
{
- unsigned int eax, ebx, ecx, edx;
- unsigned int leaf, subleaf;
+ struct vcpu *curr = current;
+ unsigned int leaf = regs->_eax, subleaf = regs->_ecx;
+ struct cpuid_leaf res;
if ( hvm_check_cpuid_faulting(current) )
{
@@ -2370,21 +2371,13 @@ static int vmx_do_cpuid(struct cpu_user_regs *regs)
return 1; /* Don't advance the guest IP! */
}
- eax = regs->eax;
- ebx = regs->ebx;
- ecx = regs->ecx;
- edx = regs->edx;
-
- leaf = regs->eax;
- subleaf = regs->ecx;
+ guest_cpuid(curr, leaf, subleaf, &res);
+ HVMTRACE_5D(CPUID, leaf, res.a, res.b, res.c, res.d);
- hvm_cpuid(leaf, &eax, &ebx, &ecx, &edx);
- HVMTRACE_5D(CPUID, leaf, eax, ebx, ecx, edx);
-
- regs->eax = eax;
- regs->ebx = ebx;
- regs->ecx = ecx;
- regs->edx = edx;
+ regs->rax = res.a;
+ regs->rbx = res.b;
+ regs->rcx = res.c;
+ regs->rdx = res.d;
return hvm_monitor_cpuid(get_instruction_length(), leaf, subleaf);
}
@@ -3562,15 +3555,7 @@ void vmx_vmexit_handler(struct cpu_user_regs *regs)
}
case EXIT_REASON_CPUID:
{
- int rc;
-
- if ( is_pvh_vcpu(v) )
- {
- pv_cpuid(regs);
- rc = 0;
- }
- else
- rc = vmx_do_cpuid(regs);
+ int rc = vmx_do_cpuid(regs);
/*
* rc < 0 error in monitor/vm_event, crash
diff --git a/xen/arch/x86/traps.c b/xen/arch/x86/traps.c
index 2d211d1..02f2d5c 100644
--- a/xen/arch/x86/traps.c
+++ b/xen/arch/x86/traps.c
@@ -1412,6 +1412,7 @@ static int emulate_forced_invalid_op(struct cpu_user_regs
*regs)
{
char sig[5], instr[2];
unsigned long eip, rc;
+ struct cpuid_leaf res;
eip = regs->rip;
@@ -1444,7 +1445,12 @@ static int emulate_forced_invalid_op(struct
cpu_user_regs *regs)
eip += sizeof(instr);
- pv_cpuid(regs);
+ guest_cpuid(current, regs->_eax, regs->_ecx, &res);
+
+ regs->rax = res.a;
+ regs->rbx = res.b;
+ regs->rcx = res.c;
+ regs->rdx = res.d;
instruction_done(regs, eip);
@@ -3246,10 +3252,10 @@ static int priv_op_wbinvd(struct x86_emulate_ctxt *ctxt)
return X86EMUL_OKAY;
}
-int pv_emul_cpuid(unsigned int *eax, unsigned int *ebx, unsigned int *ecx,
- unsigned int *edx, struct x86_emulate_ctxt *ctxt)
+int pv_emul_cpuid(unsigned int leaf, unsigned int subleaf,
+ struct cpuid_leaf *res, struct x86_emulate_ctxt *ctxt)
{
- struct cpu_user_regs regs = *ctxt->regs;
+ struct vcpu *curr = current;
/*
* x86_emulate uses this function to query CPU features for its own
@@ -3258,7 +3264,6 @@ int pv_emul_cpuid(unsigned int *eax, unsigned int *ebx,
unsigned int *ecx,
*/
if ( ctxt->opcode == X86EMUL_OPC(0x0f, 0xa2) )
{
- const struct vcpu *curr = current;
/* If cpuid faulting is enabled and CPL>0 leave the #GP untouched. */
if ( curr->arch.cpuid_faulting &&
@@ -3266,16 +3271,7 @@ int pv_emul_cpuid(unsigned int *eax, unsigned int *ebx,
unsigned int *ecx,
return X86EMUL_EXCEPTION;
}
- regs._eax = *eax;
- regs._ecx = *ecx;
-
- pv_cpuid(®s);
-
- *eax = regs._eax;
- *ebx = regs._ebx;
- *ecx = regs._ecx;
- *edx = regs._edx;
-
+ guest_cpuid(curr, leaf, subleaf, res);
return X86EMUL_OKAY;
}
diff --git a/xen/arch/x86/x86_emulate/x86_emulate.c
b/xen/arch/x86/x86_emulate/x86_emulate.c
index 3076c0c..4456df9 100644
--- a/xen/arch/x86/x86_emulate/x86_emulate.c
+++ b/xen/arch/x86/x86_emulate/x86_emulate.c
@@ -1265,19 +1265,19 @@ static bool vcpu_has(
struct x86_emulate_ctxt *ctxt,
const struct x86_emulate_ops *ops)
{
- unsigned int ebx = 0, ecx = 0, edx = 0;
+ struct cpuid_leaf res;
int rc = X86EMUL_OKAY;
fail_if(!ops->cpuid);
- rc = ops->cpuid(&eax, &ebx, &ecx, &edx, ctxt);
+ rc = ops->cpuid(eax, 0, &res, ctxt);
if ( rc == X86EMUL_OKAY )
{
switch ( reg )
{
- case EAX: reg = eax; break;
- case EBX: reg = ebx; break;
- case ECX: reg = ecx; break;
- case EDX: reg = edx; break;
+ case EAX: reg = res.a; break;
+ case EBX: reg = res.b; break;
+ case ECX: reg = res.c; break;
+ case EDX: reg = res.d; break;
default: BUG();
}
if ( !(reg & (1U << bit)) )
@@ -4502,15 +4502,15 @@ x86_emulate(
case 0xfc: /* clzero */
{
- unsigned int eax = 1, ebx = 0, dummy = 0;
+ struct cpuid_leaf res;
unsigned long zero = 0;
base = ad_bytes == 8 ? _regs.eax :
ad_bytes == 4 ? (uint32_t)_regs.eax : (uint16_t)_regs.eax;
limit = 0;
if ( vcpu_has_clflush() &&
- ops->cpuid(&eax, &ebx, &dummy, &dummy, ctxt) == X86EMUL_OKAY )
- limit = ((ebx >> 8) & 0xff) * 8;
+ ops->cpuid(1, 0, &res, ctxt) == X86EMUL_OKAY )
+ limit = ((res.b >> 8) & 0xff) * 8;
generate_exception_if(limit < sizeof(long) ||
(limit & (limit - 1)), EXC_UD);
base &= ~(limit - 1);
@@ -5150,17 +5150,18 @@ x86_emulate(
dst.val = test_cc(b, _regs.eflags);
break;
- case X86EMUL_OPC(0x0f, 0xa2): /* cpuid */ {
- unsigned int eax = _regs.eax, ebx = _regs.ebx;
- unsigned int ecx = _regs.ecx, edx = _regs.edx;
+ case X86EMUL_OPC(0x0f, 0xa2): /* cpuid */
+ {
+ struct cpuid_leaf res;
+
fail_if(ops->cpuid == NULL);
- rc = ops->cpuid(&eax, &ebx, &ecx, &edx, ctxt);
+ rc = ops->cpuid(_regs._eax, _regs._ecx, &res, ctxt);
generate_exception_if(rc == X86EMUL_EXCEPTION,
EXC_GP, 0); /* CPUID Faulting? */
if ( rc != X86EMUL_OKAY )
goto done;
- _regs.eax = eax; _regs.ebx = ebx;
- _regs.ecx = ecx; _regs.edx = edx;
+ _regs.eax = res.a; _regs.ebx = res.b;
+ _regs.ecx = res.c; _regs.edx = res.d;
break;
}
diff --git a/xen/arch/x86/x86_emulate/x86_emulate.h
b/xen/arch/x86/x86_emulate/x86_emulate.h
index 75f57ba..769bb32 100644
--- a/xen/arch/x86/x86_emulate/x86_emulate.h
+++ b/xen/arch/x86/x86_emulate/x86_emulate.h
@@ -164,6 +164,11 @@ enum x86_emulate_fpu_type {
X86EMUL_FPU_ymm /* AVX/XOP instruction set (%ymm0-%ymm7/15) */
};
+struct cpuid_leaf
+{
+ uint32_t a, b, c, d;
+};
+
struct x86_emulate_state;
/*
@@ -415,10 +420,9 @@ struct x86_emulate_ops
* #GP[0]. Used to implement CPUID faulting.
*/
int (*cpuid)(
- unsigned int *eax,
- unsigned int *ebx,
- unsigned int *ecx,
- unsigned int *edx,
+ unsigned int leaf,
+ unsigned int subleaf,
+ struct cpuid_leaf *res,
struct x86_emulate_ctxt *ctxt);
/*
diff --git a/xen/include/asm-x86/cpuid.h b/xen/include/asm-x86/cpuid.h
index 05f2c9a..a6488af 100644
--- a/xen/include/asm-x86/cpuid.h
+++ b/xen/include/asm-x86/cpuid.h
@@ -17,6 +17,7 @@
#ifndef __ASSEMBLY__
#include <xen/types.h>
+#include <asm/x86_emulate.h>
#include <public/sysctl.h>
extern const uint32_t known_features[FSCAPINTS];
@@ -64,6 +65,9 @@ extern struct cpuidmasks cpuidmask_defaults;
/* Whether or not cpuid faulting is available for the current domain. */
DECLARE_PER_CPU(bool, cpuid_faulting_enabled);
+void guest_cpuid(const struct vcpu *v, unsigned int leaf,
+ unsigned int subleaf, struct cpuid_leaf *res);
+
#endif /* __ASSEMBLY__ */
#endif /* !__X86_CPUID_H__ */
diff --git a/xen/include/asm-x86/hvm/emulate.h
b/xen/include/asm-x86/hvm/emulate.h
index 68a95e4..88e4856 100644
--- a/xen/include/asm-x86/hvm/emulate.h
+++ b/xen/include/asm-x86/hvm/emulate.h
@@ -57,12 +57,8 @@ void hvm_emulate_init_per_insn(
unsigned int insn_bytes);
void hvm_emulate_writeback(
struct hvm_emulate_ctxt *hvmemul_ctxt);
-int hvmemul_cpuid(
- unsigned int *eax,
- unsigned int *ebx,
- unsigned int *ecx,
- unsigned int *edx,
- struct x86_emulate_ctxt *ctxt);
+int hvmemul_cpuid(unsigned int leaf, unsigned int subleaf,
+ struct cpuid_leaf *res, struct x86_emulate_ctxt *ctxt);
struct segment_register *hvmemul_get_seg_reg(
enum x86_segment seg,
struct hvm_emulate_ctxt *hvmemul_ctxt);
diff --git a/xen/include/asm-x86/mm.h b/xen/include/asm-x86/mm.h
index a15029c..d573ca1 100644
--- a/xen/include/asm-x86/mm.h
+++ b/xen/include/asm-x86/mm.h
@@ -504,8 +504,8 @@ extern int mmcfg_intercept_write(enum x86_segment seg,
void *p_data,
unsigned int bytes,
struct x86_emulate_ctxt *ctxt);
-int pv_emul_cpuid(unsigned int *eax, unsigned int *ebx, unsigned int *ecx,
- unsigned int *edx, struct x86_emulate_ctxt *ctxt);
+int pv_emul_cpuid(unsigned int leaf, unsigned int subleaf,
+ struct cpuid_leaf *res, struct x86_emulate_ctxt *ctxt);
int ptwr_do_page_fault(struct vcpu *, unsigned long,
struct cpu_user_regs *);
--
2.1.4
_______________________________________________
Xen-devel mailing list
Xen-devel@xxxxxxxxxxxxx
https://lists.xen.org/xen-devel
|
![]() |
Lists.xenproject.org is hosted with RackSpace, monitoring our |