diff options
author | Jan Beulich <jbeulich@suse.com> | 2013-10-14 09:51:40 +0200 |
---|---|---|
committer | Jan Beulich <jbeulich@suse.com> | 2013-10-14 09:51:40 +0200 |
commit | 82ed8716b08ccf2f6239262ecc269c5cd60ef5dd (patch) | |
tree | 593cea15dea26f5bffe14320c7cfc2e936088f3c /xen/arch/x86/hvm/io.c | |
parent | f21399e148386ecf3826ab81159eca58cfab2147 (diff) | |
download | xen-82ed8716b08ccf2f6239262ecc269c5cd60ef5dd.tar.gz xen-82ed8716b08ccf2f6239262ecc269c5cd60ef5dd.tar.bz2 xen-82ed8716b08ccf2f6239262ecc269c5cd60ef5dd.zip |
x86/HVM: fix direct PCI port I/O emulation retry and error handling
dpci_ioport_{read,write}() guest memory access failure handling should
be modelled after process_portio_intercept()'s (and others): Upon
encountering an error on other than the first iteration, the count
successfully handled needs to be stored and X86EMUL_OKAY returned, in
order for the generic instruction emulator to update register state
correctly before reporting failure or retrying (both of which would
only happen after re-invoking emulation).
Further we leverage (and slightly extend, due to the above mentioned
need to return X86EMUL_OKAY) the "large MMIO" retry model.
Note that there is still a special case not explicitly taken care of
here: While the first retry on the last iteration of a "rep ins"
correctly recovers the already read data, an eventual subsequent retry
is being handled by the pre-existing mmio-large logic (through
hvmemul_do_io() storing the [recovered] data [again], also taking into
consideration that the emulator converts a single iteration "ins" to
->read_io() plus ->write()).
Also fix an off-by-one in the mmio-large-read logic, and slightly
simplify the copying of the data.
Signed-off-by: Jan Beulich <jbeulich@suse.com>
Reviewed-by: Andrew Cooper <andrew.cooper3@citrix.com>
Acked-by: Keir Fraser <keir@xen.org>
Diffstat (limited to 'xen/arch/x86/hvm/io.c')
-rw-r--r-- | xen/arch/x86/hvm/io.c | 77 |
1 files changed, 64 insertions, 13 deletions
diff --git a/xen/arch/x86/hvm/io.c b/xen/arch/x86/hvm/io.c index 77698c4db2..6e344e4f49 100644 --- a/xen/arch/x86/hvm/io.c +++ b/xen/arch/x86/hvm/io.c @@ -294,12 +294,21 @@ void hvm_io_assist(void) static int dpci_ioport_read(uint32_t mport, ioreq_t *p) { - int i, step = p->df ? -p->size : p->size; + struct hvm_vcpu_io *vio = ¤t->arch.hvm_vcpu.hvm_io; + int rc = X86EMUL_OKAY, i, step = p->df ? -p->size : p->size; uint32_t data = 0; for ( i = 0; i < p->count; i++ ) { - switch ( p->size ) + if ( vio->mmio_retrying ) + { + if ( vio->mmio_large_read_bytes != p->size ) + return X86EMUL_UNHANDLEABLE; + memcpy(&data, vio->mmio_large_read, p->size); + vio->mmio_large_read_bytes = 0; + vio->mmio_retrying = 0; + } + else switch ( p->size ) { case 1: data = inb(mport); @@ -316,22 +325,51 @@ static int dpci_ioport_read(uint32_t mport, ioreq_t *p) if ( p->data_is_ptr ) { - int ret; - ret = hvm_copy_to_guest_phys(p->data + step * i, &data, p->size); - if ( (ret == HVMCOPY_gfn_paged_out) || - (ret == HVMCOPY_gfn_shared) ) - return X86EMUL_RETRY; + switch ( hvm_copy_to_guest_phys(p->data + step * i, + &data, p->size) ) + { + case HVMCOPY_okay: + break; + case HVMCOPY_gfn_paged_out: + case HVMCOPY_gfn_shared: + rc = X86EMUL_RETRY; + break; + case HVMCOPY_bad_gfn_to_mfn: + /* Drop the write as real hardware would. */ + continue; + case HVMCOPY_bad_gva_to_gfn: + ASSERT(0); + /* fall through */ + default: + rc = X86EMUL_UNHANDLEABLE; + break; + } + if ( rc != X86EMUL_OKAY) + break; } else p->data = data; } - - return X86EMUL_OKAY; + + if ( rc == X86EMUL_RETRY ) + { + vio->mmio_retry = 1; + vio->mmio_large_read_bytes = p->size; + memcpy(vio->mmio_large_read, &data, p->size); + } + + if ( i != 0 ) + { + p->count = i; + rc = X86EMUL_OKAY; + } + + return rc; } static int dpci_ioport_write(uint32_t mport, ioreq_t *p) { - int i, step = p->df ? -p->size : p->size; + int rc = X86EMUL_OKAY, i, step = p->df ? -p->size : p->size; uint32_t data; for ( i = 0; i < p->count; i++ ) @@ -346,7 +384,8 @@ static int dpci_ioport_write(uint32_t mport, ioreq_t *p) break; case HVMCOPY_gfn_paged_out: case HVMCOPY_gfn_shared: - return X86EMUL_RETRY; + rc = X86EMUL_RETRY; + break; case HVMCOPY_bad_gfn_to_mfn: data = ~0; break; @@ -354,8 +393,11 @@ static int dpci_ioport_write(uint32_t mport, ioreq_t *p) ASSERT(0); /* fall through */ default: - return X86EMUL_UNHANDLEABLE; + rc = X86EMUL_UNHANDLEABLE; + break; } + if ( rc != X86EMUL_OKAY) + break; } switch ( p->size ) @@ -374,7 +416,16 @@ static int dpci_ioport_write(uint32_t mport, ioreq_t *p) } } - return X86EMUL_OKAY; + if ( rc == X86EMUL_RETRY ) + current->arch.hvm_vcpu.hvm_io.mmio_retry = 1; + + if ( i != 0 ) + { + p->count = i; + rc = X86EMUL_OKAY; + } + + return rc; } int dpci_ioport_intercept(ioreq_t *p) |