[ARM][CMSE] Add CMSE header and builtins

This is patch C2 as mentioned in RFC
http://lists.llvm.org/pipermail/cfe-dev/2019-March/061834.html

This adds CMSE builtin functions, and introduces arm_cmse.h header which has
useful macros, functions, and data types for end-users of CMSE.

Patch by Javed Absar.

Diferential Revision: https://reviews.llvm.org/D70817
This commit is contained in:
Momchil Velikov 2019-12-12 14:59:04 +00:00
parent bf13a71095
commit 600d123c6f
8 changed files with 409 additions and 0 deletions

View File

@ -166,6 +166,12 @@ BUILTIN(__builtin_arm_crc32cw, "UiUiUi", "nc")
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", "")

View File

@ -3,6 +3,7 @@ set(files
altivec.h
ammintrin.h
arm_acle.h
arm_cmse.h
armintr.h
arm64intr.h
avx2intrin.h

View File

@ -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 <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 */

View File

@ -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 <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
}

View File

@ -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 <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
}

View File

@ -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* %{{.*}})
}

View File

@ -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 <arm_cmse.h>
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'
}

View File

@ -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 <arm_cmse.h>
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);
}