1 /* SPDX-License-Identifier: BSD-2-Clause */
2 /*
3 * Copyright (c) 2022, Linaro Limited
4 */
5
6 #ifndef __MEMTAG_H
7 #define __MEMTAG_H
8
9 #include <assert.h>
10 #include <string.h>
11 #include <types_ext.h>
12 #include <util.h>
13
14 #if defined(CFG_MEMTAG) && defined(__aarch64__)
15 #define MEMTAG_IS_ENABLED 1
16 #define MEMTAG_TAG_SHIFT 56
17 #define MEMTAG_TAG_WIDTH 4
18 #define MEMTAG_TAG_MASK (BIT(MEMTAG_TAG_WIDTH) - 1)
19
20 #define MEMTAG_GRANULE_SIZE 16
21 #else
22 #define MEMTAG_IS_ENABLED 0
23 #define MEMTAG_GRANULE_SIZE 1
24 #define MEMTAG_TAG_WIDTH 0
25 #endif
26
27 #define MEMTAG_GRANULE_MASK (MEMTAG_GRANULE_SIZE - 1)
28
29 struct __memtag_ops {
30 void *(*set_tags)(void *addr, size_t size, uint8_t tag);
31 void *(*set_random_tags)(void *addr, size_t size);
32 void (*clear_mem)(void *addr, size_t size);
33 uint8_t (*read_tag)(const void *addr);
34 };
35
36 extern const struct __memtag_ops __memtag_ops_disabled;
37 extern const struct __memtag_ops *__memtag_ops;
38
__memtag_disabled_set_tags(void * addr,size_t size __unused,uint8_t tag __unused)39 static inline void *__memtag_disabled_set_tags(void *addr, size_t size __unused,
40 uint8_t tag __unused)
41 {
42 return addr;
43 }
44
__memtag_disabled_set_random_tags(void * addr,size_t size __unused)45 static inline void *__memtag_disabled_set_random_tags(void *addr,
46 size_t size __unused)
47 {
48 return addr;
49 }
50
__memtag_disabled_clear_mem(void * addr,size_t size)51 static inline void __memtag_disabled_clear_mem(void *addr, size_t size)
52 {
53 memset(addr, 0, size);
54 }
55
__memtag_disabled_read_tag(const void * addr __unused)56 static inline uint8_t __memtag_disabled_read_tag(const void *addr __unused)
57 {
58 return 0;
59 }
60
61 /*
62 * memtag_set_tags() - Tag a memory range
63 * @addr: Start of memory range
64 * @size: Size of memory range
65 * @tag: Tag to use
66 *
67 * The memory range is updated with the supplied tag. An eventual tag
68 * already present in the upper bits of the address in @addr is ignored.
69 *
70 * @addr and @size must be aligned/multiple of MEMTAG_GRANULE_SIZE.
71 *
72 * Returns an address with the new tag inserted to be used to access this
73 * memory area.
74 */
memtag_set_tags(void * addr,size_t size,uint8_t tag)75 static inline void *memtag_set_tags(void *addr, size_t size, uint8_t tag)
76 {
77 #if MEMTAG_IS_ENABLED
78 return __memtag_ops->set_tags(addr, size, tag);
79 #else
80 return __memtag_disabled_set_tags(addr, size, tag);
81 #endif
82 }
83
84 /*
85 * memtag_set_random_tags() - Tag a memory range with a random tag
86 * @addr: Start of memory range
87 * @size: Size of memory range
88 *
89 * The memory range is updated with a randomly generated tag. An eventual
90 * tag already present in the upper bits of the address in @addr is
91 * ignored.
92 *
93 * @addr and @size must be aligned/multiple of MEMTAG_GRANULE_SIZE.
94 *
95 * Returns an address with the new tag inserted to be used to access this
96 * memory area.
97 */
memtag_set_random_tags(void * addr,size_t size)98 static inline void *memtag_set_random_tags(void *addr, size_t size)
99 {
100 #if MEMTAG_IS_ENABLED
101 return __memtag_ops->set_random_tags(addr, size);
102 #else
103 return __memtag_disabled_set_random_tags(addr, size);
104 #endif
105 }
106
memtag_clear_mem(void * addr,size_t size)107 static inline void memtag_clear_mem(void *addr, size_t size)
108 {
109 #if MEMTAG_IS_ENABLED
110 __memtag_ops->clear_mem(addr, size);
111 #else
112 __memtag_disabled_clear_mem(addr, size);
113 #endif
114 }
115
116 /*
117 * memtag_strip_tag_vaddr() - Removes an eventual tag from an address
118 * @addr: Address to strip
119 *
120 * Returns a vaddr_t without an eventual tag.
121 */
memtag_strip_tag_vaddr(const void * addr)122 static inline vaddr_t memtag_strip_tag_vaddr(const void *addr)
123 {
124 vaddr_t va = (vaddr_t)addr;
125
126 #if MEMTAG_IS_ENABLED
127 va &= ~SHIFT_U64(MEMTAG_TAG_MASK, MEMTAG_TAG_SHIFT);
128 #endif
129
130 return va;
131 }
132
133 /*
134 * memtag_strip_tag_const() - Removes an eventual tag from an address
135 * @addr: Address to strip
136 *
137 * Returns the address without an eventual tag.
138 */
memtag_strip_tag_const(const void * addr)139 static inline const void *memtag_strip_tag_const(const void *addr)
140 {
141 return (const void *)memtag_strip_tag_vaddr(addr);
142 }
143
144 /*
145 * memtag_strip_tag() - Removes an eventual tag from an address
146 * @addr: Address to strip
147 *
148 * Returns the address without an eventual tag.
149 */
memtag_strip_tag(void * addr)150 static inline void *memtag_strip_tag(void *addr)
151 {
152 return (void *)memtag_strip_tag_vaddr(addr);
153 }
154
155 /*
156 * memtag_insert_tag_vaddr() - Inserts a tag into an address
157 * @addr: Address to transform
158 * @tag: Tag to insert
159 *
160 * Returns the address with the new tag inserted.
161 */
memtag_insert_tag_vaddr(vaddr_t addr,uint8_t tag __maybe_unused)162 static inline vaddr_t memtag_insert_tag_vaddr(vaddr_t addr,
163 uint8_t tag __maybe_unused)
164 {
165 vaddr_t va = memtag_strip_tag_vaddr((void *)addr);
166
167 #if MEMTAG_IS_ENABLED
168 va |= SHIFT_U64(tag, MEMTAG_TAG_SHIFT);
169 #endif
170
171 return va;
172 }
173
174 /*
175 * memtag_insert_tag() - Inserts a tag into an address
176 * @addr: Address to transform
177 * @tag: Tag to insert
178 *
179 * Returns the address with the new tag inserted.
180 */
memtag_insert_tag(void * addr,uint8_t tag)181 static inline void *memtag_insert_tag(void *addr, uint8_t tag)
182 {
183 return (void *)memtag_insert_tag_vaddr((vaddr_t)addr, tag);
184 }
185
186 /*
187 * memtag_get_tag() - Extract a tag from an address
188 * @addr: Address with an eventual tag
189 *
190 * Returns the extracted tag.
191 */
memtag_get_tag(const void * addr __maybe_unused)192 static inline uint8_t memtag_get_tag(const void *addr __maybe_unused)
193 {
194 #if MEMTAG_IS_ENABLED
195 uint64_t va = (vaddr_t)addr;
196
197 return (va >> MEMTAG_TAG_SHIFT) & MEMTAG_TAG_MASK;
198 #else
199 return 0;
200 #endif
201 }
202
memtag_read_tag(const void * addr)203 static inline uint8_t memtag_read_tag(const void *addr)
204 {
205 #if MEMTAG_IS_ENABLED
206 return __memtag_ops->read_tag(addr);
207 #else
208 return __memtag_disabled_read_tag(addr);
209 #endif
210 }
211
memtag_assert_tag(const void * addr __maybe_unused)212 static inline void memtag_assert_tag(const void *addr __maybe_unused)
213 {
214 assert(memtag_get_tag(addr) == memtag_read_tag(addr));
215 }
216
217 #if MEMTAG_IS_ENABLED
218 void memtag_init_ops(unsigned int memtag_impl);
219 #else
memtag_init_ops(unsigned int memtag_impl __unused)220 static inline void memtag_init_ops(unsigned int memtag_impl __unused)
221 {
222 }
223 #endif
224
memtag_is_enabled(void)225 static inline bool memtag_is_enabled(void)
226 {
227 #if MEMTAG_IS_ENABLED
228 return __memtag_ops != &__memtag_ops_disabled;
229 #else
230 return false;
231 #endif
232 }
233
234 #endif /*__MEMTAG_H*/
235