diff --git a/hw/intc/armv7m_nvic.c b/hw/intc/armv7m_nvic.c
index 369ef94..5820414 100644
--- a/hw/intc/armv7m_nvic.c
+++ b/hw/intc/armv7m_nvic.c
@@ -289,6 +289,36 @@ static uint32_t nvic_readl(nvic_state *s, uint32_t offset)
         return 0x01111110;
     case 0xd70: /* ISAR4.  */
         return 0x01310102;
+    case 0xd90: /* MPU type register.  */
+        cpu = ARM_CPU(current_cpu);
+        return cpu->pmsav7_dregion << 8;
+    case 0xd94: /* MPU control register.  */
+        cpu = ARM_CPU(current_cpu);
+        return cpu->env.v7m.mpu_ctrl;
+    case 0xd98: /* MPU_RNR.  */
+        cpu = ARM_CPU(current_cpu);
+        return cpu->env.cp15.c6_rgnr;
+    case 0xd9c: /* MPU_RBAR: MPU region base address register.  */
+    case 0xda4: /* MPU_RBAR_A1.  */
+    case 0xdac: /* MPU_RBAR_A2.  */
+    case 0xdb4: /* MPU_RBAR_A3.  */
+        cpu = ARM_CPU(current_cpu);
+        if (cpu->pmsav7_dregion == 0)
+            return 0;
+        val = cpu->env.pmsav7.drbar[cpu->env.cp15.c6_rgnr];
+        val |= cpu->env.cp15.c6_rgnr;
+        return val;
+    case 0xda0: /* MPU_RSAR: MPU region attribute and size register.  */
+    case 0xda8: /* MPU_RSAR_A1.  */
+    case 0xdb0: /* MPU_RSAR_A2.  */
+    case 0xdb8: /* MPU_RSAR_A3.  */
+        cpu = ARM_CPU(current_cpu);
+        if (cpu->pmsav7_dregion == 0)
+            return 0;
+        val = cpu->env.pmsav7.dracr[cpu->env.cp15.c6_rgnr];
+        val <<= 16;
+        val |= cpu->env.pmsav7.drsr[cpu->env.cp15.c6_rgnr];
+        return val;
     /* TODO: Implement debug registers.  */
     default:
         qemu_log_mask(LOG_GUEST_ERROR, "NVIC: Bad read offset 0x%x\n", offset);
@@ -406,6 +436,57 @@ static void nvic_writel(nvic_state *s, uint32_t offset, uint32_t value)
         qemu_log_mask(LOG_UNIMP,
                       "NVIC: AUX fault status registers unimplemented\n");
         break;
