--- /dev/null
+//===---- arm_cmse.h - Arm CMSE support -----------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef __ARM_CMSE_H
+#define __ARM_CMSE_H
+
+#if (__ARM_FEATURE_CMSE & 0x1)
+#include <stddef.h>
+#include <stdint.h>
+
+#define __ARM_CMSE_SECURE_MODE (__ARM_FEATURE_CMSE & 0x2)
+#define CMSE_MPU_READWRITE 1 /* checks if readwrite_ok field is set */
+#define CMSE_AU_NONSECURE 2 /* checks if permissions have secure field unset */
+#define CMSE_MPU_UNPRIV 4 /* sets T flag on TT insrtuction */
+#define CMSE_MPU_READ 8 /* checks if read_ok field is set */
+#define CMSE_MPU_NONSECURE 16 /* sets A flag, checks if secure field unset */
+#define CMSE_NONSECURE (CMSE_AU_NONSECURE | CMSE_MPU_NONSECURE)
+
+#define cmse_check_pointed_object(p, f) \
+ cmse_check_address_range((p), sizeof(*(p)), (f))
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+typedef union {
+ struct cmse_address_info {
+#ifdef __ARM_BIG_ENDIAN
+ /* __ARM_BIG_ENDIAN */
+#if (__ARM_CMSE_SECURE_MODE)
+ unsigned idau_region : 8;
+ unsigned idau_region_valid : 1;
+ unsigned secure : 1;
+ unsigned nonsecure_readwrite_ok : 1;
+ unsigned nonsecure_read_ok : 1;
+#else
+ unsigned : 12;
+#endif
+ unsigned readwrite_ok : 1;
+ unsigned read_ok : 1;
+#if (__ARM_CMSE_SECURE_MODE)
+ unsigned sau_region_valid : 1;
+#else
+ unsigned : 1;
+#endif
+ unsigned mpu_region_valid : 1;
+#if (__ARM_CMSE_SECURE_MODE)
+ unsigned sau_region : 8;
+#else
+ unsigned : 8;
+#endif
+ unsigned mpu_region : 8;
+
+#else /* __ARM_LITTLE_ENDIAN */
+ unsigned mpu_region : 8;
+#if (__ARM_CMSE_SECURE_MODE)
+ unsigned sau_region : 8;
+#else
+ unsigned : 8;
+#endif
+ unsigned mpu_region_valid : 1;
+#if (__ARM_CMSE_SECURE_MODE)
+ unsigned sau_region_valid : 1;
+#else
+ unsigned : 1;
+#endif
+ unsigned read_ok : 1;
+ unsigned readwrite_ok : 1;
+#if (__ARM_CMSE_SECURE_MODE)
+ unsigned nonsecure_read_ok : 1;
+ unsigned nonsecure_readwrite_ok : 1;
+ unsigned secure : 1;
+ unsigned idau_region_valid : 1;
+ unsigned idau_region : 8;
+#else
+ unsigned : 12;
+#endif
+#endif /*__ARM_LITTLE_ENDIAN */
+ } flags;
+ unsigned value;
+} cmse_address_info_t;
+
+static cmse_address_info_t __attribute__((__always_inline__, __nodebug__))
+cmse_TT(void *__p) {
+ cmse_address_info_t __u;
+ __u.value = __builtin_arm_cmse_TT(__p);
+ return __u;
+}
+static cmse_address_info_t __attribute__((__always_inline__, __nodebug__))
+cmse_TTT(void *__p) {
+ cmse_address_info_t __u;
+ __u.value = __builtin_arm_cmse_TTT(__p);
+ return __u;
+}
+
+#if __ARM_CMSE_SECURE_MODE
+static cmse_address_info_t __attribute__((__always_inline__, __nodebug__))
+cmse_TTA(void *__p) {
+ cmse_address_info_t __u;
+ __u.value = __builtin_arm_cmse_TTA(__p);
+ return __u;
+}
+static cmse_address_info_t __attribute__((__always_inline__, __nodebug__))
+cmse_TTAT(void *__p) {
+ cmse_address_info_t __u;
+ __u.value = __builtin_arm_cmse_TTAT(__p);
+ return __u;
+}
+#endif
+
+#define cmse_TT_fptr(p) cmse_TT(__builtin_bit_cast(void *, (p)))
+#define cmse_TTT_fptr(p) cmse_TTT(__builtin_bit_cast(void *, (p)))
+
+#if __ARM_CMSE_SECURE_MODE
+#define cmse_TTA_fptr(p) cmse_TTA(__builtin_bit_cast(void *, (p)))
+#define cmse_TTAT_fptr(p) cmse_TTAT(__builtin_bit_cast(void *, (p)))
+#endif
+
+static void *__attribute__((__always_inline__))
+cmse_check_address_range(void *__pb, size_t __s, int __flags) {
+ uintptr_t __begin = (uintptr_t)__pb;
+ uintptr_t __end = __begin + __s - 1;
+
+ if (__end < __begin)
+ return NULL; /* wrap around check */
+
+ /* Check whether the range crosses a 32-bytes aligned address */
+ const int __single_check = (__begin ^ __end) < 0x20u;
+
+ /* execute the right variant of the TT instructions */
+ void *__pe = (void *)__end;
+ cmse_address_info_t __permb, __perme;
+ switch (__flags & (CMSE_MPU_UNPRIV | CMSE_MPU_NONSECURE)) {
+ case 0:
+ __permb = cmse_TT(__pb);
+ __perme = __single_check ? __permb : cmse_TT(__pe);
+ break;
+ case CMSE_MPU_UNPRIV:
+ __permb = cmse_TTT(__pb);
+ __perme = __single_check ? __permb : cmse_TTT(__pe);
+ break;
+#if __ARM_CMSE_SECURE_MODE
+ case CMSE_MPU_NONSECURE:
+ __permb = cmse_TTA(__pb);
+ __perme = __single_check ? __permb : cmse_TTA(__pe);
+ break;
+ case CMSE_MPU_UNPRIV | CMSE_MPU_NONSECURE:
+ __permb = cmse_TTAT(__pb);
+ __perme = __single_check ? __permb : cmse_TTAT(__pe);
+ break;
+#endif
+ /* if CMSE_NONSECURE is specified w/o __ARM_CMSE_SECURE_MODE */
+ default:
+ return NULL;
+ }
+
+ /* check that the range does not cross MPU, SAU, or IDAU region boundaries */
+ if (__permb.value != __perme.value)
+ return NULL;
+#if !(__ARM_CMSE_SECURE_MODE)
+ /* CMSE_AU_NONSECURE is only supported when __ARM_FEATURE_CMSE & 0x2 */
+ if (__flags & CMSE_AU_NONSECURE)
+ return NULL;
+#endif
+
+ /* check the permission on the range */
+ switch (__flags & ~(CMSE_MPU_UNPRIV | CMSE_MPU_NONSECURE)) {
+#if (__ARM_CMSE_SECURE_MODE)
+ case CMSE_MPU_READ | CMSE_MPU_READWRITE | CMSE_AU_NONSECURE:
+ case CMSE_MPU_READWRITE | CMSE_AU_NONSECURE:
+ return __permb.flags.nonsecure_readwrite_ok ? __pb : NULL;
+
+ case CMSE_MPU_READ | CMSE_AU_NONSECURE:
+ return __permb.flags.nonsecure_read_ok ? __pb : NULL;
+
+ case CMSE_AU_NONSECURE:
+ return __permb.flags.secure ? NULL : __pb;
+#endif
+ case CMSE_MPU_READ | CMSE_MPU_READWRITE:
+ case CMSE_MPU_READWRITE:
+ return __permb.flags.readwrite_ok ? __pb : NULL;
+
+ case CMSE_MPU_READ:
+ return __permb.flags.read_ok ? __pb : NULL;
+
+ default:
+ return NULL;
+ }
+}
+
+#if __ARM_CMSE_SECURE_MODE
+static int __attribute__((__always_inline__, __nodebug__))
+cmse_nonsecure_caller(void) {
+ return !((uintptr_t)__builtin_return_address(0) & 1);
+}
+
+#define cmse_nsfptr_create(p) \
+ __builtin_bit_cast(__typeof__(p), \
+ (__builtin_bit_cast(uintptr_t, p) & ~(uintptr_t)1))
+
+#define cmse_is_nsfptr(p) ((__builtin_bit_cast(uintptr_t, p) & 1) == 0)
+
+#endif /* __ARM_CMSE_SECURE_MODE */
+
+void __attribute__((__noreturn__)) cmse_abort(void);
+#if defined(__cplusplus)
+}
+#endif
+
+#endif /* (__ARM_FEATURE_CMSE & 0x1) */
+
+#endif /* __ARM_CMSE_H */
--- /dev/null
+// RUN: %clang -mlittle-endian -target thumbv8m.base-eabi -emit-llvm -S -o - %s | FileCheck %s
+// RUN: %clang -mbig-endian -target thumbv8m.base-eabi -emit-llvm -S -o - %s | FileCheck %s
+
+#include <arm_cmse.h>
+
+unsigned test_cmse_primitives(void *p) {
+// CHECK: define {{.*}} i32 @test_cmse_primitives
+ cmse_address_info_t tt_val, ttt_val;
+ unsigned sum;
+
+ tt_val = cmse_TT(p);
+ ttt_val = cmse_TTT(p);
+// CHECK: call i32 @llvm.arm.cmse.tt
+// CHECK: call i32 @llvm.arm.cmse.ttt
+// CHECK-NOT: llvm.arm.cmse.tta
+// CHECK-NOT: llvm.arm.cmse.ttat
+
+ sum = tt_val.value;
+ sum += ttt_val.value;
+
+ sum += tt_val.flags.mpu_region;
+ sum += tt_val.flags.mpu_region_valid;
+ sum += tt_val.flags.read_ok;
+ sum += tt_val.flags.readwrite_ok;
+
+ return sum;
+}
+
+void *test_address_range(void *p) {
+// CHECK: define {{.*}} i8* @test_address_range
+ return cmse_check_address_range(p, 128, CMSE_MPU_UNPRIV
+ | CMSE_MPU_READWRITE
+ | CMSE_MPU_READ);
+// CHECK: call i32 @llvm.arm.cmse.tt
+// CHECK: call i32 @llvm.arm.cmse.ttt
+// CHECK-NOT: llvm.arm.cmse.tta
+// CHECK-NOT: llvm.arm.cmse.ttat
+}
+
+typedef struct {
+ int x, y, z;
+} Point;
+
+void *test_pointed_object(void *p) {
+// CHECK: define {{.*}} i8* @test_pointed_object
+ Point *pt = (Point *)p;
+ cmse_check_pointed_object(pt, CMSE_MPU_READ);
+// CHECK: call i32 @llvm.arm.cmse.tt
+// CHECK: call i32 @llvm.arm.cmse.ttt
+// CHECK-NOT: call i32 @llvm.arm.cmse.tta
+// CHECK-NOT: call i32 @llvm.arm.cmse.ttat
+}
--- /dev/null
+// RUN: %clang -mlittle-endian -mcmse -target thumbv8m.base-eabi -emit-llvm -S -o - %s | FileCheck %s
+// RUN: %clang -mbig-endian -mcmse -target thumbv8m.base-eabi -emit-llvm -S -o - %s | FileCheck %s
+
+#include <arm_cmse.h>
+
+unsigned test_cmse_primitives(void *p) {
+// CHECK: define {{.*}} i32 @test_cmse_primitives
+ cmse_address_info_t tt_val, ttt_val;
+ cmse_address_info_t tta_val, ttat_val;
+ unsigned sum;
+
+ tt_val = cmse_TT(p);
+ ttt_val = cmse_TTT(p);
+ tta_val = cmse_TTA(p);
+ ttat_val = cmse_TTAT(p);
+// CHECK: call i32 @llvm.arm.cmse.tt
+// CHECK: call i32 @llvm.arm.cmse.ttt
+// CHECK: call i32 @llvm.arm.cmse.tta
+// CHECK: call i32 @llvm.arm.cmse.ttat
+
+ sum = tt_val.value;
+ sum += ttt_val.value;
+ sum += tta_val.value;
+ sum += ttat_val.value;
+
+ sum += tt_val.flags.mpu_region;
+ sum += tt_val.flags.sau_region;
+ sum += tt_val.flags.mpu_region_valid;
+ sum += tt_val.flags.sau_region_valid;
+ sum += tt_val.flags.read_ok;
+ sum += tt_val.flags.readwrite_ok;
+ sum += tt_val.flags.nonsecure_read_ok;
+ sum += tt_val.flags.nonsecure_readwrite_ok;
+ sum += tt_val.flags.secure;
+ sum += tt_val.flags.idau_region_valid;
+ sum += tt_val.flags.idau_region;
+
+ return sum;
+}
+
+void *test_address_range(void *p) {
+// CHECK: define {{.*}} i8* @test_address_range
+ return cmse_check_address_range(p, 128, CMSE_MPU_UNPRIV
+ | CMSE_MPU_NONSECURE
+ | CMSE_MPU_READWRITE);
+// CHECK: call i32 @llvm.arm.cmse.tt
+// CHECK: call i32 @llvm.arm.cmse.ttt
+// CHECK: call i32 @llvm.arm.cmse.tta
+// CHECK: call i32 @llvm.arm.cmse.ttat
+}
+
+typedef struct {
+ int x, y, z;
+} Point;
+
+void *test_pointed_object(void *p) {
+// CHECK: define {{.*}} i8* @test_pointed_object
+ Point *pt = (Point *)p;
+ cmse_check_pointed_object(pt, CMSE_NONSECURE
+ | CMSE_MPU_READ
+ | CMSE_AU_NONSECURE);
+// CHECK: call i32 @llvm.arm.cmse.tt
+// CHECK: call i32 @llvm.arm.cmse.ttt
+// CHECK: call i32 @llvm.arm.cmse.tta
+// CHECK: call i32 @llvm.arm.cmse.ttat
+}