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