armv7m: decide whether faults are MemManage or BusFault

General logic is that operations stopped by the MPU are MemManage,
and those which go through the MPU and are caught by the unassigned
handle are BusFault.
Signed-off-by: Michael Davidsaver <mdavidsaver@gmail.com>
diff --git a/target/arm/helper.c b/target/arm/helper.c
index 559ea48..f3966e8 100644
--- a/target/arm/helper.c
+++ b/target/arm/helper.c
@@ -6159,12 +6159,35 @@
         break;
     case EXCP_PREFETCH_ABORT:
     case EXCP_DATA_ABORT:
-        /* TODO: if we implemented the MPU registers, this is where we
-         * should set the MMFAR, etc from exception.fsr and exception.vaddress.
-         */
-        armv7m_nvic_set_pending(env->nvic, ARMV7M_EXCP_MEM);
-        env->v7m.mmfar = env->exception.vaddress;
-        env->v7m.cfsr = (1<<1)|(1<<7); /* DACCVIOL and MMARVALID */
+        switch (env->exception.fsr & 0xf) {
+        case 0x8: /* External Abort */
+            switch (cs->exception_index) {
+            case EXCP_PREFETCH_ABORT:
+                env->v7m.cfsr |= (1<<(8+1)); /* PRECISERR */
+                break;
+            case EXCP_DATA_ABORT:
+                env->v7m.cfsr |= (1<<(8+0)); /* IBUSERR */
+                break;
+            }
+            armv7m_nvic_set_pending(env->nvic, ARMV7M_EXCP_BUS);
+            env->v7m.bfar = env->exception.vaddress;
+            env->v7m.cfsr |= (1<<(8+7)); /* BFARVALID */
+            break;
+        case 0xd: /* Permission fault */
+        default:
+            switch (cs->exception_index) {
+            case EXCP_PREFETCH_ABORT:
+                env->v7m.cfsr |= (1<<0); /* IACCVIOL */
+                break;
+            case EXCP_DATA_ABORT:
+                env->v7m.cfsr |= (1<<1); /* DACCVIOL */
+                break;
+            }
+            armv7m_nvic_set_pending(env->nvic, ARMV7M_EXCP_MEM);
+            env->v7m.mmfar = env->exception.vaddress;
+            env->v7m.cfsr |= (1<<7); /* MMARVALID */
+            break;
+        }
         break;
     case EXCP_BKPT:
         if (semihosting_enabled()) {