diff mbox

[RFC,ARM,5/8] ARMv8-M Security Extension's cmse_nonsecure_entry: clear registers

Message ID 001701d13f80$5022d980$f0688c80$@foss.arm.com
State New
Headers show

Commit Message

Thomas Preudhomme Dec. 26, 2015, 1:54 a.m. UTC
[Sending on behalf of Andre Vieira]

Hello,

This patch extends support for the ARMv8-M Security Extensions 'cmse_nonsecure_entry' attribute to safeguard against leak of information through unbanked registers.

When returning from a nonsecure entry function we clear all caller-saved registers that are not used to pass return values, by writing either the LR, in case of general purpose registers, or the value 0, in case of FP registers. We use the LR to write to APSR and FPSCR too. We currently only support 32 FP registers as in we only clear D0-D7.
We currently do not support entry functions that pass arguments or return variables on the stack and we diagnose this. This patch relies on the existing code to make sure callee-saved registers used in cmse_nonsecure_entry functions are saved and restored thus retaining their nonsecure mode value, this should be happening already as it is required by AAPCS.


*** gcc/ChangeLog ***
2015-10-27  Andre Vieira        <andre.simoesdiasvieira@arm.com>
            Thomas Preud'homme  <thomas.preudhomme@arm.com>

        * gcc/config/arm/arm.c (output_return_instruction): Clear
          registers.
          (thumb2_expand_return): Likewise.
          (thumb1_expand_epilogue): Likewise.
          (arm_expand_epilogue): Likewise.
          (cmse_nonsecure_entry_clear_before_return): New.
        * gcc/config/arm/arm.h (TARGET_DSP_ADD): New macro define.
        * gcc/config/arm/thumb1.md (*epilogue_insns): Change length attribute.
        * gcc/config/arm/thumb2.md (*thumb2_return): Likewise.

*** gcc/testsuite/ChangeLog ***
2015-10-27  Andre Vieira        <andre.simoesdiasvieira@arm.com>
            Thomas Preud'homme  <thomas.preudhomme@arm.com>

        * gcc.target/arm/cmse/cmse.exp: Test different multilibs separate.
        * gcc.target/arm/cmse/baseline/cmse-2.c: Test that registers are cleared.
        * gcc.target/arm/cmse/mainline/soft/cmse-5.c: New.
        * gcc.target/arm/cmse/mainline/hard/cmse-5.c: New.
        * gcc.target/arm/cmse/mainline/hard-sp/cmse-5.c: New.
        * gcc.target/arm/cmse/mainline/softfp/cmse-5.c: New.
        * gcc.target/arm/cmse/mainline/softfp-sp/cmse-5.c: New.



We welcome any comments.

Cheers,

Andre

Comments

