aboutsummaryrefslogtreecommitdiff
path: root/clang/lib/Headers/arm_cmse.h
blob: ecf50ecc5c8e03b5f383b9d00fa4e7cc2b3eade5 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
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 */