Index: include/clang/Basic/BuiltinsARM.def =================================================================== --- include/clang/Basic/BuiltinsARM.def +++ include/clang/Basic/BuiltinsARM.def @@ -164,6 +164,12 @@ BUILTIN(__builtin_arm_crc32d, "UiUiLLUi", "nc") BUILTIN(__builtin_arm_crc32cd, "UiUiLLUi", "nc") +// ARMv8-M Security Extensions a.k.a CMSE +BUILTIN(__builtin_arm_cmse_TT, "Uiv*", "n") +BUILTIN(__builtin_arm_cmse_TTT, "Uiv*", "n") +BUILTIN(__builtin_arm_cmse_TTA, "Uiv*", "n") +BUILTIN(__builtin_arm_cmse_TTAT, "Uiv*", "n") + // HINT BUILTIN(__builtin_arm_nop, "v", "") BUILTIN(__builtin_arm_yield, "v", "") Index: lib/Headers/CMakeLists.txt =================================================================== --- lib/Headers/CMakeLists.txt +++ lib/Headers/CMakeLists.txt @@ -3,6 +3,7 @@ altivec.h ammintrin.h arm_acle.h + arm_cmse.h armintr.h arm64intr.h avx2intrin.h Index: lib/Headers/arm_cmse.h =================================================================== --- /dev/null +++ lib/Headers/arm_cmse.h @@ -0,0 +1,197 @@ +//===---- 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 +#include + +#define __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 (__CMSE_SECURE_MODE) + unsigned idau_region:8; /*bit 31-24*/ + unsigned idau_region_valid:1; /*bit 23-23*/ + unsigned secure:1; /*bit 22-22*/ + unsigned nonsecure_readwrite_ok:1; /*bit 21-21*/ + unsigned nonsecure_read_ok:1; /*bit 20-20*/ +#else + unsigned :12; /*bit 31-20*/ +#endif + unsigned readwrite_ok:1; /*bit 19-19*/ + unsigned read_ok:1; /*bit 18-18*/ +#if (__CMSE_SECURE_MODE) + unsigned sau_region_valid:1; /*bit 17-17*/ +#else + unsigned :1; /*bit 17-17*/ +#endif + unsigned mpu_region_valid:1; /*bit 16-16*/ +#if (__CMSE_SECURE_MODE) + unsigned sau_region:8; /*bit 15-08*/ +#else + unsigned :8; /*bit 15-08*/ +#endif + unsigned mpu_region:8; /*bit 07-00*/ + +#else /* __ARM_LITTLE_ENDIAN */ + unsigned mpu_region:8; /*bit 31-24*/ +#if (__CMSE_SECURE_MODE) + unsigned sau_region:8; /*bit 23-16*/ +#else + unsigned :8; /*bit 23-16*/ +#endif + unsigned mpu_region_valid:1; /*bit 15-15*/ +#if (__CMSE_SECURE_MODE) + unsigned sau_region_valid:1; /*bit 14-14*/ +#else + unsigned :1; /*bit 14-14*/ +#endif + unsigned read_ok:1; /*bit 13-13*/ + unsigned readwrite_ok:1; /*bit 12-12*/ +#if (__CMSE_SECURE_MODE) + unsigned nonsecure_read_ok:1; /*bit 11-11*/ + unsigned nonsecure_readwrite_ok:1; /*bit 10-10*/ + unsigned secure:1; /*bit 09-09*/ + unsigned idau_region_valid:1; /*bit 08-08*/ + unsigned idau_region:8; /*bit 07-00*/ +#else + unsigned :12; /*bit 11-00*/ +#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 __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((void*)p)) +#define cmse_TTT_fptr(p) (cmse_TTT((void*)p)) + +#if __CMSE_SECURE_MODE +#define cmse_TTA_fptr(p) (cmse_TTA((void*)p)) +#define cmse_TTAT_fptr(p) (cmse_TTAT((void*)p)) +#endif + +static void* __attribute__((__always_inline__)) cmse_check_address_range(void *p, size_t s, int flags){ + cmse_address_info_t permb, perme; + char *pb = (char *)p; + char *pe = pb + s -1; + + if ((uintptr_t)pe < (uintptr_t)pb) return NULL; // wrap around check + + // execute the right variant of the TT instructions + const int singleCheck = (((uintptr_t) pb ^ (uintptr_t) pe) < 0x20u); + + switch (flags & (CMSE_MPU_UNPRIV | CMSE_MPU_NONSECURE)) { + case 0: + permb = cmse_TT(pb); + perme = singleCheck ? permb : cmse_TT(pe); + break; + case CMSE_MPU_UNPRIV: + permb = cmse_TTT(pb); + perme = singleCheck ? permb : cmse_TTT(pe); + break; +#if __CMSE_SECURE_MODE + case CMSE_MPU_NONSECURE: + permb = cmse_TTA(pb); + perme = singleCheck ? permb : cmse_TTA(pe); + break; + case CMSE_MPU_UNPRIV | CMSE_MPU_NONSECURE: + permb = cmse_TTAT(pb); + perme = singleCheck ? permb : cmse_TTAT(pe); + break; +#endif + // if CMSE_NONSECURE is specified w/o __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 ! (__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 (__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 ? p : NULL; + + case CMSE_MPU_READ|CMSE_AU_NONSECURE: + return permb.flags.nonsecure_read_ok ? p : NULL; + + case CMSE_AU_NONSECURE: + return permb.flags.secure ? NULL : p; +#endif + case CMSE_MPU_READ|CMSE_MPU_READWRITE: + case CMSE_MPU_READWRITE: + return permb.flags.readwrite_ok ? p : NULL; + + case CMSE_MPU_READ: + return permb.flags.read_ok ? p : NULL; + + default: + return NULL; + } +} + +#if __CMSE_SECURE_MODE +static int __attribute__((__always_inline__, __nodebug__)) cmse_nonsecure_caller(void) { + return !((intptr_t)__builtin_return_address(0) & 1); +} + +#define cmse_nsfptr_create(p) ((__typeof__(p)) ((intptr_t) p & ~1)) +#define cmse_is_nsfptr(p) (!((intptr_t) p & 1)) +#endif // __CMSE_SECURE_MODE + +void __attribute__((__noreturn__)) cmse_abort(void); +#if defined(__cplusplus) +} +#endif +#endif /* (__ARM_FEATURE_CMSE & 0x1) */ +#endif /* __ARM_CMSE_H */ Index: test/CodeGen/arm-cmse-nonsecure.c =================================================================== --- /dev/null +++ test/CodeGen/arm-cmse-nonsecure.c @@ -0,0 +1,52 @@ +// RUN: %clang -fvisibility=default -mlittle-endian -Xclang -ffreestanding -target thumbv8m.base-linux-gnueabihf -emit-llvm -S -o - %s | FileCheck %s +// RUN-: %clang -fvisibility=default -mbig-endian -Xclang -ffreestanding -target thumbv8m.base-linux-gnueabihf -emit-llvm -S -o - %s | FileCheck %s + +#include + +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 +} Index: test/CodeGen/arm-cmse-secure.c =================================================================== --- /dev/null +++ test/CodeGen/arm-cmse-secure.c @@ -0,0 +1,66 @@ +// RUN: %clang -fvisibility=default -mlittle-endian -mcmse -Xclang -ffreestanding -target thumbv8m.base-linux-gnueabihf -emit-llvm -S -o - %s | FileCheck %s +// RUN: %clang -fvisibility=default -mbig-endian -mcmse -Xclang -ffreestanding -target thumbv8m.base-linux-gnueabihf -emit-llvm -S -o - %s | FileCheck %s + +#include + +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 +} Index: test/CodeGen/arm-cmse.c =================================================================== --- /dev/null +++ test/CodeGen/arm-cmse.c @@ -0,0 +1,20 @@ +// RUN: %clang_cc1 -triple thumbv8m.base-none-eabi -O1 -emit-llvm %s -o - | FileCheck %s +int test_cmse_TT(void *p){ + return __builtin_arm_cmse_TT(p); + // CHECK: call i32 @llvm.arm.cmse.tt(i8* %{{.*}}) +} + +int test_cmse_TTT(void *p){ + return __builtin_arm_cmse_TTT(p); + // CHECK: call i32 @llvm.arm.cmse.ttt(i8* %{{.*}}) +} + +int test_cmse_TTA(void *p){ + return __builtin_arm_cmse_TTA(p); + // CHECK: call i32 @llvm.arm.cmse.tta(i8* %{{.*}}) +} + +int test_cmse_TTAT(void *p){ + return __builtin_arm_cmse_TTAT(p); + // CHECK: call i32 @llvm.arm.cmse.ttat(i8* %{{.*}}) +} Index: test/Headers/arm-cmse-header-ns.c =================================================================== --- /dev/null +++ test/Headers/arm-cmse-header-ns.c @@ -0,0 +1,27 @@ +// RUN: %clang_cc1 -triple thumbv8m.base-linux-gnueabihf -fsyntax-only -ffreestanding %s 2>&1 | FileCheck --check-prefix=CHECK-c %s +// RUN: not %clang_cc1 -triple thumbv8m.base-linux-gnueabihf -fsyntax-only -ffreestanding -x c++ %s 2>&1 | FileCheck --check-prefix=CHECK-cpp %s + +#include + +typedef void (*callback_t)(void); + +void func(callback_t fptr, void *p) +{ + cmse_TT(p); + cmse_TTT(p); + cmse_TT_fptr(fptr); + cmse_TTT_fptr(fptr); + + cmse_TTA(p); + cmse_TTAT(p); + cmse_TTA_fptr(fptr); + cmse_TTAT_fptr(fptr); +// CHECK-c: warning: implicit declaration of function 'cmse_TTA' +// CHECK-c: warning: implicit declaration of function 'cmse_TTAT' +// CHECK-c: warning: implicit declaration of function 'cmse_TTA_fptr' +// CHECK-c: warning: implicit declaration of function 'cmse_TTAT_fptr' +// CHECK-cpp: error: use of undeclared identifier 'cmse_TTA' +// CHECK-cpp: error: use of undeclared identifier 'cmse_TTAT' +// CHECK-cpp: error: use of undeclared identifier 'cmse_TTA_fptr' +// CHECK-cpp: error: use of undeclared identifier 'cmse_TTAT_fptr' +} Index: test/Headers/arm-cmse-header.c =================================================================== --- /dev/null +++ test/Headers/arm-cmse-header.c @@ -0,0 +1,20 @@ +// RUN: %clang_cc1 -triple thumbv8m.base-linux-gnueabihf -fsyntax-only -ffreestanding %s -verify -mcmse +// RUN: %clang_cc1 -triple thumbv8m.base-linux-gnueabihf -fsyntax-only -ffreestanding -x c++ %s -verify -mcmse +// expected-no-diagnostics + +#include + +typedef void (*callback_t)(void); + +void func(callback_t fptr, void *p) +{ + cmse_TT(p); + cmse_TTT(p); + cmse_TTA(p); + cmse_TTAT(p); + + cmse_TT_fptr(fptr); + cmse_TTT_fptr(fptr); + cmse_TTA_fptr(fptr); + cmse_TTAT_fptr(fptr); +}