Andre Vieira (lists) Jan. 29, 2016, 5:07 p.m. UTC | #1
On 26/12/15 01:54, Thomas Preud'homme wrote:
> [Sending on behalf of Andre Vieira]
>
> Hello,
>
> This patch extends support for the ARMv8-M Security Extensions 'cmse_nonsecure_entry' attribute to safeguard against leak of information through unbanked registers.
>
> When returning from a nonsecure entry function we clear all caller-saved registers that are not used to pass return values, by writing either the LR, in case of general purpose registers, or the value 0, in case of FP registers. We use the LR to write to APSR and FPSCR too. We currently only support 32 FP registers as in we only clear D0-D7.
> We currently do not support entry functions that pass arguments or return variables on the stack and we diagnose this. This patch relies on the existing code to make sure callee-saved registers used in cmse_nonsecure_entry functions are saved and restored thus retaining their nonsecure mode value, this should be happening already as it is required by AAPCS.
>
>
> *** gcc/ChangeLog ***
> 2015-10-27  Andre Vieira        <andre.simoesdiasvieira@arm.com>
>              Thomas Preud'homme  <thomas.preudhomme@arm.com>
>
>          * gcc/config/arm/arm.c (output_return_instruction): Clear
>            registers.
>            (thumb2_expand_return): Likewise.
>            (thumb1_expand_epilogue): Likewise.
>            (arm_expand_epilogue): Likewise.
>            (cmse_nonsecure_entry_clear_before_return): New.
>          * gcc/config/arm/arm.h (TARGET_DSP_ADD): New macro define.
>          * gcc/config/arm/thumb1.md (*epilogue_insns): Change length attribute.
>          * gcc/config/arm/thumb2.md (*thumb2_return): Likewise.
>
> *** gcc/testsuite/ChangeLog ***
> 2015-10-27  Andre Vieira        <andre.simoesdiasvieira@arm.com>
>              Thomas Preud'homme  <thomas.preudhomme@arm.com>
>
>          * gcc.target/arm/cmse/cmse.exp: Test different multilibs separate.
>          * gcc.target/arm/cmse/baseline/cmse-2.c: Test that registers are cleared.
>          * gcc.target/arm/cmse/mainline/soft/cmse-5.c: New.
>          * gcc.target/arm/cmse/mainline/hard/cmse-5.c: New.
>          * gcc.target/arm/cmse/mainline/hard-sp/cmse-5.c: New.
>          * gcc.target/arm/cmse/mainline/softfp/cmse-5.c: New.
>          * gcc.target/arm/cmse/mainline/softfp-sp/cmse-5.c: New.
>
>
> diff --git a/gcc/config/arm/arm.h b/gcc/config/arm/arm.h
> index f12e3c93bbe24b10ed8eee6687161826773ef649..b06e0586a3da50f57645bda13629bc4dbd3d53b7 100644
> --- a/gcc/config/arm/arm.h
> +++ b/gcc/config/arm/arm.h
> @@ -230,6 +230,9 @@ extern void (*arm_lang_output_object_attributes_hook)(void);
>   /* Integer SIMD instructions, and extend-accumulate instructions.  */
>   #define TARGET_INT_SIMD \
>     (TARGET_32BIT && arm_arch6 && (arm_arch_notm || arm_arch7em))
> +/* Parallel addition and subtraction instructions.  */
> +#define TARGET_DSP_ADD \
> +  (TARGET_ARM_ARCH >= 6 && (arm_arch_notm || arm_arch7em))
>
>   /* Should MOVW/MOVT be used in preference to a constant pool.  */
>   #define TARGET_USE_MOVT \
> diff --git a/gcc/config/arm/arm.c b/gcc/config/arm/arm.c
> index e530b772e3cc053c16421a2a2861d815d53ebb01..0700478ca38307f35d0cb01f83ea182802ba28fa 100644
> --- a/gcc/config/arm/arm.c
> +++ b/gcc/config/arm/arm.c
> @@ -19755,6 +19755,24 @@ output_return_instruction (rtx operand, bool really_return, bool reverse,
>   	default:
>   	  if (IS_CMSE_ENTRY (func_type))
>   	    {
> +	      char flags[12] = "APSR_nzcvq";
> +	      /* Check if we have to clear the 'GE bits' which is only used if
> +		 parallel add and subtraction instructions are available.  */
> +	      if (TARGET_DSP_ADD)
> +		{
> +		  /* If so also clear the ge flags.  */
> +		  flags[10] = 'g';
> +		  flags[11] = '\0';
> +		}
> +	      snprintf (instr, sizeof (instr),  "msr%s\t%s, %%|lr", conditional,
> +			flags);
> +	      output_asm_insn (instr, & operand);
> +	      if (TARGET_HARD_FLOAT && TARGET_VFP)
> +		{
> +		  snprintf (instr, sizeof (instr), "vmsr%s\tfpscr, %%|lr",
> +			    conditional);
> +		  output_asm_insn (instr, & operand);
> +		}
>   	      snprintf (instr, sizeof (instr), "bxns%s\t%%|lr", conditional);
>   	    }
>   	  /* Use bx if it's available.  */
> @@ -23999,6 +24017,17 @@ thumb_pop (FILE *f, unsigned long mask)
>   static void
>   thumb1_cmse_nonsecure_entry_return (FILE *f, int reg_containing_return_addr)
>   {
> +  char flags[12] = "APSR_nzcvq";
> +  /* Check if we have to clear the 'GE bits' which is only used if
> +     parallel add and subtraction instructions are available.  */
> +  if (TARGET_DSP_ADD)
> +    {
> +      flags[10] = 'g';
> +      flags[11] = '\0';
> +    }
> +  asm_fprintf (f, "\tmsr\t%s, %r\n", flags, reg_containing_return_addr);
> +  if (TARGET_HARD_FLOAT && TARGET_VFP)
> +    asm_fprintf (f, "\tvmsr\tfpscr, %r\n", reg_containing_return_addr);
>     asm_fprintf (f, "\tbxns\t%r\n", reg_containing_return_addr);
>   }
>
> @@ -25140,6 +25169,139 @@ thumb1_expand_prologue (void)
>       cfun->machine->lr_save_eliminated = 0;
>   }
>
> +/* Clear caller saved registers not used to pass return values and leaked
> +   condition flags before exiting a cmse_nonsecure_entry function.  */
> +
> +void
> +cmse_nonsecure_entry_clear_before_return (void)
> +{
> +  uint64_t to_clear_mask;
> +  int regno, maxregno = IP_REGNUM;
> +  tree result_type;
> +  rtx result_rtl;
> +
> +  to_clear_mask = (1LL << (NUM_ARG_REGS)) - 1;
> +  to_clear_mask |= (1LL << IP_REGNUM);
> +  /* If we are not dealing with -mfloat-abi=softfp we will need to clear VFP
> +     registers.  We also check TARGET_VFP to make sure these are present.  */
> +  if (TARGET_HARD_FLOAT && TARGET_VFP)
> +    {
> +      uint64_t float_mask = (1LL << (D7_VFP_REGNUM + 1)) - 1;
> +      float_mask &= ~((1LL << FIRST_VFP_REGNUM) - 1);
> +      to_clear_mask |= float_mask;
> +      maxregno = LAST_VFP_REGNUM;
> +    }
> +
> +  /* If the user has defined registers to be caller saved, these are no longer
> +     restored by the function before returning and must thus be cleared for
> +     security purposes.  */
> +  for (regno = NUM_ARG_REGS; regno < LAST_VFP_REGNUM; regno++)
> +    {
> +      /* We leave registers traditionally used to pass arguments untouched,
> +	 since if these should not be made callee-saved by the user.  */
> +      if (regno >= FIRST_VFP_REGNUM && regno <= D7_VFP_REGNUM)
> +	continue;
> +      if (regno >= IP_REGNUM && regno <= PC_REGNUM)
> +	continue;
> +      if (call_used_regs[regno])
> +	to_clear_mask |= (1LL << regno);
> +    }
> +
> +  /* Make sure we do not clear the registers used to pass the result in.  */
> +  result_type = TREE_TYPE (DECL_RESULT (current_function_decl));
> +  if (!VOID_TYPE_P (result_type))
> +    {
> +      rtx reg;
> +
> +      result_rtl = arm_function_value (result_type, current_function_decl, 0);
> +
> +      /* No need to check that we return in registers, because we don't
> +	 support returning on stack yet.  */
> +      switch (GET_MODE (result_rtl))
> +	{
> +	case BLKmode:
> +	  /* We are dealing with a return in multiple VFP registers.  */
> +	  {
> +	    int i;
> +	    /* This should really only occur when dealing with an hard float
> +	       abi.  */
> +	    gcc_assert (TARGET_HARD_FLOAT_ABI && TARGET_VFP);
> +
> +	    for (i = 0; i < XVECLEN (result_rtl, 0); i++)
> +	      {
> +		reg = XEXP (XVECEXP (result_rtl, 0, i), 0);
> +		gcc_assert (REG_P (reg));
> +		/* If we are dealing with DF mode, make sure you don't clear
> +		   either registers it addresses.  */
> +		if (GET_MODE (reg) == DFmode)
> +		  to_clear_mask &= ~(1LL << (REGNO (reg) + 1));
> +		to_clear_mask &= ~(1LL << REGNO (reg));
> +	      }
> +	  }
> +	  break;
> +	case DImode:
> +	  to_clear_mask &= ~2; /* Don't clear r0 and r1.  */
> +	case SImode:
> +	  to_clear_mask &= ~1; /* Don't clear r0.  */
> +	  break;
> +	case DFmode:
> +	  /* If we have -mfloat-abi=hard, do not clear registers used to pass
> +	     return value.  */
> +	  if (TARGET_HARD_FLOAT_ABI && TARGET_VFP)
> +	    /* Don't clear s0 and s1.  */
> +	    to_clear_mask &= ~(1LL << (FIRST_VFP_REGNUM + 1));
> +	  else
> +	    to_clear_mask &= ~2; /* Don't clear r0, r1.  */
> +	case SFmode:
> +	  /* If we have -mfloat-abi=hard, do not clear registers used to pass
> +	     return value.  */
> +	  if (TARGET_HARD_FLOAT_ABI && TARGET_VFP)
> +	    /* Don't clear s0.  */
> +	    to_clear_mask &= ~(1LL << FIRST_VFP_REGNUM);
> +	  else
> +	    to_clear_mask &= ~1; /* Don't clear r0.  */
> +	  break;
> +	default:
> +	  /* We are missing a mode, though AAPCS says we may only return in
> +	     r0 and r1, s0-d15/d0-d7 so something went wrong here.  */
> +	  gcc_unreachable ();
> +	  break;
> +	}
> +    }
> +
> +    for (regno = 0; regno <= maxregno; regno++)
> +      {
> +	if (!(to_clear_mask & (1LL << regno)))
> +	  continue;
> +
> +	/* If regno is a vfp register, even and its successor is also to
> +	   be cleared, use vmov.  */
> +	if (IS_VFP_REGNUM (regno))
> +	  {
> +	    if (TARGET_VFP_DOUBLE
> +		&& VFP_REGNO_OK_FOR_DOUBLE (regno)
> +		&& to_clear_mask & (1LL << (regno + 1)))
> +	      {
> +		emit_move_insn (gen_rtx_REG (DFmode, regno++),
> +				CONST1_RTX (DFmode));
> +		emit_use (gen_rtx_REG (DFmode, regno));
> +	      }
> +	    else
> +	      {
> +		emit_move_insn (gen_rtx_REG (SFmode, regno),
> +				CONST1_RTX (SFmode));
> +		emit_use (gen_rtx_REG (SFmode, regno));
> +	      }
> +	  }
> +	else
> +	  {
> +	    emit_move_insn (gen_rtx_REG (SImode, regno),
> +			    gen_rtx_REG (SImode, LR_REGNUM));
> +	    emit_use (gen_rtx_REG (SImode, regno));
> +	  }
> +      }
> +}
> +
>   /* Generate pattern *pop_multiple_with_stack_update_and_return if single
>      POP instruction can be generated.  LR should be replaced by PC.  All
>      the checks required are already done by  USE_RETURN_INSN ().  Hence,
> @@ -25189,6 +25351,8 @@ thumb2_expand_return (bool simple_return)
>       }
>     else
>       {
> +      if (IS_CMSE_ENTRY (arm_current_func_type ()))
> +	cmse_nonsecure_entry_clear_before_return ();
>         emit_jump_insn (simple_return_rtx);
>       }
>   }
> @@ -25247,6 +25411,10 @@ thumb1_expand_epilogue (void)
>
>     if (! df_regs_ever_live_p (LR_REGNUM))
>       emit_use (gen_rtx_REG (SImode, LR_REGNUM));
> +
> +  /* Clear all caller-saved regs that are not used to return.  */
> +  if (IS_CMSE_ENTRY (arm_current_func_type ()))
> +    cmse_nonsecure_entry_clear_before_return ();
>   }
>
>   /* Epilogue code for APCS frame.  */
> @@ -25681,6 +25849,14 @@ arm_expand_epilogue (bool really_return)
>   				   stack_pointer_rtx, stack_pointer_rtx);
>       }
>
> +    /* Clear all caller-saved regs that are not used to return.  */
> +    if (IS_CMSE_ENTRY (arm_current_func_type ()))
> +      {
> +	/* CMSE_ENTRY always returns!  */
> +	gcc_assert (really_return);
> +	cmse_nonsecure_entry_clear_before_return ();
> +      }
> +
>     if (!really_return)
>       return;
>
> diff --git a/gcc/config/arm/thumb1.md b/gcc/config/arm/thumb1.md
> index d2a0420a2e9c71bb954d134fb88a86d694cf786d..cdcf397b0cab0ea7f246ad830ad28b07fed71d8b 100644
> --- a/gcc/config/arm/thumb1.md
> +++ b/gcc/config/arm/thumb1.md
> @@ -1757,8 +1757,15 @@
>     "*
>       return thumb1_unexpanded_epilogue ();
>     "
> -  ; Length is absolute worst case
> -  [(set_attr "length" "44")
> +  ; Length is absolute worst case, when using CMSE and if this is an entry
> +  ; function an extra 4 (msr) to 8 (vmsr) extra might be added.
> +  [(set (attr "length")
> +	(if_then_else
> +	 (match_test "IS_CMSE_ENTRY (arm_current_func_type ())")
> +	 (if_then_else (match_test "TARGET_HARD_FLOAT && TARGET_VFP")
> +	  (const_int 52)
> +	  (const_int 48))
> +	 (const_int 44)))
>      (set_attr "type" "block")
>      ;; We don't clobber the conditions, but the potential length of this
>      ;; operation is sufficient to make conditionalizing the sequence
> diff --git a/gcc/config/arm/thumb2.md b/gcc/config/arm/thumb2.md
> index a724752a39cf2862c68a53ef28a239de2ae1ed96..0c07df91fe8844a7e7437ef5b7f00f74815d63f4 100644
> --- a/gcc/config/arm/thumb2.md
> +++ b/gcc/config/arm/thumb2.md
> @@ -1106,7 +1106,15 @@
>     "TARGET_THUMB2"
>     "* return output_return_instruction (const_true_rtx, true, false, true);"
>     [(set_attr "type" "branch")
> -   (set_attr "length" "4")]
> +  ; If this is a return from a cmse_nonsecure_entry function then code will be
> +  ; added to clear the APSR and potentially the FPSCR if VFP is available, so
> +  ; we adapt the length accordingly.
> +   (set (attr "length")
> +    (if_then_else (match_test "IS_CMSE_ENTRY (arm_current_func_type ())")
> +     (if_then_else (match_test "TARGET_HARD_FLOAT && TARGET_VFP")
> +      (const_int 12)
> +      (const_int 8))
> +     (const_int 4)))]
>   )
>
>   (define_insn_and_split "thumb2_eh_return"
> diff --git a/gcc/testsuite/gcc.target/arm/cmse/baseline/cmse-2.c b/gcc/testsuite/gcc.target/arm/cmse/baseline/cmse-2.c
> new file mode 100644
> index 0000000000000000000000000000000000000000..7cef73372fd5f5be080bdfe30999694564deaa9a
> --- /dev/null
> +++ b/gcc/testsuite/gcc.target/arm/cmse/baseline/cmse-2.c
> @@ -0,0 +1,18 @@
> +/* { dg-do compile } */
> +/* { dg-require-effective-target arm_arch_v8m_base_ok } */
> +/* { dg-add-options arm_arch_v8m_base } */
> +/* { dg-options "-mcmse" }  */
> +
> +extern float bar (void);
> +
> +float __attribute__ ((cmse_nonsecure_entry))
> +foo (void)
> +{
> +  return bar ();
> +}
> +/* { dg-final { scan-assembler "mov\tr1, lr" } } */
> +/* { dg-final { scan-assembler "mov\tr2, lr" } } */
> +/* { dg-final { scan-assembler "mov\tr3, lr" } } */
> +/* { dg-final { scan-assembler "mov\tip, lr" } } */
> +/* { dg-final { scan-assembler "msr\tAPSR_nzcvq, r1" } } */
> +/* { dg-final { scan-assembler "bxns\tr1" } } */
> diff --git a/gcc/testsuite/gcc.target/arm/cmse/cmse.exp b/gcc/testsuite/gcc.target/arm/cmse/cmse.exp
> index 6d4bfec1d12bc575218e0b8c33559d0a4da9ec70..592349ad07257f8074f9443a599ae08c36136a80 100644
> --- a/gcc/testsuite/gcc.target/arm/cmse/cmse.exp
> +++ b/gcc/testsuite/gcc.target/arm/cmse/cmse.exp
> @@ -38,6 +38,26 @@ set LTO_TORTURE_OPTIONS ""
>   gcc-dg-runtest [lsort [glob $srcdir/$subdir/*.c]] \
>   	"" $DEFAULT_CFLAGS
>
> +if {[check_effective_target_arm_arch_v8m_base_ok]} then {
> +    # Baseline only
> +    gcc-dg-runtest [lsort [glob $srcdir/$subdir/baseline/*.c]] \
> +	    "" $DEFAULT_CFLAGS
> +}
> +
> +if {[check_effective_target_arm_arch_v8m_main_ok]} then {
> +    # Mainline -mfloat-abi=soft
> +    gcc-dg-runtest [lsort [glob $srcdir/$subdir/mainline/soft/*.c]] \
> +	    "-mfloat-abi=soft" $DEFAULT_CFLAGS
> +    gcc-dg-runtest [lsort [glob $srcdir/$subdir/mainline/softfp/*.c]] \
> +	    "" $DEFAULT_CFLAGS
> +    gcc-dg-runtest [lsort [glob $srcdir/$subdir/mainline/softfp-sp/*.c]] \
> +	    "" $DEFAULT_CFLAGS
> +    gcc-dg-runtest [lsort [glob $srcdir/$subdir/mainline/hard/*.c]] \
> +	    "" $DEFAULT_CFLAGS
> +    gcc-dg-runtest [lsort [glob $srcdir/$subdir/mainline/hard-sp/*.c]] \
> +	    "" $DEFAULT_CFLAGS
> +}
> +
>   set LTO_TORTURE_OPTIONS ${saved-lto_torture_options}
>   set dg-do-what-default ${saved-dg-do-what-default}
>
> diff --git a/gcc/testsuite/gcc.target/arm/cmse/mainline/hard-sp/cmse-5.c b/gcc/testsuite/gcc.target/arm/cmse/mainline/hard-sp/cmse-5.c
> new file mode 100644
> index 0000000000000000000000000000000000000000..ab607aa1b7210acc7983bfb401e95117eb8c0d17
> --- /dev/null
> +++ b/gcc/testsuite/gcc.target/arm/cmse/mainline/hard-sp/cmse-5.c
> @@ -0,0 +1,37 @@
> +/* { dg-do compile } */
> +/* { dg-require-effective-target arm_arch_v8m_main_ok } */
> +/* { dg-add-options arm_arch_v8m_main } */
> +/* { dg-skip-if "Do not combine float-abi= hard | soft | softfp" {*-*-*} {"-mfloat-abi=soft" -mfloat-abi=softfp } {""} } */
> +/* { dg-skip-if "Skip these if testing double precision" {*-*-*} {"-mfpu=fpv[4-5]-d16"} {""} } */
> +/* { dg-options "-mcmse -mfloat-abi=hard -mfpu=fpv5-sp-d16" }  */
> +
> +extern float bar (void);
> +
> +float __attribute__ ((cmse_nonsecure_entry))
> +foo (void)
> +{
> +  return bar ();
> +}
> +/* { dg-final { scan-assembler "mov\tr0, lr" } } */
> +/* { dg-final { scan-assembler "mov\tr1, lr" } } */
> +/* { dg-final { scan-assembler "mov\tr2, lr" } } */
> +/* { dg-final { scan-assembler "mov\tr3, lr" } } */
> +/* { dg-final { scan-assembler "mov\tip, lr" } } */
> +/* { dg-final { scan-assembler-not "vmov\.f32\ts0, #1\.0" } } */
> +/* { dg-final { scan-assembler "vmov\.f32\ts1, #1\.0" } } */
> +/* { dg-final { scan-assembler "vmov\.f32\ts2, #1\.0" } } */
> +/* { dg-final { scan-assembler "vmov\.f32\ts3, #1\.0" } } */
> +/* { dg-final { scan-assembler "vmov\.f32\ts4, #1\.0" } } */
> +/* { dg-final { scan-assembler "vmov\.f32\ts5, #1\.0" } } */
> +/* { dg-final { scan-assembler "vmov\.f32\ts6, #1\.0" } } */
> +/* { dg-final { scan-assembler "vmov\.f32\ts7, #1\.0" } } */
> +/* { dg-final { scan-assembler "vmov\.f32\ts8, #1\.0" } } */
> +/* { dg-final { scan-assembler "vmov\.f32\ts9, #1\.0" } } */
> +/* { dg-final { scan-assembler "vmov\.f32\ts10, #1\.0" } } */
> +/* { dg-final { scan-assembler "vmov\.f32\ts11, #1\.0" } } */
> +/* { dg-final { scan-assembler "vmov\.f32\ts12, #1\.0" } } */
> +/* { dg-final { scan-assembler "vmov\.f32\ts13, #1\.0" } } */
> +/* { dg-final { scan-assembler "vmov\.f32\ts14, #1\.0" } } */
> +/* { dg-final { scan-assembler "vmov\.f32\ts15, #1\.0" } } */
> +/* { dg-final { scan-assembler "msr\tAPSR_nzcvq, lr" { target { arm_arch_v8m_main_ok && { ! arm_dsp } } } } } */
> +/* { dg-final { scan-assembler "msr\tAPSR_nzcvqg, lr" { target { arm_arch_v8m_main_ok && arm_dsp } } } } */
> diff --git a/gcc/testsuite/gcc.target/arm/cmse/mainline/hard/cmse-5.c b/gcc/testsuite/gcc.target/arm/cmse/mainline/hard/cmse-5.c
> new file mode 100644
> index 0000000000000000000000000000000000000000..5ed7e1d6404b8f17d630895eb06df5921a1a965f
> --- /dev/null
> +++ b/gcc/testsuite/gcc.target/arm/cmse/mainline/hard/cmse-5.c
> @@ -0,0 +1,30 @@
> +/* { dg-do compile } */
> +/* { dg-require-effective-target arm_arch_v8m_main_ok } */
> +/* { dg-add-options arm_arch_v8m_main } */
> +/* { dg-skip-if "Do not combine float-abi= hard | soft | softfp" {*-*-*} {"-mfloat-abi=soft" -mfloat-abi=softfp } {""} } */
> +/* { dg-skip-if "Skip these if testing single precision" {*-*-*} {"-mfpu=*-sp-*"} {""} } */
> +/* { dg-options "-mcmse -mfloat-abi=hard -mfpu=fpv5-d16" }  */
> +
> +extern float bar (void);
> +
> +float __attribute__ ((cmse_nonsecure_entry))
> +foo (void)
> +{
> +  return bar ();
> +}
> +/* { dg-final { scan-assembler "mov\tr0, lr" } } */
> +/* { dg-final { scan-assembler "mov\tr1, lr" } } */
> +/* { dg-final { scan-assembler "mov\tr2, lr" } } */
> +/* { dg-final { scan-assembler "mov\tr3, lr" } } */
> +/* { dg-final { scan-assembler "mov\tip, lr" } } */
> +/* { dg-final { scan-assembler-not "vmov\.f32\ts0, #1\.0" } } */
> +/* { dg-final { scan-assembler "vmov\.f32\ts1, #1\.0" } } */
> +/* { dg-final { scan-assembler "vmov\.f64\td1, #1\.0" } } */
> +/* { dg-final { scan-assembler "vmov\.f64\td2, #1\.0" } } */
> +/* { dg-final { scan-assembler "vmov\.f64\td3, #1\.0" } } */
> +/* { dg-final { scan-assembler "vmov\.f64\td4, #1\.0" } } */
> +/* { dg-final { scan-assembler "vmov\.f64\td5, #1\.0" } } */
> +/* { dg-final { scan-assembler "vmov\.f64\td6, #1\.0" } } */
> +/* { dg-final { scan-assembler "vmov\.f64\td7, #1\.0" } } */
> +/* { dg-final { scan-assembler "msr\tAPSR_nzcvq, lr" { target { arm_arch_v8m_main_ok && { ! arm_dsp } } } } } */
> +/* { dg-final { scan-assembler "msr\tAPSR_nzcvqg, lr" { target { arm_arch_v8m_main_ok && arm_dsp } } } } */
> diff --git a/gcc/testsuite/gcc.target/arm/cmse/mainline/soft/cmse-5.c b/gcc/testsuite/gcc.target/arm/cmse/mainline/soft/cmse-5.c
> new file mode 100644
> index 0000000000000000000000000000000000000000..d24683f52eba11df5b41eef0e5a8e365fb206fe8
> --- /dev/null
> +++ b/gcc/testsuite/gcc.target/arm/cmse/mainline/soft/cmse-5.c
> @@ -0,0 +1,22 @@
> +/* { dg-do compile } */
> +/* { dg-require-effective-target arm_arch_v8m_main_ok } */
> +/* { dg-add-options arm_arch_v8m_main } */
> +/* { dg-skip-if "Do not combine float-abi= hard | soft | softfp" {*-*-*} {"-mfloat-abi=hard" -mfloat-abi=softfp } {""} } */
> +/* { dg-options "-mcmse -mfloat-abi=soft" }  */
> +
> +extern float bar (void);
> +
> +float __attribute__ ((cmse_nonsecure_entry))
> +foo (void)
> +{
> +  return bar ();
> +}
> +
> +/* { dg-final { scan-assembler "mov\tr1, lr" } } */
> +/* { dg-final { scan-assembler "mov\tr2, lr" } } */
> +/* { dg-final { scan-assembler "mov\tr3, lr" } } */
> +/* { dg-final { scan-assembler "mov\tip, lr" } } */
> +/* { dg-final { scan-assembler-not "vmov" } } */
> +/* { dg-final { scan-assembler-not "vmsr" } } */
> +/* { dg-final { scan-assembler "msr\tAPSR_nzcvq, lr" { target { arm_arch_v8m_main_ok && { ! arm_dsp } } } } } */
> +/* { dg-final { scan-assembler "msr\tAPSR_nzcvqg, lr" { target { arm_arch_v8m_main_ok && arm_dsp } } } } */
> diff --git a/gcc/testsuite/gcc.target/arm/cmse/mainline/softfp-sp/cmse-5.c b/gcc/testsuite/gcc.target/arm/cmse/mainline/softfp-sp/cmse-5.c
> new file mode 100644
> index 0000000000000000000000000000000000000000..c22775ace8361729c36130422475522fbaca05b5
> --- /dev/null
> +++ b/gcc/testsuite/gcc.target/arm/cmse/mainline/softfp-sp/cmse-5.c
> @@ -0,0 +1,39 @@
> +/* { dg-do compile } */
> +/* { dg-require-effective-target arm_arch_v8m_main_ok } */
> +/* { dg-add-options arm_arch_v8m_main } */
> +/* { dg-skip-if "Do not combine float-abi= hard | soft | softfp" {*-*-*} {"-mfloat-abi=soft" -mfloat-abi=hard } {""} } */
> +/* { dg-skip-if "Skip these if testing double precision" {*-*-*} {"-mfpu=fpv[4-5]-d16"} {""} } */
> +/* { dg-options "-mcmse -mfloat-abi=softfp -mfpu=fpv5-sp-d16" }  */
> +
> +extern float bar (void);
> +
> +float __attribute__ ((cmse_nonsecure_entry))
> +foo (void)
> +{
> +  return bar ();
> +}
> +/* { dg-final { scan-assembler "__acle_se_foo:" } } */
> +/* { dg-final { scan-assembler-not "mov\tr0, lr" } } */
> +/* { dg-final { scan-assembler "mov\tr1, lr" } } */
> +/* { dg-final { scan-assembler "mov\tr2, lr" } } */
> +/* { dg-final { scan-assembler "mov\tr3, lr" } } */
> +/* { dg-final { scan-assembler "mov\tip, lr" } } */
> +/* { dg-final { scan-assembler "vmov\.f32\ts0, #1\.0" } } */
> +/* { dg-final { scan-assembler "vmov\.f32\ts1, #1\.0" } } */
> +/* { dg-final { scan-assembler "vmov\.f32\ts2, #1\.0" } } */
> +/* { dg-final { scan-assembler "vmov\.f32\ts3, #1\.0" } } */
> +/* { dg-final { scan-assembler "vmov\.f32\ts4, #1\.0" } } */
> +/* { dg-final { scan-assembler "vmov\.f32\ts5, #1\.0" } } */
> +/* { dg-final { scan-assembler "vmov\.f32\ts6, #1\.0" } } */
> +/* { dg-final { scan-assembler "vmov\.f32\ts7, #1\.0" } } */
> +/* { dg-final { scan-assembler "vmov\.f32\ts8, #1\.0" } } */
> +/* { dg-final { scan-assembler "vmov\.f32\ts9, #1\.0" } } */
> +/* { dg-final { scan-assembler "vmov\.f32\ts10, #1\.0" } } */
> +/* { dg-final { scan-assembler "vmov\.f32\ts11, #1\.0" } } */
> +/* { dg-final { scan-assembler "vmov\.f32\ts12, #1\.0" } } */
> +/* { dg-final { scan-assembler "vmov\.f32\ts13, #1\.0" } } */
> +/* { dg-final { scan-assembler "vmov\.f32\ts14, #1\.0" } } */
> +/* { dg-final { scan-assembler "vmov\.f32\ts15, #1\.0" } } */
> +/* { dg-final { scan-assembler "msr\tAPSR_nzcvq, lr" { target { arm_arch_v8m_main_ok && { ! arm_dsp } } } } } */
> +/* { dg-final { scan-assembler "msr\tAPSR_nzcvqg, lr" { target { arm_arch_v8m_main_ok && arm_dsp } } } } */
> +/* { dg-final { scan-assembler "bxns" } } */
> diff --git a/gcc/testsuite/gcc.target/arm/cmse/mainline/softfp/cmse-5.c b/gcc/testsuite/gcc.target/arm/cmse/mainline/softfp/cmse-5.c
> new file mode 100644
> index 0000000000000000000000000000000000000000..28b05c391081bd42407b4e56a1e84daa549fa06e
> --- /dev/null
> +++ b/gcc/testsuite/gcc.target/arm/cmse/mainline/softfp/cmse-5.c
> @@ -0,0 +1,31 @@
> +/* { dg-do compile } */
> +/* { dg-require-effective-target arm_arch_v8m_main_ok } */
> +/* { dg-add-options arm_arch_v8m_main } */
> +/* { dg-skip-if "Do not combine float-abi= hard | soft | softfp" {*-*-*} {"-mfloat-abi=soft" -mfloat-abi=hard } {""} } */
> +/* { dg-skip-if "Skip these if testing single precision" {*-*-*} {"-mfpu=*-sp-*"} {""} } */
> +/* { dg-options "-mcmse -mfloat-abi=softfp -mfpu=fpv5-d16" }  */
> +
> +extern float bar (void);
> +
> +float __attribute__ ((cmse_nonsecure_entry))
> +foo (void)
> +{
> +  return bar ();
> +}
> +/* { dg-final { scan-assembler "__acle_se_foo:" } } */
> +/* { dg-final { scan-assembler-not "mov\tr0, lr" } } */
> +/* { dg-final { scan-assembler "mov\tr1, lr" } } */
> +/* { dg-final { scan-assembler "mov\tr2, lr" } } */
> +/* { dg-final { scan-assembler "mov\tr3, lr" } } */
> +/* { dg-final { scan-assembler "mov\tip, lr" } } */
> +/* { dg-final { scan-assembler "vmov\.f64\td0, #1\.0" } } */
> +/* { dg-final { scan-assembler "vmov\.f64\td1, #1\.0" } } */
> +/* { dg-final { scan-assembler "vmov\.f64\td2, #1\.0" } } */
> +/* { dg-final { scan-assembler "vmov\.f64\td3, #1\.0" } } */
> +/* { dg-final { scan-assembler "vmov\.f64\td4, #1\.0" } } */
> +/* { dg-final { scan-assembler "vmov\.f64\td5, #1\.0" } } */
> +/* { dg-final { scan-assembler "vmov\.f64\td6, #1\.0" } } */
> +/* { dg-final { scan-assembler "vmov\.f64\td7, #1\.0" } } */
> +/* { dg-final { scan-assembler "msr\tAPSR_nzcvq, lr" { target { arm_arch_v8m_main_ok && { ! arm_dsp } } } } } */
> +/* { dg-final { scan-assembler "msr\tAPSR_nzcvqg, lr" { target { arm_arch_v8m_main_ok && arm_dsp } } } } */
> +/* { dg-final { scan-assembler "bxns" } } */
>
> We welcome any comments.
>
> Cheers,
>
> Andre
>
Ping.
diff mbox

Patch

diff --git a/gcc/config/arm/arm.h b/gcc/config/arm/arm.h
index f12e3c93bbe24b10ed8eee6687161826773ef649..b06e0586a3da50f57645bda13629bc4dbd3d53b7 100644
--- a/gcc/config/arm/arm.h
+++ b/gcc/config/arm/arm.h
@@ -230,6 +230,9 @@  extern void (*arm_lang_output_object_attributes_hook)(void);
 /* Integer SIMD instructions, and extend-accumulate instructions.  */
 #define TARGET_INT_SIMD \
   (TARGET_32BIT && arm_arch6 && (arm_arch_notm || arm_arch7em))
+/* Parallel addition and subtraction instructions.  */
+#define TARGET_DSP_ADD \
+  (TARGET_ARM_ARCH >= 6 && (arm_arch_notm || arm_arch7em))
 
 /* Should MOVW/MOVT be used in preference to a constant pool.  */
 #define TARGET_USE_MOVT \
diff --git a/gcc/config/arm/arm.c b/gcc/config/arm/arm.c
index e530b772e3cc053c16421a2a2861d815d53ebb01..0700478ca38307f35d0cb01f83ea182802ba28fa 100644
--- a/gcc/config/arm/arm.c
+++ b/gcc/config/arm/arm.c
@@ -19755,6 +19755,24 @@  output_return_instruction (rtx operand, bool really_return, bool reverse,
 	default:
 	  if (IS_CMSE_ENTRY (func_type))
 	    {
+	      char flags[12] = "APSR_nzcvq";
+	      /* Check if we have to clear the 'GE bits' which is only used if
+		 parallel add and subtraction instructions are available.  */
+	      if (TARGET_DSP_ADD)
+		{
+		  /* If so also clear the ge flags.  */
+		  flags[10] = 'g';
+		  flags[11] = '\0';
+		}
+	      snprintf (instr, sizeof (instr),  "msr%s\t%s, %%|lr", conditional,
+			flags);
+	      output_asm_insn (instr, & operand);
+	      if (TARGET_HARD_FLOAT && TARGET_VFP)
+		{
+		  snprintf (instr, sizeof (instr), "vmsr%s\tfpscr, %%|lr",
+			    conditional);
+		  output_asm_insn (instr, & operand);
+		}
 	      snprintf (instr, sizeof (instr), "bxns%s\t%%|lr", conditional);
 	    }
 	  /* Use bx if it's available.  */
@@ -23999,6 +24017,17 @@  thumb_pop (FILE *f, unsigned long mask)
 static void
 thumb1_cmse_nonsecure_entry_return (FILE *f, int reg_containing_return_addr)
 {
+  char flags[12] = "APSR_nzcvq";
+  /* Check if we have to clear the 'GE bits' which is only used if
+     parallel add and subtraction instructions are available.  */
+  if (TARGET_DSP_ADD)
+    {
+      flags[10] = 'g';
+      flags[11] = '\0';
+    }
+  asm_fprintf (f, "\tmsr\t%s, %r\n", flags, reg_containing_return_addr);
+  if (TARGET_HARD_FLOAT && TARGET_VFP)
+    asm_fprintf (f, "\tvmsr\tfpscr, %r\n", reg_containing_return_addr);
   asm_fprintf (f, "\tbxns\t%r\n", reg_containing_return_addr);
 }
 
@@ -25140,6 +25169,139 @@  thumb1_expand_prologue (void)
     cfun->machine->lr_save_eliminated = 0;
 }
 
+/* Clear caller saved registers not used to pass return values and leaked
+   condition flags before exiting a cmse_nonsecure_entry function.  */
+
+void
+cmse_nonsecure_entry_clear_before_return (void)
+{
+  uint64_t to_clear_mask;
+  int regno, maxregno = IP_REGNUM;
+  tree result_type;
+  rtx result_rtl;
+
+  to_clear_mask = (1LL << (NUM_ARG_REGS)) - 1;
+  to_clear_mask |= (1LL << IP_REGNUM);
+  /* If we are not dealing with -mfloat-abi=softfp we will need to clear VFP
+     registers.  We also check TARGET_VFP to make sure these are present.  */
+  if (TARGET_HARD_FLOAT && TARGET_VFP)
+    {
+      uint64_t float_mask = (1LL << (D7_VFP_REGNUM + 1)) - 1;
+      float_mask &= ~((1LL << FIRST_VFP_REGNUM) - 1);
+      to_clear_mask |= float_mask;
+      maxregno = LAST_VFP_REGNUM;
+    }
+
+  /* If the user has defined registers to be caller saved, these are no longer
+     restored by the function before returning and must thus be cleared for
+     security purposes.  */
+  for (regno = NUM_ARG_REGS; regno < LAST_VFP_REGNUM; regno++)
+    {
+      /* We leave registers traditionally used to pass arguments untouched,
+	 since if these should not be made callee-saved by the user.  */
+      if (regno >= FIRST_VFP_REGNUM && regno <= D7_VFP_REGNUM)
+	continue;
+      if (regno >= IP_REGNUM && regno <= PC_REGNUM)
+	continue;
+      if (call_used_regs[regno])
+	to_clear_mask |= (1LL << regno);
+    }
+
+  /* Make sure we do not clear the registers used to pass the result in.  */
+  result_type = TREE_TYPE (DECL_RESULT (current_function_decl));
+  if (!VOID_TYPE_P (result_type))
+    {
+      rtx reg;
+
+      result_rtl = arm_function_value (result_type, current_function_decl, 0);
+
+      /* No need to check that we return in registers, because we don't
+	 support returning on stack yet.  */
+      switch (GET_MODE (result_rtl))
+	{
+	case BLKmode:
+	  /* We are dealing with a return in multiple VFP registers.  */
+	  {
+	    int i;
+	    /* This should really only occur when dealing with an hard float
+	       abi.  */
+	    gcc_assert (TARGET_HARD_FLOAT_ABI && TARGET_VFP);
+
+	    for (i = 0; i < XVECLEN (result_rtl, 0); i++)
+	      {
+		reg = XEXP (XVECEXP (result_rtl, 0, i), 0);
+		gcc_assert (REG_P (reg));
+		/* If we are dealing with DF mode, make sure you don't clear
+		   either registers it addresses.  */
+		if (GET_MODE (reg) == DFmode)
+		  to_clear_mask &= ~(1LL << (REGNO (reg) + 1));
+		to_clear_mask &= ~(1LL << REGNO (reg));
+	      }
+	  }
+	  break;
+	case DImode:
+	  to_clear_mask &= ~2; /* Don't clear r0 and r1.  */
+	case SImode:
+	  to_clear_mask &= ~1; /* Don't clear r0.  */
+	  break;
+	case DFmode:
+	  /* If we have -mfloat-abi=hard, do not clear registers used to pass
+	     return value.  */
+	  if (TARGET_HARD_FLOAT_ABI && TARGET_VFP)
+	    /* Don't clear s0 and s1.  */
+	    to_clear_mask &= ~(1LL << (FIRST_VFP_REGNUM + 1));
+	  else
+	    to_clear_mask &= ~2; /* Don't clear r0, r1.  */
+	case SFmode:
+	  /* If we have -mfloat-abi=hard, do not clear registers used to pass
+	     return value.  */
+	  if (TARGET_HARD_FLOAT_ABI && TARGET_VFP)
+	    /* Don't clear s0.  */
+	    to_clear_mask &= ~(1LL << FIRST_VFP_REGNUM);
+	  else
+	    to_clear_mask &= ~1; /* Don't clear r0.  */
+	  break;
+	default:
+	  /* We are missing a mode, though AAPCS says we may only return in
+	     r0 and r1, s0-d15/d0-d7 so something went wrong here.  */
+	  gcc_unreachable ();
+	  break;
+	}
+    }
+
+    for (regno = 0; regno <= maxregno; regno++)
+      {
+	if (!(to_clear_mask & (1LL << regno)))
+	  continue;
+
+	/* If regno is a vfp register, even and its successor is also to
+	   be cleared, use vmov.  */
+	if (IS_VFP_REGNUM (regno))
+	  {
+	    if (TARGET_VFP_DOUBLE
+		&& VFP_REGNO_OK_FOR_DOUBLE (regno)
+		&& to_clear_mask & (1LL << (regno + 1)))
+	      {
+		emit_move_insn (gen_rtx_REG (DFmode, regno++),
+				CONST1_RTX (DFmode));
+		emit_use (gen_rtx_REG (DFmode, regno));
+	      }
+	    else
+	      {
+		emit_move_insn (gen_rtx_REG (SFmode, regno),
+				CONST1_RTX (SFmode));
+		emit_use (gen_rtx_REG (SFmode, regno));
+	      }
+	  }
+	else
+	  {
+	    emit_move_insn (gen_rtx_REG (SImode, regno),
+			    gen_rtx_REG (SImode, LR_REGNUM));
+	    emit_use (gen_rtx_REG (SImode, regno));
+	  }
+      }
+}
+
 /* Generate pattern *pop_multiple_with_stack_update_and_return if single
    POP instruction can be generated.  LR should be replaced by PC.  All
    the checks required are already done by  USE_RETURN_INSN ().  Hence,
@@ -25189,6 +25351,8 @@  thumb2_expand_return (bool simple_return)
     }
   else
     {
+      if (IS_CMSE_ENTRY (arm_current_func_type ()))
+	cmse_nonsecure_entry_clear_before_return ();
       emit_jump_insn (simple_return_rtx);
     }
 }
@@ -25247,6 +25411,10 @@  thumb1_expand_epilogue (void)
 
   if (! df_regs_ever_live_p (LR_REGNUM))
     emit_use (gen_rtx_REG (SImode, LR_REGNUM));
+
+  /* Clear all caller-saved regs that are not used to return.  */
+  if (IS_CMSE_ENTRY (arm_current_func_type ()))
+    cmse_nonsecure_entry_clear_before_return ();
 }
 
 /* Epilogue code for APCS frame.  */
@@ -25681,6 +25849,14 @@  arm_expand_epilogue (bool really_return)
 				   stack_pointer_rtx, stack_pointer_rtx);
     }
 
