[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index] Re: [Xen-devel] [PATCH V5 01/12] xen/mem_event: Cleanup of mem_event structures
On 13/02/15 16:33, Tamas K Lengyel wrote: > The public mem_event structures used to communicate with helper applications > via > shared rings have been used in different settings. However, the variable names > within this structure have not reflected this fact, resulting in the reuse of > variables to mean different things under different scenarios. > > This patch remedies the issue by clearly defining the structure members based > on > the actual context within which the structure is used. > > Signed-off-by: Razvan Cojocaru <rcojocaru@xxxxxxxxxxxxxxx> > Signed-off-by: Tamas K Lengyel <tamas.lengyel@xxxxxxxxxxxx> > --- > v5: Style fixes > Convert gfn to uint32_t It is perfectly possible to have guests with more memory than is covered by 44 bits, or PV guests whose frames reside above the 44bit boundary. All gfn values should be 64bits wide. ~Andrew > and define mem_access flags bits as we can now save > space on the ring this way > Split non-mem_event flags into access/paging flags > v4: Attach mem_event version to each outgoing request directly in mem_event. > v3: Add padding to mem_event structures. > Add version field to mem_event structures and checks for it. > --- > tools/libxc/xc_mem_event.c | 2 +- > tools/libxc/xc_private.h | 2 +- > tools/tests/xen-access/xen-access.c | 45 +++++---- > tools/xenpaging/xenpaging.c | 51 ++++++----- > xen/arch/x86/hvm/hvm.c | 177 > +++++++++++++++++++----------------- > xen/arch/x86/mm/mem_sharing.c | 16 +++- > xen/arch/x86/mm/p2m.c | 163 ++++++++++++++++++--------------- > xen/common/mem_access.c | 6 ++ > xen/common/mem_event.c | 2 + > xen/include/public/mem_event.h | 173 ++++++++++++++++++++++++++--------- > xen/include/public/memory.h | 11 ++- > 11 files changed, 401 insertions(+), 247 deletions(-) > > diff --git a/tools/libxc/xc_mem_event.c b/tools/libxc/xc_mem_event.c > index 8c0be4e..1b5f7c3 100644 > --- a/tools/libxc/xc_mem_event.c > +++ b/tools/libxc/xc_mem_event.c > @@ -42,7 +42,7 @@ int xc_mem_event_control(xc_interface *xch, domid_t > domain_id, unsigned int op, > > int xc_mem_event_memop(xc_interface *xch, domid_t domain_id, > unsigned int op, unsigned int mode, > - uint64_t gfn, void *buffer) > + uint32_t gfn, void *buffer) > { > xen_mem_event_op_t meo; > > diff --git a/tools/libxc/xc_private.h b/tools/libxc/xc_private.h > index 45b8644..bc021b8 100644 > --- a/tools/libxc/xc_private.h > +++ b/tools/libxc/xc_private.h > @@ -427,7 +427,7 @@ int xc_mem_event_control(xc_interface *xch, domid_t > domain_id, unsigned int op, > unsigned int mode, uint32_t *port); > int xc_mem_event_memop(xc_interface *xch, domid_t domain_id, > unsigned int op, unsigned int mode, > - uint64_t gfn, void *buffer); > + uint32_t gfn, void *buffer); > /* > * Enables mem_event and returns the mapped ring page indicated by param. > * param can be HVM_PARAM_PAGING/ACCESS/SHARING_RING_PFN > diff --git a/tools/tests/xen-access/xen-access.c > b/tools/tests/xen-access/xen-access.c > index 6cb382d..68f05db 100644 > --- a/tools/tests/xen-access/xen-access.c > +++ b/tools/tests/xen-access/xen-access.c > @@ -551,13 +551,21 @@ int main(int argc, char *argv[]) > continue; > } > > + if ( req.version != MEM_EVENT_INTERFACE_VERSION ) > + { > + ERROR("Error: mem_event interface version mismatch!\n"); > + interrupted = -1; > + continue; > + } > + > memset( &rsp, 0, sizeof (rsp) ); > + rsp.version = MEM_EVENT_INTERFACE_VERSION; > rsp.vcpu_id = req.vcpu_id; > rsp.flags = req.flags; > > switch (req.reason) { > - case MEM_EVENT_REASON_VIOLATION: > - rc = xc_get_mem_access(xch, domain_id, req.gfn, &access); > + case MEM_EVENT_REASON_MEM_ACCESS: > + rc = xc_get_mem_access(xch, domain_id, req.u.mem_access.gfn, > &access); > if (rc < 0) > { > ERROR("Error %d getting mem_access event\n", rc); > @@ -565,23 +573,23 @@ int main(int argc, char *argv[]) > continue; > } > > - printf("PAGE ACCESS: %c%c%c for GFN %"PRIx64" (offset %06" > + printf("PAGE ACCESS: %c%c%c for GFN %"PRIx32" (offset %06" > PRIx64") gla %016"PRIx64" (valid: %c; fault in gpt: > %c; fault with gla: %c) (vcpu %u)\n", > - req.access_r ? 'r' : '-', > - req.access_w ? 'w' : '-', > - req.access_x ? 'x' : '-', > - req.gfn, > - req.offset, > - req.gla, > - req.gla_valid ? 'y' : 'n', > - req.fault_in_gpt ? 'y' : 'n', > - req.fault_with_gla ? 'y': 'n', > + (req.u.mem_access.flags & MEM_ACCESS_R) ? 'r' : '-', > + (req.u.mem_access.flags & MEM_ACCESS_W) ? 'w' : '-', > + (req.u.mem_access.flags & MEM_ACCESS_X) ? 'x' : '-', > + req.u.mem_access.gfn, > + req.u.mem_access.offset, > + req.u.mem_access.gla, > + (req.u.mem_access.flags & MEM_ACCESS_GLA_VALID) ? 'y' > : 'n', > + (req.u.mem_access.flags & MEM_ACCESS_FAULT_IN_GPT) ? > 'y' : 'n', > + (req.u.mem_access.flags & MEM_ACCESS_FAULT_WITH_GLA) > ? 'y': 'n', > req.vcpu_id); > > if ( default_access != after_first_access ) > { > rc = xc_set_mem_access(xch, domain_id, > after_first_access, > - req.gfn, 1); > + req.u.mem_access.gfn, 1); > if (rc < 0) > { > ERROR("Error %d setting gfn to access_type %d\n", rc, > @@ -592,13 +600,12 @@ int main(int argc, char *argv[]) > } > > > - rsp.gfn = req.gfn; > - rsp.p2mt = req.p2mt; > + rsp.u.mem_access.gfn = req.u.mem_access.gfn; > break; > - case MEM_EVENT_REASON_INT3: > - printf("INT3: rip=%016"PRIx64", gfn=%"PRIx64" (vcpu %d)\n", > - req.gla, > - req.gfn, > + case MEM_EVENT_REASON_SOFTWARE_BREAKPOINT: > + printf("INT3: rip=%016"PRIx64", gfn=%"PRIx32" (vcpu %d)\n", > + req.regs.x86.rip, > + req.u.software_breakpoint.gfn, > req.vcpu_id); > > /* Reinject */ > diff --git a/tools/xenpaging/xenpaging.c b/tools/xenpaging/xenpaging.c > index 82c1ee4..29ca7c7 100644 > --- a/tools/xenpaging/xenpaging.c > +++ b/tools/xenpaging/xenpaging.c > @@ -684,9 +684,9 @@ static int xenpaging_resume_page(struct xenpaging > *paging, mem_event_response_t > * This allows page-out of these gfns if the target grows again. > */ > if (paging->num_paged_out > paging->policy_mru_size) > - policy_notify_paged_in(rsp->gfn); > + policy_notify_paged_in(rsp->u.mem_paging.gfn); > else > - policy_notify_paged_in_nomru(rsp->gfn); > + policy_notify_paged_in_nomru(rsp->u.mem_paging.gfn); > > /* Record number of resumed pages */ > paging->num_paged_out--; > @@ -874,7 +874,8 @@ int main(int argc, char *argv[]) > } > xch = paging->xc_handle; > > - DPRINTF("starting %s for domain_id %u with pagefile %s\n", argv[0], > paging->mem_event.domain_id, filename); > + DPRINTF("starting %s for domain_id %u with pagefile %s\n", > + argv[0], paging->mem_event.domain_id, filename); > > /* ensure that if we get a signal, we'll do cleanup, then exit */ > act.sa_handler = close_handler; > @@ -910,49 +911,52 @@ int main(int argc, char *argv[]) > > get_request(&paging->mem_event, &req); > > - if ( req.gfn > paging->max_pages ) > + if ( req.u.mem_paging.gfn > paging->max_pages ) > { > - ERROR("Requested gfn %"PRIx64" higher than max_pages %x\n", > req.gfn, paging->max_pages); > + ERROR("Requested gfn %"PRIx32" higher than max_pages %x\n", > + req.u.mem_paging.gfn, paging->max_pages); > goto out; > } > > /* Check if the page has already been paged in */ > - if ( test_and_clear_bit(req.gfn, paging->bitmap) ) > + if ( test_and_clear_bit(req.u.mem_paging.gfn, paging->bitmap) ) > { > /* Find where in the paging file to read from */ > - slot = paging->gfn_to_slot[req.gfn]; > + slot = paging->gfn_to_slot[req.u.mem_paging.gfn]; > > /* Sanity check */ > - if ( paging->slot_to_gfn[slot] != req.gfn ) > + if ( paging->slot_to_gfn[slot] != req.u.mem_paging.gfn ) > { > - ERROR("Expected gfn %"PRIx64" in slot %d, but found gfn > %lx\n", req.gfn, slot, paging->slot_to_gfn[slot]); > + ERROR("Expected gfn %"PRIx32" in slot %d, but found gfn > %lx\n", > + req.u.mem_paging.gfn, slot, > paging->slot_to_gfn[slot]); > goto out; > } > > - if ( req.flags & MEM_EVENT_FLAG_DROP_PAGE ) > + if ( req.u.mem_paging.flags & MEM_PAGING_DROP_PAGE ) > { > - DPRINTF("drop_page ^ gfn %"PRIx64" pageslot %d\n", > req.gfn, slot); > + DPRINTF("drop_page ^ gfn %"PRIx32" pageslot %d\n", > + req.u.mem_paging.gfn, slot); > /* Notify policy of page being dropped */ > - policy_notify_dropped(req.gfn); > + policy_notify_dropped(req.u.mem_paging.gfn); > } > else > { > /* Populate the page */ > - if ( xenpaging_populate_page(paging, req.gfn, slot) < 0 ) > + if ( xenpaging_populate_page(paging, > req.u.mem_paging.gfn, slot) < 0 ) > { > - ERROR("Error populating page %"PRIx64"", req.gfn); > + ERROR("Error populating page %"PRIx32"", > req.u.mem_paging.gfn); > goto out; > } > } > > /* Prepare the response */ > - rsp.gfn = req.gfn; > + rsp.u.mem_paging.gfn = req.u.mem_paging.gfn; > rsp.vcpu_id = req.vcpu_id; > rsp.flags = req.flags; > > if ( xenpaging_resume_page(paging, &rsp, 1) < 0 ) > { > - PERROR("Error resuming page %"PRIx64"", req.gfn); > + PERROR("Error resuming page %"PRIx32"", > req.u.mem_paging.gfn); > goto out; > } > > @@ -965,23 +969,24 @@ int main(int argc, char *argv[]) > else > { > DPRINTF("page %s populated (domain = %d; vcpu = %d;" > - " gfn = %"PRIx64"; paused = %d; evict_fail = %d)\n", > - req.flags & MEM_EVENT_FLAG_EVICT_FAIL ? "not" : > "already", > - paging->mem_event.domain_id, req.vcpu_id, req.gfn, > + " gfn = %"PRIx32"; paused = %d; evict_fail = %d)\n", > + req.u.mem_paging.flags & MEM_PAGING_EVICT_FAIL ? > "not" : "already", > + paging->mem_event.domain_id, req.vcpu_id, > req.u.mem_paging.gfn, > !!(req.flags & MEM_EVENT_FLAG_VCPU_PAUSED) , > - !!(req.flags & MEM_EVENT_FLAG_EVICT_FAIL) ); > + !!(req.u.mem_paging.flags & MEM_PAGING_EVICT_FAIL) ); > > /* Tell Xen to resume the vcpu */ > - if (( req.flags & MEM_EVENT_FLAG_VCPU_PAUSED ) || ( > req.flags & MEM_EVENT_FLAG_EVICT_FAIL )) > + if (( req.flags & MEM_EVENT_FLAG_VCPU_PAUSED ) || > + ( req.u.mem_paging.flags & MEM_PAGING_EVICT_FAIL )) > { > /* Prepare the response */ > - rsp.gfn = req.gfn; > + rsp.u.mem_paging.gfn = req.u.mem_paging.gfn; > rsp.vcpu_id = req.vcpu_id; > rsp.flags = req.flags; > > if ( xenpaging_resume_page(paging, &rsp, 0) < 0 ) > { > - PERROR("Error resuming page %"PRIx64"", req.gfn); > + PERROR("Error resuming page %"PRIx32"", > req.u.mem_paging.gfn); > goto out; > } > } > diff --git a/xen/arch/x86/hvm/hvm.c b/xen/arch/x86/hvm/hvm.c > index b03ee4e..fe5f568 100644 > --- a/xen/arch/x86/hvm/hvm.c > +++ b/xen/arch/x86/hvm/hvm.c > @@ -6324,48 +6324,42 @@ static void > hvm_mem_event_fill_regs(mem_event_request_t *req) > const struct cpu_user_regs *regs = guest_cpu_user_regs(); > const struct vcpu *curr = current; > > - req->x86_regs.rax = regs->eax; > - req->x86_regs.rcx = regs->ecx; > - req->x86_regs.rdx = regs->edx; > - req->x86_regs.rbx = regs->ebx; > - req->x86_regs.rsp = regs->esp; > - req->x86_regs.rbp = regs->ebp; > - req->x86_regs.rsi = regs->esi; > - req->x86_regs.rdi = regs->edi; > - > - req->x86_regs.r8 = regs->r8; > - req->x86_regs.r9 = regs->r9; > - req->x86_regs.r10 = regs->r10; > - req->x86_regs.r11 = regs->r11; > - req->x86_regs.r12 = regs->r12; > - req->x86_regs.r13 = regs->r13; > - req->x86_regs.r14 = regs->r14; > - req->x86_regs.r15 = regs->r15; > - > - req->x86_regs.rflags = regs->eflags; > - req->x86_regs.rip = regs->eip; > - > - req->x86_regs.msr_efer = curr->arch.hvm_vcpu.guest_efer; > - req->x86_regs.cr0 = curr->arch.hvm_vcpu.guest_cr[0]; > - req->x86_regs.cr3 = curr->arch.hvm_vcpu.guest_cr[3]; > - req->x86_regs.cr4 = curr->arch.hvm_vcpu.guest_cr[4]; > -} > - > -static int hvm_memory_event_traps(long p, uint32_t reason, > - unsigned long value, unsigned long old, > - bool_t gla_valid, unsigned long gla) > -{ > - struct vcpu* v = current; > - struct domain *d = v->domain; > - mem_event_request_t req = { .reason = reason }; > + req->regs.x86.rax = regs->eax; > + req->regs.x86.rcx = regs->ecx; > + req->regs.x86.rdx = regs->edx; > + req->regs.x86.rbx = regs->ebx; > + req->regs.x86.rsp = regs->esp; > + req->regs.x86.rbp = regs->ebp; > + req->regs.x86.rsi = regs->esi; > + req->regs.x86.rdi = regs->edi; > + > + req->regs.x86.r8 = regs->r8; > + req->regs.x86.r9 = regs->r9; > + req->regs.x86.r10 = regs->r10; > + req->regs.x86.r11 = regs->r11; > + req->regs.x86.r12 = regs->r12; > + req->regs.x86.r13 = regs->r13; > + req->regs.x86.r14 = regs->r14; > + req->regs.x86.r15 = regs->r15; > + > + req->regs.x86.rflags = regs->eflags; > + req->regs.x86.rip = regs->eip; > + > + req->regs.x86.msr_efer = curr->arch.hvm_vcpu.guest_efer; > + req->regs.x86.cr0 = curr->arch.hvm_vcpu.guest_cr[0]; > + req->regs.x86.cr3 = curr->arch.hvm_vcpu.guest_cr[3]; > + req->regs.x86.cr4 = curr->arch.hvm_vcpu.guest_cr[4]; > +} > + > +static int hvm_memory_event_traps(uint64_t parameters, mem_event_request_t > *req) > +{ > int rc; > + struct vcpu *v = current; > + struct domain *d = v->domain; > > - if ( !(p & HVMPME_MODE_MASK) ) > + if ( !(parameters & HVMPME_MODE_MASK) ) > return 0; > > - if ( (p & HVMPME_onchangeonly) && (value == old) ) > - return 1; > - > rc = mem_event_claim_slot(d, &d->mem_event->access); > if ( rc == -ENOSYS ) > { > @@ -6376,85 +6370,106 @@ static int hvm_memory_event_traps(long p, uint32_t > reason, > else if ( rc < 0 ) > return rc; > > - if ( (p & HVMPME_MODE_MASK) == HVMPME_mode_sync ) > + if ( (parameters & HVMPME_MODE_MASK) == HVMPME_mode_sync ) > { > - req.flags |= MEM_EVENT_FLAG_VCPU_PAUSED; > + req->flags |= MEM_EVENT_FLAG_VCPU_PAUSED; > mem_event_vcpu_pause(v); > } > > - req.gfn = value; > - req.vcpu_id = v->vcpu_id; > - if ( gla_valid ) > - { > - req.offset = gla & ((1 << PAGE_SHIFT) - 1); > - req.gla = gla; > - req.gla_valid = 1; > - } > - else > - { > - req.gla = old; > - } > - > - hvm_mem_event_fill_regs(&req); > - mem_event_put_request(d, &d->mem_event->access, &req); > - > + hvm_mem_event_fill_regs(req); > + mem_event_put_request(d, &d->mem_event->access, req); > + > return 1; > } > > +static void hvm_memory_event_cr(uint32_t reason, unsigned long value, > + unsigned long old) > +{ > + mem_event_request_t req = { > + .reason = reason, > + .vcpu_id = current->vcpu_id, > + .u.mov_to_cr.new_value = value, > + .u.mov_to_cr.old_value = old > + }; > + uint64_t parameters = 0; > + > + switch(reason) > + { > + case MEM_EVENT_REASON_MOV_TO_CR0: > + parameters = current->domain->arch.hvm_domain > + .params[HVM_PARAM_MEMORY_EVENT_CR0]; > + break; > + case MEM_EVENT_REASON_MOV_TO_CR3: > + parameters = current->domain->arch.hvm_domain > + .params[HVM_PARAM_MEMORY_EVENT_CR3]; > + break; > + case MEM_EVENT_REASON_MOV_TO_CR4: > + parameters = current->domain->arch.hvm_domain > + .params[HVM_PARAM_MEMORY_EVENT_CR4]; > + break; > + }; > + > + if ( (parameters & HVMPME_onchangeonly) && (value == old) ) > + return; > + > + hvm_memory_event_traps(parameters, &req); > +} > + > void hvm_memory_event_cr0(unsigned long value, unsigned long old) > { > - hvm_memory_event_traps(current->domain->arch.hvm_domain > - .params[HVM_PARAM_MEMORY_EVENT_CR0], > - MEM_EVENT_REASON_CR0, > - value, old, 0, 0); > + hvm_memory_event_cr(MEM_EVENT_REASON_MOV_TO_CR0, value, old); > } > > void hvm_memory_event_cr3(unsigned long value, unsigned long old) > { > - hvm_memory_event_traps(current->domain->arch.hvm_domain > - .params[HVM_PARAM_MEMORY_EVENT_CR3], > - MEM_EVENT_REASON_CR3, > - value, old, 0, 0); > + hvm_memory_event_cr(MEM_EVENT_REASON_MOV_TO_CR3, value, old); > } > > void hvm_memory_event_cr4(unsigned long value, unsigned long old) > { > - hvm_memory_event_traps(current->domain->arch.hvm_domain > - .params[HVM_PARAM_MEMORY_EVENT_CR4], > - MEM_EVENT_REASON_CR4, > - value, old, 0, 0); > + hvm_memory_event_cr(MEM_EVENT_REASON_MOV_TO_CR4, value, old); > } > > void hvm_memory_event_msr(unsigned long msr, unsigned long value) > { > + mem_event_request_t req = { > + .reason = MEM_EVENT_REASON_MOV_TO_MSR, > + .vcpu_id = current->vcpu_id, > + .u.mov_to_msr.msr = msr, > + .u.mov_to_msr.value = value, > + }; > + > hvm_memory_event_traps(current->domain->arch.hvm_domain > - .params[HVM_PARAM_MEMORY_EVENT_MSR], > - MEM_EVENT_REASON_MSR, > - value, ~value, 1, msr); > + .params[HVM_PARAM_MEMORY_EVENT_MSR], > + &req); > } > > int hvm_memory_event_int3(unsigned long gla) > { > uint32_t pfec = PFEC_page_present; > - unsigned long gfn; > - gfn = paging_gva_to_gfn(current, gla, &pfec); > + mem_event_request_t req = { > + .reason = MEM_EVENT_REASON_SOFTWARE_BREAKPOINT, > + .vcpu_id = current->vcpu_id, > + .u.software_breakpoint.gfn = paging_gva_to_gfn(current, gla, &pfec) > + }; > > return hvm_memory_event_traps(current->domain->arch.hvm_domain > - .params[HVM_PARAM_MEMORY_EVENT_INT3], > - MEM_EVENT_REASON_INT3, > - gfn, 0, 1, gla); > + .params[HVM_PARAM_MEMORY_EVENT_INT3], > + &req); > } > > int hvm_memory_event_single_step(unsigned long gla) > { > uint32_t pfec = PFEC_page_present; > - unsigned long gfn; > - gfn = paging_gva_to_gfn(current, gla, &pfec); > + mem_event_request_t req = { > + .reason = MEM_EVENT_REASON_SINGLESTEP, > + .vcpu_id = current->vcpu_id, > + .u.singlestep.gfn = paging_gva_to_gfn(current, gla, &pfec) > + }; > > return hvm_memory_event_traps(current->domain->arch.hvm_domain > - .params[HVM_PARAM_MEMORY_EVENT_SINGLE_STEP], > - MEM_EVENT_REASON_SINGLESTEP, > - gfn, 0, 1, gla); > + > .params[HVM_PARAM_MEMORY_EVENT_SINGLE_STEP], > + &req); > } > > int nhvm_vcpu_hostrestore(struct vcpu *v, struct cpu_user_regs *regs) > diff --git a/xen/arch/x86/mm/mem_sharing.c b/xen/arch/x86/mm/mem_sharing.c > index 7c0fc7d..8a192ef 100644 > --- a/xen/arch/x86/mm/mem_sharing.c > +++ b/xen/arch/x86/mm/mem_sharing.c > @@ -559,7 +559,12 @@ int mem_sharing_notify_enomem(struct domain *d, unsigned > long gfn, > { > struct vcpu *v = current; > int rc; > - mem_event_request_t req = { .gfn = gfn }; > + mem_event_request_t req = { > + .reason = MEM_EVENT_REASON_MEM_SHARING, > + .vcpu_id = v->vcpu_id, > + .u.mem_sharing.gfn = gfn, > + .u.mem_sharing.p2mt = p2m_ram_shared > + }; > > if ( (rc = __mem_event_claim_slot(d, > &d->mem_event->share, allow_sleep)) < 0 ) > @@ -571,9 +576,6 @@ int mem_sharing_notify_enomem(struct domain *d, unsigned > long gfn, > mem_event_vcpu_pause(v); > } > > - req.p2mt = p2m_ram_shared; > - req.vcpu_id = v->vcpu_id; > - > mem_event_put_request(d, &d->mem_event->share, &req); > > return 0; > @@ -598,6 +600,12 @@ int mem_sharing_sharing_resume(struct domain *d) > { > struct vcpu *v; > > + if ( rsp.version != MEM_EVENT_INTERFACE_VERSION ) > + { > + printk(XENLOG_G_WARNING "mem_event interface version > mismatch\n"); > + continue; > + } > + > if ( rsp.flags & MEM_EVENT_FLAG_DUMMY ) > continue; > > diff --git a/xen/arch/x86/mm/p2m.c b/xen/arch/x86/mm/p2m.c > index 6a06e9f..339f8fe 100644 > --- a/xen/arch/x86/mm/p2m.c > +++ b/xen/arch/x86/mm/p2m.c > @@ -1081,7 +1081,10 @@ int p2m_mem_paging_evict(struct domain *d, unsigned > long gfn) > void p2m_mem_paging_drop_page(struct domain *d, unsigned long gfn, > p2m_type_t p2mt) > { > - mem_event_request_t req = { .gfn = gfn }; > + mem_event_request_t req = { > + .reason = MEM_EVENT_REASON_MEM_PAGING, > + .u.mem_paging.gfn = gfn > + }; > > /* We allow no ring in this unique case, because it won't affect > * correctness of the guest execution at this point. If this is the only > @@ -1092,14 +1095,14 @@ void p2m_mem_paging_drop_page(struct domain *d, > unsigned long gfn, > return; > > /* Send release notification to pager */ > - req.flags = MEM_EVENT_FLAG_DROP_PAGE; > + req.u.mem_paging.flags = MEM_PAGING_DROP_PAGE; > > /* Update stats unless the page hasn't yet been evicted */ > if ( p2mt != p2m_ram_paging_out ) > atomic_dec(&d->paged_pages); > else > /* Evict will fail now, tag this request for pager */ > - req.flags |= MEM_EVENT_FLAG_EVICT_FAIL; > + req.u.mem_paging.flags |= MEM_PAGING_EVICT_FAIL; > > mem_event_put_request(d, &d->mem_event->paging, &req); > } > @@ -1128,7 +1131,10 @@ void p2m_mem_paging_drop_page(struct domain *d, > unsigned long gfn, > void p2m_mem_paging_populate(struct domain *d, unsigned long gfn) > { > struct vcpu *v = current; > - mem_event_request_t req = { .gfn = gfn }; > + mem_event_request_t req = { > + .reason = MEM_EVENT_REASON_MEM_PAGING, > + .u.mem_paging.gfn = gfn > + }; > p2m_type_t p2mt; > p2m_access_t a; > mfn_t mfn; > @@ -1157,7 +1163,7 @@ void p2m_mem_paging_populate(struct domain *d, unsigned > long gfn) > { > /* Evict will fail now, tag this request for pager */ > if ( p2mt == p2m_ram_paging_out ) > - req.flags |= MEM_EVENT_FLAG_EVICT_FAIL; > + req.u.mem_paging.flags |= MEM_PAGING_EVICT_FAIL; > > p2m_set_entry(p2m, gfn, mfn, PAGE_ORDER_4K, p2m_ram_paging_in, a); > } > @@ -1178,7 +1184,7 @@ void p2m_mem_paging_populate(struct domain *d, unsigned > long gfn) > } > > /* Send request to pager */ > - req.p2mt = p2mt; > + req.u.mem_paging.p2mt = p2mt; > req.vcpu_id = v->vcpu_id; > > mem_event_put_request(d, &d->mem_event->paging, &req); > @@ -1300,6 +1306,12 @@ void p2m_mem_paging_resume(struct domain *d) > { > struct vcpu *v; > > + if ( rsp.version != MEM_EVENT_INTERFACE_VERSION ) > + { > + printk(XENLOG_G_WARNING "mem_event interface version > mismatch\n"); > + continue; > + } > + > if ( rsp.flags & MEM_EVENT_FLAG_DUMMY ) > continue; > > @@ -1310,20 +1322,21 @@ void p2m_mem_paging_resume(struct domain *d) > v = d->vcpu[rsp.vcpu_id]; > > /* Fix p2m entry if the page was not dropped */ > - if ( !(rsp.flags & MEM_EVENT_FLAG_DROP_PAGE) ) > + if ( !(rsp.u.mem_paging.flags & MEM_PAGING_DROP_PAGE) ) > { > - gfn_lock(p2m, rsp.gfn, 0); > - mfn = p2m->get_entry(p2m, rsp.gfn, &p2mt, &a, 0, NULL); > + uint64_t gfn = rsp.u.mem_access.gfn; > + gfn_lock(p2m, gfn, 0); > + mfn = p2m->get_entry(p2m, gfn, &p2mt, &a, 0, NULL); > /* Allow only pages which were prepared properly, or pages which > * were nominated but not evicted */ > if ( mfn_valid(mfn) && (p2mt == p2m_ram_paging_in) ) > { > - p2m_set_entry(p2m, rsp.gfn, mfn, PAGE_ORDER_4K, > + p2m_set_entry(p2m, gfn, mfn, PAGE_ORDER_4K, > paging_mode_log_dirty(d) ? p2m_ram_logdirty : > p2m_ram_rw, a); > - set_gpfn_from_mfn(mfn_x(mfn), rsp.gfn); > + set_gpfn_from_mfn(mfn_x(mfn), gfn); > } > - gfn_unlock(p2m, rsp.gfn, 0); > + gfn_unlock(p2m, gfn, 0); > } > /* Unpause domain */ > if ( rsp.flags & MEM_EVENT_FLAG_VCPU_PAUSED ) > @@ -1341,92 +1354,94 @@ static void > p2m_mem_event_fill_regs(mem_event_request_t *req) > /* Architecture-specific vmcs/vmcb bits */ > hvm_funcs.save_cpu_ctxt(curr, &ctxt); > > - req->x86_regs.rax = regs->eax; > - req->x86_regs.rcx = regs->ecx; > - req->x86_regs.rdx = regs->edx; > - req->x86_regs.rbx = regs->ebx; > - req->x86_regs.rsp = regs->esp; > - req->x86_regs.rbp = regs->ebp; > - req->x86_regs.rsi = regs->esi; > - req->x86_regs.rdi = regs->edi; > - > - req->x86_regs.r8 = regs->r8; > - req->x86_regs.r9 = regs->r9; > - req->x86_regs.r10 = regs->r10; > - req->x86_regs.r11 = regs->r11; > - req->x86_regs.r12 = regs->r12; > - req->x86_regs.r13 = regs->r13; > - req->x86_regs.r14 = regs->r14; > - req->x86_regs.r15 = regs->r15; > - > - req->x86_regs.rflags = regs->eflags; > - req->x86_regs.rip = regs->eip; > - > - req->x86_regs.dr7 = curr->arch.debugreg[7]; > - req->x86_regs.cr0 = ctxt.cr0; > - req->x86_regs.cr2 = ctxt.cr2; > - req->x86_regs.cr3 = ctxt.cr3; > - req->x86_regs.cr4 = ctxt.cr4; > - > - req->x86_regs.sysenter_cs = ctxt.sysenter_cs; > - req->x86_regs.sysenter_esp = ctxt.sysenter_esp; > - req->x86_regs.sysenter_eip = ctxt.sysenter_eip; > - > - req->x86_regs.msr_efer = ctxt.msr_efer; > - req->x86_regs.msr_star = ctxt.msr_star; > - req->x86_regs.msr_lstar = ctxt.msr_lstar; > + req->regs.x86.rax = regs->eax; > + req->regs.x86.rcx = regs->ecx; > + req->regs.x86.rdx = regs->edx; > + req->regs.x86.rbx = regs->ebx; > + req->regs.x86.rsp = regs->esp; > + req->regs.x86.rbp = regs->ebp; > + req->regs.x86.rsi = regs->esi; > + req->regs.x86.rdi = regs->edi; > + > + req->regs.x86.r8 = regs->r8; > + req->regs.x86.r9 = regs->r9; > + req->regs.x86.r10 = regs->r10; > + req->regs.x86.r11 = regs->r11; > + req->regs.x86.r12 = regs->r12; > + req->regs.x86.r13 = regs->r13; > + req->regs.x86.r14 = regs->r14; > + req->regs.x86.r15 = regs->r15; > + > + req->regs.x86.rflags = regs->eflags; > + req->regs.x86.rip = regs->eip; > + > + req->regs.x86.dr7 = curr->arch.debugreg[7]; > + req->regs.x86.cr0 = ctxt.cr0; > + req->regs.x86.cr2 = ctxt.cr2; > + req->regs.x86.cr3 = ctxt.cr3; > + req->regs.x86.cr4 = ctxt.cr4; > + > + req->regs.x86.sysenter_cs = ctxt.sysenter_cs; > + req->regs.x86.sysenter_esp = ctxt.sysenter_esp; > + req->regs.x86.sysenter_eip = ctxt.sysenter_eip; > + > + req->regs.x86.msr_efer = ctxt.msr_efer; > + req->regs.x86.msr_star = ctxt.msr_star; > + req->regs.x86.msr_lstar = ctxt.msr_lstar; > > hvm_get_segment_register(curr, x86_seg_fs, &seg); > - req->x86_regs.fs_base = seg.base; > + req->regs.x86.fs_base = seg.base; > > hvm_get_segment_register(curr, x86_seg_gs, &seg); > - req->x86_regs.gs_base = seg.base; > + req->regs.x86.gs_base = seg.base; > > hvm_get_segment_register(curr, x86_seg_cs, &seg); > - req->x86_regs.cs_arbytes = seg.attr.bytes; > + req->regs.x86.cs_arbytes = seg.attr.bytes; > } > > -void p2m_mem_event_emulate_check(struct vcpu *v, const mem_event_response_t > *rsp) > +void p2m_mem_event_emulate_check(struct vcpu *v, > + const mem_event_response_t *rsp) > { > /* Mark vcpu for skipping one instruction upon rescheduling. */ > - if ( rsp->flags & MEM_EVENT_FLAG_EMULATE ) > + if ( rsp->flags & MEM_ACCESS_EMULATE ) > { > xenmem_access_t access; > bool_t violation = 1; > + const struct mem_event_mem_access *data = &rsp->u.mem_access; > > - if ( p2m_get_mem_access(v->domain, rsp->gfn, &access) == 0 ) > + if ( p2m_get_mem_access(v->domain, data->gfn, &access) == 0 ) > { > switch ( access ) > { > case XENMEM_access_n: > case XENMEM_access_n2rwx: > default: > - violation = rsp->access_r || rsp->access_w || rsp->access_x; > + violation = data->flags & MEM_ACCESS_RWX; > break; > > case XENMEM_access_r: > - violation = rsp->access_w || rsp->access_x; > + violation = data->flags & MEM_ACCESS_WX; > break; > > case XENMEM_access_w: > - violation = rsp->access_r || rsp->access_x; > + violation = data->flags & MEM_ACCESS_RX; > break; > > case XENMEM_access_x: > - violation = rsp->access_r || rsp->access_w; > + violation = data->flags & MEM_ACCESS_RW; > break; > > case XENMEM_access_rx: > case XENMEM_access_rx2rw: > - violation = rsp->access_w; > + violation = data->flags & MEM_ACCESS_W; > break; > > case XENMEM_access_wx: > - violation = rsp->access_r; > + violation = data->flags & MEM_ACCESS_R; > break; > > case XENMEM_access_rw: > - violation = rsp->access_x; > + violation = data->flags & MEM_ACCESS_X; > break; > > case XENMEM_access_rwx: > @@ -1532,7 +1547,7 @@ bool_t p2m_mem_access_check(paddr_t gpa, unsigned long > gla, > if ( v->arch.mem_event.emulate_flags ) > { > hvm_mem_event_emulate_one((v->arch.mem_event.emulate_flags & > - MEM_EVENT_FLAG_EMULATE_NOWRITE) != 0, > + MEM_ACCESS_EMULATE_NOWRITE) != 0, > TRAP_invalid_op, > HVM_DELIVER_NO_ERROR_CODE); > > v->arch.mem_event.emulate_flags = 0; > @@ -1544,24 +1559,28 @@ bool_t p2m_mem_access_check(paddr_t gpa, unsigned > long gla, > if ( req ) > { > *req_ptr = req; > - req->reason = MEM_EVENT_REASON_VIOLATION; > + req->reason = MEM_EVENT_REASON_MEM_ACCESS; > > /* Pause the current VCPU */ > if ( p2ma != p2m_access_n2rwx ) > req->flags |= MEM_EVENT_FLAG_VCPU_PAUSED; > > /* Send request to mem event */ > - req->gfn = gfn; > - req->offset = gpa & ((1 << PAGE_SHIFT) - 1); > - req->gla_valid = npfec.gla_valid; > - req->gla = gla; > - if ( npfec.kind == npfec_kind_with_gla ) > - req->fault_with_gla = 1; > - else if ( npfec.kind == npfec_kind_in_gpt ) > - req->fault_in_gpt = 1; > - req->access_r = npfec.read_access; > - req->access_w = npfec.write_access; > - req->access_x = npfec.insn_fetch; > + req->u.mem_access.gfn = gfn; > + req->u.mem_access.offset = gpa & ((1 << PAGE_SHIFT) - 1); > + if ( npfec.gla_valid ) > + { > + req->u.mem_access.flags |= MEM_ACCESS_GLA_VALID; > + req->u.mem_access.gla = gla; > + > + if ( npfec.kind == npfec_kind_with_gla ) > + req->u.mem_access.flags |= MEM_ACCESS_FAULT_WITH_GLA; > + else if ( npfec.kind == npfec_kind_in_gpt ) > + req->u.mem_access.flags |= MEM_ACCESS_FAULT_IN_GPT; > + } > + req->u.mem_access.flags |= npfec.read_access ? MEM_ACCESS_R : 0; > + req->u.mem_access.flags |= npfec.write_access ? MEM_ACCESS_W : 0; > + req->u.mem_access.flags |= npfec.insn_fetch ? MEM_ACCESS_X : 0; > req->vcpu_id = v->vcpu_id; > > p2m_mem_event_fill_regs(req); > diff --git a/xen/common/mem_access.c b/xen/common/mem_access.c > index d8aac5f..9c5b7a6 100644 > --- a/xen/common/mem_access.c > +++ b/xen/common/mem_access.c > @@ -38,6 +38,12 @@ void mem_access_resume(struct domain *d) > { > struct vcpu *v; > > + if ( rsp.version != MEM_EVENT_INTERFACE_VERSION ) > + { > + printk(XENLOG_G_WARNING "mem_event interface version > mismatch\n"); > + continue; > + } > + > if ( rsp.flags & MEM_EVENT_FLAG_DUMMY ) > continue; > > diff --git a/xen/common/mem_event.c b/xen/common/mem_event.c > index 7cfbe8e..8ab06ce 100644 > --- a/xen/common/mem_event.c > +++ b/xen/common/mem_event.c > @@ -291,6 +291,8 @@ void mem_event_put_request(struct domain *d, > #endif > } > > + req->version = MEM_EVENT_INTERFACE_VERSION; > + > mem_event_ring_lock(med); > > /* Due to the reservations, this step must succeed. */ > diff --git a/xen/include/public/mem_event.h b/xen/include/public/mem_event.h > index 599f9e8..1ef65d3 100644 > --- a/xen/include/public/mem_event.h > +++ b/xen/include/public/mem_event.h > @@ -28,39 +28,59 @@ > #define _XEN_PUBLIC_MEM_EVENT_H > > #include "xen.h" > + > +#define MEM_EVENT_INTERFACE_VERSION 0x00000001 > + > +#if defined(__XEN__) || defined(__XEN_TOOLS__) > + > #include "io/ring.h" > > -/* Memory event flags */ > +/* > + * Memory event flags > + */ > + > +/* > + * VCPU_PAUSED in a request signals that the vCPU triggering the event has > been > + * paused > + * VCPU_PAUSED in a response signals to unpause the vCPU > + */ > #define MEM_EVENT_FLAG_VCPU_PAUSED (1 << 0) > -#define MEM_EVENT_FLAG_DROP_PAGE (1 << 1) > -#define MEM_EVENT_FLAG_EVICT_FAIL (1 << 2) > -#define MEM_EVENT_FLAG_FOREIGN (1 << 3) > -#define MEM_EVENT_FLAG_DUMMY (1 << 4) > + > /* > - * Emulate the fault-causing instruction (if set in the event response > flags). > - * This will allow the guest to continue execution without lifting the page > - * access restrictions. > + * Flags to aid debugging mem_event > + */ > +#define MEM_EVENT_FLAG_FOREIGN (1 << 1) > +#define MEM_EVENT_FLAG_DUMMY (1 << 2) > + > +/* > + * Reasons for the vm event request > */ > -#define MEM_EVENT_FLAG_EMULATE (1 << 5) > + > +/* Default case */ > +#define MEM_EVENT_REASON_UNKNOWN 0 > +/* Memory access violation */ > +#define MEM_EVENT_REASON_MEM_ACCESS 1 > +/* Memory sharing event */ > +#define MEM_EVENT_REASON_MEM_SHARING 2 > +/* Memory paging event */ > +#define MEM_EVENT_REASON_MEM_PAGING 3 > +/* CR0 was updated */ > +#define MEM_EVENT_REASON_MOV_TO_CR0 4 > +/* CR3 was updated */ > +#define MEM_EVENT_REASON_MOV_TO_CR3 5 > +/* CR4 was updated */ > +#define MEM_EVENT_REASON_MOV_TO_CR4 6 > +/* An MSR was updated. Does NOT honour HVMPME_onchangeonly */ > +#define MEM_EVENT_REASON_MOV_TO_MSR 7 > +/* Debug operation executed (e.g. int3) */ > +#define MEM_EVENT_REASON_SOFTWARE_BREAKPOINT 8 > +/* Single-step (e.g. MTF) */ > +#define MEM_EVENT_REASON_SINGLESTEP 9 > + > /* > - * Same as MEM_EVENT_FLAG_EMULATE, but with write operations or operations > - * potentially having side effects (like memory mapped or port I/O) disabled. > + * Using a custom struct (not hvm_hw_cpu) so as to not fill > + * the mem_event ring buffer too quickly. > */ > -#define MEM_EVENT_FLAG_EMULATE_NOWRITE (1 << 6) > - > -/* Reasons for the memory event request */ > -#define MEM_EVENT_REASON_UNKNOWN 0 /* typical reason */ > -#define MEM_EVENT_REASON_VIOLATION 1 /* access violation, GFN is > address */ > -#define MEM_EVENT_REASON_CR0 2 /* CR0 was hit: gfn is new CR0 > value, gla is previous */ > -#define MEM_EVENT_REASON_CR3 3 /* CR3 was hit: gfn is new CR3 > value, gla is previous */ > -#define MEM_EVENT_REASON_CR4 4 /* CR4 was hit: gfn is new CR4 > value, gla is previous */ > -#define MEM_EVENT_REASON_INT3 5 /* int3 was hit: gla/gfn are RIP */ > -#define MEM_EVENT_REASON_SINGLESTEP 6 /* single step was invoked: > gla/gfn are RIP */ > -#define MEM_EVENT_REASON_MSR 7 /* MSR was hit: gfn is MSR value, > gla is MSR address; > - does NOT honour > HVMPME_onchangeonly */ > - > -/* Using a custom struct (not hvm_hw_cpu) so as to not fill > - * the mem_event ring buffer too quickly. */ > struct mem_event_regs_x86 { > uint64_t rax; > uint64_t rcx; > @@ -97,31 +117,102 @@ struct mem_event_regs_x86 { > uint32_t _pad; > }; > > -typedef struct mem_event_st { > - uint32_t flags; > - uint32_t vcpu_id; > +/* > + * mem_access flag definitions > + * > + * These flags are set only as part of a mem_event request. > + * > + * R/W/X: Defines the type of violation that has triggered the event > + * Multiple types can be set in a single violation! > + * GLA_VALID: If the gla field holds a guest VA associated with the event > + * FAULT_WITH_GLA: If the violation was triggered by accessing gla > + * FAULT_IN_GPT: If the violation was triggered during translating gla > + */ > +#define MEM_ACCESS_R (1 << 0) > +#define MEM_ACCESS_W (1 << 1) > +#define MEM_ACCESS_X (1 << 2) > +#define MEM_ACCESS_RWX (MEM_ACCESS_R | MEM_ACCESS_W | > MEM_ACCESS_X) > +#define MEM_ACCESS_RW (MEM_ACCESS_R | MEM_ACCESS_W) > +#define MEM_ACCESS_RX (MEM_ACCESS_R | MEM_ACCESS_X) > +#define MEM_ACCESS_WX (MEM_ACCESS_W | MEM_ACCESS_X) > +#define MEM_ACCESS_GLA_VALID (1 << 3) > +#define MEM_ACCESS_FAULT_WITH_GLA (1 << 4) > +#define MEM_ACCESS_FAULT_IN_GPT (1 << 5) > +/* > + * The following flags can be set in the response. > + * > + * Emulate the fault-causing instruction (if set in the event response > flags). > + * This will allow the guest to continue execution without lifting the page > + * access restrictions. > + */ > +#define MEM_ACCESS_EMULATE (1 << 6) > +/* > + * Same as MEM_ACCESS_EMULATE, but with write operations or operations > + * potentially having side effects (like memory mapped or port I/O) disabled. > + */ > +#define MEM_ACCESS_EMULATE_NOWRITE (1 << 7) > > - uint64_t gfn; > +struct mem_event_mem_access { > + uint32_t gfn; > + uint32_t flags; /* MEM_ACCESS_* */ > uint64_t offset; > - uint64_t gla; /* if gla_valid */ > + uint64_t gla; /* if flags has MEM_ACCESS_GLA_VALID set */ > +}; > + > +struct mem_event_mov_to_cr { > + uint64_t new_value; > + uint64_t old_value; > +}; > > +struct mem_event_debug { > + uint32_t gfn; > + uint32_t _pad; > +}; > + > +struct mem_event_mov_to_msr { > + uint64_t msr; > + uint64_t value; > +}; > + > +#define MEM_PAGING_DROP_PAGE (1 << 0) > +#define MEM_PAGING_EVICT_FAIL (1 << 1) > +struct mem_event_paging { > + uint32_t gfn; > + uint32_t p2mt; > + uint32_t flags; > + uint32_t _pad; > +}; > + > +struct mem_event_sharing { > + uint32_t gfn; > uint32_t p2mt; > +}; > + > +typedef struct mem_event_st { > + uint32_t version; /* MEM_EVENT_INTERFACE_VERSION */ > + uint32_t flags; /* MEM_EVENT_FLAG_* */ > + uint32_t reason; /* MEM_EVENT_REASON_* */ > + uint32_t vcpu_id; > > - uint16_t access_r:1; > - uint16_t access_w:1; > - uint16_t access_x:1; > - uint16_t gla_valid:1; > - uint16_t fault_with_gla:1; > - uint16_t fault_in_gpt:1; > - uint16_t available:10; > + union { > + struct mem_event_paging mem_paging; > + struct mem_event_sharing mem_sharing; > + struct mem_event_mem_access mem_access; > + struct mem_event_mov_to_cr mov_to_cr; > + struct mem_event_mov_to_msr mov_to_msr; > + struct mem_event_debug software_breakpoint; > + struct mem_event_debug singlestep; > + } u; > > - uint16_t reason; > - struct mem_event_regs_x86 x86_regs; > + union { > + struct mem_event_regs_x86 x86; > + } regs; > } mem_event_request_t, mem_event_response_t; > > DEFINE_RING_TYPES(mem_event, mem_event_request_t, mem_event_response_t); > > -#endif > +#endif /* defined(__XEN__) || defined(__XEN_TOOLS__) */ > +#endif /* _XEN_PUBLIC_MEM_EVENT_H */ > > /* > * Local variables: > diff --git a/xen/include/public/memory.h b/xen/include/public/memory.h > index 595f953..2ef1728 100644 > --- a/xen/include/public/memory.h > +++ b/xen/include/public/memory.h > @@ -380,7 +380,8 @@ struct xen_mem_event_op { > /* PAGING_PREP IN: buffer to immediately fill page in */ > uint64_aligned_t buffer; > /* Other OPs */ > - uint64_aligned_t gfn; /* IN: gfn of page being operated on > */ > + uint32_t gfn; /* IN: gfn of page being operated on */ > + uint32_t _pad; > }; > typedef struct xen_mem_event_op xen_mem_event_op_t; > DEFINE_XEN_GUEST_HANDLE(xen_mem_event_op_t); > @@ -469,21 +470,21 @@ struct xen_mem_sharing_op { > union { > struct mem_sharing_op_nominate { /* OP_NOMINATE_xxx */ > union { > - uint64_aligned_t gfn; /* IN: gfn to nominate */ > + uint32_t gfn; /* IN: gfn to nominate */ > uint32_t grant_ref; /* IN: grant ref to nominate */ > } u; > uint64_aligned_t handle; /* OUT: the handle */ > } nominate; > struct mem_sharing_op_share { /* OP_SHARE/ADD_PHYSMAP */ > - uint64_aligned_t source_gfn; /* IN: the gfn of the source > page */ > + uint32_t source_gfn; /* IN: the gfn of the source page > */ > + uint32_t client_gfn; /* IN: the client gfn */ > uint64_aligned_t source_handle; /* IN: handle to the source page > */ > - uint64_aligned_t client_gfn; /* IN: the client gfn */ > uint64_aligned_t client_handle; /* IN: handle to the client page > */ > domid_t client_domain; /* IN: the client domain id */ > } share; > struct mem_sharing_op_debug { /* OP_DEBUG_xxx */ > union { > - uint64_aligned_t gfn; /* IN: gfn to debug */ > + uint32_t gfn; /* IN: gfn to debug */ > uint64_aligned_t mfn; /* IN: mfn to debug */ > uint32_t gref; /* IN: gref to debug */ > } u; _______________________________________________ Xen-devel mailing list Xen-devel@xxxxxxxxxxxxx http://lists.xen.org/xen-devel
|
Lists.xenproject.org is hosted with RackSpace, monitoring our |