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