diff mbox

target-arm: ARMv4 emulation

Message ID E1NFZXZ-0003Wt-Mq@lists.gnu.org
State New
Headers show

Commit Message

Filip Navara Dec. 1, 2009, 8:41 p.m. UTC
While most of the ARMv5 instructions are backward compatible with ARMv4, there
are few important differences. Most notably the stack pop and load instructions
ignore the lowest bit, which is used by ARMv5 to switch to Thumb mode. A
base-updated data-abort model is used on ARM7TDMI, CP15 coprocessor is not
present and several instructions of later architectures are not implemented.

This patch introduces flags for the V5, CP15 and ABORT_BU (base-updated abort
model) features. When V5 feature is not set the bit 0 on POP, LD and LDM of PC
register is ignored and doesn't swith to/from Thumb mode and several
instructions are treated as unimplemented (BLX, PLD, BKPT, LDRD, STRD).

Added are processor definitions for ARM7TDMI and ARM920T.

Based on patches by Ulrich Hecht <uli@suse.de> and Vincent Sanders <vince@kyllikki.org>.

Signed-off-by: Filip Navara <filip.navara@gmail.com>
---
 target-arm/cpu.h       |    7 ++-
 target-arm/helper.c    |   31 ++++++++++++
 target-arm/translate.c |  119 ++++++++++++++++++++++++++++++++++-------------
 3 files changed, 123 insertions(+), 34 deletions(-)

Comments

Marc Andre Tanner Dec. 18, 2009, 9:32 p.m. UTC | #1
On Tue, Dec 01, 2009 at 09:41:43PM +0100, Filip Navara wrote:
> While most of the ARMv5 instructions are backward compatible with ARMv4, there
> are few important differences. Most notably the stack pop and load instructions
> ignore the lowest bit, which is used by ARMv5 to switch to Thumb mode. A
> base-updated data-abort model is used on ARM7TDMI, CP15 coprocessor is not
> present and several instructions of later architectures are not implemented.
> 
> This patch introduces flags for the V5, CP15 and ABORT_BU (base-updated abort
> model) features. When V5 feature is not set the bit 0 on POP, LD and LDM of PC
> register is ignored and doesn't swith to/from Thumb mode and several
> instructions are treated as unimplemented (BLX, PLD, BKPT, LDRD, STRD).
> 
> Added are processor definitions for ARM7TDMI and ARM920T.
> 
> Based on patches by Ulrich Hecht <uli@suse.de> and Vincent Sanders <vince@kyllikki.org>.

Could someone please review + apply this patch, bonus points if it ends
up in 0.12.

Thanks,
Marc
Aurelien Jarno Dec. 18, 2009, 10:05 p.m. UTC | #2
On Fri, Dec 18, 2009 at 10:32:37PM +0100, Marc Andre Tanner wrote:
> On Tue, Dec 01, 2009 at 09:41:43PM +0100, Filip Navara wrote:
> > While most of the ARMv5 instructions are backward compatible with ARMv4, there
> > are few important differences. Most notably the stack pop and load instructions
> > ignore the lowest bit, which is used by ARMv5 to switch to Thumb mode. A
> > base-updated data-abort model is used on ARM7TDMI, CP15 coprocessor is not
> > present and several instructions of later architectures are not implemented.
> > 
> > This patch introduces flags for the V5, CP15 and ABORT_BU (base-updated abort
> > model) features. When V5 feature is not set the bit 0 on POP, LD and LDM of PC
> > register is ignored and doesn't swith to/from Thumb mode and several
> > instructions are treated as unimplemented (BLX, PLD, BKPT, LDRD, STRD).
> > 
> > Added are processor definitions for ARM7TDMI and ARM920T.
> > 
> > Based on patches by Ulrich Hecht <uli@suse.de> and Vincent Sanders <vince@kyllikki.org>.
> 
> Could someone please review + apply this patch, bonus points if it ends
> up in 0.12.
> 

ARM stuff is usually reviewed by Paul Brook. Paul, do you plan to review
it, or should I do it?
Filip Navara Jan. 14, 2010, 9:55 a.m. UTC | #3
On Fri, Dec 18, 2009 at 11:05 PM, Aurelien Jarno <aurelien@aurel32.net>wrote:

> On Fri, Dec 18, 2009 at 10:32:37PM +0100, Marc Andre Tanner wrote:
> > On Tue, Dec 01, 2009 at 09:41:43PM +0100, Filip Navara wrote:
> > > While most of the ARMv5 instructions are backward compatible with
> ARMv4, there
> > > are few important differences. Most notably the stack pop and load
> instructions
> > > ignore the lowest bit, which is used by ARMv5 to switch to Thumb mode.
> A
> > > base-updated data-abort model is used on ARM7TDMI, CP15 coprocessor is
> not
> > > present and several instructions of later architectures are not
> implemented.
> > >
> > > This patch introduces flags for the V5, CP15 and ABORT_BU (base-updated
> abort
> > > model) features. When V5 feature is not set the bit 0 on POP, LD and
> LDM of PC
> > > register is ignored and doesn't swith to/from Thumb mode and several
> > > instructions are treated as unimplemented (BLX, PLD, BKPT, LDRD, STRD).
> > >
> > > Added are processor definitions for ARM7TDMI and ARM920T.
> > >
> > > Based on patches by Ulrich Hecht <uli@suse.de> and Vincent Sanders <
> vince@kyllikki.org>.
> >
> > Could someone please review + apply this patch, bonus points if it ends
> > up in 0.12.
> >
>
> ARM stuff is usually reviewed by Paul Brook. Paul, do you plan to review
> it, or should I do it?
>
> I would appreciate at least some progress on this patch. A "no, because..."
would be fine with me too.

Best regards,
Filip Navara
Laurent Desnogues Jan. 14, 2010, 10:11 a.m. UTC | #4
Since you wanted a "no, because..." there's at least
one :-)

