diff mbox

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

Message ID 56FBFB4C.5080704@arm.com
State New
Headers show

Commit Message

Andre Vieira (lists) March 30, 2016, 4:14 p.m. UTC
On 29/03/16 17:49, Andre Vieira (lists) wrote:
> On 29/01/16 17:07, Andre Vieira (lists) wrote:
>> 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.
>>
> Hi there,
> 
> This is an updated version of the previous patch to clear the unused
> bits when returning a compound type. As per the ABI these have undefined
> values and thus may leak security sensitive information.
> 
> Any comments?
> 
> Cheers,
> Andre
> 
> *** ChangeLog ***
> 2016-03-29  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/thumb1.md (*epilogue_insns): Change length
> attribute.
>         * gcc/config/arm/thumb2.md (*thumb2_return): Likewise.
> 
> *** gcc/testsuite/ChangeLog ***
> 2016-03-29  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/struct-1.c: New.
>         * gcc.target/arm/cmse/bitfield-1.c: New.
>         * gcc.target/arm/cmse/bitfield-2.c: New.
>         * gcc.target/arm/cmse/bitfield-3.c: New.
>         * 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.
> 
Hi there,

I realized that in the patch in
https://gcc.gnu.org/ml/gcc-patches/2016-03/msg01524.html, that I left
two mistakes in two of the testcases where I was doing a compile test
instead of run test and within those two also noticed that I was using
'char' where I should've been using 'signed char', so heres a respun
version.

Any comments?

Cheers,
Andre

*** gcc/ChangeLog ***
2016-03-30  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/thumb1.md (*epilogue_insns): Change length
attribute.
        * gcc/config/arm/thumb2.md (*thumb2_return): Likewise.

*** gcc/testsuite/ChangeLog ***
2016-03-30  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/struct-1.c: New.
        * gcc.target/arm/cmse/bitfield-1.c: New.
        * gcc.target/arm/cmse/bitfield-2.c: New.
        * gcc.target/arm/cmse/bitfield-3.c: New.
        * 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 mbox

Patch

diff --git a/gcc/config/arm/arm.c b/gcc/config/arm/arm.c
index 40e6a786b933b09dbfd89b64449acb0356c66f24..7e250a489e0cf3b6588ae3f816ad77233f9be370 100644
--- a/gcc/config/arm/arm.c
+++ b/gcc/config/arm/arm.c
@@ -19808,6 +19808,23 @@  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_INT_SIMD)
+		{
+		  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.  */
@@ -24045,6 +24062,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_INT_SIMD)
+    {
+      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);
 }
 
