xref: /freebsd/contrib/llvm-project/clang/lib/Headers/arm_cmse.h (revision 5b27928474e6a4103d65b347544705c40c9618fd)
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)
28 extern "C" {
29 #endif
30 
31 typedef 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 
88 static cmse_address_info_t __attribute__((__always_inline__, __nodebug__))
cmse_TT(void * __p)89 cmse_TT(void *__p) {
90   cmse_address_info_t __u;
91   __u.value = __builtin_arm_cmse_TT(__p);
92   return __u;
93 }
94 static cmse_address_info_t __attribute__((__always_inline__, __nodebug__))
cmse_TTT(void * __p)95 cmse_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
102 static cmse_address_info_t __attribute__((__always_inline__, __nodebug__))
cmse_TTA(void * __p)103 cmse_TTA(void *__p) {
104   cmse_address_info_t __u;
105   __u.value = __builtin_arm_cmse_TTA(__p);
106   return __u;
107 }
108 static cmse_address_info_t __attribute__((__always_inline__, __nodebug__))
cmse_TTAT(void * __p)109 cmse_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 
124 static void *__attribute__((__always_inline__))
cmse_check_address_range(void * __pb,size_t __s,int __flags)125 cmse_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
197 static int __attribute__((__always_inline__, __nodebug__))
cmse_nonsecure_caller(void)198 cmse_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 
210 void __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 */
218