Index: clang/include/clang/Basic/BuiltinsARM.def =================================================================== --- clang/include/clang/Basic/BuiltinsARM.def +++ clang/include/clang/Basic/BuiltinsARM.def @@ -166,6 +166,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: clang/lib/Headers/CMakeLists.txt =================================================================== --- clang/lib/Headers/CMakeLists.txt +++ clang/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: clang/lib/Headers/arm_cmse.h =================================================================== --- /dev/null +++ clang/lib/Headers/arm_cmse.h @@ -0,0 +1,217 @@ +//===---- 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 __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; /* bits 31-24 */ + unsigned idau_region_valid : 1; /* bits 23-23 */ + unsigned secure : 1; /* bits 22-22 */ + unsigned nonsecure_readwrite_ok : 1; /* bits 21-21 */ + unsigned nonsecure_read_ok : 1; /* bits 20-20 */ +#else + unsigned : 12; /* bits 31-20 */ +#endif + unsigned readwrite_ok : 1; /* bits 19-19 */ + unsigned read_ok : 1; /* bits 18-18 */ +#if (__ARM_CMSE_SECURE_MODE) + unsigned sau_region_valid : 1; /* bits 17-17 */ +#else + unsigned : 1; /* bits 17-17 */ +#endif + unsigned mpu_region_valid : 1; /* bits 16-16 */ +#if (__ARM_CMSE_SECURE_MODE) + unsigned sau_region : 8; /* bits 15-08 */ +#else + unsigned : 8; /* bits 15-08 */ +#endif + unsigned mpu_region : 8; /* bits 07-00 */ + +#else /* __ARM_LITTLE_ENDIAN */ + unsigned mpu_region : 8; /* bits 31-24 */ +#if (__ARM_CMSE_SECURE_MODE) + unsigned sau_region : 8; /* bits 23-16 */ +#else + unsigned : 8; /* bits 23-16 */ +#endif + unsigned mpu_region_valid : 1; /* bits 15-15 */ +#if (__ARM_CMSE_SECURE_MODE) + unsigned sau_region_valid : 1; /* bits 14-14 */ +#else + unsigned : 1; /* bits 14-14*/ +#endif + unsigned read_ok : 1; /* bits 13-13 */ + unsigned readwrite_ok : 1; /* bits 12-12 */ +#if (__ARM_CMSE_SECURE_MODE) + unsigned nonsecure_read_ok : 1; /* bits 11-11 */ + unsigned nonsecure_readwrite_ok : 1; /* bits 10-10 */ + unsigned secure : 1; /* bits 09-09 */ + unsigned idau_region_valid : 1; /* bits 08-08 */ + unsigned idau_region : 8; /* bits 07-00 */ +#else + unsigned : 12; /* bits 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 __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 singleCheck = (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 = singleCheck ? permb : cmse_TT(pe); + break; + case CMSE_MPU_UNPRIV: + permb = cmse_TTT(pb); + perme = singleCheck ? permb : cmse_TTT(pe); + break; +#if __ARM_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 __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 */ Index: clang/test/CodeGen/arm-cmse-nonsecure.c =================================================================== --- /dev/null +++ clang/test/CodeGen/arm-cmse-nonsecure.c @@ -0,0 +1,52 @@ +// 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 + +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: clang/test/CodeGen/arm-cmse-secure.c =================================================================== --- /dev/null +++ clang/test/CodeGen/arm-cmse-secure.c @@ -0,0 +1,66 @@ +// 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 + +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: clang/test/CodeGen/arm-cmse.c =================================================================== --- /dev/null +++ clang/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: clang/test/Headers/arm-cmse-header-ns.c =================================================================== --- /dev/null +++ clang/test/Headers/arm-cmse-header-ns.c @@ -0,0 +1,27 @@ +// RUN: %clang_cc1 -triple thumbv8m.base-eabi -fsyntax-only %s 2>&1 | FileCheck --check-prefix=CHECK-c %s +// RUN: not %clang_cc1 -triple thumbv8m.base-eabi -fsyntax-only -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: clang/test/Headers/arm-cmse-header.c =================================================================== --- /dev/null +++ clang/test/Headers/arm-cmse-header.c @@ -0,0 +1,20 @@ +// RUN: %clang_cc1 -triple thumbv8m.base-eabi -fsyntax-only -ffreestanding %s -verify -mcmse +// RUN: %clang_cc1 -triple thumbv8m.base-eabi -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); +}