@@ -25186,6 +25214,235 @@  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;
+  uint32_t padding_bits_to_clear = 0;
+  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.  */
+      if (RECORD_OR_UNION_TYPE_P (result_type))
+	{
+	  unsigned current_addr, cont_width, alignment, bitfield_width,
+		   next_addr;
+	  tree field, field_t;
+
+	  /* May only contain one register as the AAPCS limits the
+	     returning of composite types to one register.  */
+	  gcc_assert (REG_P (result_rtl));
+	  reg = result_rtl;
+
+	  /* Which must thus be r0.  */
+	  gcc_assert (REG_P (reg) && REGNO (reg) == 0);
+	  to_clear_mask &= ~1LL;
+
+	  /* Clear unused bits as per the AAPCS these have unspecified
+	     values and may thus leak secret information.  We use the algorithm
+	     specified in Section 7.1.7.1 "Bit-fields no larger than their
+	     container" of the Procedure Call Standard for the ARM Architecture
+	     to determine which bits are unused.  */
+	  field = TYPE_FIELDS (result_type);
+	  /* 'current_addr' is the current bit address.  */
+	  current_addr = 0;
+	  while (field && TREE_CODE (field) == FIELD_DECL)
+	    {
+	      field_t = TREE_TYPE (field);
+	      if (DECL_BIT_FIELD (field))
+		{
+		  tree bitfield_t = DECL_BIT_FIELD_TYPE (field);
+		  /* 'cont_width' is the container's width.  */
+		  cont_width = TREE_INT_CST_ELT (TYPE_SIZE (bitfield_t), 0);
+		  /* 'bitfield_width' is the width of the bitfield.  */
+		  bitfield_width = TREE_INT_CST_ELT (DECL_SIZE (field), 0);
+		  /* 'alignment' is the alignment of the bitfield type.  */
+		  alignment = TYPE_ALIGN (bitfield_t);
+		}
+	      else
+		{
+		  /* For non-bitfields 'bitfield_width' and 'cont_width' are
+		     the same, the size of the field member type.  */
+		  cont_width = TREE_INT_CST_ELT (TYPE_SIZE (field_t), 0);
+		  bitfield_width = cont_width;
+		  alignment = TYPE_ALIGN (field_t);
+		}
+
+	      if ((bitfield_width == 0)
+		  || (bitfield_width >
+		      (cont_width - (current_addr % alignment))))
+		{
+		  /* 'next_addr' is the next container bit address.  */
+		  next_addr =
+		    alignment * ((current_addr + alignment - 1)/alignment);
+		  /* We make sure we clear all bits between the current and
+		     next address.  */
+		  padding_bits_to_clear |= (unsigned)
+		    (((uint64_t)UINT_MAX >> (32 - (next_addr))) + 1u)
+		    - (1u << current_addr);
+		  current_addr = next_addr;
+		}
+	      current_addr += bitfield_width;
+	      field = TREE_CHAIN (field);
+	    }
+
+	  /* Don't forget to clear all unused trailing bits in r0.  */
+	  if (current_addr < 32)
+	    padding_bits_to_clear |= UINT_MAX - (1u << current_addr) + 1u;
+	}
+      else
+	{
+	  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;
+	    }
+	}
+    }
+
+  if (padding_bits_to_clear != 0)
+    {
+      rtx reg_rtx;
+      /* Padding bits to clear is not 0 so we know we are dealing with
+         returning a composite type, which only uses r0.  Let's make sure that
+	 r1-r3 is cleared too, we will use r1 as a scratch register.  */
+      gcc_assert ((to_clear_mask & 0xe) == 0xe);
+
+      reg_rtx = gen_rtx_REG (SImode, 1);
+
+      emit_move_insn (reg_rtx,
+		      GEN_INT ((((~padding_bits_to_clear) << 16u) >> 16u)));
+
+      /* Also fill the top half of the negated padding_bits_to_clear.  */
+      if (((~padding_bits_to_clear) >> 16) > 0)
+	emit_insn (gen_rtx_SET (gen_rtx_ZERO_EXTRACT (SImode, reg_rtx,
+						      GEN_INT (16),
+						      GEN_INT (16)),
+				GEN_INT ((~padding_bits_to_clear) >> 16)));
+
+      emit_insn (gen_andsi3 (gen_rtx_REG (SImode, 0),
+			   gen_rtx_REG (SImode, 0),
+			   reg_rtx));
+    }
+
+  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,
@@ -25235,6 +25492,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);
     }
 }
