master
  1//===---- arm_cmse.h - Arm CMSE support -----------------------------------===//
  2//
  3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
  4// See https://llvm.org/LICENSE.txt for license information.
  5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
  6//
  7//===----------------------------------------------------------------------===//
  8
  9#ifndef __ARM_CMSE_H
 10#define __ARM_CMSE_H
 11
 12#if (__ARM_FEATURE_CMSE & 0x1)
 13#include <stddef.h>
 14#include <stdint.h>
 15
 16#define __ARM_CMSE_SECURE_MODE (__ARM_FEATURE_CMSE & 0x2)
 17#define CMSE_MPU_READWRITE 1 /* checks if readwrite_ok field is set */
 18#define CMSE_AU_NONSECURE  2 /* checks if permissions have secure field unset */
 19#define CMSE_MPU_UNPRIV    4 /* sets T flag on TT insrtuction */
 20#define CMSE_MPU_READ      8 /* checks if read_ok field is set */
 21#define CMSE_MPU_NONSECURE 16 /* sets A flag, checks if secure field unset */
 22#define CMSE_NONSECURE (CMSE_AU_NONSECURE | CMSE_MPU_NONSECURE)
 23
 24#define cmse_check_pointed_object(p, f) \
 25  cmse_check_address_range((p), sizeof(*(p)), (f))
 26
 27#if defined(__cplusplus)
 28extern "C" {
 29#endif
 30
 31typedef union {
 32  struct cmse_address_info {
 33#ifdef __ARM_BIG_ENDIAN
 34    /* __ARM_BIG_ENDIAN */
 35#if (__ARM_CMSE_SECURE_MODE)
 36    unsigned idau_region : 8;
 37    unsigned idau_region_valid : 1;
 38    unsigned secure : 1;
 39    unsigned nonsecure_readwrite_ok : 1;
 40    unsigned nonsecure_read_ok : 1;
 41#else
 42    unsigned : 12;
 43#endif
 44    unsigned readwrite_ok : 1;
 45    unsigned read_ok : 1;
 46#if (__ARM_CMSE_SECURE_MODE)
 47    unsigned sau_region_valid : 1;
 48#else
 49    unsigned : 1;
 50#endif
 51    unsigned mpu_region_valid : 1;
 52#if (__ARM_CMSE_SECURE_MODE)
 53    unsigned sau_region : 8;
 54#else
 55    unsigned : 8;
 56#endif
 57    unsigned mpu_region : 8;
 58
 59#else /* __ARM_LITTLE_ENDIAN */
 60    unsigned mpu_region : 8;
 61#if (__ARM_CMSE_SECURE_MODE)
 62    unsigned sau_region : 8;
 63#else
 64    unsigned : 8;
 65#endif
 66    unsigned mpu_region_valid : 1;
 67#if (__ARM_CMSE_SECURE_MODE)
 68    unsigned sau_region_valid : 1;
 69#else
 70    unsigned : 1;
 71#endif
 72    unsigned read_ok : 1;
 73    unsigned readwrite_ok : 1;
 74#if (__ARM_CMSE_SECURE_MODE)
 75    unsigned nonsecure_read_ok : 1;
 76    unsigned nonsecure_readwrite_ok : 1;
 77    unsigned secure : 1;
 78    unsigned idau_region_valid : 1;
 79    unsigned idau_region : 8;
 80#else
 81    unsigned : 12;
 82#endif
 83#endif /*__ARM_LITTLE_ENDIAN */
 84  } flags;
 85  unsigned value;
 86} cmse_address_info_t;
 87
 88static cmse_address_info_t __attribute__((__always_inline__, __nodebug__))
 89cmse_TT(void *__p) {
 90  cmse_address_info_t __u;
 91  __u.value = __builtin_arm_cmse_TT(__p);
 92  return __u;
 93}
 94static cmse_address_info_t __attribute__((__always_inline__, __nodebug__))
 95cmse_TTT(void *__p) {
 96  cmse_address_info_t __u;
 97  __u.value = __builtin_arm_cmse_TTT(__p);
 98  return __u;
 99}
100
101#if __ARM_CMSE_SECURE_MODE
102static cmse_address_info_t __attribute__((__always_inline__, __nodebug__))
103cmse_TTA(void *__p) {
104  cmse_address_info_t __u;
105  __u.value = __builtin_arm_cmse_TTA(__p);
106  return __u;
107}
108static cmse_address_info_t __attribute__((__always_inline__, __nodebug__))
109cmse_TTAT(void *__p) {
110  cmse_address_info_t __u;
111  __u.value = __builtin_arm_cmse_TTAT(__p);
112  return __u;
113}
114#endif
115
116#define cmse_TT_fptr(p) cmse_TT(__builtin_bit_cast(void *, (p)))
117#define cmse_TTT_fptr(p) cmse_TTT(__builtin_bit_cast(void *, (p)))
118
119#if __ARM_CMSE_SECURE_MODE
120#define cmse_TTA_fptr(p) cmse_TTA(__builtin_bit_cast(void *, (p)))
121#define cmse_TTAT_fptr(p) cmse_TTAT(__builtin_bit_cast(void *, (p)))
122#endif
123
124static void *__attribute__((__always_inline__))
125cmse_check_address_range(void *__pb, size_t __s, int __flags) {
126  uintptr_t __begin = (uintptr_t)__pb;
127  uintptr_t __end = __begin + __s - 1;
128
129  if (__end < __begin)
130    return NULL; /* wrap around check */
131
132  /* Check whether the range crosses a 32-bytes aligned address */
133  const int __single_check = (__begin ^ __end) < 0x20u;
134
135  /* execute the right variant of the TT instructions */
136  void *__pe = (void *)__end;
137  cmse_address_info_t __permb, __perme;
138  switch (__flags & (CMSE_MPU_UNPRIV | CMSE_MPU_NONSECURE)) {
139  case 0:
140    __permb = cmse_TT(__pb);
141    __perme = __single_check ? __permb : cmse_TT(__pe);
142    break;
143  case CMSE_MPU_UNPRIV:
144    __permb = cmse_TTT(__pb);
145    __perme = __single_check ? __permb : cmse_TTT(__pe);
146    break;
147#if __ARM_CMSE_SECURE_MODE
148  case CMSE_MPU_NONSECURE:
149    __permb = cmse_TTA(__pb);
150    __perme = __single_check ? __permb : cmse_TTA(__pe);
151    break;
152  case CMSE_MPU_UNPRIV | CMSE_MPU_NONSECURE:
153    __permb = cmse_TTAT(__pb);
154    __perme = __single_check ? __permb : cmse_TTAT(__pe);
155    break;
156#endif
157  /* if CMSE_NONSECURE is specified w/o __ARM_CMSE_SECURE_MODE */
158  default:
159    return NULL;
160  }
161
162  /* check that the range does not cross MPU, SAU, or IDAU region boundaries */
163  if (__permb.value != __perme.value)
164    return NULL;
165#if !(__ARM_CMSE_SECURE_MODE)
166  /* CMSE_AU_NONSECURE is only supported when __ARM_FEATURE_CMSE & 0x2 */
167  if (__flags & CMSE_AU_NONSECURE)
168    return NULL;
169#endif
170
171  /* check the permission on the range */
172  switch (__flags & ~(CMSE_MPU_UNPRIV | CMSE_MPU_NONSECURE)) {
173#if (__ARM_CMSE_SECURE_MODE)
174  case CMSE_MPU_READ | CMSE_MPU_READWRITE | CMSE_AU_NONSECURE:
175  case CMSE_MPU_READWRITE | CMSE_AU_NONSECURE:
176    return __permb.flags.nonsecure_readwrite_ok ? __pb : NULL;
177
178  case CMSE_MPU_READ | CMSE_AU_NONSECURE:
179    return __permb.flags.nonsecure_read_ok ? __pb : NULL;
180
181  case CMSE_AU_NONSECURE:
182    return __permb.flags.secure ? NULL : __pb;
183#endif
184  case CMSE_MPU_READ | CMSE_MPU_READWRITE:
185  case CMSE_MPU_READWRITE:
186    return __permb.flags.readwrite_ok ? __pb : NULL;
187
188  case CMSE_MPU_READ:
189    return __permb.flags.read_ok ? __pb : NULL;
190
191  default:
192    return NULL;
193  }
194}
195
196#if __ARM_CMSE_SECURE_MODE
197static int __attribute__((__always_inline__, __nodebug__))
198cmse_nonsecure_caller(void) {
199  return !((uintptr_t)__builtin_return_address(0) & 1);
200}
201
202#define cmse_nsfptr_create(p)                                                  \
203  __builtin_bit_cast(__typeof__(p),                                            \
204                     (__builtin_bit_cast(uintptr_t, p) & ~(uintptr_t)1))
205
206#define cmse_is_nsfptr(p) ((__builtin_bit_cast(uintptr_t, p) & 1) == 0)
207
208#endif /* __ARM_CMSE_SECURE_MODE */
209
210void __attribute__((__noreturn__)) cmse_abort(void);
211#if defined(__cplusplus)
212}
213#endif
214
215#endif /* (__ARM_FEATURE_CMSE & 0x1) */
216
217#endif /* __ARM_CMSE_H */