1 /*
2  * Copyright (c) 2021, Commonwealth Scientific and Industrial Research
3  * Organisation (CSIRO) ABN 41 687 119 230.
4  *
5  * SPDX-License-Identifier: Apache-2.0
6  *
7  * Generate memory regions from devicetree nodes.
8  */
9 
10 #ifndef ZEPHYR_INCLUDE_LINKER_DEVICETREE_REGIONS_H_
11 #define ZEPHYR_INCLUDE_LINKER_DEVICETREE_REGIONS_H_
12 
13 #include <zephyr/devicetree.h>
14 #include <zephyr/sys/util.h>
15 #include <zephyr/toolchain.h>
16 
17 /**
18  * @brief Get the linker memory-region name in a token form
19  *
20  * This attempts to use the zephyr,memory-region property (with
21  * non-alphanumeric characters replaced with underscores) returning a token.
22  *
23  * Example devicetree fragment:
24  *
25  * @code{.dts}
26  *     / {
27  *             soc {
28  *                     sram1: memory@2000000 {
29  *                         zephyr,memory-region = "MY_NAME";
30  *                     };
31  *                     sram2: memory@2001000 {
32  *                         zephyr,memory-region = "MY@OTHER@NAME";
33  *                     };
34  *             };
35  *     };
36  * @endcode
37  *
38  * Example usage:
39  *
40  * @code{.c}
41  *    LINKER_DT_NODE_REGION_NAME_TOKEN(DT_NODELABEL(sram1)) // MY_NAME
42  *    LINKER_DT_NODE_REGION_NAME_TOKEN(DT_NODELABEL(sram2)) // MY_OTHER_NAME
43  * @endcode
44  *
45  * @param node_id node identifier
46  * @return the name of the memory memory region the node will generate
47  */
48 #define LINKER_DT_NODE_REGION_NAME_TOKEN(node_id) \
49 	DT_STRING_TOKEN(node_id, zephyr_memory_region)
50 
51 /**
52  * @brief Get the linker memory-region name
53  *
54  * This attempts to use the zephyr,memory-region property (with
55  * non-alphanumeric characters replaced with underscores).
56  *
57  * Example devicetree fragment:
58  *
59  * @code{.dts}
60  *     / {
61  *             soc {
62  *                     sram1: memory@2000000 {
63  *                         zephyr,memory-region = "MY_NAME";
64  *                     };
65  *                     sram2: memory@2001000 {
66  *                         zephyr,memory-region = "MY@OTHER@NAME";
67  *                     };
68  *             };
69  *     };
70  * @endcode
71  *
72  * Example usage:
73  *
74  * @code{.c}
75  *    LINKER_DT_NODE_REGION_NAME(DT_NODELABEL(sram1)) // "MY_NAME"
76  *    LINKER_DT_NODE_REGION_NAME(DT_NODELABEL(sram2)) // "MY_OTHER_NAME"
77  * @endcode
78  *
79  * @param node_id node identifier
80  * @return the name of the memory memory region the node will generate
81  */
82 #define LINKER_DT_NODE_REGION_NAME(node_id) \
83 	STRINGIFY(LINKER_DT_NODE_REGION_NAME_TOKEN(node_id))
84 
85 #define _DT_MEMORY_REGION_FLAGS_TOKEN(n)    DT_STRING_TOKEN(n, zephyr_memory_region_flags)
86 #define _DT_MEMORY_REGION_FLAGS_UNQUOTED(n) DT_STRING_UNQUOTED(n, zephyr_memory_region_flags)
87 
88 #define _LINKER_L_PAREN (
89 #define _LINKER_R_PAREN )
90 #define _LINKER_ENCLOSE_PAREN(x) _LINKER_L_PAREN x _LINKER_R_PAREN
91 
92 #define _LINKER_IS_EMPTY_TOKEN_          1
93 #define _LINKER_IS_EMPTY_TOKEN_EXPAND(x) _LINKER_IS_EMPTY_TOKEN_##x
94 #define _LINKER_IS_EMPTY_TOKEN(x)        _LINKER_IS_EMPTY_TOKEN_EXPAND(x)
95 
96 /**
97  * @brief Get the linker memory-region flags with parentheses.
98  *
99  * This attempts to return the zephyr,memory-region-flags property
100  * with parentheses.
101  * Return empty string if not set the property.
102  *
103  * Example devicetree fragment:
104  *
105  * @code{.dts}
106  *     / {
107  *             soc {
108  *                     rx: memory@2000000 {
109  *                             zephyr,memory-region = "READ_EXEC";
110  *                             zephyr,memory-region-flags = "rx";
111  *                     };
112  *                     rx_not_w: memory@2001000 {
113  *                             zephyr,memory-region = "READ_EXEC_NOT_WRITE";
114  *                             zephyr,memory-region-flags = "rx!w";
115  *                     };
116  *                     no_flags: memory@2001000 {
117  *                             zephyr,memory-region = "NO_FLAGS";
118  *                     };
119  *             };
120  *     };
121  * @endcode
122  *
123  * Example usage:
124  *
125  * @code{.c}
126  *    LINKER_DT_NODE_REGION_FLAGS(DT_NODELABEL(rx))       // (rx)
127  *    LINKER_DT_NODE_REGION_FLAGS(DT_NODELABEL(rx_not_w)) // (rx!w)
128  *    LINKER_DT_NODE_REGION_FLAGS(DT_NODELABEL(no_flags)) // [flags will not be specified]
129  * @endcode
130  *
131  * @param node_id node identifier
132  * @return the value of the memory region flag specified in the device tree
133  *         enclosed in parentheses.
134  */
135 
136 #define LINKER_DT_NODE_REGION_FLAGS(node_id)                                                       \
137 	COND_CODE_1(DT_NODE_HAS_PROP(node_id, zephyr_memory_region_flags),                         \
138 		    (COND_CODE_1(_LINKER_IS_EMPTY_TOKEN(_DT_MEMORY_REGION_FLAGS_TOKEN(node_id)),   \
139 				 (),                                                               \
140 				 (_LINKER_ENCLOSE_PAREN(                                           \
141 					_DT_MEMORY_REGION_FLAGS_UNQUOTED(node_id))                 \
142 				 ))),                                                              \
143 		    (_LINKER_ENCLOSE_PAREN(rw)))
144 
145 /** @cond INTERNAL_HIDDEN */
146 
147 #define _DT_COMPATIBLE	zephyr_memory_region
148 
149 #define _DT_SECTION_PREFIX(node_id)	UTIL_CAT(__, LINKER_DT_NODE_REGION_NAME_TOKEN(node_id))
150 #define _DT_SECTION_START(node_id)	UTIL_CAT(_DT_SECTION_PREFIX(node_id), _start)
151 #define _DT_SECTION_END(node_id)	UTIL_CAT(_DT_SECTION_PREFIX(node_id), _end)
152 #define _DT_SECTION_SIZE(node_id)	UTIL_CAT(_DT_SECTION_PREFIX(node_id), _size)
153 #define _DT_SECTION_LOAD(node_id)	UTIL_CAT(_DT_SECTION_PREFIX(node_id), _load_start)
154 
155 /**
156  * @brief Declare a memory region
157  *
158  * Example devicetree fragment:
159  *
160  * @code{.dts}
161  *    test_sram: sram@20010000 {
162  *        compatible = "zephyr,memory-region", "mmio-sram";
163  *        reg = < 0x20010000 0x1000 >;
164  *        zephyr,memory-region = "FOOBAR";
165  *        zephyr,memory-region-flags = "rw";
166  *    };
167  * @endcode
168  *
169  * will result in:
170  *
171  * @code{.unparsed}
172  *    FOOBAR (rw) : ORIGIN = (0x20010000), LENGTH = (0x1000)
173  * @endcode
174  *
175  * @param node_id devicetree node identifier
176  * @param attr region attributes
177  */
178 #define _REGION_DECLARE(node_id)                                                                   \
179 	LINKER_DT_NODE_REGION_NAME_TOKEN(node_id)                                                  \
180 	LINKER_DT_NODE_REGION_FLAGS(node_id)                                                       \
181 		: ORIGIN = DT_REG_ADDR(node_id),                                                   \
182 		  LENGTH = DT_REG_SIZE(node_id)
183 
184 /**
185  * @brief Declare a memory section from the device tree nodes with
186  *	  compatible 'zephyr,memory-region'
187  *
188  * Example devicetree fragment:
189  *
190  * @code{.dts}
191  *    test_sram: sram@20010000 {
192  *        compatible = "zephyr,memory-region", "mmio-sram";
193  *        reg = < 0x20010000 0x1000 >;
194  *        zephyr,memory-region = "FOOBAR";
195  *    };
196  * @endcode
197  *
198  * will result in:
199  *
200  * @code{.unparsed}
201  *    FOOBAR (NOLOAD) :
202  *    {
203  *        __FOOBAR_start = .;
204  *        KEEP(*(FOOBAR))
205  *        KEEP(*(FOOBAR.*))
206  *        __FOOBAR_end = .;
207  *    } > FOOBAR
208  *    __FOOBAR_size = __FOOBAR_end - __FOOBAR_start;
209  *    __FOOBAR_load_start = LOADADDR(FOOBAR);
210  * @endcode
211  *
212  * @param node_id devicetree node identifier
213  */
214 #define _SECTION_DECLARE(node_id)								\
215 	LINKER_DT_NODE_REGION_NAME_TOKEN(node_id) (NOLOAD) :					\
216 	{											\
217 		_DT_SECTION_START(node_id) = .;							\
218 		KEEP(*(LINKER_DT_NODE_REGION_NAME_TOKEN(node_id)))				\
219 		KEEP(*(LINKER_DT_NODE_REGION_NAME_TOKEN(node_id).*))				\
220 		_DT_SECTION_END(node_id) = .;							\
221 	} > LINKER_DT_NODE_REGION_NAME_TOKEN(node_id)						\
222 	_DT_SECTION_SIZE(node_id) = _DT_SECTION_END(node_id) - _DT_SECTION_START(node_id);	\
223 	_DT_SECTION_LOAD(node_id) = LOADADDR(LINKER_DT_NODE_REGION_NAME_TOKEN(node_id));
224 
225 /** @endcond */
226 
227 /**
228  * @brief Generate linker memory regions from the device tree nodes with
229  *        compatible 'zephyr,memory-region'
230  *
231  * Note: for now we do not deal with MEMORY attributes since those are
232  * optional, not actually used by Zephyr and they will likely conflict with the
233  * MPU configuration.
234  */
235 #define LINKER_DT_REGIONS() \
236 	DT_FOREACH_STATUS_OKAY(_DT_COMPATIBLE, _REGION_DECLARE)
237 
238 /**
239  * @brief Generate linker memory sections from the device tree nodes with
240  *        compatible 'zephyr,memory-region'
241  */
242 #define LINKER_DT_SECTIONS() \
243 	DT_FOREACH_STATUS_OKAY(_DT_COMPATIBLE, _SECTION_DECLARE)
244 
245 #endif /* ZEPHYR_INCLUDE_LINKER_DEVICETREE_REGIONS_H_ */
246