6. Cortex-M Security Extensions (CMSE)

The tiarmclang compiler tools support the Cortex-M Security Extensions (CMSE) for the Armv8-M architecture, specifically the Arm Cortex-M33 processor.

This section of the user guide provides a tour through the CMSE security extension mechanisms that are supported in the compiler tools with particular attention to how the compiler tools are used to facilitate:

  • Accessing code in secure memory from code in non-secure memory, and

  • Calling code in non-secure memory from code in secure memory

6.1. Memory Partitions

As discussed in Section 3 of the TrustZone(R) technology for Armv8-M Architecture document, in an application that makes use of the security extensions, the memory space accessible to the Cortex-M33 processor is divided into Secure and Non-secure memory regions.

../../_images/CMSE_memory_regions.jpg

6.1.1. Secure Memory Region

The Secure Memory Region contains two partitions:

Secure

Memory in the Secure partition of the secure memory region contains code, data, and peripherals that are only accessible from secure software or secure masters.

Non-secure Callable

The Non-secure Callable partition of the secure memory region is the only area of target memory that may contain Secure Gateway (SG) instructions that facilitate transitions from Non-secure code state to Secure code state. The table of linker-generated secure gateway veneers that is allocated to the Non-secure Callable partition constitutes the only means whereby a function defined in the Non-secure Memory Region can gain entry to a function defined in the Secure partition.

Each secure gateway veneer in the table begins with an SG instruction that changes the processor state from Non-secure to Secure, followed by a branch to the definition of the secure function being called. Thus, the actual address is only known to code that resides in the Secure Memory Region.

6.1.2. Non-secure Memory Region

Memory in the Non-secure Memory Region contains code, data, and peripherals that are accessible from all software running on the Arm processor. Code that resides in non-secure memory may only access code and data that is defined in non-secure memory. Code defined in non-secure memory may only call a function defined in secure memory via the secure gateway veneer that is associated with that secure function.

6.2. Enabling CMSE Support

The Cortex-M Security Extensions (CMSE) are only available when building an application to be run on the Arm Cortex-M33 processor. Code that makes use of CMSE support mechanisms must #include the <arm_cmse.h> header file prior to the use of any CMSE intrinsics in a compilation unit.

To enable CMSE support attributes and pre-defined macro symbols in the tiarmclang compiler, the following options should be used:

-mcpu=cortex-m33 -mcmse

6.3. Example Application

To explore how different aspects of the CMSE support mechanisms work in the tiarmclang tool chain, consider a simple example application where:

  • Secure code on the Arm processor provides 3 functions:

    • set_event_handler() - registers the address of an event handler function that is to be called when an event occurs.

    • wait_on_event() - polls a secure peripheral for the occurrence of an event, and when the event occurs, will call the event handler function that is currently registered.

    • default_event_handler() - in the case where a custom event handler function has not been registered, the default_event_handler() function will get called when an event occurs

  • The application running in non-secure memory relies on secure code to monitor a secure peripheral that is used to trigger an action to be performed by the non-secure application.

6.3.1. Defining Non-secure Entry Functions

An non-secure entry function in this context refers to a function defined in secure memory that can be called from non-secure code via a secure gateway veneer. Such a function must be annotated with a cmse_nonsecure_entry attribute.

For example, the definition of set_event_handler() could be written as follows:


#include <arm_cmse.h>

typedef void __attribute__((cmse_nonsecure_call)) nsfunc(void);
extern int receive_signal(void);
extern void default_event_handler(void);

nsfunc *callback_fcn = (nsfunc *)default_event_handler;

__attribute__((cmse_nonsecure_entry)) void set_event_handler(nsfunc *fp)
{
  if (fp)
    callback_fcn = cmse_nsfptr_create(fp);
  else
    callback_fcn = (nsfunc *)default_event_handler;
}

__attribute__((cmse_nonsecure_entry)) void wait_on_event()
{
  while (!receive_signal()) ;
 
  if (cmse_is_nsfptr(callback_fcn)) 
    callback_fcn();
  else 
    ((void (*)(void))callback_fcn)();
}

Compiling the source file with tiarmclang as follows:

%> tiarmclang -mcpu=cortex-m33 -mcmse -S secure_code.c

Yields the following compiler-generated assembly definition of the set_event_handler() function:

@ Definition of set_event_handler function
        .section        .text.set_event_handler,"ax",%progbits
        .hidden set_event_handler               @ -- Begin function
        .globl  set_event_handler
        .p2align        1
        .type   set_event_handler,%function
        .code   16                              @ @set_event_handler
        .thumb_func
        .globl  __acle_se_set_event_handler
        .type   __acle_se_set_event_handler,%function
__acle_se_set_event_handler:
set_event_handler:
        .pad    #4
        sub     sp, #4
        str     r0, [sp]
        ldr     r0, [sp]
        cbz     r0, .LBB0_2
        b       .LBB0_1
.LBB0_1:                                @ %if.then
        ldr     r0, [sp]
        movw    r1, :lower16:callback_fcn
        movt    r1, :upper16:callback_fcn
        str     r0, [r1]
        b       .LBB0_3
.LBB0_2:
        movw    r1, :lower16:callback_fcn
        movt    r1, :upper16:callback_fcn
        movw    r0, :lower16:default_event_handler
        movt    r0, :upper16:default_event_handler
        str     r0, [r1]
        b       .LBB0_3
.LBB0_3:                                @ %if.end
        add     sp, #4
        mrs     r12, control
        tst.w   r12, #8
        beq     .LBB0_5

        vmov    d0, lr, lr
        vmov    d1, lr, lr
        vmov    d2, lr, lr
        vmov    d3, lr, lr
        vmov    d4, lr, lr
        vmov    d5, lr, lr
        vmov    d6, lr, lr
        vmov    d7, lr, lr
        vmrs    r12, fpscr
        bic     r12, r12, #159
        bic     r12, r12, #4026531840
        vmsr    fpscr, r12
.LBB0_5:                                @ %if.end
        mov     r0, lr
        mov     r1, lr
        mov     r2, lr
        mov     r3, lr
        mov     r12, lr
        msr     apsr_nzcvqg, lr
        bxns    lr

The application of the cmse_nonsecure_entry attribute to the definition of set_default_handler() has a few notable impacts on the above compiler-generated code:

  • In addition to the normal set_event_handler label to mark the start of the function, the compiler also defines __acle_se_set_event_handler at the same address. When the program is linked, the label set_even_handler will get redefined to the address of the first instruction in the secure gateway veneer that is generated by the linker for set_event_handler(). The definition of the __acle_se_event_handler label will remain at the location of the actual function’s first instruction. See Building a Secure Image and Creating an Import Library for more details about what happens at link-time.

  • The compiler generates code to clear the contents of caller-saved registers (including FPU registers) except those that hold the result value and the return address of the entry function.

  • The compiler generates code to clear registers and flags that have undefined values at the return of a procedure, according to the Arm procedure call standard.

  • The compiler generates code to save and restore callee-saved registers as dictated by the Arm procedure call standard.

  • The compiler generates a BXNS instruction to return to its caller. If the function was called from a non-secure function, then the BXNS instruction will change the state of the processor from Secure to Non-secure before returning to the non-secure caller function.

6.3.2. Calling Non-secure Functions

A function defined in non-secure memory may only be called from code in secure memory via a function pointer that has been annotated with a cmse_nonsecure_call attribute. Such a function pointer is referred to as a non-secure function pointer.

In the imagined CMSE example application under consideration, the non-secure application code relies on the wait_on_event() function that is defined in secure memory to monitor a secure peripheral for a signal and then call an event handler function. The definition of wait_on_event() must account for the possibility that a non-secure function has been registered as an event handler that must be called via a non-secure function pointer.

Here is a possible implementation of the wait_on_event() function:


#include <arm_cmse.h>

typedef void __attribute__((cmse_nonsecure_call)) nsfunc(void);
extern int receive_signal(void);
extern void default_event_handler(void);

nsfunc *callback_fcn = (nsfunc *)default_event_handler;

__attribute__((cmse_nonsecure_entry)) void set_event_handler(nsfunc *fp)
{
  if (fp)
    callback_fcn = cmse_nsfptr_create(fp);
  else
    callback_fcn = (nsfunc *)default_event_handler;
}

__attribute__((cmse_nonsecure_entry)) void wait_on_event()
{
  while (!receive_signal()) ;
 
  if (cmse_is_nsfptr(callback_fcn)) 
    callback_fcn();
  else 
    ((void (*)(void))callback_fcn)();
}

