diff --git a/qemu/aarch64.h b/qemu/aarch64.h index b1b0e14e..22a06c32 100644 --- a/qemu/aarch64.h +++ b/qemu/aarch64.h @@ -1707,6 +1707,7 @@ #define helper_v7m_mrs helper_v7m_mrs_aarch64 #define helper_v7m_msr helper_v7m_msr_aarch64 #define helper_v7m_tt helper_v7m_tt_aarch64 +#define helper_v8m_stackcheck helper_v8m_stackcheck_aarch64 #define helper_vfp_absd helper_vfp_absd_aarch64 #define helper_vfp_abss helper_vfp_abss_aarch64 #define helper_vfp_addd helper_vfp_addd_aarch64 diff --git a/qemu/aarch64eb.h b/qemu/aarch64eb.h index cfc7e929..b7edcf40 100644 --- a/qemu/aarch64eb.h +++ b/qemu/aarch64eb.h @@ -1707,6 +1707,7 @@ #define helper_v7m_mrs helper_v7m_mrs_aarch64eb #define helper_v7m_msr helper_v7m_msr_aarch64eb #define helper_v7m_tt helper_v7m_tt_aarch64eb +#define helper_v8m_stackcheck helper_v8m_stackcheck_aarch64eb #define helper_vfp_absd helper_vfp_absd_aarch64eb #define helper_vfp_abss helper_vfp_abss_aarch64eb #define helper_vfp_addd helper_vfp_addd_aarch64eb diff --git a/qemu/arm.h b/qemu/arm.h index 424ace34..6a0a2808 100644 --- a/qemu/arm.h +++ b/qemu/arm.h @@ -1707,6 +1707,7 @@ #define helper_v7m_mrs helper_v7m_mrs_arm #define helper_v7m_msr helper_v7m_msr_arm #define helper_v7m_tt helper_v7m_tt_arm +#define helper_v8m_stackcheck helper_v8m_stackcheck_arm #define helper_vfp_absd helper_vfp_absd_arm #define helper_vfp_abss helper_vfp_abss_arm #define helper_vfp_addd helper_vfp_addd_arm diff --git a/qemu/armeb.h b/qemu/armeb.h index fa0d42f8..815dae32 100644 --- a/qemu/armeb.h +++ b/qemu/armeb.h @@ -1707,6 +1707,7 @@ #define helper_v7m_mrs helper_v7m_mrs_armeb #define helper_v7m_msr helper_v7m_msr_armeb #define helper_v7m_tt helper_v7m_tt_armeb +#define helper_v8m_stackcheck helper_v8m_stackcheck_armeb #define helper_vfp_absd helper_vfp_absd_armeb #define helper_vfp_abss helper_vfp_abss_armeb #define helper_vfp_addd helper_vfp_addd_armeb diff --git a/qemu/header_gen.py b/qemu/header_gen.py index 1b20dede..539b5742 100644 --- a/qemu/header_gen.py +++ b/qemu/header_gen.py @@ -1713,6 +1713,7 @@ symbols = ( 'helper_v7m_mrs', 'helper_v7m_msr', 'helper_v7m_tt', + 'helper_v8m_stackcheck', 'helper_vfp_absd', 'helper_vfp_abss', 'helper_vfp_addd', diff --git a/qemu/m68k.h b/qemu/m68k.h index 698ab027..2513dc91 100644 --- a/qemu/m68k.h +++ b/qemu/m68k.h @@ -1707,6 +1707,7 @@ #define helper_v7m_mrs helper_v7m_mrs_m68k #define helper_v7m_msr helper_v7m_msr_m68k #define helper_v7m_tt helper_v7m_tt_m68k +#define helper_v8m_stackcheck helper_v8m_stackcheck_m68k #define helper_vfp_absd helper_vfp_absd_m68k #define helper_vfp_abss helper_vfp_abss_m68k #define helper_vfp_addd helper_vfp_addd_m68k diff --git a/qemu/mips.h b/qemu/mips.h index e82f0a24..52787ac3 100644 --- a/qemu/mips.h +++ b/qemu/mips.h @@ -1707,6 +1707,7 @@ #define helper_v7m_mrs helper_v7m_mrs_mips #define helper_v7m_msr helper_v7m_msr_mips #define helper_v7m_tt helper_v7m_tt_mips +#define helper_v8m_stackcheck helper_v8m_stackcheck_mips #define helper_vfp_absd helper_vfp_absd_mips #define helper_vfp_abss helper_vfp_abss_mips #define helper_vfp_addd helper_vfp_addd_mips diff --git a/qemu/mips64.h b/qemu/mips64.h index 5910faac..111340c5 100644 --- a/qemu/mips64.h +++ b/qemu/mips64.h @@ -1707,6 +1707,7 @@ #define helper_v7m_mrs helper_v7m_mrs_mips64 #define helper_v7m_msr helper_v7m_msr_mips64 #define helper_v7m_tt helper_v7m_tt_mips64 +#define helper_v8m_stackcheck helper_v8m_stackcheck_mips64 #define helper_vfp_absd helper_vfp_absd_mips64 #define helper_vfp_abss helper_vfp_abss_mips64 #define helper_vfp_addd helper_vfp_addd_mips64 diff --git a/qemu/mips64el.h b/qemu/mips64el.h index 01ff6312..e3e336fb 100644 --- a/qemu/mips64el.h +++ b/qemu/mips64el.h @@ -1707,6 +1707,7 @@ #define helper_v7m_mrs helper_v7m_mrs_mips64el #define helper_v7m_msr helper_v7m_msr_mips64el #define helper_v7m_tt helper_v7m_tt_mips64el +#define helper_v8m_stackcheck helper_v8m_stackcheck_mips64el #define helper_vfp_absd helper_vfp_absd_mips64el #define helper_vfp_abss helper_vfp_abss_mips64el #define helper_vfp_addd helper_vfp_addd_mips64el diff --git a/qemu/mipsel.h b/qemu/mipsel.h index 58edacdf..4e1e51ec 100644 --- a/qemu/mipsel.h +++ b/qemu/mipsel.h @@ -1707,6 +1707,7 @@ #define helper_v7m_mrs helper_v7m_mrs_mipsel #define helper_v7m_msr helper_v7m_msr_mipsel #define helper_v7m_tt helper_v7m_tt_mipsel +#define helper_v8m_stackcheck helper_v8m_stackcheck_mipsel #define helper_vfp_absd helper_vfp_absd_mipsel #define helper_vfp_abss helper_vfp_abss_mipsel #define helper_vfp_addd helper_vfp_addd_mipsel diff --git a/qemu/powerpc.h b/qemu/powerpc.h index ddd9b974..85be0364 100644 --- a/qemu/powerpc.h +++ b/qemu/powerpc.h @@ -1707,6 +1707,7 @@ #define helper_v7m_mrs helper_v7m_mrs_powerpc #define helper_v7m_msr helper_v7m_msr_powerpc #define helper_v7m_tt helper_v7m_tt_powerpc +#define helper_v8m_stackcheck helper_v8m_stackcheck_powerpc #define helper_vfp_absd helper_vfp_absd_powerpc #define helper_vfp_abss helper_vfp_abss_powerpc #define helper_vfp_addd helper_vfp_addd_powerpc diff --git a/qemu/riscv32.h b/qemu/riscv32.h index 3fe3360d..7c8e6283 100644 --- a/qemu/riscv32.h +++ b/qemu/riscv32.h @@ -1707,6 +1707,7 @@ #define helper_v7m_mrs helper_v7m_mrs_riscv32 #define helper_v7m_msr helper_v7m_msr_riscv32 #define helper_v7m_tt helper_v7m_tt_riscv32 +#define helper_v8m_stackcheck helper_v8m_stackcheck_riscv32 #define helper_vfp_absd helper_vfp_absd_riscv32 #define helper_vfp_abss helper_vfp_abss_riscv32 #define helper_vfp_addd helper_vfp_addd_riscv32 diff --git a/qemu/riscv64.h b/qemu/riscv64.h index 6a763473..7fbef4e3 100644 --- a/qemu/riscv64.h +++ b/qemu/riscv64.h @@ -1707,6 +1707,7 @@ #define helper_v7m_mrs helper_v7m_mrs_riscv64 #define helper_v7m_msr helper_v7m_msr_riscv64 #define helper_v7m_tt helper_v7m_tt_riscv64 +#define helper_v8m_stackcheck helper_v8m_stackcheck_riscv64 #define helper_vfp_absd helper_vfp_absd_riscv64 #define helper_vfp_abss helper_vfp_abss_riscv64 #define helper_vfp_addd helper_vfp_addd_riscv64 diff --git a/qemu/sparc.h b/qemu/sparc.h index 539b16b8..b9d2b7b6 100644 --- a/qemu/sparc.h +++ b/qemu/sparc.h @@ -1707,6 +1707,7 @@ #define helper_v7m_mrs helper_v7m_mrs_sparc #define helper_v7m_msr helper_v7m_msr_sparc #define helper_v7m_tt helper_v7m_tt_sparc +#define helper_v8m_stackcheck helper_v8m_stackcheck_sparc #define helper_vfp_absd helper_vfp_absd_sparc #define helper_vfp_abss helper_vfp_abss_sparc #define helper_vfp_addd helper_vfp_addd_sparc diff --git a/qemu/sparc64.h b/qemu/sparc64.h index a22768b7..09c7f480 100644 --- a/qemu/sparc64.h +++ b/qemu/sparc64.h @@ -1707,6 +1707,7 @@ #define helper_v7m_mrs helper_v7m_mrs_sparc64 #define helper_v7m_msr helper_v7m_msr_sparc64 #define helper_v7m_tt helper_v7m_tt_sparc64 +#define helper_v8m_stackcheck helper_v8m_stackcheck_sparc64 #define helper_vfp_absd helper_vfp_absd_sparc64 #define helper_vfp_abss helper_vfp_abss_sparc64 #define helper_vfp_addd helper_vfp_addd_sparc64 diff --git a/qemu/target/arm/helper.h b/qemu/target/arm/helper.h index 86afa4bf..155ffa43 100644 --- a/qemu/target/arm/helper.h +++ b/qemu/target/arm/helper.h @@ -71,6 +71,8 @@ DEF_HELPER_2(v7m_blxns, void, env, i32) DEF_HELPER_3(v7m_tt, i32, env, i32, i32) +DEF_HELPER_2(v8m_stackcheck, void, env, i32) + DEF_HELPER_4(access_check_cp_reg, void, env, ptr, i32, i32) DEF_HELPER_3(set_cp_reg, void, env, ptr, i32) DEF_HELPER_2(get_cp_reg, i32, env, ptr) diff --git a/qemu/target/arm/internals.h b/qemu/target/arm/internals.h index 5fc81195..2a23c5e6 100644 --- a/qemu/target/arm/internals.h +++ b/qemu/target/arm/internals.h @@ -819,4 +819,18 @@ static inline bool v7m_using_psp(CPUARMState *env) env->v7m.control[env->v7m.secure] & R_V7M_CONTROL_SPSEL_MASK; } +/** + * v7m_sp_limit: Return SP limit for current CPU state + * Return the SP limit value for the current CPU security state + * and stack pointer. + */ +static inline uint32_t v7m_sp_limit(CPUARMState *env) +{ + if (v7m_using_psp(env)) { + return env->v7m.psplim[env->v7m.secure]; + } else { + return env->v7m.msplim[env->v7m.secure]; + } +} + #endif diff --git a/qemu/target/arm/op_helper.c b/qemu/target/arm/op_helper.c index 08a5cf3d..2b0ff5f2 100644 --- a/qemu/target/arm/op_helper.c +++ b/qemu/target/arm/op_helper.c @@ -237,6 +237,25 @@ void arm_cpu_do_transaction_failed(CPUState *cs, hwaddr physaddr, #endif /* !defined(CONFIG_USER_ONLY) */ +void HELPER(v8m_stackcheck)(CPUARMState *env, uint32_t newvalue) +{ + /* + * Perform the v8M stack limit check for SP updates from translated code, + * raising an exception if the limit is breached. + */ + if (newvalue < v7m_sp_limit(env)) { + CPUState *cs = CPU(arm_env_get_cpu(env)); + + /* + * Stack limit exceptions are a rare case, so rather than syncing + * PC/condbits before the call, we use cpu_restore_state() to + * get them right before raising the exception. + */ + cpu_restore_state(cs, GETPC(), true); + raise_exception(env, EXCP_STKOF, 0, 1); + } +} + uint32_t HELPER(add_setq)(CPUARMState *env, uint32_t a, uint32_t b) { uint32_t res = a + b; diff --git a/qemu/target/arm/translate.c b/qemu/target/arm/translate.c index d32d62eb..1240acf7 100644 --- a/qemu/target/arm/translate.c +++ b/qemu/target/arm/translate.c @@ -235,6 +235,25 @@ static void store_reg(DisasContext *s, int reg, TCGv_i32 var) tcg_temp_free_i32(tcg_ctx, var); } +/* + * Variant of store_reg which applies v8M stack-limit checks before updating + * SP. If the check fails this will result in an exception being taken. + * We disable the stack checks for CONFIG_USER_ONLY because we have + * no idea what the stack limits should be in that case. + * If stack checking is not being done this just acts like store_reg(). + */ +static void store_sp_checked(DisasContext *s, TCGv_i32 var) +{ + TCGContext *tcg_ctx = s->uc->tcg_ctx; + +#ifndef CONFIG_USER_ONLY + if (s->v8m_stackcheck) { + gen_helper_v8m_stackcheck(tcg_ctx, tcg_ctx->cpu_env, var); + } +#endif + store_reg(s, 13, var); +} + /* Value extensions. */ #define gen_uxtb(var) tcg_gen_ext8u_i32(tcg_ctx, var, var) #define gen_uxth(var) tcg_gen_ext16u_i32(tcg_ctx, var, var) @@ -10769,7 +10788,13 @@ static void disas_thumb2_insn(DisasContext *s, uint32_t insn) if (gen_thumb2_data_op(s, op, conds, 0, tmp, tmp2)) goto illegal_op; tcg_temp_free_i32(tcg_ctx, tmp2); - if (rd != 15) { + if (rd == 13 && + ((op == 2 && rn == 15) || + (op == 8 && rn == 13) || + (op == 13 && rn == 13))) { + /* MOV SP, ... or ADD SP, SP, ... or SUB SP, SP, ... */ + store_sp_checked(s, tmp); + } else if (rd != 15) { store_reg(s, rd, tmp); } else { tcg_temp_free_i32(tcg_ctx, tmp); @@ -11453,8 +11478,15 @@ static void disas_thumb2_insn(DisasContext *s, uint32_t insn) gen_jmp(s, s->pc + offset); } } else { - /* Data processing immediate. */ + /* + * 0b1111_0xxx_xxxx_0xxx_xxxx_xxxx + * - Data-processing (modified immediate, plain binary immediate) + */ if (insn & (1 << 25)) { + /* + * 0b1111_0x1x_xxxx_0xxx_xxxx_xxxx + * - Data-processing (plain binary immediate) + */ if (insn & (1 << 24)) { if (insn & (1 << 20)) goto illegal_op; @@ -11550,6 +11582,7 @@ static void disas_thumb2_insn(DisasContext *s, uint32_t insn) tmp = tcg_temp_new_i32(tcg_ctx); tcg_gen_movi_i32(tcg_ctx, tmp, imm); } + store_reg(s, rd, tmp); } else { /* Add/sub 12-bit immediate. */ if (rn == 15) { @@ -11560,17 +11593,27 @@ static void disas_thumb2_insn(DisasContext *s, uint32_t insn) offset += imm; tmp = tcg_temp_new_i32(tcg_ctx); tcg_gen_movi_i32(tcg_ctx, tmp, offset); + store_reg(s, rd, tmp); } else { tmp = load_reg(s, rn); if (insn & (1 << 23)) tcg_gen_subi_i32(tcg_ctx, tmp, tmp, imm); else tcg_gen_addi_i32(tcg_ctx, tmp, tmp, imm); + if (rn == 13 && rd == 13) { + /* ADD SP, SP, imm or SUB SP, SP, imm */ + store_sp_checked(s, tmp); + } else { + store_reg(s, rd, tmp); + } } } - store_reg(s, rd, tmp); } } else { + /* + * 0b1111_0x0x_xxxx_0xxx_xxxx_xxxx + * - Data-processing (modified immediate) + */ int shifter_out = 0; /* modified 12-bit immediate. */ shift = ((insn & 0x04000000) >> 23) | ((insn & 0x7000) >> 12); @@ -11612,7 +11655,11 @@ static void disas_thumb2_insn(DisasContext *s, uint32_t insn) goto illegal_op; tcg_temp_free_i32(tcg_ctx, tmp2); rd = (insn >> 8) & 0xf; - if (rd != 15) { + if (rd == 13 && rn == 13 + && (op == 8 || op == 13)) { + /* ADD(S) SP, SP, imm or SUB(S) SP, SP, imm */ + store_sp_checked(s, tmp); + } else if (rd != 15) { store_reg(s, rd, tmp); } else { tcg_temp_free_i32(tcg_ctx, tmp); @@ -11945,7 +11992,12 @@ static void disas_thumb_insn(DisasContext *s, uint32_t insn) tmp2 = load_reg(s, rm); tcg_gen_add_i32(tcg_ctx, tmp, tmp, tmp2); tcg_temp_free_i32(tcg_ctx, tmp2); - store_reg(s, rd, tmp); + if (rd == 13) { + /* ADD SP, SP, reg */ + store_sp_checked(s, tmp); + } else { + store_reg(s, rd, tmp); + } break; case 1: /* cmp */ tmp = load_reg(s, rd); @@ -11956,7 +12008,12 @@ static void disas_thumb_insn(DisasContext *s, uint32_t insn) break; case 2: /* mov/cpy */ tmp = load_reg(s, rm); - store_reg(s, rd, tmp); + if (rd == 13) { + /* MOV SP, reg */ + store_sp_checked(s, tmp); + } else { + store_reg(s, rd, tmp); + } break; case 3: { @@ -12284,7 +12341,10 @@ static void disas_thumb_insn(DisasContext *s, uint32_t insn) break; case 10: - /* add to high reg */ + /* + * 0b1010_xxxx_xxxx_xxxx + * - Add PC/SP (immediate) + */ rd = (insn >> 8) & 7; if (insn & (1 << 11)) { /* SP */ @@ -12304,13 +12364,17 @@ static void disas_thumb_insn(DisasContext *s, uint32_t insn) op = (insn >> 8) & 0xf; switch (op) { case 0: - /* adjust stack pointer */ + /* + * 0b1011_0000_xxxx_xxxx + * - ADD (SP plus immediate) + * - SUB (SP minus immediate) + */ tmp = load_reg(s, 13); val = (insn & 0x7f) * 4; if (insn & (1 << 7)) val = -(int32_t)val; tcg_gen_addi_i32(tcg_ctx, tmp, tmp, val); - store_reg(s, 13, tmp); + store_sp_checked(s, tmp); break; case 2: /* sign/zero extend. */ diff --git a/qemu/x86_64.h b/qemu/x86_64.h index 8483df6d..10bfcd3b 100644 --- a/qemu/x86_64.h +++ b/qemu/x86_64.h @@ -1707,6 +1707,7 @@ #define helper_v7m_mrs helper_v7m_mrs_x86_64 #define helper_v7m_msr helper_v7m_msr_x86_64 #define helper_v7m_tt helper_v7m_tt_x86_64 +#define helper_v8m_stackcheck helper_v8m_stackcheck_x86_64 #define helper_vfp_absd helper_vfp_absd_x86_64 #define helper_vfp_abss helper_vfp_abss_x86_64 #define helper_vfp_addd helper_vfp_addd_x86_64