+    /* Clear all caller-saved regs that are not used to return.  */
+    if (IS_CMSE_ENTRY (arm_current_func_type ()))
+      {
+	/* CMSE_ENTRY always returns!  */
+	gcc_assert (really_return);
+	cmse_nonsecure_entry_clear_before_return ();
+      }
+
   if (!really_return)
     return;
 
diff --git a/gcc/config/arm/thumb1.md b/gcc/config/arm/thumb1.md
index d2a0420a2e9c71bb954d134fb88a86d694cf786d..cdcf397b0cab0ea7f246ad830ad28b07fed71d8b 100644
--- a/gcc/config/arm/thumb1.md
+++ b/gcc/config/arm/thumb1.md
@@ -1757,8 +1757,15 @@ 
   "*
     return thumb1_unexpanded_epilogue ();
   "
-  ; Length is absolute worst case
-  [(set_attr "length" "44")
+  ; Length is absolute worst case, when using CMSE and if this is an entry
+  ; function an extra 4 (msr) to 8 (vmsr) extra might be added.
+  [(set (attr "length")
+	(if_then_else
+	 (match_test "IS_CMSE_ENTRY (arm_current_func_type ())")
+	 (if_then_else (match_test "TARGET_HARD_FLOAT && TARGET_VFP")
+	  (const_int 52)
+	  (const_int 48))
+	 (const_int 44)))
    (set_attr "type" "block")
    ;; We don't clobber the conditions, but the potential length of this
    ;; operation is sufficient to make conditionalizing the sequence
diff --git a/gcc/config/arm/thumb2.md b/gcc/config/arm/thumb2.md
index a724752a39cf2862c68a53ef28a239de2ae1ed96..0c07df91fe8844a7e7437ef5b7f00f74815d63f4 100644
--- a/gcc/config/arm/thumb2.md
+++ b/gcc/config/arm/thumb2.md
@@ -1106,7 +1106,15 @@ 
   "TARGET_THUMB2"
   "* return output_return_instruction (const_true_rtx, true, false, true);"
   [(set_attr "type" "branch")
-   (set_attr "length" "4")]
+  ; If this is a return from a cmse_nonsecure_entry function then code will be
+  ; added to clear the APSR and potentially the FPSCR if VFP is available, so
+  ; we adapt the length accordingly.
+   (set (attr "length")
+    (if_then_else (match_test "IS_CMSE_ENTRY (arm_current_func_type ())")
+     (if_then_else (match_test "TARGET_HARD_FLOAT && TARGET_VFP")
+      (const_int 12)
+      (const_int 8))
+     (const_int 4)))]
 )
 
 (define_insn_and_split "thumb2_eh_return"
diff --git a/gcc/testsuite/gcc.target/arm/cmse/baseline/cmse-2.c b/gcc/testsuite/gcc.target/arm/cmse/baseline/cmse-2.c
new file mode 100644
index 0000000000000000000000000000000000000000..7cef73372fd5f5be080bdfe30999694564deaa9a
--- /dev/null
+++ b/gcc/testsuite/gcc.target/arm/cmse/baseline/cmse-2.c
@@ -0,0 +1,18 @@ 
+/* { dg-do compile } */
+/* { dg-require-effective-target arm_arch_v8m_base_ok } */
+/* { dg-add-options arm_arch_v8m_base } */
+/* { dg-options "-mcmse" }  */
+
+extern float bar (void);
+
+float __attribute__ ((cmse_nonsecure_entry))
+foo (void)
+{
+  return bar ();
+}
+/* { dg-final { scan-assembler "mov\tr1, lr" } } */
+/* { dg-final { scan-assembler "mov\tr2, lr" } } */
+/* { dg-final { scan-assembler "mov\tr3, lr" } } */
+/* { dg-final { scan-assembler "mov\tip, lr" } } */
+/* { dg-final { scan-assembler "msr\tAPSR_nzcvq, r1" } } */
+/* { dg-final { scan-assembler "bxns\tr1" } } */
diff --git a/gcc/testsuite/gcc.target/arm/cmse/cmse.exp b/gcc/testsuite/gcc.target/arm/cmse/cmse.exp
index 6d4bfec1d12bc575218e0b8c33559d0a4da9ec70..592349ad07257f8074f9443a599ae08c36136a80 100644
--- a/gcc/testsuite/gcc.target/arm/cmse/cmse.exp
+++ b/gcc/testsuite/gcc.target/arm/cmse/cmse.exp
@@ -38,6 +38,26 @@  set LTO_TORTURE_OPTIONS ""
 gcc-dg-runtest [lsort [glob $srcdir/$subdir/*.c]] \
 	"" $DEFAULT_CFLAGS
 
+if {[check_effective_target_arm_arch_v8m_base_ok]} then {
+    # Baseline only
+    gcc-dg-runtest [lsort [glob $srcdir/$subdir/baseline/*.c]] \
+	    "" $DEFAULT_CFLAGS
+}
+
+if {[check_effective_target_arm_arch_v8m_main_ok]} then {
+    # Mainline -mfloat-abi=soft
+    gcc-dg-runtest [lsort [glob $srcdir/$subdir/mainline/soft/*.c]] \
+	    "-mfloat-abi=soft" $DEFAULT_CFLAGS
+    gcc-dg-runtest [lsort [glob $srcdir/$subdir/mainline/softfp/*.c]] \
+	    "" $DEFAULT_CFLAGS
+    gcc-dg-runtest [lsort [glob $srcdir/$subdir/mainline/softfp-sp/*.c]] \
+	    "" $DEFAULT_CFLAGS
+    gcc-dg-runtest [lsort [glob $srcdir/$subdir/mainline/hard/*.c]] \
+	    "" $DEFAULT_CFLAGS
+    gcc-dg-runtest [lsort [glob $srcdir/$subdir/mainline/hard-sp/*.c]] \
+	    "" $DEFAULT_CFLAGS
+}
+
 set LTO_TORTURE_OPTIONS ${saved-lto_torture_options}
 set dg-do-what-default ${saved-dg-do-what-default}
 
diff --git a/gcc/testsuite/gcc.target/arm/cmse/mainline/hard-sp/cmse-5.c b/gcc/testsuite/gcc.target/arm/cmse/mainline/hard-sp/cmse-5.c
new file mode 100644
index 0000000000000000000000000000000000000000..ab607aa1b7210acc7983bfb401e95117eb8c0d17
--- /dev/null
+++ b/gcc/testsuite/gcc.target/arm/cmse/mainline/hard-sp/cmse-5.c
@@ -0,0 +1,37 @@ 
+/* { dg-do compile } */
+/* { dg-require-effective-target arm_arch_v8m_main_ok } */
+/* { dg-add-options arm_arch_v8m_main } */
+/* { dg-skip-if "Do not combine float-abi= hard | soft | softfp" {*-*-*} {"-mfloat-abi=soft" -mfloat-abi=softfp } {""} } */
+/* { dg-skip-if "Skip these if testing double precision" {*-*-*} {"-mfpu=fpv[4-5]-d16"} {""} } */
+/* { dg-options "-mcmse -mfloat-abi=hard -mfpu=fpv5-sp-d16" }  */
+
+extern float bar (void);
+
+float __attribute__ ((cmse_nonsecure_entry))
+foo (void)
+{
+  return bar ();
+}
+/* { dg-final { scan-assembler "mov\tr0, lr" } } */
+/* { dg-final { scan-assembler "mov\tr1, lr" } } */
+/* { dg-final { scan-assembler "mov\tr2, lr" } } */
+/* { dg-final { scan-assembler "mov\tr3, lr" } } */
+/* { dg-final { scan-assembler "mov\tip, lr" } } */
+/* { dg-final { scan-assembler-not "vmov\.f32\ts0, #1\.0" } } */
+/* { dg-final { scan-assembler "vmov\.f32\ts1, #1\.0" } } */
+/* { dg-final { scan-assembler "vmov\.f32\ts2, #1\.0" } } */
+/* { dg-final { scan-assembler "vmov\.f32\ts3, #1\.0" } } */
+/* { dg-final { scan-assembler "vmov\.f32\ts4, #1\.0" } } */
+/* { dg-final { scan-assembler "vmov\.f32\ts5, #1\.0" } } */
+/* { dg-final { scan-assembler "vmov\.f32\ts6, #1\.0" } } */
+/* { dg-final { scan-assembler "vmov\.f32\ts7, #1\.0" } } */
+/* { dg-final { scan-assembler "vmov\.f32\ts8, #1\.0" } } */
+/* { dg-final { scan-assembler "vmov\.f32\ts9, #1\.0" } } */
+/* { dg-final { scan-assembler "vmov\.f32\ts10, #1\.0" } } */
+/* { dg-final { scan-assembler "vmov\.f32\ts11, #1\.0" } } */
+/* { dg-final { scan-assembler "vmov\.f32\ts12, #1\.0" } } */
+/* { dg-final { scan-assembler "vmov\.f32\ts13, #1\.0" } } */
+/* { dg-final { scan-assembler "vmov\.f32\ts14, #1\.0" } } */
+/* { dg-final { scan-assembler "vmov\.f32\ts15, #1\.0" } } */
+/* { dg-final { scan-assembler "msr\tAPSR_nzcvq, lr" { target { arm_arch_v8m_main_ok && { ! arm_dsp } } } } } */
+/* { dg-final { scan-assembler "msr\tAPSR_nzcvqg, lr" { target { arm_arch_v8m_main_ok && arm_dsp } } } } */
diff --git a/gcc/testsuite/gcc.target/arm/cmse/mainline/hard/cmse-5.c b/gcc/testsuite/gcc.target/arm/cmse/mainline/hard/cmse-5.c
new file mode 100644
index 0000000000000000000000000000000000000000..5ed7e1d6404b8f17d630895eb06df5921a1a965f
--- /dev/null
+++ b/gcc/testsuite/gcc.target/arm/cmse/mainline/hard/cmse-5.c
@@ -0,0 +1,30 @@ 
+/* { dg-do compile } */
+/* { dg-require-effective-target arm_arch_v8m_main_ok } */
+/* { dg-add-options arm_arch_v8m_main } */
+/* { dg-skip-if "Do not combine float-abi= hard | soft | softfp" {*-*-*} {"-mfloat-abi=soft" -mfloat-abi=softfp } {""} } */
+/* { dg-skip-if "Skip these if testing single precision" {*-*-*} {"-mfpu=*-sp-*"} {""} } */
+/* { dg-options "-mcmse -mfloat-abi=hard -mfpu=fpv5-d16" }  */
+
+extern float bar (void);
+
+float __attribute__ ((cmse_nonsecure_entry))
+foo (void)
+{
+  return bar ();
+}
+/* { dg-final { scan-assembler "mov\tr0, lr" } } */
+/* { dg-final { scan-assembler "mov\tr1, lr" } } */
+/* { dg-final { scan-assembler "mov\tr2, lr" } } */
+/* { dg-final { scan-assembler "mov\tr3, lr" } } */
+/* { dg-final { scan-assembler "mov\tip, lr" } } */
+/* { dg-final { scan-assembler-not "vmov\.f32\ts0, #1\.0" } } */
+/* { dg-final { scan-assembler "vmov\.f32\ts1, #1\.0" } } */
+/* { dg-final { scan-assembler "vmov\.f64\td1, #1\.0" } } */
+/* { dg-final { scan-assembler "vmov\.f64\td2, #1\.0" } } */
+/* { dg-final { scan-assembler "vmov\.f64\td3, #1\.0" } } */
+/* { dg-final { scan-assembler "vmov\.f64\td4, #1\.0" } } */
+/* { dg-final { scan-assembler "vmov\.f64\td5, #1\.0" } } */
+/* { dg-final { scan-assembler "vmov\.f64\td6, #1\.0" } } */
+/* { dg-final { scan-assembler "vmov\.f64\td7, #1\.0" } } */
+/* { dg-final { scan-assembler "msr\tAPSR_nzcvq, lr" { target { arm_arch_v8m_main_ok && { ! arm_dsp } } } } } */
+/* { dg-final { scan-assembler "msr\tAPSR_nzcvqg, lr" { target { arm_arch_v8m_main_ok && arm_dsp } } } } */
diff --git a/gcc/testsuite/gcc.target/arm/cmse/mainline/soft/cmse-5.c b/gcc/testsuite/gcc.target/arm/cmse/mainline/soft/cmse-5.c
new file mode 100644
index 0000000000000000000000000000000000000000..d24683f52eba11df5b41eef0e5a8e365fb206fe8
--- /dev/null
+++ b/gcc/testsuite/gcc.target/arm/cmse/mainline/soft/cmse-5.c
@@ -0,0 +1,22 @@ 
+/* { dg-do compile } */
+/* { dg-require-effective-target arm_arch_v8m_main_ok } */
+/* { dg-add-options arm_arch_v8m_main } */
+/* { dg-skip-if "Do not combine float-abi= hard | soft | softfp" {*-*-*} {"-mfloat-abi=hard" -mfloat-abi=softfp } {""} } */
+/* { dg-options "-mcmse -mfloat-abi=soft" }  */
+
+extern float bar (void);
+
+float __attribute__ ((cmse_nonsecure_entry))
+foo (void)
+{
+  return bar ();
+}
+
+/* { dg-final { scan-assembler "mov\tr1, lr" } } */
+/* { dg-final { scan-assembler "mov\tr2, lr" } } */
+/* { dg-final { scan-assembler "mov\tr3, lr" } } */
+/* { dg-final { scan-assembler "mov\tip, lr" } } */
+/* { dg-final { scan-assembler-not "vmov" } } */
+/* { dg-final { scan-assembler-not "vmsr" } } */
+/* { dg-final { scan-assembler "msr\tAPSR_nzcvq, lr" { target { arm_arch_v8m_main_ok && { ! arm_dsp } } } } } */
+/* { dg-final { scan-assembler "msr\tAPSR_nzcvqg, lr" { target { arm_arch_v8m_main_ok && arm_dsp } } } } */
diff --git a/gcc/testsuite/gcc.target/arm/cmse/mainline/softfp-sp/cmse-5.c b/gcc/testsuite/gcc.target/arm/cmse/mainline/softfp-sp/cmse-5.c
new file mode 100644
index 0000000000000000000000000000000000000000..c22775ace8361729c36130422475522fbaca05b5
--- /dev/null
+++ b/gcc/testsuite/gcc.target/arm/cmse/mainline/softfp-sp/cmse-5.c
@@ -0,0 +1,39 @@ 
+/* { dg-do compile } */
+/* { dg-require-effective-target arm_arch_v8m_main_ok } */
+/* { dg-add-options arm_arch_v8m_main } */
+/* { dg-skip-if "Do not combine float-abi= hard | soft | softfp" {*-*-*} {"-mfloat-abi=soft" -mfloat-abi=hard } {""} } */
+/* { dg-skip-if "Skip these if testing double precision" {*-*-*} {"-mfpu=fpv[4-5]-d16"} {""} } */
+/* { dg-options "-mcmse -mfloat-abi=softfp -mfpu=fpv5-sp-d16" }  */
+
+extern float bar (void);
+
+float __attribute__ ((cmse_nonsecure_entry))
+foo (void)
+{
+  return bar ();
+}
+/* { dg-final { scan-assembler "__acle_se_foo:" } } */
+/* { dg-final { scan-assembler-not "mov\tr0, lr" } } */
+/* { dg-final { scan-assembler "mov\tr1, lr" } } */
+/* { dg-final { scan-assembler "mov\tr2, lr" } } */
+/* { dg-final { scan-assembler "mov\tr3, lr" } } */
+/* { dg-final { scan-assembler "mov\tip, lr" } } */
+/* { dg-final { scan-assembler "vmov\.f32\ts0, #1\.0" } } */
+/* { dg-final { scan-assembler "vmov\.f32\ts1, #1\.0" } } */
+/* { dg-final { scan-assembler "vmov\.f32\ts2, #1\.0" } } */
+/* { dg-final { scan-assembler "vmov\.f32\ts3, #1\.0" } } */
+/* { dg-final { scan-assembler "vmov\.f32\ts4, #1\.0" } } */
+/* { dg-final { scan-assembler "vmov\.f32\ts5, #1\.0" } } */
+/* { dg-final { scan-assembler "vmov\.f32\ts6, #1\.0" } } */
+/* { dg-final { scan-assembler "vmov\.f32\ts7, #1\.0" } } */
+/* { dg-final { scan-assembler "vmov\.f32\ts8, #1\.0" } } */
+/* { dg-final { scan-assembler "vmov\.f32\ts9, #1\.0" } } */
+/* { dg-final { scan-assembler "vmov\.f32\ts10, #1\.0" } } */
+/* { dg-final { scan-assembler "vmov\.f32\ts11, #1\.0" } } */
+/* { dg-final { scan-assembler "vmov\.f32\ts12, #1\.0" } } */
+/* { dg-final { scan-assembler "vmov\.f32\ts13, #1\.0" } } */
+/* { dg-final { scan-assembler "vmov\.f32\ts14, #1\.0" } } */
+/* { dg-final { scan-assembler "vmov\.f32\ts15, #1\.0" } } */
+/* { dg-final { scan-assembler "msr\tAPSR_nzcvq, lr" { target { arm_arch_v8m_main_ok && { ! arm_dsp } } } } } */
+/* { dg-final { scan-assembler "msr\tAPSR_nzcvqg, lr" { target { arm_arch_v8m_main_ok && arm_dsp } } } } */
+/* { dg-final { scan-assembler "bxns" } } */
diff --git a/gcc/testsuite/gcc.target/arm/cmse/mainline/softfp/cmse-5.c b/gcc/testsuite/gcc.target/arm/cmse/mainline/softfp/cmse-5.c
new file mode 100644
index 0000000000000000000000000000000000000000..28b05c391081bd42407b4e56a1e84daa549fa06e
--- /dev/null
+++ b/gcc/testsuite/gcc.target/arm/cmse/mainline/softfp/cmse-5.c
@@ -0,0 +1,31 @@ 
+/* { dg-do compile } */
+/* { dg-require-effective-target arm_arch_v8m_main_ok } */
+/* { dg-add-options arm_arch_v8m_main } */
+/* { dg-skip-if "Do not combine float-abi= hard | soft | softfp" {*-*-*} {"-mfloat-abi=soft" -mfloat-abi=hard } {""} } */
+/* { dg-skip-if "Skip these if testing single precision" {*-*-*} {"-mfpu=*-sp-*"} {""} } */
+/* { dg-options "-mcmse -mfloat-abi=softfp -mfpu=fpv5-d16" }  */
+
+extern float bar (void);
+
+float __attribute__ ((cmse_nonsecure_entry))
+foo (void)
+{
+  return bar ();
+}
+/* { dg-final { scan-assembler "__acle_se_foo:" } } */
+/* { dg-final { scan-assembler-not "mov\tr0, lr" } } */
+/* { dg-final { scan-assembler "mov\tr1, lr" } } */
+/* { dg-final { scan-assembler "mov\tr2, lr" } } */
+/* { dg-final { scan-assembler "mov\tr3, lr" } } */
+/* { dg-final { scan-assembler "mov\tip, lr" } } */
+/* { dg-final { scan-assembler "vmov\.f64\td0, #1\.0" } } */
+/* { dg-final { scan-assembler "vmov\.f64\td1, #1\.0" } } */
+/* { dg-final { scan-assembler "vmov\.f64\td2, #1\.0" } } */
+/* { dg-final { scan-assembler "vmov\.f64\td3, #1\.0" } } */
+/* { dg-final { scan-assembler "vmov\.f64\td4, #1\.0" } } */
+/* { dg-final { scan-assembler "vmov\.f64\td5, #1\.0" } } */
+/* { dg-final { scan-assembler "vmov\.f64\td6, #1\.0" } } */
+/* { dg-final { scan-assembler "vmov\.f64\td7, #1\.0" } } */
+/* { dg-final { scan-assembler "msr\tAPSR_nzcvq, lr" { target { arm_arch_v8m_main_ok && { ! arm_dsp } } } } } */
+/* { dg-final { scan-assembler "msr\tAPSR_nzcvqg, lr" { target { arm_arch_v8m_main_ok && arm_dsp } } } } */
+/* { dg-final { scan-assembler "bxns" } } */