aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorKeir Fraser <keir.fraser@citrix.com>2009-12-17 06:27:55 +0000
committerKeir Fraser <keir.fraser@citrix.com>2009-12-17 06:27:55 +0000
commit507c9e76fbb099599f606549f4f31f9cde3d2762 (patch)
tree074cf131a4d2a35680a7644d7d039ef8b9ff0a8c
parent8992ab0adfe9b708574759383f3798121fdf977c (diff)
downloadxen-507c9e76fbb099599f606549f4f31f9cde3d2762.tar.gz
xen-507c9e76fbb099599f606549f4f31f9cde3d2762.tar.bz2
xen-507c9e76fbb099599f606549f4f31f9cde3d2762.zip
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 <Patrick.Colp@citrix.com>
-rw-r--r--xen/arch/x86/hvm/emulate.c21
-rw-r--r--xen/arch/x86/hvm/hvm.c22
-rw-r--r--xen/arch/x86/hvm/intercept.c41
-rw-r--r--xen/arch/x86/hvm/io.c34
-rw-r--r--xen/include/asm-x86/hvm/support.h3
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,
};
/*