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
24extern u32 __dtb_test_fdt_base_begin;
25extern u32 __dtb_test_fdt_overlay_begin;
26extern u32 __dtb_test_fdt_overlay_stacked_begin;
27
28static void *fdt;
29
30static 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
51static 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
57static 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
72static 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}
82OVERLAY_TEST(fdt_overlay_change_int_property, 0);
83
84static 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}
94OVERLAY_TEST(fdt_overlay_change_str_property, 0);
95
96static 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}
106OVERLAY_TEST(fdt_overlay_add_str_property, 0);
107
108static 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}
119OVERLAY_TEST(fdt_overlay_add_node_by_phandle, 0);
120
121static 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}
132OVERLAY_TEST(fdt_overlay_add_node_by_path, 0);
133
134static 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}
146OVERLAY_TEST(fdt_overlay_add_subnode_property, 0);
147
148static 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}
170OVERLAY_TEST(fdt_overlay_local_phandle, 0);
171
172static 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}
200OVERLAY_TEST(fdt_overlay_local_phandles, 0);
201
202static 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}
212OVERLAY_TEST(fdt_overlay_stacked, 0);
213
214int 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);
279err3:
280	free(fdt_overlay_copy);
281err2:
282	free(fdt);
283err1:
284	return ret;
285}
286