diff --git a/xen/arch/arm/io.c b/xen/arch/arm/io.c
index c3e9239ffe..1f4cb8f37d 100644
--- a/xen/arch/arm/io.c
+++ b/xen/arch/arm/io.c
@@ -26,8 +26,9 @@
 
 #include "decode.h"
 
-static int handle_read(const struct mmio_handler *handler, struct vcpu *v,
-                       mmio_info_t *info)
+static enum io_state handle_read(const struct mmio_handler *handler,
+                                 struct vcpu *v,
+                                 mmio_info_t *info)
 {
     const struct hsr_dabt dabt = info->dabt;
     struct cpu_user_regs *regs = guest_cpu_user_regs();
@@ -40,7 +41,7 @@ static int handle_read(const struct mmio_handler *handler, struct vcpu *v,
     uint8_t size = (1 << dabt.size) * 8;
 
     if ( !handler->ops->read(v, info, &r, handler->priv) )
-        return 0;
+        return IO_ABORT;
 
     /*
      * Sign extend if required.
@@ -60,17 +61,20 @@ static int handle_read(const struct mmio_handler *handler, struct vcpu *v,
 
     set_user_reg(regs, dabt.reg, r);
 
-    return 1;
+    return IO_HANDLED;
 }
 
-static int handle_write(const struct mmio_handler *handler, struct vcpu *v,
-                        mmio_info_t *info)
+static enum io_state handle_write(const struct mmio_handler *handler,
+                                  struct vcpu *v,
+                                  mmio_info_t *info)
 {
     const struct hsr_dabt dabt = info->dabt;
     struct cpu_user_regs *regs = guest_cpu_user_regs();
+    int ret;
 
-    return handler->ops->write(v, info, get_user_reg(regs, dabt.reg),
-                               handler->priv);
+    ret = handler->ops->write(v, info, get_user_reg(regs, dabt.reg),
+                              handler->priv);
+    return ( ret ) ? IO_HANDLED : IO_ABORT;
 }
 
 /* This function assumes that mmio regions are not overlapped */
@@ -103,9 +107,9 @@ static const struct mmio_handler *find_mmio_handler(struct domain *d,
     return handler;
 }
 
-int try_handle_mmio(struct cpu_user_regs *regs,
-                    const union hsr hsr,
-                    paddr_t gpa)
+enum io_state try_handle_mmio(struct cpu_user_regs *regs,
+                              const union hsr hsr,
+                              paddr_t gpa)
 {
     struct vcpu *v = current;
     const struct mmio_handler *handler = NULL;
@@ -119,11 +123,11 @@ int try_handle_mmio(struct cpu_user_regs *regs,
 
     handler = find_mmio_handler(v->domain, info.gpa);
     if ( !handler )
-        return 0;
+        return IO_UNHANDLED;
 
     /* All the instructions used on emulated MMIO region should be valid */
     if ( !dabt.valid )
-        return 0;
+        return IO_ABORT;
 
     /*
      * Erratum 766422: Thumb store translation fault to Hypervisor may
@@ -138,7 +142,7 @@ int try_handle_mmio(struct cpu_user_regs *regs,
         if ( rc )
         {
             gprintk(XENLOG_DEBUG, "Unable to decode instruction\n");
-            return 0;
+            return IO_ABORT;
         }
     }
 
diff --git a/xen/arch/arm/traps.c b/xen/arch/arm/traps.c
index 2f8d790bb3..1e85f99ec1 100644
--- a/xen/arch/arm/traps.c
+++ b/xen/arch/arm/traps.c
@@ -1964,10 +1964,21 @@ static void do_trap_stage2_abort_guest(struct cpu_user_regs *regs,
          *
          * Note that emulated region cannot be executed
          */
-        if ( is_data && try_handle_mmio(regs, hsr, gpa) )
+        if ( is_data )
         {
-            advance_pc(regs, hsr);
-            return;
+            enum io_state state = try_handle_mmio(regs, hsr, gpa);
+
+            switch ( state )
+            {
+            case IO_ABORT:
+                goto inject_abt;
+            case IO_HANDLED:
+                advance_pc(regs, hsr);
+                return;
+            case IO_UNHANDLED:
+                /* IO unhandled, try another way to handle it. */
+                break;
+            }
         }
 
         /*
@@ -1988,6 +1999,7 @@ static void do_trap_stage2_abort_guest(struct cpu_user_regs *regs,
                 hsr.bits, xabt.fsc);
     }
 
+inject_abt:
     gdprintk(XENLOG_DEBUG, "HSR=0x%x pc=%#"PRIregister" gva=%#"PRIvaddr
              " gpa=%#"PRIpaddr"\n", hsr.bits, regs->pc, gva, gpa);
     if ( is_data )
diff --git a/xen/include/asm-arm/mmio.h b/xen/include/asm-arm/mmio.h
index c941073257..c8dadb5006 100644
--- a/xen/include/asm-arm/mmio.h
+++ b/xen/include/asm-arm/mmio.h
@@ -32,6 +32,13 @@ typedef struct
     paddr_t gpa;
 } mmio_info_t;
 
+enum io_state
+{
+    IO_ABORT,       /* The IO was handled by the helper and led to an abort. */
+    IO_HANDLED,     /* The IO was successfully handled by the helper. */
+    IO_UNHANDLED,   /* The IO was not handled by the helper. */
+};
+
 typedef int (*mmio_read_t)(struct vcpu *v, mmio_info_t *info,
                            register_t *r, void *priv);
 typedef int (*mmio_write_t)(struct vcpu *v, mmio_info_t *info,
@@ -56,9 +63,9 @@ struct vmmio {
     struct mmio_handler *handlers;
 };
 
-int try_handle_mmio(struct cpu_user_regs *regs,
-                    const union hsr hsr,
-                    paddr_t gpa);
+enum io_state try_handle_mmio(struct cpu_user_regs *regs,
+                              const union hsr hsr,
+                              paddr_t gpa);
 void register_mmio_handler(struct domain *d,
                            const struct mmio_handler_ops *ops,
                            paddr_t addr, paddr_t size, void *priv);