+    case 0xd94: /* MPU control register.  */
+        cpu = ARM_CPU(current_cpu);
+        if (cpu->pmsav7_dregion == 0)
+            break;
+        cpu->env.v7m.mpu_ctrl = value & 0x7;
+        if (cpu->env.v7m.mpu_ctrl & MPU_CTRL_ENABLE)
+            cpu->env.cp15.sctlr_ns |= SCTLR_M;
+        else
+            cpu->env.cp15.sctlr_ns &= ~SCTLR_M;
+        /* TODO: mimic MPU_CTRL_HFNMIENA */
+        if (cpu->env.v7m.mpu_ctrl & MPU_CTRL_PRIVDEFENA)
+            cpu->env.cp15.sctlr_ns |= SCTLR_BR;
+        else
+            cpu->env.cp15.sctlr_ns &= ~SCTLR_BR;
+        /* This may enable/disable the MMU, so do a TLB flush.  */
+        tlb_flush(CPU(cpu), 1);
+        break;
+    case 0xd98: /* MPU_RNR.  */
+        cpu = ARM_CPU(current_cpu);
+        value &= 0xff;
+        if (value < cpu->pmsav7_dregion)
+            cpu->env.cp15.c6_rgnr = value;
+        break;
+    case 0xd9c: /* MPU_RBAR: MPU region base address register.  */
+    case 0xda4: /* MPU_RBAR_A1.  */
+    case 0xdac: /* MPU_RBAR_A2.  */
+    case 0xdb4: /* MPU_RBAR_A3.  */
+        cpu = ARM_CPU(current_cpu);
+        if (cpu->pmsav7_dregion == 0)
+            break;
+        if (value & 0x10) {
+            /* region update */
+            uint32_t region = value & 0x0f;
+            if (region < cpu->pmsav7_dregion)
+                cpu->env.cp15.c6_rgnr = region;
+        }
+        value &= ~0x1f;
+        cpu->env.pmsav7.drbar[cpu->env.cp15.c6_rgnr] = value;
+        tlb_flush(CPU(cpu), 1); /* Mappings may have changed - purge! */
+        break;
+    case 0xda0: /* MPU_RSAR: MPU region attribute and size register.  */
+    case 0xda8: /* MPU_RSAR_A1.  */
+    case 0xdb0: /* MPU_RSAR_A2.  */
+    case 0xdb8: /* MPU_RSAR_A3.  */
+        cpu = ARM_CPU(current_cpu);
+        if (cpu->pmsav7_dregion == 0)
+            break;
+        cpu->env.pmsav7.dracr[cpu->env.cp15.c6_rgnr] = value >> 16;
+        cpu->env.pmsav7.drsr[cpu->env.cp15.c6_rgnr] = value & 0xffff;
+        tlb_flush(CPU(cpu), 1); /* Mappings may have changed - purge! */
+        break;
     case 0xf00: /* Software Triggered Interrupt Register */
         if ((value & 0x1ff) < s->num_irq) {
             gic_set_pending_private(&s->gic, 0, value & 0x1ff);
@@ -445,6 +526,19 @@ static uint64_t nvic_sysreg_read(void *opaque, hwaddr addr,
             return val & 0xffff;
         }
         break;
+    case 0xda0 ... 0xdb7: /* MPU_RSAR and aliases.  */
+        cpu = ARM_CPU(current_cpu);
+        if (cpu->pmsav7_dregion == 0)
+            break;
+        if ((size == 2) && (offset & 7) == 0) {
+            val = cpu->env.pmsav7.drsr[cpu->env.cp15.c6_rgnr];
+            return val & 0xffff;
+        }
+        if ((size == 2) && (offset & 7) == 2) {
+            val = cpu->env.pmsav7.dracr[cpu->env.cp15.c6_rgnr];
+            return val & 0xffff;
+        }
+        break;
     case 0xfe0 ... 0xfff: /* ID.  */
         if (offset & 3) {
             return 0;
@@ -465,6 +559,7 @@ static void nvic_sysreg_write(void *opaque, hwaddr addr,
     nvic_state *s = (nvic_state *)opaque;
     uint32_t offset = addr;
     int i;
+    ARMCPU *cpu;
 
     switch (offset) {
     case 0xd18 ... 0xd23: /* System Handler Priority.  */
@@ -488,6 +583,24 @@ static void nvic_sysreg_write(void *opaque, hwaddr addr,
             break;
         }
         break;
+    case 0xda0 ... 0xdb7: /* MPU_RSAR and aliases.  */
+        cpu = ARM_CPU(current_cpu);
+        if (cpu->pmsav7_dregion == 0)
+            break;
+        if ((size == 2) && (offset & 7) == 0) {
+            value |= cpu->env.pmsav7.dracr[cpu->env.cp15.c6_rgnr] << 16;
+            offset &= ~2;
+            size = 4;
+            break;
+        }
+        if ((size == 2) && (offset & 7) == 2) {
+            value <<= 16;
+            value |= cpu->env.pmsav7.drsr[cpu->env.cp15.c6_rgnr];
+            offset &= ~2;
+            size = 4;
+            break;
+        }
+        break;
     }
     if (size == 4) {
         nvic_writel(s, offset, value);
diff --git a/target-arm/cpu.h b/target-arm/cpu.h
index 1089f63..d603f71 100644
--- a/target-arm/cpu.h
+++ b/target-arm/cpu.h
@@ -393,6 +393,7 @@ typedef struct CPUARMState {
         uint32_t dfsr;
         uint32_t mmfar;
         uint32_t bfar;
+        uint32_t mpu_ctrl;
     } v7m;
 
     /* Information associated with an exception about to be taken:
@@ -903,6 +904,11 @@ enum arm_cpu_mode {
 #define DFSR_BKPT           0x00000002
 #define DFSR_HALTED         0x00000001
 
+/* V7M MPU_CTRL bits */
+#define MPU_CTRL_PRIVDEFENA 0x00000004
+#define MPU_CTRL_HFNMIENA   0x00000002
+#define MPU_CTRL_ENABLE     0x00000001
+
 /* If adding a feature bit which corresponds to a Linux ELF
  * HWCAP bit, remember to update the feature-bit-to-hwcap
  * mapping in linux-user/elfload.c:get_elf_hwcap().
diff --git a/target-arm/helper.c b/target-arm/helper.c
index 637dbf6..542e075 100644
--- a/target-arm/helper.c
+++ b/target-arm/helper.c
@@ -4552,7 +4552,10 @@ void arm_v7m_cpu_do_interrupt(CPUState *cs)
     case EXCP_DATA_ABORT:
         env->v7m.mmfar = env->exception.vaddress;
         env->v7m.cfsr |= CFSR_MMARVALID;
-        /* TODO: further decoding of exception.fsr */
+        if (cs->exception_index == EXCP_PREFETCH_ABORT)
+            env->v7m.cfsr |= CFSR_IACCVIOL;
+        else
+            env->v7m.cfsr |= CFSR_DACCVIOL;
         armv7m_nvic_set_pending(env->nvic, ARMV7M_EXCP_MEM);
         return;
     case EXCP_BKPT:
@@ -5973,6 +5976,8 @@ static bool get_phys_addr_pmsav7(CPUARMState *env, uint32_t address,
                 get_phys_addr_pmsav7_default(env, mmu_idx, address, prot);
         } else { /* a MPU hit! */
             uint32_t ap = extract32(env->pmsav7.dracr[n], 8, 3);
+            if ((ap == 7) && arm_feature(env, ARM_FEATURE_M))
+                ap = 6;
 
             if (is_user) { /* User mode AP bit decoding */
                 switch (ap) {
diff --git a/target-arm/machine.c b/target-arm/machine.c
index 11dcf29..0bdeaba 100644
--- a/target-arm/machine.c
+++ b/target-arm/machine.c
@@ -107,6 +107,7 @@ static const VMStateDescription vmstate_m = {
         VMSTATE_UINT32(env.v7m.dfsr, ARMCPU),
         VMSTATE_UINT32(env.v7m.mmfar, ARMCPU),
         VMSTATE_UINT32(env.v7m.bfar, ARMCPU),
+        VMSTATE_UINT32(env.v7m.mpu_ctrl, ARMCPU),
         VMSTATE_END_OF_LIST()
     }
 };