On Tue, Dec 1, 2009 at 9:41 PM, Filip Navara <filip.navara@gmail.com> wrote:
> While most of the ARMv5 instructions are backward compatible with ARMv4, there
> are few important differences. Most notably the stack pop and load instructions
> ignore the lowest bit, which is used by ARMv5 to switch to Thumb mode. A
> base-updated data-abort model is used on ARM7TDMI, CP15 coprocessor is not
> present and several instructions of later architectures are not implemented.
>
> This patch introduces flags for the V5, CP15 and ABORT_BU (base-updated abort
> model) features. When V5 feature is not set the bit 0 on POP, LD and LDM of PC
> register is ignored and doesn't swith to/from Thumb mode and several
> instructions are treated as unimplemented (BLX, PLD, BKPT, LDRD, STRD).
>
> Added are processor definitions for ARM7TDMI and ARM920T.
>
> Based on patches by Ulrich Hecht <uli@suse.de> and Vincent Sanders <vince@kyllikki.org>.
>
> Signed-off-by: Filip Navara <filip.navara@gmail.com>
> ---
>  target-arm/cpu.h       |    7 ++-
>  target-arm/helper.c    |   31 ++++++++++++
>  target-arm/translate.c |  119 ++++++++++++++++++++++++++++++++++-------------
>  3 files changed, 123 insertions(+), 34 deletions(-)
>
> diff --git a/target-arm/cpu.h b/target-arm/cpu.h
> index 4a1c53f..b8e1e4b 100644
> --- a/target-arm/cpu.h
> +++ b/target-arm/cpu.h
> @@ -334,6 +334,7 @@ enum arm_features {
>     ARM_FEATURE_AUXCR,  /* ARM1026 Auxiliary control register.  */
>     ARM_FEATURE_XSCALE, /* Intel XScale extensions.  */
>     ARM_FEATURE_IWMMXT, /* Intel iwMMXt extension.  */
> +    ARM_FEATURE_V5,
>     ARM_FEATURE_V6,
>     ARM_FEATURE_V6K,
>     ARM_FEATURE_V7,
> @@ -345,7 +346,9 @@ enum arm_features {
>     ARM_FEATURE_DIV,
>     ARM_FEATURE_M, /* Microcontroller profile.  */
>     ARM_FEATURE_OMAPCP, /* OMAP specific CP15 ops handling.  */
> -    ARM_FEATURE_THUMB2EE
> +    ARM_FEATURE_THUMB2EE,
> +    ARM_FEATURE_CP15, /* ARM7TDMI, ARM7TDMI-S, ARM7EJ-S, and ARM9TDMI cores do not have a CP15 */
> +    ARM_FEATURE_ABORT_BU /* base updated abort model, e.g. ARMxTDMI */
>  };
>
>  static inline int arm_feature(CPUARMState *env, int feature)
> @@ -371,6 +374,8 @@ void cpu_arm_set_cp_io(CPUARMState *env, int cpnum,
>  #define IS_M(env) arm_feature(env, ARM_FEATURE_M)
>  #define ARM_CPUID(env) (env->cp15.c0_cpuid)
>
> +#define ARM_CPUID_ARM7TDMI    0x41807000 /* guess; no CP15 on ARM7TDMI */
> +#define ARM_CPUID_ARM920T     0x41129200
>  #define ARM_CPUID_ARM1026     0x4106a262
>  #define ARM_CPUID_ARM926      0x41069265
>  #define ARM_CPUID_ARM946      0x41059461
> diff --git a/target-arm/helper.c b/target-arm/helper.c
> index b3aec99..b82959f 100644
> --- a/target-arm/helper.c
> +++ b/target-arm/helper.c
> @@ -44,19 +44,34 @@ static void cpu_reset_model_id(CPUARMState *env, uint32_t id)
>  {
>     env->cp15.c0_cpuid = id;
>     switch (id) {
> +    case ARM_CPUID_ARM7TDMI:
> +        set_feature(env, ARM_FEATURE_ABORT_BU);
> +        break;
> +    case ARM_CPUID_ARM920T:
> +        set_feature(env, ARM_FEATURE_ABORT_BU);

ARM920T is using an ARM9TDMI which uses the Base Restored
Data Abort Model and not the Updated one.

Ref: http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.ddi0180a/ch02s01s01.html


Laurent

> +        set_feature(env, ARM_FEATURE_CP15);
> +        env->cp15.c0_cachetype = 0x0d172172;
> +        env->cp15.c1_sys = 0x00000078;
> +        break;
>     case ARM_CPUID_ARM926:
> +        set_feature(env, ARM_FEATURE_V5);
>         set_feature(env, ARM_FEATURE_VFP);
> +        set_feature(env, ARM_FEATURE_CP15);
>         env->vfp.xregs[ARM_VFP_FPSID] = 0x41011090;
>         env->cp15.c0_cachetype = 0x1dd20d2;
>         env->cp15.c1_sys = 0x00090078;
>         break;
>     case ARM_CPUID_ARM946:
> +        set_feature(env, ARM_FEATURE_V5);
> +        set_feature(env, ARM_FEATURE_CP15);
>         set_feature(env, ARM_FEATURE_MPU);
>         env->cp15.c0_cachetype = 0x0f004006;
>         env->cp15.c1_sys = 0x00000078;
>         break;
>     case ARM_CPUID_ARM1026:
> +        set_feature(env, ARM_FEATURE_V5);
>         set_feature(env, ARM_FEATURE_VFP);
> +        set_feature(env, ARM_FEATURE_CP15);
>         set_feature(env, ARM_FEATURE_AUXCR);
>         env->vfp.xregs[ARM_VFP_FPSID] = 0x410110a0;
>         env->cp15.c0_cachetype = 0x1dd20d2;
> @@ -64,8 +79,10 @@ static void cpu_reset_model_id(CPUARMState *env, uint32_t id)
>         break;
>     case ARM_CPUID_ARM1136_R2:
>     case ARM_CPUID_ARM1136:
> +        set_feature(env, ARM_FEATURE_V5);
>         set_feature(env, ARM_FEATURE_V6);
>         set_feature(env, ARM_FEATURE_VFP);
> +        set_feature(env, ARM_FEATURE_CP15);
>         set_feature(env, ARM_FEATURE_AUXCR);
>         env->vfp.xregs[ARM_VFP_FPSID] = 0x410120b4;
>         env->vfp.xregs[ARM_VFP_MVFR0] = 0x11111111;
> @@ -75,9 +92,11 @@ static void cpu_reset_model_id(CPUARMState *env, uint32_t id)
>         env->cp15.c0_cachetype = 0x1dd20d2;
>         break;
>     case ARM_CPUID_ARM11MPCORE:
> +        set_feature(env, ARM_FEATURE_V5);
>         set_feature(env, ARM_FEATURE_V6);
>         set_feature(env, ARM_FEATURE_V6K);
>         set_feature(env, ARM_FEATURE_VFP);
> +        set_feature(env, ARM_FEATURE_CP15);
>         set_feature(env, ARM_FEATURE_AUXCR);
>         env->vfp.xregs[ARM_VFP_FPSID] = 0x410120b4;
>         env->vfp.xregs[ARM_VFP_MVFR0] = 0x11111111;
> @@ -87,9 +106,11 @@ static void cpu_reset_model_id(CPUARMState *env, uint32_t id)
>         env->cp15.c0_cachetype = 0x1dd20d2;
>         break;
>     case ARM_CPUID_CORTEXA8:
> +        set_feature(env, ARM_FEATURE_V5);
>         set_feature(env, ARM_FEATURE_V6);
>         set_feature(env, ARM_FEATURE_V6K);
>         set_feature(env, ARM_FEATURE_V7);
> +        set_feature(env, ARM_FEATURE_CP15);
>         set_feature(env, ARM_FEATURE_AUXCR);
>         set_feature(env, ARM_FEATURE_THUMB2);
>         set_feature(env, ARM_FEATURE_VFP);
> @@ -129,6 +150,7 @@ static void cpu_reset_model_id(CPUARMState *env, uint32_t id)
>         env->cp15.c0_ccsid[1] = 0x200fe015; /* 16k L1 icache. */
>         break;
>     case ARM_CPUID_CORTEXM3:
> +        set_feature(env, ARM_FEATURE_V5);
>         set_feature(env, ARM_FEATURE_V6);
>         set_feature(env, ARM_FEATURE_THUMB2);
>         set_feature(env, ARM_FEATURE_V7);
> @@ -136,6 +158,7 @@ static void cpu_reset_model_id(CPUARMState *env, uint32_t id)
>         set_feature(env, ARM_FEATURE_DIV);
>         break;
>     case ARM_CPUID_ANY: /* For userspace emulation.  */
> +        set_feature(env, ARM_FEATURE_V5);
>         set_feature(env, ARM_FEATURE_V6);
>         set_feature(env, ARM_FEATURE_V6K);
>         set_feature(env, ARM_FEATURE_V7);
> @@ -149,6 +172,8 @@ static void cpu_reset_model_id(CPUARMState *env, uint32_t id)
>         break;
>     case ARM_CPUID_TI915T:
>     case ARM_CPUID_TI925T:
> +        set_feature(env, ARM_FEATURE_V5);
> +        set_feature(env, ARM_FEATURE_CP15);
>         set_feature(env, ARM_FEATURE_OMAPCP);
>         env->cp15.c0_cpuid = ARM_CPUID_TI925T; /* Depends on wiring.  */
>         env->cp15.c0_cachetype = 0x5109149;
> @@ -161,6 +186,8 @@ static void cpu_reset_model_id(CPUARMState *env, uint32_t id)
>     case ARM_CPUID_PXA260:
>     case ARM_CPUID_PXA261:
>     case ARM_CPUID_PXA262:
> +        set_feature(env, ARM_FEATURE_V5);
> +        set_feature(env, ARM_FEATURE_CP15);
>         set_feature(env, ARM_FEATURE_XSCALE);
>         /* JTAG_ID is ((id << 28) | 0x09265013) */
>         env->cp15.c0_cachetype = 0xd172172;
> @@ -172,6 +199,8 @@ static void cpu_reset_model_id(CPUARMState *env, uint32_t id)
>     case ARM_CPUID_PXA270_B1:
>     case ARM_CPUID_PXA270_C0:
>     case ARM_CPUID_PXA270_C5:
> +        set_feature(env, ARM_FEATURE_V5);
> +        set_feature(env, ARM_FEATURE_CP15);
>         set_feature(env, ARM_FEATURE_XSCALE);
>         /* JTAG_ID is ((id << 28) | 0x09265013) */
>         set_feature(env, ARM_FEATURE_IWMMXT);
> @@ -306,6 +335,8 @@ struct arm_cpu_t {
>  };
>
>  static const struct arm_cpu_t arm_cpu_names[] = {
> +       { ARM_CPUID_ARM7TDMI, "arm7tdmi"},
> +    { ARM_CPUID_ARM920T, "arm920t"},
>     { ARM_CPUID_ARM926, "arm926"},
>     { ARM_CPUID_ARM946, "arm946"},
>     { ARM_CPUID_ARM1026, "arm1026"},
> diff --git a/target-arm/translate.c b/target-arm/translate.c
> index 45bf772..48602bb 100644
> --- a/target-arm/translate.c
> +++ b/target-arm/translate.c
> @@ -34,9 +34,10 @@
>  #define GEN_HELPER 1
>  #include "helpers.h"
>
> +#define ENABLE_ARCH_5     arm_feature(env, ARM_FEATURE_V5)
>  #define ENABLE_ARCH_5J    0
>  #define ENABLE_ARCH_6     arm_feature(env, ARM_FEATURE_V6)
> -#define ENABLE_ARCH_6K   arm_feature(env, ARM_FEATURE_V6K)
> +#define ENABLE_ARCH_6K    arm_feature(env, ARM_FEATURE_V6K)
>  #define ENABLE_ARCH_6T2   arm_feature(env, ARM_FEATURE_THUMB2)
>  #define ENABLE_ARCH_7     arm_feature(env, ARM_FEATURE_V7)
>
> @@ -2463,8 +2464,10 @@ static int disas_cp15_insn(CPUState *env, DisasContext *s, uint32_t insn)
>     TCGv tmp, tmp2;
>
>     /* M profile cores use memory mapped registers instead of cp15.  */
> -    if (arm_feature(env, ARM_FEATURE_M))
> -       return 1;
> +    if (arm_feature(env, ARM_FEATURE_M) ||
> +        !arm_feature(env, ARM_FEATURE_CP15)) {
> +           return 1;
> +    }
>
>     if ((insn & (1 << 25)) == 0) {
>         if (insn & (1 << 20)) {
> @@ -6000,9 +6003,10 @@ static void disas_arm_insn(CPUState * env, DisasContext *s)
>                 goto illegal_op;
>             return;
>         }
> -        if ((insn & 0x0d70f000) == 0x0550f000)
> +        if ((insn & 0x0d70f000) == 0x0550f000) {
> +            ARCH(5);
>             return; /* PLD */
> -        else if ((insn & 0x0ffffdff) == 0x01010000) {
> +        } else if ((insn & 0x0ffffdff) == 0x01010000) {
>             ARCH(6);
>             /* setend */
>             if (insn & (1 << 9)) {
> @@ -6119,7 +6123,7 @@ static void disas_arm_insn(CPUState * env, DisasContext *s)
>         } else if ((insn & 0x0e000000) == 0x0a000000) {
>             /* branch link and change to thumb (blx <offset>) */
>             int32_t offset;
> -
> +            ARCH(5);
>             val = (uint32_t)s->pc;
>             tmp = new_tmp();
>             tcg_gen_movi_i32(tmp, val);
> @@ -6141,8 +6145,10 @@ static void disas_arm_insn(CPUState * env, DisasContext *s)
>             }
>         } else if ((insn & 0x0fe00000) == 0x0c400000) {
>             /* Coprocessor double register transfer.  */
> +            ARCH(5);
>         } else if ((insn & 0x0f000010) == 0x0e000010) {
>             /* Additional coprocessor register transfer.  */
> +            ARCH(5);
>         } else if ((insn & 0x0ff10020) == 0x01000000) {
>             uint32_t mask;
>             uint32_t val;
> @@ -6245,6 +6251,7 @@ static void disas_arm_insn(CPUState * env, DisasContext *s)
>                 gen_bx(s, tmp);
>             } else if (op1 == 3) {
>                 /* clz */
> +                               ARCH(5);
>                 rd = (insn >> 12) & 0xf;
>                 tmp = load_reg(s, rm);
>                 gen_helper_clz(tmp, tmp);
> @@ -6266,8 +6273,8 @@ static void disas_arm_insn(CPUState * env, DisasContext *s)
>         case 0x3:
>             if (op1 != 1)
>               goto illegal_op;
> -
>             /* branch link/exchange thumb (blx) */
> +            ARCH(5);
>             tmp = load_reg(s, rm);
>             tmp2 = new_tmp();
>             tcg_gen_movi_i32(tmp2, s->pc);
> @@ -6275,6 +6282,7 @@ static void disas_arm_insn(CPUState * env, DisasContext *s)
>             gen_bx(s, tmp);
>             break;
>         case 0x5: /* saturating add/subtract */
> +            ARCH(5);
>             rd = (insn >> 12) & 0xf;
>             rn = (insn >> 16) & 0xf;
>             tmp = load_reg(s, rm);
> @@ -6289,6 +6297,7 @@ static void disas_arm_insn(CPUState * env, DisasContext *s)
>             store_reg(s, rd, tmp);
>             break;
>         case 7: /* bkpt */
> +            ARCH(5);
>             gen_set_condexec(s);
>             gen_set_pc_im(s->pc - 4);
>             gen_exception(EXCP_BKPT);
> @@ -6298,6 +6307,7 @@ static void disas_arm_insn(CPUState * env, DisasContext *s)
>         case 0xa:
>         case 0xc:
>         case 0xe:
> +            ARCH(5);
>             rs = (insn >> 8) & 0xf;
>             rn = (insn >> 12) & 0xf;
>             rd = (insn >> 16) & 0xf;
> @@ -7019,7 +7029,7 @@ static void disas_arm_insn(CPUState * env, DisasContext *s)
>             }
>             if (insn & (1 << 20)) {
>                 /* Complete the load.  */
> -                if (rd == 15)
> +                if (rd == 15 && ENABLE_ARCH_5)
>                     gen_bx(s, tmp);
>                 else
>                     store_reg(s, rd, tmp);
> @@ -7029,6 +7039,7 @@ static void disas_arm_insn(CPUState * env, DisasContext *s)
>         case 0x09:
>             {
>                 int j, n, user, loaded_base;
> +                int crement = 0;
>                 TCGv loaded_var;
>                 /* load/store multiple words */
>                 /* XXX: store correct base if write back */
> @@ -7069,6 +7080,38 @@ static void disas_arm_insn(CPUState * env, DisasContext *s)
>                         tcg_gen_addi_i32(addr, addr, -((n - 1) * 4));
>                     }
>                 }
> +
> +                if (insn & (1 << 21)) {
> +                    /* write back */
> +                    if (insn & (1 << 23)) {
> +                        if (insn & (1 << 24)) {
> +                            /* pre increment */
> +                        } else {
> +                            /* post increment */
> +                            crement = 4;
> +                        }
> +                    } else {
> +                        if (insn & (1 << 24)) {
> +                            /* pre decrement */
> +                            if (n != 1) {
> +                                crement = -((n - 1) * 4);
> +                            }
> +                        } else {
> +                            /* post decrement */
> +                            crement = -(n * 4);
> +                        }
> +                    }
> +                    if (arm_feature(env, ARM_FEATURE_ABORT_BU)) {
> +                        /* base-updated abort model: update base register
> +                           before an abort can happen */
> +                        crement += (n - 1) * 4;
> +                        tmp = new_tmp();
> +                        tcg_gen_addi_i32(tmp, addr, crement);
> +                        store_reg(s, rn, tmp);
> +                    }
> +
> +                }
> +
>                 j = 0;
>                 for(i=0;i<16;i++) {
>                     if (insn & (1 << i)) {
> @@ -7076,7 +7119,11 @@ static void disas_arm_insn(CPUState * env, DisasContext *s)
>                             /* load */
>                             tmp = gen_ld32(addr, IS_USER(s));
>                             if (i == 15) {
> -                                gen_bx(s, tmp);
> +                                if (ENABLE_ARCH_5) {
> +                                    gen_bx(s, tmp);
> +                                } else {
> +                                    store_reg(s, i, tmp);
> +                                }
>                             } else if (user) {
>                                 tmp2 = tcg_const_i32(i);
>                                 gen_helper_set_user_reg(tmp2, tmp);
> @@ -7111,25 +7158,8 @@ static void disas_arm_insn(CPUState * env, DisasContext *s)
>                             tcg_gen_addi_i32(addr, addr, 4);
>                     }
>                 }
> -                if (insn & (1 << 21)) {
> -                    /* write back */
> -                    if (insn & (1 << 23)) {
> -                        if (insn & (1 << 24)) {
> -                            /* pre increment */
> -                        } else {
> -                            /* post increment */
> -                            tcg_gen_addi_i32(addr, addr, 4);
> -                        }
> -                    } else {
> -                        if (insn & (1 << 24)) {
> -                            /* pre decrement */
> -                            if (n != 1)
> -                                tcg_gen_addi_i32(addr, addr, -((n - 1) * 4));
> -                        } else {
> -                            /* post decrement */
> -                            tcg_gen_addi_i32(addr, addr, -(n * 4));
> -                        }
> -                    }
> +                if (!arm_feature(env, ARM_FEATURE_ABORT_BU) && (insn & (1 << 21))) {
> +                    tcg_gen_addi_i32(addr, addr, crement);
>                     store_reg(s, rn, addr);
>                 } else {
>                     dead_tmp(addr);
> @@ -7290,6 +7320,7 @@ static int disas_thumb2_insn(CPUState *env, DisasContext *s, uint16_t insn_hw1)
>            16-bit instructions to get correct prefetch abort behavior.  */
>         insn = insn_hw1;
>         if ((insn & (1 << 12)) == 0) {
> +            ARCH(5);
>             /* Second half of blx.  */
>             offset = ((insn & 0x7ff) << 1);
>             tmp = load_reg(s, 14);
> @@ -7346,6 +7377,7 @@ static int disas_thumb2_insn(CPUState *env, DisasContext *s, uint16_t insn_hw1)
>             /* Other load/store, table branch.  */
>             if (insn & 0x01200000) {
>                 /* Load/store doubleword.  */
> +                ARCH(5);
>                 if (rn == 15) {
>                     addr = new_tmp();
>                     tcg_gen_movi_i32(addr, s->pc & ~3);
> @@ -7516,7 +7548,7 @@ static int disas_thumb2_insn(CPUState *env, DisasContext *s, uint16_t insn_hw1)
>                     if (insn & (1 << 20)) {
>                         /* Load.  */
>                         tmp = gen_ld32(addr, IS_USER(s));
> -                        if (i == 15) {
> +                        if (i == 15 && ENABLE_ARCH_5) {
>                             gen_bx(s, tmp);
>                         } else {
>                             store_reg(s, i, tmp);
> @@ -7863,6 +7895,7 @@ static int disas_thumb2_insn(CPUState *env, DisasContext *s, uint16_t insn_hw1)
>                     gen_jmp(s, offset);
>                 } else {
>                     /* blx */
> +                    ARCH(5);
>                     offset &= ~(uint32_t)2;
>                     gen_bx_im(s, offset);
>                 }
> @@ -8227,7 +8260,7 @@ static int disas_thumb2_insn(CPUState *env, DisasContext *s, uint16_t insn_hw1)
>                 case 2: tmp = gen_ld32(addr, user); break;
>                 default: goto illegal_op;
>                 }
> -                if (rs == 15) {
> +                if (rs == 15 && ENABLE_ARCH_5) {
>                     gen_bx(s, tmp);
>                 } else {
>                     store_reg(s, rs, tmp);
> @@ -8270,6 +8303,7 @@ static void disas_thumb_insn(CPUState *env, DisasContext *s)
>     TCGv tmp;
>     TCGv tmp2;
>     TCGv addr;
> +    int crement;
>
>     if (s->condexec_mask) {
>         cond = s->condexec_cond;
> @@ -8402,6 +8436,7 @@ static void disas_thumb_insn(CPUState *env, DisasContext *s)
>             case 3:/* branch [and link] exchange thumb register */
>                 tmp = load_reg(s, rm);
>                 if (insn & (1 << 7)) {
> +                    ARCH(5);
>                     val = (uint32_t)s->pc | 1;
>                     tmp2 = new_tmp();
>                     tcg_gen_movi_i32(tmp2, val);
> @@ -8766,8 +8801,13 @@ static void disas_thumb_insn(CPUState *env, DisasContext *s)
>             /* write back the new stack pointer */
>             store_reg(s, 13, addr);
>             /* set the new PC value */
> -            if ((insn & 0x0900) == 0x0900)
> -                gen_bx(s, tmp);
> +            if ((insn & 0x0900) == 0x0900) {
> +                if (ENABLE_ARCH_5) {
> +                    gen_bx(s, tmp);
> +                } else {
> +                    store_reg(s, 15, tmp);
> +                }
> +            }
>             break;
>
>         case 1: case 3: case 9: case 11: /* czb */
> @@ -8856,6 +8896,19 @@ static void disas_thumb_insn(CPUState *env, DisasContext *s)
>         /* load/store multiple */
>         rn = (insn >> 8) & 0x7;
>         addr = load_reg(s, rn);
> +        if (arm_feature(env, ARM_FEATURE_ABORT_BU) && (insn & (1 << rn)) == 0) {
> +            /* base-updated abort model: update base register
> +               before an abort can happen */
> +            crement = 0;
> +            for (i = 0; i < 8; i++) {
> +                if (insn & (1 << i)) {
> +                    crement += 4;
> +                }
> +            }
> +            tmp = new_tmp();
> +            tcg_gen_addi_i32(tmp, addr, crement);
> +            store_reg(s, rn, tmp);
> +        }
>         for (i = 0; i < 8; i++) {
>             if (insn & (1 << i)) {
>                 if (insn & (1 << 11)) {
> @@ -8872,7 +8925,7 @@ static void disas_thumb_insn(CPUState *env, DisasContext *s)
>             }
>         }
>         /* Base register writeback.  */
> -        if ((insn & (1 << rn)) == 0) {
> +        if (!arm_feature(env, ARM_FEATURE_ABORT_BU) && (insn & (1 << rn)) == 0) {
>             store_reg(s, rn, addr);
>         } else {
>             dead_tmp(addr);
> --
> 1.6.5.1.1367.gcd48
>
>
>
>
>
diff mbox

Patch

diff --git a/target-arm/cpu.h b/target-arm/cpu.h
index 4a1c53f..b8e1e4b 100644
--- a/target-arm/cpu.h
+++ b/target-arm/cpu.h
@@ -334,6 +334,7 @@  enum arm_features {
     ARM_FEATURE_AUXCR,  /* ARM1026 Auxiliary control register.  */
     ARM_FEATURE_XSCALE, /* Intel XScale extensions.  */
     ARM_FEATURE_IWMMXT, /* Intel iwMMXt extension.  */
+    ARM_FEATURE_V5,
     ARM_FEATURE_V6,
     ARM_FEATURE_V6K,
     ARM_FEATURE_V7,
@@ -345,7 +346,9 @@  enum arm_features {
     ARM_FEATURE_DIV,
     ARM_FEATURE_M, /* Microcontroller profile.  */
     ARM_FEATURE_OMAPCP, /* OMAP specific CP15 ops handling.  */
-    ARM_FEATURE_THUMB2EE
+    ARM_FEATURE_THUMB2EE,
+    ARM_FEATURE_CP15, /* ARM7TDMI, ARM7TDMI-S, ARM7EJ-S, and ARM9TDMI cores do not have a CP15 */
+    ARM_FEATURE_ABORT_BU /* base updated abort model, e.g. ARMxTDMI */
 };
 
 static inline int arm_feature(CPUARMState *env, int feature)
@@ -371,6 +374,8 @@  void cpu_arm_set_cp_io(CPUARMState *env, int cpnum,
 #define IS_M(env) arm_feature(env, ARM_FEATURE_M)
 #define ARM_CPUID(env) (env->cp15.c0_cpuid)
 
+#define ARM_CPUID_ARM7TDMI    0x41807000 /* guess; no CP15 on ARM7TDMI */
+#define ARM_CPUID_ARM920T     0x41129200
 #define ARM_CPUID_ARM1026     0x4106a262
 #define ARM_CPUID_ARM926      0x41069265
 #define ARM_CPUID_ARM946      0x41059461
diff --git a/target-arm/helper.c b/target-arm/helper.c
index b3aec99..b82959f 100644
--- a/target-arm/helper.c
+++ b/target-arm/helper.c
@@ -44,19 +44,34 @@  static void cpu_reset_model_id(CPUARMState *env, uint32_t id)
 {
     env->cp15.c0_cpuid = id;
     switch (id) {
+    case ARM_CPUID_ARM7TDMI:
+        set_feature(env, ARM_FEATURE_ABORT_BU);
+        break;
+    case ARM_CPUID_ARM920T:
+        set_feature(env, ARM_FEATURE_ABORT_BU);
+        set_feature(env, ARM_FEATURE_CP15);
+        env->cp15.c0_cachetype = 0x0d172172;
+        env->cp15.c1_sys = 0x00000078;
+        break;
     case ARM_CPUID_ARM926:
+        set_feature(env, ARM_FEATURE_V5);
         set_feature(env, ARM_FEATURE_VFP);
+        set_feature(env, ARM_FEATURE_CP15);
         env->vfp.xregs[ARM_VFP_FPSID] = 0x41011090;
         env->cp15.c0_cachetype = 0x1dd20d2;
         env->cp15.c1_sys = 0x00090078;
         break;
     case ARM_CPUID_ARM946:
+        set_feature(env, ARM_FEATURE_V5);
+        set_feature(env, ARM_FEATURE_CP15);
         set_feature(env, ARM_FEATURE_MPU);
         env->cp15.c0_cachetype = 0x0f004006;
         env->cp15.c1_sys = 0x00000078;
         break;
     case ARM_CPUID_ARM1026:
+        set_feature(env, ARM_FEATURE_V5);
         set_feature(env, ARM_FEATURE_VFP);
+        set_feature(env, ARM_FEATURE_CP15);
         set_feature(env, ARM_FEATURE_AUXCR);
         env->vfp.xregs[ARM_VFP_FPSID] = 0x410110a0;
         env->cp15.c0_cachetype = 0x1dd20d2;
@@ -64,8 +79,10 @@  static void cpu_reset_model_id(CPUARMState *env, uint32_t id)
         break;
     case ARM_CPUID_ARM1136_R2:
     case ARM_CPUID_ARM1136:
+        set_feature(env, ARM_FEATURE_V5);
         set_feature(env, ARM_FEATURE_V6);
         set_feature(env, ARM_FEATURE_VFP);
+        set_feature(env, ARM_FEATURE_CP15);
         set_feature(env, ARM_FEATURE_AUXCR);
         env->vfp.xregs[ARM_VFP_FPSID] = 0x410120b4;
         env->vfp.xregs[ARM_VFP_MVFR0] = 0x11111111;
@@ -75,9 +92,11 @@  static void cpu_reset_model_id(CPUARMState *env, uint32_t id)
         env->cp15.c0_cachetype = 0x1dd20d2;
         break;
     case ARM_CPUID_ARM11MPCORE:
+        set_feature(env, ARM_FEATURE_V5);
         set_feature(env, ARM_FEATURE_V6);
         set_feature(env, ARM_FEATURE_V6K);
         set_feature(env, ARM_FEATURE_VFP);
+        set_feature(env, ARM_FEATURE_CP15);
         set_feature(env, ARM_FEATURE_AUXCR);
         env->vfp.xregs[ARM_VFP_FPSID] = 0x410120b4;
         env->vfp.xregs[ARM_VFP_MVFR0] = 0x11111111;
@@ -87,9 +106,11 @@  static void cpu_reset_model_id(CPUARMState *env, uint32_t id)
         env->cp15.c0_cachetype = 0x1dd20d2;
         break;
     case ARM_CPUID_CORTEXA8:
+        set_feature(env, ARM_FEATURE_V5);
         set_feature(env, ARM_FEATURE_V6);
         set_feature(env, ARM_FEATURE_V6K);
         set_feature(env, ARM_FEATURE_V7);
+        set_feature(env, ARM_FEATURE_CP15);
         set_feature(env, ARM_FEATURE_AUXCR);
         set_feature(env, ARM_FEATURE_THUMB2);
         set_feature(env, ARM_FEATURE_VFP);
@@ -129,6 +150,7 @@  static void cpu_reset_model_id(CPUARMState *env, uint32_t id)
         env->cp15.c0_ccsid[1] = 0x200fe015; /* 16k L1 icache. */
         break;
     case ARM_CPUID_CORTEXM3:
+        set_feature(env, ARM_FEATURE_V5);
         set_feature(env, ARM_FEATURE_V6);
         set_feature(env, ARM_FEATURE_THUMB2);
         set_feature(env, ARM_FEATURE_V7);
@@ -136,6 +158,7 @@  static void cpu_reset_model_id(CPUARMState *env, uint32_t id)
         set_feature(env, ARM_FEATURE_DIV);
         break;
     case ARM_CPUID_ANY: /* For userspace emulation.  */
+        set_feature(env, ARM_FEATURE_V5);
         set_feature(env, ARM_FEATURE_V6);
         set_feature(env, ARM_FEATURE_V6K);
         set_feature(env, ARM_FEATURE_V7);
@@ -149,6 +172,8 @@  static void cpu_reset_model_id(CPUARMState *env, uint32_t id)
         break;
     case ARM_CPUID_TI915T:
     case ARM_CPUID_TI925T:
+        set_feature(env, ARM_FEATURE_V5);
+        set_feature(env, ARM_FEATURE_CP15);
         set_feature(env, ARM_FEATURE_OMAPCP);
         env->cp15.c0_cpuid = ARM_CPUID_TI925T; /* Depends on wiring.  */
         env->cp15.c0_cachetype = 0x5109149;
@@ -161,6 +186,8 @@  static void cpu_reset_model_id(CPUARMState *env, uint32_t id)
     case ARM_CPUID_PXA260:
     case ARM_CPUID_PXA261:
     case ARM_CPUID_PXA262:
+        set_feature(env, ARM_FEATURE_V5);
+        set_feature(env, ARM_FEATURE_CP15);
         set_feature(env, ARM_FEATURE_XSCALE);
         /* JTAG_ID is ((id << 28) | 0x09265013) */
         env->cp15.c0_cachetype = 0xd172172;
@@ -172,6 +199,8 @@  static void cpu_reset_model_id(CPUARMState *env, uint32_t id)
     case ARM_CPUID_PXA270_B1:
     case ARM_CPUID_PXA270_C0:
     case ARM_CPUID_PXA270_C5:
+        set_feature(env, ARM_FEATURE_V5);
+        set_feature(env, ARM_FEATURE_CP15);
         set_feature(env, ARM_FEATURE_XSCALE);
         /* JTAG_ID is ((id << 28) | 0x09265013) */
         set_feature(env, ARM_FEATURE_IWMMXT);
@@ -306,6 +335,8 @@  struct arm_cpu_t {
 };
 
 static const struct arm_cpu_t arm_cpu_names[] = {
+	{ ARM_CPUID_ARM7TDMI, "arm7tdmi"},
+    { ARM_CPUID_ARM920T, "arm920t"},
     { ARM_CPUID_ARM926, "arm926"},
     { ARM_CPUID_ARM946, "arm946"},
     { ARM_CPUID_ARM1026, "arm1026"},
diff --git a/target-arm/translate.c b/target-arm/translate.c
index 45bf772..48602bb 100644
--- a/target-arm/translate.c
+++ b/target-arm/translate.c
@@ -34,9 +34,10 @@ 
 #define GEN_HELPER 1
 #include "helpers.h"
 
+#define ENABLE_ARCH_5     arm_feature(env, ARM_FEATURE_V5)
 #define ENABLE_ARCH_5J    0
 #define ENABLE_ARCH_6     arm_feature(env, ARM_FEATURE_V6)
-#define ENABLE_ARCH_6K   arm_feature(env, ARM_FEATURE_V6K)
+#define ENABLE_ARCH_6K    arm_feature(env, ARM_FEATURE_V6K)
 #define ENABLE_ARCH_6T2   arm_feature(env, ARM_FEATURE_THUMB2)
 #define ENABLE_ARCH_7     arm_feature(env, ARM_FEATURE_V7)
 
@@ -2463,8 +2464,10 @@  static int disas_cp15_insn(CPUState *env, DisasContext *s, uint32_t insn)
     TCGv tmp, tmp2;
 
     /* M profile cores use memory mapped registers instead of cp15.  */
-    if (arm_feature(env, ARM_FEATURE_M))
-	return 1;
+    if (arm_feature(env, ARM_FEATURE_M) ||
+        !arm_feature(env, ARM_FEATURE_CP15)) {
+	    return 1;
+    }
 
     if ((insn & (1 << 25)) == 0) {
         if (insn & (1 << 20)) {
@@ -6000,9 +6003,10 @@  static void disas_arm_insn(CPUState * env, DisasContext *s)
                 goto illegal_op;
             return;
         }
-        if ((insn & 0x0d70f000) == 0x0550f000)
+        if ((insn & 0x0d70f000) == 0x0550f000) {
+            ARCH(5);
             return; /* PLD */
-        else if ((insn & 0x0ffffdff) == 0x01010000) {
+        } else if ((insn & 0x0ffffdff) == 0x01010000) {
             ARCH(6);
             /* setend */
             if (insn & (1 << 9)) {
@@ -6119,7 +6123,7 @@  static void disas_arm_insn(CPUState * env, DisasContext *s)
         } else if ((insn & 0x0e000000) == 0x0a000000) {
             /* branch link and change to thumb (blx <offset>) */
             int32_t offset;
-
+            ARCH(5);
             val = (uint32_t)s->pc;
             tmp = new_tmp();
             tcg_gen_movi_i32(tmp, val);
@@ -6141,8 +6145,10 @@  static void disas_arm_insn(CPUState * env, DisasContext *s)
             }
         } else if ((insn & 0x0fe00000) == 0x0c400000) {
             /* Coprocessor double register transfer.  */
+            ARCH(5);
         } else if ((insn & 0x0f000010) == 0x0e000010) {
             /* Additional coprocessor register transfer.  */
+            ARCH(5);
         } else if ((insn & 0x0ff10020) == 0x01000000) {
             uint32_t mask;
             uint32_t val;
@@ -6245,6 +6251,7 @@  static void disas_arm_insn(CPUState * env, DisasContext *s)
                 gen_bx(s, tmp);
             } else if (op1 == 3) {
                 /* clz */
+				ARCH(5);
                 rd = (insn >> 12) & 0xf;
                 tmp = load_reg(s, rm);
                 gen_helper_clz(tmp, tmp);
@@ -6266,8 +6273,8 @@  static void disas_arm_insn(CPUState * env, DisasContext *s)
         case 0x3:
             if (op1 != 1)
               goto illegal_op;
-
             /* branch link/exchange thumb (blx) */
+            ARCH(5);
             tmp = load_reg(s, rm);
             tmp2 = new_tmp();
             tcg_gen_movi_i32(tmp2, s->pc);
@@ -6275,6 +6282,7 @@  static void disas_arm_insn(CPUState * env, DisasContext *s)
             gen_bx(s, tmp);
             break;
         case 0x5: /* saturating add/subtract */
+            ARCH(5);
             rd = (insn >> 12) & 0xf;
             rn = (insn >> 16) & 0xf;
             tmp = load_reg(s, rm);
@@ -6289,6 +6297,7 @@  static void disas_arm_insn(CPUState * env, DisasContext *s)
             store_reg(s, rd, tmp);
             break;
         case 7: /* bkpt */
+            ARCH(5);
             gen_set_condexec(s);
             gen_set_pc_im(s->pc - 4);
             gen_exception(EXCP_BKPT);
@@ -6298,6 +6307,7 @@  static void disas_arm_insn(CPUState * env, DisasContext *s)
         case 0xa:
         case 0xc:
         case 0xe:
+            ARCH(5);
             rs = (insn >> 8) & 0xf;
             rn = (insn >> 12) & 0xf;
             rd = (insn >> 16) & 0xf;
@@ -7019,7 +7029,7 @@  static void disas_arm_insn(CPUState * env, DisasContext *s)
             }
             if (insn & (1 << 20)) {
                 /* Complete the load.  */
-                if (rd == 15)
+                if (rd == 15 && ENABLE_ARCH_5)
                     gen_bx(s, tmp);
                 else
                     store_reg(s, rd, tmp);
@@ -7029,6 +7039,7 @@  static void disas_arm_insn(CPUState * env, DisasContext *s)
         case 0x09:
             {
                 int j, n, user, loaded_base;
+                int crement = 0;
                 TCGv loaded_var;
                 /* load/store multiple words */
                 /* XXX: store correct base if write back */
@@ -7069,6 +7080,38 @@  static void disas_arm_insn(CPUState * env, DisasContext *s)
                         tcg_gen_addi_i32(addr, addr, -((n - 1) * 4));
                     }
                 }
+
+                if (insn & (1 << 21)) {
+                    /* write back */
+                    if (insn & (1 << 23)) {
+                        if (insn & (1 << 24)) {
+                            /* pre increment */
+                        } else {
+                            /* post increment */
+                            crement = 4;
+                        }
+                    } else {
+                        if (insn & (1 << 24)) {
+                            /* pre decrement */
+                            if (n != 1) {
+                                crement = -((n - 1) * 4);
+                            }
+                        } else {
+                            /* post decrement */
+                            crement = -(n * 4);
+                        }
+                    }
+                    if (arm_feature(env, ARM_FEATURE_ABORT_BU)) {
+                        /* base-updated abort model: update base register
+                           before an abort can happen */
+                        crement += (n - 1) * 4;
+                        tmp = new_tmp();
+                        tcg_gen_addi_i32(tmp, addr, crement);
+                        store_reg(s, rn, tmp);
+                    }
+
+                }
+
                 j = 0;
                 for(i=0;i<16;i++) {
                     if (insn & (1 << i)) {
@@ -7076,7 +7119,11 @@  static void disas_arm_insn(CPUState * env, DisasContext *s)
                             /* load */
                             tmp = gen_ld32(addr, IS_USER(s));
                             if (i == 15) {
-                                gen_bx(s, tmp);
+                                if (ENABLE_ARCH_5) {
+                                    gen_bx(s, tmp);
+                                } else {
+                                    store_reg(s, i, tmp);
+                                }
                             } else if (user) {
                                 tmp2 = tcg_const_i32(i);
                                 gen_helper_set_user_reg(tmp2, tmp);
@@ -7111,25 +7158,8 @@  static void disas_arm_insn(CPUState * env, DisasContext *s)
                             tcg_gen_addi_i32(addr, addr, 4);
                     }
                 }
-                if (insn & (1 << 21)) {
-                    /* write back */
-                    if (insn & (1 << 23)) {
-                        if (insn & (1 << 24)) {
-                            /* pre increment */
-                        } else {
-                            /* post increment */
-                            tcg_gen_addi_i32(addr, addr, 4);
-                        }
-                    } else {
-                        if (insn & (1 << 24)) {
-                            /* pre decrement */
-                            if (n != 1)
-                                tcg_gen_addi_i32(addr, addr, -((n - 1) * 4));
-                        } else {
-                            /* post decrement */
-                            tcg_gen_addi_i32(addr, addr, -(n * 4));
-                        }
-                    }
+                if (!arm_feature(env, ARM_FEATURE_ABORT_BU) && (insn & (1 << 21))) {
+                    tcg_gen_addi_i32(addr, addr, crement);
                     store_reg(s, rn, addr);
                 } else {
                     dead_tmp(addr);
@@ -7290,6 +7320,7 @@  static int disas_thumb2_insn(CPUState *env, DisasContext *s, uint16_t insn_hw1)
            16-bit instructions to get correct prefetch abort behavior.  */
         insn = insn_hw1;
         if ((insn & (1 << 12)) == 0) {
+            ARCH(5);
             /* Second half of blx.  */
             offset = ((insn & 0x7ff) << 1);
             tmp = load_reg(s, 14);
@@ -7346,6 +7377,7 @@  static int disas_thumb2_insn(CPUState *env, DisasContext *s, uint16_t insn_hw1)
             /* Other load/store, table branch.  */
             if (insn & 0x01200000) {
                 /* Load/store doubleword.  */
+                ARCH(5);
                 if (rn == 15) {
                     addr = new_tmp();
                     tcg_gen_movi_i32(addr, s->pc & ~3);
@@ -7516,7 +7548,7 @@  static int disas_thumb2_insn(CPUState *env, DisasContext *s, uint16_t insn_hw1)
                     if (insn & (1 << 20)) {
                         /* Load.  */
                         tmp = gen_ld32(addr, IS_USER(s));
-                        if (i == 15) {
+                        if (i == 15 && ENABLE_ARCH_5) {
                             gen_bx(s, tmp);
                         } else {
                             store_reg(s, i, tmp);
@@ -7863,6 +7895,7 @@  static int disas_thumb2_insn(CPUState *env, DisasContext *s, uint16_t insn_hw1)
                     gen_jmp(s, offset);
                 } else {
                     /* blx */
+                    ARCH(5);
                     offset &= ~(uint32_t)2;
                     gen_bx_im(s, offset);
                 }
@@ -8227,7 +8260,7 @@  static int disas_thumb2_insn(CPUState *env, DisasContext *s, uint16_t insn_hw1)
                 case 2: tmp = gen_ld32(addr, user); break;
                 default: goto illegal_op;
                 }
-                if (rs == 15) {
+                if (rs == 15 && ENABLE_ARCH_5) {
                     gen_bx(s, tmp);
                 } else {
                     store_reg(s, rs, tmp);
@@ -8270,6 +8303,7 @@  static void disas_thumb_insn(CPUState *env, DisasContext *s)
     TCGv tmp;
     TCGv tmp2;
     TCGv addr;
+    int crement;
 
     if (s->condexec_mask) {
         cond = s->condexec_cond;
@@ -8402,6 +8436,7 @@  static void disas_thumb_insn(CPUState *env, DisasContext *s)
             case 3:/* branch [and link] exchange thumb register */
                 tmp = load_reg(s, rm);
                 if (insn & (1 << 7)) {
+                    ARCH(5);
                     val = (uint32_t)s->pc | 1;
                     tmp2 = new_tmp();
                     tcg_gen_movi_i32(tmp2, val);
@@ -8766,8 +8801,13 @@  static void disas_thumb_insn(CPUState *env, DisasContext *s)
             /* write back the new stack pointer */
             store_reg(s, 13, addr);
             /* set the new PC value */
-            if ((insn & 0x0900) == 0x0900)
-                gen_bx(s, tmp);
+            if ((insn & 0x0900) == 0x0900) {
+                if (ENABLE_ARCH_5) {
+                    gen_bx(s, tmp);
+                } else {
+                    store_reg(s, 15, tmp);
+                }
+            }
             break;
 
         case 1: case 3: case 9: case 11: /* czb */
@@ -8856,6 +8896,19 @@  static void disas_thumb_insn(CPUState *env, DisasContext *s)
         /* load/store multiple */
         rn = (insn >> 8) & 0x7;
         addr = load_reg(s, rn);
+        if (arm_feature(env, ARM_FEATURE_ABORT_BU) && (insn & (1 << rn)) == 0) {
+            /* base-updated abort model: update base register
+               before an abort can happen */
+            crement = 0;
+            for (i = 0; i < 8; i++) {
+                if (insn & (1 << i)) {
+                    crement += 4;
+                }
+            }
+            tmp = new_tmp();
+            tcg_gen_addi_i32(tmp, addr, crement);
+            store_reg(s, rn, tmp);
+        }
         for (i = 0; i < 8; i++) {
             if (insn & (1 << i)) {
                 if (insn & (1 << 11)) {
@@ -8872,7 +8925,7 @@  static void disas_thumb_insn(CPUState *env, DisasContext *s)
             }
         }
         /* Base register writeback.  */
-        if ((insn & (1 << rn)) == 0) {
+        if (!arm_feature(env, ARM_FEATURE_ABORT_BU) && (insn & (1 << rn)) == 0) {
             store_reg(s, rn, addr);
         } else {
             dead_tmp(addr);