@@ -25293,6 +25552,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.  */
@@ -25727,6 +25990,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 1b01ef6ce731fe3ff37c3d8c048fb9d5e7829b35..e895fc77d5dc68cc723f1daf7e80b8b83ce3b20b 100644
--- a/gcc/config/arm/thumb1.md
+++ b/gcc/config/arm/thumb1.md
@@ -1843,8 +1843,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 992536593d6c0a8b8fe5a324f32e279c69746157..7161571fbbd08af739c64d78119ee28032aaa412 100644
--- a/gcc/config/arm/thumb2.md
+++ b/gcc/config/arm/thumb2.md
@@ -1118,7 +1118,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/bitfield-1.c b/gcc/testsuite/gcc.target/arm/cmse/bitfield-1.c
new file mode 100644
index 0000000000000000000000000000000000000000..4c33b00a826ecd69ff770f3ede91aca6f96f828d
--- /dev/null
+++ b/gcc/testsuite/gcc.target/arm/cmse/bitfield-1.c
@@ -0,0 +1,41 @@ 
+/* { dg-do run } */
+/* { dg-require-effective-target arm_cmse_ok } */
+/* { dg-options "--save-temps -mcmse -Wl,--section-start,.gnu.sgstubs=0x20400000" } */
+#include <limits.h>
+#include <stdlib.h>
+
+typedef struct
+{
+  unsigned short  a : 6;
+  unsigned char	  b : 3;
+  unsigned char	  c;
+  unsigned short  d : 8;
+} test_st;
+
+test_st __attribute__ ((cmse_nonsecure_entry)) foo (void)
+{
+  test_st t;
+  t.a = 63u;
+  t.b = 7u;
+  t.c = UCHAR_MAX;
+  t.d = 255u;
+  return t;
+}
+
+int
+main (void)
+{
+  test_st t;
+  t = foo ();
+  if (t.a != 63u
+      || t.b != 7u
+      || t.c != UCHAR_MAX
+      || t.d != 255u)
+    abort ();
+  return 0;
+}
+
+/* { dg-final { scan-assembler "movw\tr1, #1855" } } */
+/* { dg-final { scan-assembler "movt\tr1, 65535" } } */
+/* { dg-final { scan-assembler "ands\tr0(, r0)?, r1" } } */
+
diff --git a/gcc/testsuite/gcc.target/arm/cmse/bitfield-2.c b/gcc/testsuite/gcc.target/arm/cmse/bitfield-2.c
new file mode 100644
index 0000000000000000000000000000000000000000..d6a31c3538be9be238b0ca16ac575c06f6e0d6be
--- /dev/null
+++ b/gcc/testsuite/gcc.target/arm/cmse/bitfield-2.c
@@ -0,0 +1,38 @@ 
+/* { dg-do run } */
+/* { dg-require-effective-target arm_cmse_ok } */
+/* { dg-options "--save-temps -mcmse -Wl,--section-start,.gnu.sgstubs=0x20400000" } */
+#include <stdlib.h>
+
+typedef struct
+{
+  short	      a : 7;
+  signed char b : 3;
+  short	      c : 11;
+} test_st;
+
+test_st __attribute__ ((cmse_nonsecure_entry)) foo (void)
+{
+  test_st t;
+  t.a = -64;
+  t.b = -4 ;
+  t.c = -1024;
+  return t;
+}
+
+int
+main (void)
+{
+  test_st t;
+  t = foo ();
+  if (t.a != -64
+      || t.b != -4
+      || t.c != -1024)
+    abort ();
+  return 0;
+}
+
+/* { dg-final { scan-assembler "movw\tr1, #1919" } } */
+/* { dg-final { scan-assembler "movt\tr1, 2047" } } */
+/* { dg-final { scan-assembler "ands\tr0(, r0)?, r1" } } */
+
+
diff --git a/gcc/testsuite/gcc.target/arm/cmse/bitfield-3.c b/gcc/testsuite/gcc.target/arm/cmse/bitfield-3.c
new file mode 100644
index 0000000000000000000000000000000000000000..1209630633ed25b5663a63b416429d226fa172f6
--- /dev/null
+++ b/gcc/testsuite/gcc.target/arm/cmse/bitfield-3.c
@@ -0,0 +1,40 @@ 
+/* { dg-do run } */
+/* { dg-require-effective-target arm_cmse_ok } */
+/* { dg-options "--save-temps -mcmse -Wl,--section-start,.gnu.sgstubs=0x20400000" } */
+#include <limits.h>
+#include <stdlib.h>
+
+typedef struct
+{
+  short	      a;
+  signed char b : 2;
+  short		: 1;
+  signed char c : 3;
+} test_st;
+
+test_st __attribute__ ((cmse_nonsecure_entry)) foo (void)
+{
+  test_st t;
+  t.a = SHRT_MIN;
+  t.b = -2;
+  t.c = -4;
+  return t;
+}
+
+int
+main (void)
+{
+  test_st t;
+  t = foo ();
+  if (t.a != SHRT_MIN
+      || t.b != -2
+      || t.c != -4)
+    abort ();
+  return 0;
+}
+
+/* { dg-final { scan-assembler "movw\tr1, #65535" } } */
+/* { dg-final { scan-assembler "movt\tr1, 63" } } */
+/* { dg-final { scan-assembler "ands\tr0(, r0)?, r1" } } */
+
+
diff --git a/gcc/testsuite/gcc.target/arm/cmse/cmse.exp b/gcc/testsuite/gcc.target/arm/cmse/cmse.exp
index d42235174203100f8015fe1785108f70efe1c805..b22fb81774897ff4582fa656413f86a4aec2ff9e 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" } } */
diff --git a/gcc/testsuite/gcc.target/arm/cmse/struct-1.c b/gcc/testsuite/gcc.target/arm/cmse/struct-1.c
new file mode 100644
index 0000000000000000000000000000000000000000..16258d1417556e558989b7c3ad192338a781f503
--- /dev/null
+++ b/gcc/testsuite/gcc.target/arm/cmse/struct-1.c
@@ -0,0 +1,34 @@ 
+/* { dg-do run } */
+/* { dg-require-effective-target arm_cmse_ok } */
+/* { dg-options "--save-temps -mcmse -Wl,--section-start,.gnu.sgstubs=0x20400000" } */
+#include <limits.h>
+#include <stdlib.h>
+typedef struct
+{
+  unsigned char	  a;
+  unsigned short  b;
+} test_st;
+
+test_st __attribute__ ((cmse_nonsecure_entry)) foo (void)
+{
+  test_st t;
+  t.a = UCHAR_MAX;
+  t.b = USHRT_MAX;
+  return t;
+}
+
+int
+main (void)
+{
+  test_st t;
+  t = foo ();
+  if (t.a != UCHAR_MAX || t.b != USHRT_MAX)
+    abort ();
+  return 0;
+}
+
+/* { dg-final { scan-assembler "movs\tr1, #255" } } */
+/* { dg-final { scan-assembler "movt\tr1, 65535" } } */
+/* { dg-final { scan-assembler "ands\tr0(, r0)?, r1" } } */
+
+