1356843Sdim//===---- arm_cmse.h - Arm CMSE support -----------------------------------===//
2356843Sdim//
3356843Sdim// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4356843Sdim// See https://llvm.org/LICENSE.txt for license information.
5356843Sdim// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6356843Sdim//
7356843Sdim//===----------------------------------------------------------------------===//
8356843Sdim
9356843Sdim#ifndef __ARM_CMSE_H
10356843Sdim#define __ARM_CMSE_H
11356843Sdim
12356843Sdim#if (__ARM_FEATURE_CMSE & 0x1)
13356843Sdim#include <stddef.h>
14356843Sdim#include <stdint.h>
15356843Sdim
16356843Sdim#define __ARM_CMSE_SECURE_MODE (__ARM_FEATURE_CMSE & 0x2)
17356843Sdim#define CMSE_MPU_READWRITE 1 /* checks if readwrite_ok field is set */
18356843Sdim#define CMSE_AU_NONSECURE  2 /* checks if permissions have secure field unset */
19356843Sdim#define CMSE_MPU_UNPRIV    4 /* sets T flag on TT insrtuction */
20356843Sdim#define CMSE_MPU_READ      8 /* checks if read_ok field is set */
21356843Sdim#define CMSE_MPU_NONSECURE 16 /* sets A flag, checks if secure field unset */
22356843Sdim#define CMSE_NONSECURE (CMSE_AU_NONSECURE | CMSE_MPU_NONSECURE)
23356843Sdim
24356843Sdim#define cmse_check_pointed_object(p, f) \
25356843Sdim  cmse_check_address_range((p), sizeof(*(p)), (f))
26356843Sdim
27356843Sdim#if defined(__cplusplus)
28356843Sdimextern "C" {
29356843Sdim#endif
30356843Sdim
31356843Sdimtypedef union {
32356843Sdim  struct cmse_address_info {
33356843Sdim#ifdef __ARM_BIG_ENDIAN
34356843Sdim    /* __ARM_BIG_ENDIAN */
35356843Sdim#if (__ARM_CMSE_SECURE_MODE)
36356843Sdim    unsigned idau_region : 8;
37356843Sdim    unsigned idau_region_valid : 1;
38356843Sdim    unsigned secure : 1;
39356843Sdim    unsigned nonsecure_readwrite_ok : 1;
40356843Sdim    unsigned nonsecure_read_ok : 1;
41356843Sdim#else
42356843Sdim    unsigned : 12;
43356843Sdim#endif
44356843Sdim    unsigned readwrite_ok : 1;
45356843Sdim    unsigned read_ok : 1;
46356843Sdim#if (__ARM_CMSE_SECURE_MODE)
47356843Sdim    unsigned sau_region_valid : 1;
48356843Sdim#else
49356843Sdim    unsigned : 1;
50356843Sdim#endif
51356843Sdim    unsigned mpu_region_valid : 1;
52356843Sdim#if (__ARM_CMSE_SECURE_MODE)
53356843Sdim    unsigned sau_region : 8;
54356843Sdim#else
55356843Sdim    unsigned : 8;
56356843Sdim#endif
57356843Sdim    unsigned mpu_region : 8;
58356843Sdim
59356843Sdim#else /* __ARM_LITTLE_ENDIAN */
60356843Sdim    unsigned mpu_region : 8;
61356843Sdim#if (__ARM_CMSE_SECURE_MODE)
62356843Sdim    unsigned sau_region : 8;
63356843Sdim#else
64356843Sdim    unsigned : 8;
65356843Sdim#endif
66356843Sdim    unsigned mpu_region_valid : 1;
67356843Sdim#if (__ARM_CMSE_SECURE_MODE)
68356843Sdim    unsigned sau_region_valid : 1;
69356843Sdim#else
70356843Sdim    unsigned : 1;
71356843Sdim#endif
72356843Sdim    unsigned read_ok : 1;
73356843Sdim    unsigned readwrite_ok : 1;
74356843Sdim#if (__ARM_CMSE_SECURE_MODE)
75356843Sdim    unsigned nonsecure_read_ok : 1;
76356843Sdim    unsigned nonsecure_readwrite_ok : 1;
77356843Sdim    unsigned secure : 1;
78356843Sdim    unsigned idau_region_valid : 1;
79356843Sdim    unsigned idau_region : 8;
80356843Sdim#else
81356843Sdim    unsigned : 12;
82356843Sdim#endif
83356843Sdim#endif /*__ARM_LITTLE_ENDIAN */
84356843Sdim  } flags;
85356843Sdim  unsigned value;
86356843Sdim} cmse_address_info_t;
87356843Sdim
88356843Sdimstatic cmse_address_info_t __attribute__((__always_inline__, __nodebug__))
89356843Sdimcmse_TT(void *__p) {
90356843Sdim  cmse_address_info_t __u;
91356843Sdim  __u.value = __builtin_arm_cmse_TT(__p);
92356843Sdim  return __u;
93356843Sdim}
94356843Sdimstatic cmse_address_info_t __attribute__((__always_inline__, __nodebug__))
95356843Sdimcmse_TTT(void *__p) {
96356843Sdim  cmse_address_info_t __u;
97356843Sdim  __u.value = __builtin_arm_cmse_TTT(__p);
98356843Sdim  return __u;
99356843Sdim}
100356843Sdim
101356843Sdim#if __ARM_CMSE_SECURE_MODE
102356843Sdimstatic cmse_address_info_t __attribute__((__always_inline__, __nodebug__))
103356843Sdimcmse_TTA(void *__p) {
104356843Sdim  cmse_address_info_t __u;
105356843Sdim  __u.value = __builtin_arm_cmse_TTA(__p);
106356843Sdim  return __u;
107356843Sdim}
108356843Sdimstatic cmse_address_info_t __attribute__((__always_inline__, __nodebug__))
109356843Sdimcmse_TTAT(void *__p) {
110356843Sdim  cmse_address_info_t __u;
111356843Sdim  __u.value = __builtin_arm_cmse_TTAT(__p);
112356843Sdim  return __u;
113356843Sdim}
114356843Sdim#endif
115356843Sdim
116356843Sdim#define cmse_TT_fptr(p) cmse_TT(__builtin_bit_cast(void *, (p)))
117356843Sdim#define cmse_TTT_fptr(p) cmse_TTT(__builtin_bit_cast(void *, (p)))
118356843Sdim
119356843Sdim#if __ARM_CMSE_SECURE_MODE
120356843Sdim#define cmse_TTA_fptr(p) cmse_TTA(__builtin_bit_cast(void *, (p)))
121356843Sdim#define cmse_TTAT_fptr(p) cmse_TTAT(__builtin_bit_cast(void *, (p)))
122356843Sdim#endif
123356843Sdim
124356843Sdimstatic void *__attribute__((__always_inline__))
125356843Sdimcmse_check_address_range(void *__pb, size_t __s, int __flags) {
126356843Sdim  uintptr_t __begin = (uintptr_t)__pb;
127356843Sdim  uintptr_t __end = __begin + __s - 1;
128356843Sdim
129356843Sdim  if (__end < __begin)
130356843Sdim    return NULL; /* wrap around check */
131356843Sdim
132356843Sdim  /* Check whether the range crosses a 32-bytes aligned address */
133356843Sdim  const int __single_check = (__begin ^ __end) < 0x20u;
134356843Sdim
135356843Sdim  /* execute the right variant of the TT instructions */
136356843Sdim  void *__pe = (void *)__end;
137356843Sdim  cmse_address_info_t __permb, __perme;
138356843Sdim  switch (__flags & (CMSE_MPU_UNPRIV | CMSE_MPU_NONSECURE)) {
139356843Sdim  case 0:
140356843Sdim    __permb = cmse_TT(__pb);
141356843Sdim    __perme = __single_check ? __permb : cmse_TT(__pe);
142356843Sdim    break;
143356843Sdim  case CMSE_MPU_UNPRIV:
144356843Sdim    __permb = cmse_TTT(__pb);
145356843Sdim    __perme = __single_check ? __permb : cmse_TTT(__pe);
146356843Sdim    break;
147356843Sdim#if __ARM_CMSE_SECURE_MODE
148356843Sdim  case CMSE_MPU_NONSECURE:
149356843Sdim    __permb = cmse_TTA(__pb);
150356843Sdim    __perme = __single_check ? __permb : cmse_TTA(__pe);
151356843Sdim    break;
152356843Sdim  case CMSE_MPU_UNPRIV | CMSE_MPU_NONSECURE:
153356843Sdim    __permb = cmse_TTAT(__pb);
154356843Sdim    __perme = __single_check ? __permb : cmse_TTAT(__pe);
155356843Sdim    break;
156356843Sdim#endif
157356843Sdim  /* if CMSE_NONSECURE is specified w/o __ARM_CMSE_SECURE_MODE */
158356843Sdim  default:
159356843Sdim    return NULL;
160356843Sdim  }
161356843Sdim
162356843Sdim  /* check that the range does not cross MPU, SAU, or IDAU region boundaries */
163356843Sdim  if (__permb.value != __perme.value)
164356843Sdim    return NULL;
165356843Sdim#if !(__ARM_CMSE_SECURE_MODE)
166356843Sdim  /* CMSE_AU_NONSECURE is only supported when __ARM_FEATURE_CMSE & 0x2 */
167356843Sdim  if (__flags & CMSE_AU_NONSECURE)
168356843Sdim    return NULL;
169356843Sdim#endif
170356843Sdim
171356843Sdim  /* check the permission on the range */
172356843Sdim  switch (__flags & ~(CMSE_MPU_UNPRIV | CMSE_MPU_NONSECURE)) {
173356843Sdim#if (__ARM_CMSE_SECURE_MODE)
174356843Sdim  case CMSE_MPU_READ | CMSE_MPU_READWRITE | CMSE_AU_NONSECURE:
175356843Sdim  case CMSE_MPU_READWRITE | CMSE_AU_NONSECURE:
176356843Sdim    return __permb.flags.nonsecure_readwrite_ok ? __pb : NULL;
177356843Sdim
178356843Sdim  case CMSE_MPU_READ | CMSE_AU_NONSECURE:
179356843Sdim    return __permb.flags.nonsecure_read_ok ? __pb : NULL;
180356843Sdim
181356843Sdim  case CMSE_AU_NONSECURE:
182356843Sdim    return __permb.flags.secure ? NULL : __pb;
183356843Sdim#endif
184356843Sdim  case CMSE_MPU_READ | CMSE_MPU_READWRITE:
185356843Sdim  case CMSE_MPU_READWRITE:
186356843Sdim    return __permb.flags.readwrite_ok ? __pb : NULL;
187356843Sdim
188356843Sdim  case CMSE_MPU_READ:
189356843Sdim    return __permb.flags.read_ok ? __pb : NULL;
190356843Sdim
191356843Sdim  default:
192356843Sdim    return NULL;
193356843Sdim  }
194356843Sdim}
195356843Sdim
196356843Sdim#if __ARM_CMSE_SECURE_MODE
197356843Sdimstatic int __attribute__((__always_inline__, __nodebug__))
198356843Sdimcmse_nonsecure_caller(void) {
199356843Sdim  return !((uintptr_t)__builtin_return_address(0) & 1);
200356843Sdim}
201356843Sdim
202356843Sdim#define cmse_nsfptr_create(p)                                                  \
203356843Sdim  __builtin_bit_cast(__typeof__(p),                                            \
204356843Sdim                     (__builtin_bit_cast(uintptr_t, p) & ~(uintptr_t)1))
205356843Sdim
206356843Sdim#define cmse_is_nsfptr(p) ((__builtin_bit_cast(uintptr_t, p) & 1) == 0)
207356843Sdim
208356843Sdim#endif /* __ARM_CMSE_SECURE_MODE */
209356843Sdim
210356843Sdimvoid __attribute__((__noreturn__)) cmse_abort(void);
211356843Sdim#if defined(__cplusplus)
212356843Sdim}
213356843Sdim#endif
214356843Sdim
215356843Sdim#endif /* (__ARM_FEATURE_CMSE & 0x1) */
216356843Sdim
217356843Sdim#endif /* __ARM_CMSE_H */
218