Things to note about the above C code:

  • The set_event_handler() function uses the cmse_nsfptr_create intrinsic that is defined in <arm_cmse.h> to convert the address of the non-secure function pointer argument to a value that can be recognized as a non-secure function address by clearing the least significant bit of fp as its address is assigned to callback_fcn. Note that all functions in the application are Thumb functions which have their least significant bit set to indicate that they are thumb mode functions. The CMSE ABI exploits this fact and repurposes the least significant bit of a function address to enable secure code to recognize it as a non-secure function.

  • The wait_on_event() function is also a non-secure entry function as indicated via application of the cmse_nonsecure_entry attribute.

  • wait_on_event() uses the cmse_is_nsfptr intrinsic to recognize a function as being defined in non-secure memory. The intrinsic will recognize a function pointer as a non-secure function if the least significant bit of the function pointer value is zero.

The following code is generated by the compiler for the definition of wait_on_event():

@ Generated code for wait_for_event function

        .section        .text.wait_on_event,"ax",%progbits
        .hidden wait_on_event                   @ -- Begin function wait_on_event
        .globl  wait_on_event
        .p2align        1
        .type   wait_on_event,%function
        .code   16                              @ @wait_on_event
        .thumb_func
        .globl  __acle_se_wait_on_event
        .type   __acle_se_wait_on_event,%function
__acle_se_wait_on_event:
wait_on_event:
        push    {r7, lr}
        b       .LBB1_1
.LBB1_1:                                @ %while.cond
                                        @ =>This Inner Loop Header: Depth=1
        bl      receive_signal
        cbnz    r0, .LBB1_3
        b       .LBB1_2
.LBB1_2:                                @ %while.body
                                        @   in Loop: Header=BB1_1 Depth=1
        b       .LBB1_1
.LBB1_3:                                @ %while.end
        movw    r0, :lower16:callback_fcn
        movt    r0, :upper16:callback_fcn
        ldrb    r0, [r0]
        lsls    r0, r0, #31
        cbnz    r0, .LBB1_5
        b       .LBB1_4
.LBB1_4:                                @ %if.then
        movw    r0, :lower16:callback_fcn
        movt    r0, :upper16:callback_fcn
        ldr     r0, [r0]
        push.w  {r4, r5, r6, r7, r8, r9, r10, r11}
        bic     r0, r0, #1
        sub     sp, #136
        vlstm   sp
        mov     r1, r0
        mov     r2, r0
        mov     r3, r0
        mov     r4, r0
        mov     r5, r0
        mov     r6, r0
        mov     r7, r0
        mov     r8, r0
        mov     r9, r0
        mov     r10, r0
        mov     r11, r0
        mov     r12, r0
        msr     apsr_nzcvqg, r0
        blxns   r0
        vlldm   sp
        add     sp, #136
        pop.w   {r4, r5, r6, r7, r8, r9, r10, r11}
        b       .LBB1_6
.LBB1_5:                                @ %if.else
        movw    r0, :lower16:callback_fcn
        movt    r0, :upper16:callback_fcn
        ldr     r0, [r0]
        blx     r0
        b       .LBB1_6
.LBB1_6:                                @ %if.end
        pop.w   {r7, lr}
        mrs     r12, control
        tst.w   r12, #8
        beq     .LBB1_8

        vmov    d0, lr, lr
        vmov    d1, lr, lr
        vmov    d2, lr, lr
        vmov    d3, lr, lr
        vmov    d4, lr, lr
        vmov    d5, lr, lr
        vmov    d6, lr, lr
        vmov    d7, lr, lr
        vmrs    r12, fpscr
        bic     r12, r12, #159
        bic     r12, r12, #4026531840
        vmsr    fpscr, r12
.LBB1_8:                                @ %if.end
        mov     r0, lr
        mov     r1, lr
        mov     r2, lr
        mov     r3, lr
        mov     r12, lr
        msr     apsr_nzcvqg, lr
        bxns    lr

