1 // SPDX-License-Identifier: GPL-2.0+
2 /*
3  * Copyright (c) 2016 NextThing Co
4  * Copyright (c) 2016 Free Electrons
5  */
6 
7 #include <common.h>
8 #include <command.h>
9 #include <errno.h>
10 #include <fdt_support.h>
11 #include <image.h>
12 #include <log.h>
13 #include <malloc.h>
14 
15 #include <linux/sizes.h>
16 
17 #include <test/ut.h>
18 #include <test/overlay.h>
19 #include <test/suites.h>
20 
21 /* 4k ought to be enough for anybody */
22 #define FDT_COPY_SIZE	(4 * SZ_1K)
23 
24 extern u32 __dtb_test_fdt_base_begin;
25 extern u32 __dtb_test_fdt_overlay_begin;
26 extern u32 __dtb_test_fdt_overlay_stacked_begin;
27 
28 static void *fdt;
29 
ut_fdt_getprop_u32_by_index(void * fdt,const char * path,const char * name,int index,u32 * out)30 static int ut_fdt_getprop_u32_by_index(void *fdt, const char *path,
31 				    const char *name, int index,
32 				    u32 *out)
33 {
34 	const fdt32_t *val;
35 	int node_off;
36 	int len;
37 
38 	node_off = fdt_path_offset(fdt, path);
39 	if (node_off < 0)
40 		return node_off;
41 
42 	val = fdt_getprop(fdt, node_off, name, &len);
43 	if (!val || (len < (sizeof(uint32_t) * (index + 1))))
44 		return -FDT_ERR_NOTFOUND;
45 
46 	*out = fdt32_to_cpu(*(val + index));
47 
48 	return 0;
49 }
50 
ut_fdt_getprop_u32(void * fdt,const char * path,const char * name,u32 * out)51 static int ut_fdt_getprop_u32(void *fdt, const char *path, const char *name,
52 			   u32 *out)
53 {
54 	return ut_fdt_getprop_u32_by_index(fdt, path, name, 0, out);
55 }
56 
fdt_getprop_str(void * fdt,const char * path,const char * name,const char ** out)57 static int fdt_getprop_str(void *fdt, const char *path, const char *name,
58 			   const char **out)
59 {
60 	int node_off;
61 	int len;
62 
63 	node_off = fdt_path_offset(fdt, path);
64 	if (node_off < 0)
65 		return node_off;
66 
67 	*out = fdt_stringlist_get(fdt, node_off, name, 0, &len);
68 
69 	return len < 0 ? len : 0;
70 }
71 
fdt_overlay_change_int_property(struct unit_test_state * uts)72 static int fdt_overlay_change_int_property(struct unit_test_state *uts)
73 {
74 	u32 val = 0;
75 
76 	ut_assertok(ut_fdt_getprop_u32(fdt, "/test-node", "test-int-property",
77 				    &val));
78 	ut_asserteq(43, val);
79 
80 	return CMD_RET_SUCCESS;
81 }
82 OVERLAY_TEST(fdt_overlay_change_int_property, 0);
83 
fdt_overlay_change_str_property(struct unit_test_state * uts)84 static int fdt_overlay_change_str_property(struct unit_test_state *uts)
85 {
86 	const char *val = NULL;
87 
88 	ut_assertok(fdt_getprop_str(fdt, "/test-node", "test-str-property",
89 				    &val));
90 	ut_asserteq_str("foobar", val);
91 
92 	return CMD_RET_SUCCESS;
93 }
94 OVERLAY_TEST(fdt_overlay_change_str_property, 0);
95 
fdt_overlay_add_str_property(struct unit_test_state * uts)96 static int fdt_overlay_add_str_property(struct unit_test_state *uts)
97 {
98 	const char *val = NULL;
99 
100 	ut_assertok(fdt_getprop_str(fdt, "/test-node", "test-str-property-2",
101 				    &val));
102 	ut_asserteq_str("foobar2", val);
103 
104 	return CMD_RET_SUCCESS;
105 }
106 OVERLAY_TEST(fdt_overlay_add_str_property, 0);
107 
fdt_overlay_add_node_by_phandle(struct unit_test_state * uts)108 static int fdt_overlay_add_node_by_phandle(struct unit_test_state *uts)
109 {
110 	int off;
111 
112 	off = fdt_path_offset(fdt, "/test-node/new-node");
113 	ut_assert(off >= 0);
114 
115 	ut_assertnonnull(fdt_getprop(fdt, off, "new-property", NULL));
116 
117 	return CMD_RET_SUCCESS;
118 }
119 OVERLAY_TEST(fdt_overlay_add_node_by_phandle, 0);
120 
fdt_overlay_add_node_by_path(struct unit_test_state * uts)121 static int fdt_overlay_add_node_by_path(struct unit_test_state *uts)
122 {
123 	int off;
124 
125 	off = fdt_path_offset(fdt, "/new-node");
126 	ut_assert(off >= 0);
127 
128 	ut_assertnonnull(fdt_getprop(fdt, off, "new-property", NULL));
129 
130 	return CMD_RET_SUCCESS;
131 }
132 OVERLAY_TEST(fdt_overlay_add_node_by_path, 0);
133 
fdt_overlay_add_subnode_property(struct unit_test_state * uts)134 static int fdt_overlay_add_subnode_property(struct unit_test_state *uts)
135 {
136 	int off;
137 
138 	off = fdt_path_offset(fdt, "/test-node/sub-test-node");
139 	ut_assert(off >= 0);
140 
141 	ut_assertnonnull(fdt_getprop(fdt, off, "sub-test-property", NULL));
142 	ut_assertnonnull(fdt_getprop(fdt, off, "new-sub-test-property", NULL));
143 
144 	return CMD_RET_SUCCESS;
145 }
146 OVERLAY_TEST(fdt_overlay_add_subnode_property, 0);
147 
fdt_overlay_local_phandle(struct unit_test_state * uts)148 static int fdt_overlay_local_phandle(struct unit_test_state *uts)
149 {
150 	uint32_t local_phandle;
151 	u32 val = 0;
152 	int off;
153 
154 	off = fdt_path_offset(fdt, "/new-local-node");
155 	ut_assert(off >= 0);
156 
157 	local_phandle = fdt_get_phandle(fdt, off);
158 	ut_assert(local_phandle);
159 
160 	ut_assertok(ut_fdt_getprop_u32_by_index(fdt, "/", "test-several-phandle",
161 					     0, &val));
162 	ut_asserteq(local_phandle, val);
163 
164 	ut_assertok(ut_fdt_getprop_u32_by_index(fdt, "/", "test-several-phandle",
165 					     1, &val));
166 	ut_asserteq(local_phandle, val);
167 
168 	return CMD_RET_SUCCESS;
169 }
170 OVERLAY_TEST(fdt_overlay_local_phandle, 0);
171 
fdt_overlay_local_phandles(struct unit_test_state * uts)172 static int fdt_overlay_local_phandles(struct unit_test_state *uts)
173 {
174 	uint32_t local_phandle, test_phandle;
175 	u32 val = 0;
176 	int off;
177 
178 	off = fdt_path_offset(fdt, "/new-local-node");
179 	ut_assert(off >= 0);
180 
181 	local_phandle = fdt_get_phandle(fdt, off);
182 	ut_assert(local_phandle);
183 
184 	off = fdt_path_offset(fdt, "/test-node");
185 	ut_assert(off >= 0);
186 
187 	test_phandle = fdt_get_phandle(fdt, off);
188 	ut_assert(test_phandle);
189 
190 	ut_assertok(ut_fdt_getprop_u32_by_index(fdt, "/", "test-phandle", 0,
191 					     &val));
192 	ut_asserteq(test_phandle, val);
193 
194 	ut_assertok(ut_fdt_getprop_u32_by_index(fdt, "/", "test-phandle", 1,
195 					     &val));
196 	ut_asserteq(local_phandle, val);
197 
198 	return CMD_RET_SUCCESS;
199 }
200 OVERLAY_TEST(fdt_overlay_local_phandles, 0);
201 
fdt_overlay_stacked(struct unit_test_state * uts)202 static int fdt_overlay_stacked(struct unit_test_state *uts)
203 {
204 	u32 val = 0;
205 
206 	ut_assertok(ut_fdt_getprop_u32(fdt, "/new-local-node",
207 				       "stacked-test-int-property", &val));
208 	ut_asserteq(43, val);
209 
210 	return CMD_RET_SUCCESS;
211 }
212 OVERLAY_TEST(fdt_overlay_stacked, 0);
213 
do_ut_overlay(struct cmd_tbl * cmdtp,int flag,int argc,char * const argv[])214 int do_ut_overlay(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[])
215 {
216 	struct unit_test *tests = UNIT_TEST_SUITE_START(overlay_test);
217 	const int n_ents = UNIT_TEST_SUITE_COUNT(overlay_test);
218 	struct unit_test_state *uts;
219 	void *fdt_base = &__dtb_test_fdt_base_begin;
220 	void *fdt_overlay = &__dtb_test_fdt_overlay_begin;
221 	void *fdt_overlay_stacked = &__dtb_test_fdt_overlay_stacked_begin;
222 	void *fdt_overlay_copy, *fdt_overlay_stacked_copy;
223 	int ret = -ENOMEM;
224 
225 	uts = calloc(1, sizeof(*uts));
226 	if (!uts)
227 		return -ENOMEM;
228 
229 	ut_assertok(fdt_check_header(fdt_base));
230 	ut_assertok(fdt_check_header(fdt_overlay));
231 
232 	fdt = malloc(FDT_COPY_SIZE);
233 	if (!fdt)
234 		goto err1;
235 
236 	fdt_overlay_copy = malloc(FDT_COPY_SIZE);
237 	if (!fdt_overlay_copy)
238 		goto err2;
239 
240 	fdt_overlay_stacked_copy = malloc(FDT_COPY_SIZE);
241 	if (!fdt_overlay_stacked_copy)
242 		goto err3;
243 
244 	/*
245 	 * Resize the FDT to 4k so that we have room to operate on
246 	 *
247 	 * (and relocate it since the memory might be mapped
248 	 * read-only)
249 	 */
250 	ut_assertok(fdt_open_into(fdt_base, fdt, FDT_COPY_SIZE));
251 
252 	/*
253 	 * Resize the overlay to 4k so that we have room to operate on
254 	 *
255 	 * (and relocate it since the memory might be mapped
256 	 * read-only)
257 	 */
258 	ut_assertok(fdt_open_into(fdt_overlay, fdt_overlay_copy,
259 				  FDT_COPY_SIZE));
260 
261 	/*
262 	 * Resize the stacked overlay to 4k so that we have room to operate on
263 	 *
264 	 * (and relocate it since the memory might be mapped
265 	 * read-only)
266 	 */
267 	ut_assertok(fdt_open_into(fdt_overlay_stacked, fdt_overlay_stacked_copy,
268 				  FDT_COPY_SIZE));
269 
270 	/* Apply the overlay */
271 	ut_assertok(fdt_overlay_apply(fdt, fdt_overlay_copy));
272 
273 	/* Apply the stacked overlay */
274 	ut_assertok(fdt_overlay_apply(fdt, fdt_overlay_stacked_copy));
275 
276 	ret = cmd_ut_category("overlay", "", tests, n_ents, argc, argv);
277 
278 	free(fdt_overlay_stacked_copy);
279 err3:
280 	free(fdt_overlay_copy);
281 err2:
282 	free(fdt);
283 err1:
284 	return ret;
285 }
286