From 507c9e76fbb099599f606549f4f31f9cde3d2762 Mon Sep 17 00:00:00 2001 From: Keir Fraser Date: Thu, 17 Dec 2009 06:27:55 +0000 Subject: Memory paging support for HVM guest emulation. A new HVMCOPY return value, HVMCOPY_gfn_paged_out is defined to indicate that a gfn was paged out. This value and PFEC_page_paged, as appropriate, are caught and passed up as X86EMUL_RETRY to the emulator. This will cause the emulator to keep retrying the operation until is succeeds (once the page has been paged in). Signed-off-by: Patrick Colp --- xen/arch/x86/hvm/emulate.c | 21 ++++++++++++++++++++ xen/arch/x86/hvm/hvm.c | 22 +++++++++++++++++++++ xen/arch/x86/hvm/intercept.c | 41 ++++++++++++++++++++++----------------- xen/arch/x86/hvm/io.c | 34 +++++++++++++++++++++----------- xen/include/asm-x86/hvm/support.h | 3 ++- 5 files changed, 91 insertions(+), 30 deletions(-) diff --git a/xen/arch/x86/hvm/emulate.c b/xen/arch/x86/hvm/emulate.c index dc7a6b5f89..a636c9bef6 100644 --- a/xen/arch/x86/hvm/emulate.c +++ b/xen/arch/x86/hvm/emulate.c @@ -56,8 +56,19 @@ static int hvmemul_do_io( int value_is_ptr = (p_data == NULL); struct vcpu *curr = current; ioreq_t *p = get_ioreq(curr); + unsigned long ram_gfn = paddr_to_pfn(ram_gpa); + p2m_type_t p2mt; + mfn_t ram_mfn; int rc; + /* Check for paged out page */ + ram_mfn = gfn_to_mfn(current->domain, ram_gfn, &p2mt); + if ( p2m_is_paging(p2mt) ) + { + p2m_mem_paging_populate(curr->domain, ram_gfn); + return X86EMUL_RETRY; + } + /* * Weird-sized accesses have undefined behaviour: we discard writes * and read all-ones. @@ -271,6 +282,8 @@ static int hvmemul_linear_to_phys( } else if ( (pfn = paging_gva_to_gfn(curr, addr, &pfec)) == INVALID_GFN ) { + if ( pfec == PFEC_page_paged ) + return X86EMUL_RETRY; hvm_inject_exception(TRAP_page_fault, pfec, addr); return X86EMUL_EXCEPTION; } @@ -286,6 +299,8 @@ static int hvmemul_linear_to_phys( /* Is it contiguous with the preceding PFNs? If not then we're done. */ if ( (npfn == INVALID_GFN) || (npfn != (pfn + (reverse ? -i : i))) ) { + if ( pfec == PFEC_page_paged ) + return X86EMUL_RETRY; done /= bytes_per_rep; if ( done == 0 ) { @@ -424,6 +439,8 @@ static int __hvmemul_read( if ( rc != X86EMUL_OKAY ) return rc; return hvmemul_do_mmio(gpa, &reps, bytes, 0, IOREQ_READ, 0, p_data); + case HVMCOPY_gfn_paged_out: + return X86EMUL_RETRY; default: break; } @@ -514,6 +531,8 @@ static int hvmemul_write( return rc; return hvmemul_do_mmio(gpa, &reps, bytes, 0, IOREQ_WRITE, 0, p_data); + case HVMCOPY_gfn_paged_out: + return X86EMUL_RETRY; default: break; } @@ -687,6 +706,8 @@ static int hvmemul_rep_movs( xfree(buf); + if ( rc == HVMCOPY_gfn_paged_out ) + return X86EMUL_RETRY; if ( rc != HVMCOPY_okay ) { gdprintk(XENLOG_WARNING, "Failed memory-to-memory REP MOVS: sgpa=%" diff --git a/xen/arch/x86/hvm/hvm.c b/xen/arch/x86/hvm/hvm.c index c898ad69fb..1477099fe1 100644 --- a/xen/arch/x86/hvm/hvm.c +++ b/xen/arch/x86/hvm/hvm.c @@ -314,6 +314,11 @@ static int hvm_set_ioreq_page( mfn = mfn_x(gfn_to_mfn(d, gmfn, &p2mt)); if ( !p2m_is_ram(p2mt) ) return -EINVAL; + if ( p2m_is_paging(p2mt) ) + { + p2m_mem_paging_populate(d, gmfn); + return -ENOENT; + } ASSERT(mfn_valid(mfn)); page = mfn_to_page(mfn); @@ -1318,6 +1323,8 @@ static void *hvm_map_entry(unsigned long va) * we still treat it as a kernel-mode read (i.e. no access checks). */ pfec = PFEC_page_present; gfn = paging_gva_to_gfn(current, va, &pfec); + if ( pfec == PFEC_page_paged ) + return NULL; mfn = mfn_x(gfn_to_mfn_current(gfn, &p2mt)); if ( p2m_is_paging(p2mt) ) { @@ -1546,6 +1553,8 @@ void hvm_task_switch( &tss, prev_tr.base, sizeof(tss), PFEC_page_present); if ( rc == HVMCOPY_bad_gva_to_gfn ) goto out; + if ( rc == HVMCOPY_gfn_paged_out ) + goto out; eflags = regs->eflags; if ( taskswitch_reason == TSW_iret ) @@ -1582,11 +1591,15 @@ void hvm_task_switch( prev_tr.base, &tss, sizeof(tss), PFEC_page_present); if ( rc == HVMCOPY_bad_gva_to_gfn ) goto out; + if ( rc == HVMCOPY_gfn_paged_out ) + goto out; rc = hvm_copy_from_guest_virt( &tss, tr.base, sizeof(tss), PFEC_page_present); if ( rc == HVMCOPY_bad_gva_to_gfn ) goto out; + if ( rc == HVMCOPY_gfn_paged_out ) + goto out; if ( hvm_set_cr3(tss.cr3) ) goto out; @@ -1622,6 +1635,8 @@ void hvm_task_switch( tr.base, &tss, sizeof(tss), PFEC_page_present); if ( rc == HVMCOPY_bad_gva_to_gfn ) exn_raised = 1; + if ( rc == HVMCOPY_gfn_paged_out ) + goto out; if ( (tss.trace & 1) && !exn_raised ) hvm_inject_exception(TRAP_debug, tss_sel & 0xfff8, 0); @@ -1681,6 +1696,8 @@ static enum hvm_copy_result __hvm_copy( gfn = paging_gva_to_gfn(curr, addr, &pfec); if ( gfn == INVALID_GFN ) { + if ( pfec == PFEC_page_paged ) + return HVMCOPY_gfn_paged_out; if ( flags & HVMCOPY_fault ) hvm_inject_exception(TRAP_page_fault, pfec, addr); return HVMCOPY_bad_gva_to_gfn; @@ -1693,6 +1710,11 @@ static enum hvm_copy_result __hvm_copy( mfn = mfn_x(gfn_to_mfn_current(gfn, &p2mt)); + if ( p2m_is_paging(p2mt) ) + { + p2m_mem_paging_populate(curr->domain, gfn); + return HVMCOPY_gfn_paged_out; + } if ( p2m_is_grant(p2mt) ) return HVMCOPY_unhandleable; if ( !p2m_is_ram(p2mt) ) diff --git a/xen/arch/x86/hvm/intercept.c b/xen/arch/x86/hvm/intercept.c index 06421fe462..841dec8921 100644 --- a/xen/arch/x86/hvm/intercept.c +++ b/xen/arch/x86/hvm/intercept.c @@ -72,30 +72,31 @@ static int hvm_mmio_access(struct vcpu *v, { for ( i = 0; i < p->count; i++ ) { - rc = read_handler( - v, - p->addr + (sign * i * p->size), - p->size, &data); + rc = read_handler(v, p->addr + (sign * i * p->size), p->size, + &data); if ( rc != X86EMUL_OKAY ) break; - (void)hvm_copy_to_guest_phys( - p->data + (sign * i * p->size), - &data, - p->size); + if ( hvm_copy_to_guest_phys(p->data + (sign * i * p->size), &data, + p->size) == HVMCOPY_gfn_paged_out ) + { + rc = X86EMUL_RETRY; + break; + } } } else { for ( i = 0; i < p->count; i++ ) { - (void)hvm_copy_from_guest_phys( - &data, - p->data + (sign * i * p->size), - p->size); - rc = write_handler( - v, - p->addr + (sign * i * p->size), - p->size, data); + if ( hvm_copy_from_guest_phys(&data, + p->data + (sign * i * p->size), + p->size) == HVMCOPY_gfn_paged_out ) + { + rc = X86EMUL_RETRY; + break; + } + rc = write_handler(v, p->addr + (sign * i * p->size), p->size, + data); if ( rc != X86EMUL_OKAY ) break; } @@ -190,8 +191,12 @@ int hvm_io_intercept(ioreq_t *p, int type) int i; unsigned long addr, size; - if ( (type == HVM_PORTIO) && (dpci_ioport_intercept(p)) ) - return X86EMUL_OKAY; + if ( type == HVM_PORTIO ) + { + int rc = dpci_ioport_intercept(p); + if ( (rc == X86EMUL_OKAY) || (rc == X86EMUL_RETRY) ) + return rc; + } for ( i = 0; i < handler->num_slot; i++ ) { diff --git a/xen/arch/x86/hvm/io.c b/xen/arch/x86/hvm/io.c index 8519bfad51..29079f296d 100644 --- a/xen/arch/x86/hvm/io.c +++ b/xen/arch/x86/hvm/io.c @@ -239,7 +239,7 @@ void hvm_io_assist(void) vcpu_end_shutdown_deferral(curr); } -static void dpci_ioport_read(uint32_t mport, ioreq_t *p) +static int dpci_ioport_read(uint32_t mport, ioreq_t *p) { int i, sign = p->df ? -1 : 1; uint32_t data = 0; @@ -262,14 +262,19 @@ static void dpci_ioport_read(uint32_t mport, ioreq_t *p) } if ( p->data_is_ptr ) - (void)hvm_copy_to_guest_phys( - p->data + (sign * i * p->size), &data, p->size); + { + if ( hvm_copy_to_guest_phys(p->data + (sign * i * p->size), &data, + p->size) == HVMCOPY_gfn_paged_out ) + return X86EMUL_RETRY; + } else p->data = data; } + + return X86EMUL_OKAY; } -static void dpci_ioport_write(uint32_t mport, ioreq_t *p) +static int dpci_ioport_write(uint32_t mport, ioreq_t *p) { int i, sign = p->df ? -1 : 1; uint32_t data; @@ -278,8 +283,11 @@ static void dpci_ioport_write(uint32_t mport, ioreq_t *p) { data = p->data; if ( p->data_is_ptr ) - (void)hvm_copy_from_guest_phys( - &data, p->data + (sign * i * p->size), p->size); + { + if ( hvm_copy_from_guest_phys(&data, p->data + (sign * i * p->size), + p->size) == HVMCOPY_gfn_paged_out ) + return X86EMUL_RETRY; + } switch ( p->size ) { @@ -296,6 +304,8 @@ static void dpci_ioport_write(uint32_t mport, ioreq_t *p) BUG(); } } + + return X86EMUL_OKAY; } int dpci_ioport_intercept(ioreq_t *p) @@ -305,6 +315,7 @@ int dpci_ioport_intercept(ioreq_t *p) struct g2m_ioport *g2m_ioport; unsigned int mport, gport = p->addr; unsigned int s = 0, e = 0; + int rc; list_for_each_entry( g2m_ioport, &hd->g2m_ioport_list, list ) { @@ -314,7 +325,7 @@ int dpci_ioport_intercept(ioreq_t *p) goto found; } - return 0; + return X86EMUL_UNHANDLEABLE; found: mport = (gport - s) + g2m_ioport->mport; @@ -323,22 +334,23 @@ int dpci_ioport_intercept(ioreq_t *p) { gdprintk(XENLOG_ERR, "Error: access to gport=0x%x denied!\n", (uint32_t)p->addr); - return 0; + return X86EMUL_UNHANDLEABLE; } switch ( p->dir ) { case IOREQ_READ: - dpci_ioport_read(mport, p); + rc = dpci_ioport_read(mport, p); break; case IOREQ_WRITE: - dpci_ioport_write(mport, p); + rc = dpci_ioport_write(mport, p); break; default: gdprintk(XENLOG_ERR, "Error: couldn't handle p->dir = %d", p->dir); + rc = X86EMUL_UNHANDLEABLE; } - return 1; + return rc; } /* diff --git a/xen/include/asm-x86/hvm/support.h b/xen/include/asm-x86/hvm/support.h index a3e41655f3..e98daca937 100644 --- a/xen/include/asm-x86/hvm/support.h +++ b/xen/include/asm-x86/hvm/support.h @@ -72,7 +72,8 @@ enum hvm_copy_result { HVMCOPY_okay = 0, HVMCOPY_bad_gva_to_gfn, HVMCOPY_bad_gfn_to_mfn, - HVMCOPY_unhandleable + HVMCOPY_unhandleable, + HVMCOPY_gfn_paged_out, }; /* -- cgit v1.2.3