Some things to note about the compiler-generated code:

  • Since wait_on_event() is also a non-secure entry function, it is impacted by the application of the cmse_nonsecure_entry attribute in the same way that set_event_handler() is. Namely,

    • An __acle_se_wait_on_event label is defined at the function entry point in addition to the normal wait_on_event label.

    • The compiler generates code to clear the contents of caller-saved registers.

    • The compiler generates code to clear registers and flags that have undefined values at the return of a procedure, according to the Arm procedure call standard.

    • The compiler generates code to save and restore callee-saved registers as dictated by the Arm procedure call standard.

    • The compiler generates a BXNS instruction to return to its caller.

  • The effect of calling a function via a non-secure function pointer is:

    • The compiler will generate code to save all callee- and live caller-saved registers in secure memory.

    • The compiler will generate code to clear all callee- and caller-saved registers except:

      • The link register (lr),

      • Registers that hold the arguments of the function being called, and

      • Registers that do not hold secret information.

    • The compiler generates code to clear registers and flags that have undefined values at the entry to a procedure, according to the Arm procedure call standard.

    • The compiler generates a BLXNS instruction to effect an indirect call to the non-secure callback function.

6.3.3. Building the Example Application

An application that utilizes CMSE mechanisms will be built in two parts: the non-secure executable application and the secure image that the non-secure application can call into via a secure gateway to access data and functionality that is strictly managed by code in secure memory.

Using the above described example application as a model, this section describes first how to create a secure image, generate a table of secure gateway veneers, and produce an import library. Then how to incorporate the import library into the build of the non-secure executable application.

6.3.3.1. Building a Secure Image and Creating an Import Library

Recall that some non-secure entry functions have been defined in secure_code.c:


#include <arm_cmse.h>

typedef void __attribute__((cmse_nonsecure_call)) nsfunc(void);
extern int receive_signal(void);
extern void default_event_handler(void);

nsfunc *callback_fcn = (nsfunc *)default_event_handler;

__attribute__((cmse_nonsecure_entry)) void set_event_handler(nsfunc *fp)
{
  if (fp)
    callback_fcn = cmse_nsfptr_create(fp);
  else
    callback_fcn = (nsfunc *)default_event_handler;
}

__attribute__((cmse_nonsecure_entry)) void wait_on_event()
{
  while (!receive_signal()) ;
 
  if (cmse_is_nsfptr(callback_fcn)) 
    callback_fcn();
  else 
    ((void (*)(void))callback_fcn)();
}

In addition, dummy definitions of main, default_event_handler() and receive_signal() are provided in more_secure_code.c:

#include <stdio.h>

int main() {
  printf("dummy entry  point for secure image\n");
  return 0;
}

void default_event_handler(void) {
  printf("Default event handler invoked\n");
}

__attribute__((optnone)) int receive_signal() {
  int i = 20;
  while (i--) {
    asm(" ");
  }

  return 1;
}

The secure image can then be compiled and linked as follows:

%> tiarmclang -mcpu=cortex-m33 -mcmse secure_code.c more_secure_code.c \
   -o secure.out -Xlinker -lsecure_lnk.cmd -Xlinker --import_cmse_lib_out=implib.o

In the above command, secure.out contains definitions of the secure functions set_event_handler() and wait_on_event() and their addresses are indicated by the values of the __acle_se_set_default_handler and __acle_se_wait_on_event labels in the following disassembly output:

%> tiarmdis secure.out secure.dis
%> cat secure.dis
TEXT Section .text, 0x10bc bytes at 0x00000020
...
0405c8:              __acle_se_wait_on_event:
0405c8:               .thumb
0405c8:              :
0405c8: 80B5             PUSH            {R7, LR}
0405ca: FFE7             B               0x000405CC
0405cc: 00F0FAFC         BL              receive_signal [0x40fc4]
0405d0: 08B9             CBNZ            R0, 0x000405D6
0405d2: FFE7             B               0x000405D4
...
04070c:              __acle_se_set_event_handler:
04070c:               .thumb
04070c:              :
04070c: 82B0             ADD             SP, #-8
04070e: 0190             STR             R0, [SP, #4]
040710: 0198             LDR             R0, [SP, #4]
040712: 58B1             CBZ             R0, 0x0004072C
040714: FFE7             B               0x00040716
...

During the link step of the above tiarmclang command, the linker will generate a table of secure gateway veneers:

%> cat secure.dis
...
TEXT Section Veneer$$CMSE, 0x10 bytes at 0x00500000
050000:              set_event_handler:
050000:               .thumb
050000:              Veneer$$CMSE:
050000: 7FE97FE9         SG
050004: FEF7EEBA         B.W             __acle_se_set_event_handler [0x72c]
050008:              wait_on_event:
050008:               .thumb
050008: 7FE97FE9         SG
05000c: FEF748BA         B.W             __acle_se_wait_on_event [0x5e8]
...

Note that the linker has redefined the values of the set_event_handler and wait_on_event labels to the first instruction in the secure gateway veneers for the set_event_handler() and wait_on_event() functions, respectively.

At link-time, the linker-generated secure gateway veneer functions should be allocated into non-secure callable memory. For the purposes of this discussion, it is assumed that non-secure callable memory starts at address 0x050000. The secure_lnk.cmd linker command file used in the above tiarmclang command will instruct the linker to place the Veneer$$CMSE section in the non-secure callable memory area:

/****************************************************************************/
/* secure_lnk.cmd                                                           */
/*                                                                          */
/* Set aside some target memory for secure and non-secure callable areas    */
/* for made-up secure image example in tiarmclang user guide.               */
/*                                                                          */
/****************************************************************************/
-c
-stack  0x8000
-heap   0x2000
--args 0x1000

/* SPECIFY THE SECURE AND NON-SECURE CALLABLE MEMORY AREAS */

MEMORY
{
    SEC_DMEM    : org = 0x0030000   len = 0x10000
    SEC_PMEM    : org = 0x0040000   len = 0x10000
    NSC_MEM     : org = 0x0050000   len = 0x00100
}

/* SPECIFY THE SECTIONS ALLOCATION INTO MEMORY */

SECTIONS
{
    .intvecs    : {} > 0x0             /* INTERRUPT VECTORS                 */
    .bss        : {} > SEC_DMEM        /* GLOBAL & STATIC VARS              */
    .data       : {} > SEC_DMEM
    .sysmem     : {} > SEC_DMEM        /* DYNAMIC MEMORY ALLOCATION AREA    */
    .stack      : {} > SEC_DMEM        /* SOFTWARE SYSTEM STACK             */

    .text       : {} > SEC_PMEM        /* CODE                              */
    .cinit      : {} > SEC_PMEM        /* INITIALIZATION TABLES             */
    .const      : {} > SEC_PMEM        /* CONSTANT DATA                     */
    .rodata     : {} > SEC_PMEM

    Veneer$$CMSE: {} > NSC_MEM
}

Note

The definition of the non-secure callable memory area must be within range of the secure code memory area so that the 24-bit PC-relative branch in each secure gateway veneer can reach its destination without the need for a linker-generated trampoline.

Finally, the --import_cmse_lib_out=implib.o option in the above tiarmclang command creates an import library. An import library is simply a linker generated object file that contains a list of absolute symbols that constitute a mapping of each non-secure entry function symbol to its associated secure gateway veneer location in non-secure callable memory.

In the CMSE example under consideration, the linker generated implib.o file contains the following list of absolute symbols:

%> tiarmofd -v -o implib.ofd implib.o
%> cat implib.ofd
OBJECT FILE:  implib.o
...
 Symbol Table ".symtab"

  ...

  <1> "set_event_handler"
     Value:    0x00050001  Kind:        absolute
     Binding:  global      Type:        function
     Size:     0x0         Visibility:  STV_DEFAULT

  <2> "wait_on_event"
     Value:    0x00050009  Kind:        absolute
     Binding:  global      Type:        function
     Size:     0x0         Visibility:  STV_DEFAULT
...

The values assigned to set_event_handler and wait_on_event correspond to the locations of their secure gateway veneers in non-secure callable memory. When the linker incorporates the implib.o object file into the link of the non-secure executable application, references to set_event_handler and wait_on_event will resolve to these addresses as though they were normal thumb mode functions.

6.3.3.2. Building/Linking Non-secure Application with an Import Library

When building a non-secure application that relies on functionality provided in the secure code, the non-secure application should include an import library object file that was generated during the build of the secure image.

To conclude the CMSE example that is under consideration, assume that we have a simple implementation of a non-secure application that references the set_event_handler() and wait_on_event() functions that were defined in the secure.out image created in the above discussion.

#include <stdio.h>

extern void set_event_handler(void (*fp)(void));
extern void wait_on_event(void);

void my_event_handler(void);

int main() {
  wait_on_event();

  set_event_handler(my_event_handler);
  wait_on_event();

  return 0;
}

void my_event_handler(void) {
  printf("My event handler has been invoked\n");
}

The non-secure application can then be compiled and linked with the following tiarmclang command:

%> tiarmclang -mcpu=cortex-m33 non_secure_app.c implib.o -o non_secure.out \
   -Xlinker non_secure_lnk.cmd

An examination of the disassembler output for non_secure.out shows the definition of main() has resolved its references to set_event_handler() and wait_on_event() to the correct locations in the Veneer$$CMSE section of the secure image:

%> tiarmdis non_secure.out non_secure.dis
%> cat non_secure.dis
...
060e30:              main:
060e30:               .thumb
060e30:              :
060e30: 80B5             PUSH            {R7, LR}
060e32: 82B0             ADD             SP, #-8
060e34: 0020             MOVS            R0, #0
060e36: 0090             STR             R0, [SP]
060e38: 0190             STR             R0, [SP, #4]
060e3a: EFF7E5F8         BL              wait_on_event [0x50008]
060e3e: 40F6F160         MOVW.W          R0, #3825
060e42: C0F20600         MOVT.W          R0, #6
060e46: EFF7DBF8         BL              set_event_handler [0x50000]
060e4a: EFF7DDF8         BL              wait_on_event [0x50008]
060e4e: 0098             LDR             R0, [SP]
060e50: 02B0             ADD             SP, #8
060e52: 80BD             POP             {R7, PC}

When non_secure.out is loaded and run on a system where the secure.out has already been loaded, the first call to wait_on_event() will trigger an indirect call to default_event_handler() and the second call to wait_on_event() will trigger an indirect call back from the secure code to my_event_handler().

6.3.4. Summary of CMSE Mechanisms

Pre-Defined Macro Symbols

__ARM_FEATURE_CMSE

The __ARM_FEATURE_CMSE pre-defined macro symbol describes what CMSE extensions are available in a given compilation. The value associated with __ARM_FEATURE_CMSE is a bit-field where each bit is defined as follows:

Bit Index

Meaning

0

if set, support for TT instructions is available

1

if set, tiarmclang targets the secure state of CMSE

The value of __ARM_FEATURE_CMSE is defined according to what compiler options are specified on the tiarmclang command-line, as described below.

Compiler Options

-mcpu=cortex-m33

Instructs the tiarmclang compiler to target the Arm Cortex-M33 processor. In addition to other pre-defined macro symbols that are relevant to the Cortex-M33 processor, the tiarmclang compiler will also set bit 0 of the __ARM_FEATURE_CMSE symbol’s value when the -mcpu=cortex-m33 option is specified.

-mcmse

The -mcmse option can only be used in combination with the -mcpu=cortex-m33 and will set bit 1 of the __ARM_FEATURE_CMSE symbol’s value to indicate that the tiarmclang compiler should target the secure state of CMSE.

The use of this option instructs the tiarmclang compiler to enable the use of CMSE mechanisms that are only relevant for secure state code, like the cmse_nonsecure_entry and cmse_nonsecure_call attributes, for example.

Attributes

cmse_nonsecure_entry

Syntax

__attribute__((cmse_nonsecure_entry))

Description

The cmse_nonsecure_entry attribute can be applied to a function defined in secure to designate the function as a non-secure entry function. This will instruct the compiler to generate code to clear registers and flags that may contain secret information prior to returning to a non-secure caller.

cmse_nonsecure_call

Syntax

__attribute__((cmse_nonsecure_call))

Description

The cmse_nonsecure_call attribute can be applied to a function pointer type to designate that pointer type as a non-secure function pointer. This will instruct the compiler to generate a BLXNS instruction to effect a call to non-secure code via that function pointer type. In addition, the compiler will generate code prior to the BLXNS instruction to preserve the state of callee- and caller-saved registers and prevent leakage of secret information.

Non-secure Function Pointer Intrinsics

cmse_nsfptr_create

Signature

The cmse_nsfptr_create intrinsic is implemented as a macro in <arm_cmse.h>:

#define cmse_nsfptr_create(p) ((typeof(p))((intptr_t)(p) & ~1))

Description

The cmse_nsfptr_create() macro will clear the least significant bit of a pointer to a thumb mode function. The pointer value can then be checked via the cmse_is_nsfptr() macro to determine whether the function pointer is to be interpreted as the address of a non-secure function.

cmse_is_nsfptr

Signature

The cmse_is_nsfptr intrinsic is implemented as a macro in <arm_cmse.h>:

#define cmse_is_nsfptr(p) (!((intptr_t)(p) & 1))

Description

The cmse_is_nsfptr() macro can be used to test whether a given function pointer value should be interpreted as a non-secure function address. When such a function pointer is created by the cmse_nsfptr_create() macro, the least significant bit of its value will be cleared.