1// SPDX-License-Identifier: GPL-2.0+
2/*
3 * Some very basic tests for fdtdec, accessed through test_fdtdec command.
4 * They are easiest to use with sandbox.
5 *
6 * Copyright (c) 2011 The Chromium OS Authors.
7 */
8
9#include <command.h>
10#include <fdtdec.h>
11#include <linux/libfdt.h>
12#include <malloc.h>
13#include <os.h>
14
15/* The size of our test fdt blob */
16#define FDT_SIZE	(16 * 1024)
17
18#define CHECK(op) ({							\
19		int err = op;						\
20		if (err < 0) {						\
21			printf("%s: %s: %s\n", __func__, #op,		\
22			       fdt_strerror(err));			\
23			return err;					\
24		}							\
25									\
26		err;							\
27	})
28
29#define CHECKVAL(op, expected) ({					\
30		int err = op;						\
31		if (err != expected) {					\
32			printf("%s: %s: expected %d, but returned %d\n",\
33			       __func__, #op, expected, err);		\
34			return err;					\
35		}							\
36									\
37		err;							\
38	})
39
40#define CHECKOK(op)	CHECKVAL(op, 0)
41
42/* maximum number of nodes / aliases to generate */
43#define MAX_NODES	20
44
45/*
46 * Make a test fdt
47 *
48 * @param fdt		Device tree pointer
49 * @param size		Size of device tree blob
50 * @param aliases	Specifies alias assignments. Format is a list of items
51 *			separated by space. Items are #a where
52 *				# is the alias number
53 *				a is the node to point to
54 * @param nodes		Specifies nodes to generate (a=0, b=1), upper case
55 *			means to create a disabled node
56 */
57static int make_fdt(void *fdt, int size, const char *aliases,
58		    const char *nodes)
59{
60	char name[20], value[20];
61	const char *s;
62#if defined(DEBUG) && defined(CONFIG_SANDBOX)
63	int fd;
64#endif
65
66	CHECK(fdt_create(fdt, size));
67	CHECK(fdt_finish_reservemap(fdt));
68	CHECK(fdt_begin_node(fdt, ""));
69
70	CHECK(fdt_begin_node(fdt, "aliases"));
71	for (s = aliases; *s;) {
72		sprintf(name, "i2c%c", *s);
73		sprintf(value, "/i2c%d@0", s[1] - 'a');
74		CHECK(fdt_property_string(fdt, name, value));
75		s += 2 + (s[2] != '\0');
76	}
77	CHECK(fdt_end_node(fdt));
78
79	for (s = nodes; *s; s++) {
80		sprintf(value, "i2c%d@0", (*s & 0xdf) - 'A');
81		CHECK(fdt_begin_node(fdt, value));
82		CHECK(fdt_property_string(fdt, "compatible",
83			fdtdec_get_compatible(COMPAT_UNKNOWN)));
84		if (*s <= 'Z')
85			CHECK(fdt_property_string(fdt, "status", "disabled"));
86		CHECK(fdt_end_node(fdt));
87	}
88
89	CHECK(fdt_end_node(fdt));
90	CHECK(fdt_finish(fdt));
91	CHECK(fdt_pack(fdt));
92#if defined(DEBUG) && defined(CONFIG_SANDBOX)
93	fd = os_open("/tmp/fdtdec-text.dtb", OS_O_CREAT | OS_O_WRONLY);
94	if (fd == -1) {
95		printf("Could not open .dtb file to write\n");
96		return -1;
97	}
98	os_write(fd, fdt, size);
99	os_close(fd);
100#endif
101	return 0;
102}
103
104static int run_test(const char *aliases, const char *nodes, const char *expect)
105{
106	int list[MAX_NODES];
107	const char *s;
108	void *blob;
109	int i;
110
111	blob = malloc(FDT_SIZE);
112	if (!blob) {
113		printf("%s: out of memory\n", __func__);
114		return 1;
115	}
116
117	printf("aliases=%s, nodes=%s, expect=%s: ", aliases, nodes, expect);
118	CHECKVAL(make_fdt(blob, FDT_SIZE, aliases, nodes), 0);
119	CHECKVAL(fdtdec_find_aliases_for_id(blob, "i2c",
120			COMPAT_UNKNOWN,
121			list, ARRAY_SIZE(list)), (int)strlen(expect));
122
123	/* Check we got the right ones */
124	for (i = 0, s = expect; *s; s++, i++) {
125		int want = *s;
126		const char *name;
127		int got = ' ';
128
129		name = list[i] ? fdt_get_name(blob, list[i], NULL) : NULL;
130		if (name)
131			got = name[3] + 'a' - '0';
132
133		if (got != want) {
134			printf("Position %d: Expected '%c', got '%c' ('%s')\n",
135			       i, want, got, name);
136			return 1;
137		}
138	}
139
140	printf("pass\n");
141	free(blob);
142	return 0;
143}
144
145static int make_fdt_carveout_device(void *fdt, uint32_t na, uint32_t ns)
146{
147	const char *basename = "/display";
148	struct fdt_memory carveout = {
149#ifdef CONFIG_PHYS_64BIT
150		.start = 0x180000000,
151		.end = 0x18fffffff,
152#else
153		.start = 0x80000000,
154		.end = 0x8fffffff,
155#endif
156	};
157	fdt32_t cells[4], *ptr = cells;
158	uint32_t upper, lower;
159	fdt_size_t size;
160	char name[32];
161	int offset;
162
163	/* store one or two address cells */
164	upper = upper_32_bits(carveout.start);
165	lower = lower_32_bits(carveout.start);
166
167	if (na > 1 && upper > 0)
168		snprintf(name, sizeof(name), "%s@%x,%x", basename, upper,
169			 lower);
170	else
171		snprintf(name, sizeof(name), "%s@%x", basename, lower);
172
173	if (na > 1)
174		*ptr++ = cpu_to_fdt32(upper);
175
176	*ptr++ = cpu_to_fdt32(lower);
177
178	/* store one or two size cells */
179	size = carveout.end - carveout.start + 1;
180	upper = upper_32_bits(size);
181	lower = lower_32_bits(size);
182
183	if (ns > 1)
184		*ptr++ = cpu_to_fdt32(upper);
185
186	*ptr++ = cpu_to_fdt32(lower);
187
188	offset = CHECK(fdt_add_subnode(fdt, 0, name + 1));
189	CHECK(fdt_setprop(fdt, offset, "reg", cells, (na + ns) * sizeof(*cells)));
190
191	return fdtdec_set_carveout(fdt, name, "memory-region", 0, &carveout,
192				   "framebuffer", NULL, 0, 0);
193}
194
195static int check_fdt_carveout(void *fdt, uint32_t address_cells,
196			      uint32_t size_cells)
197{
198#ifdef CONFIG_PHYS_64BIT
199	const char *name = "/display@1,80000000";
200	const struct fdt_memory expected = {
201		.start = 0x180000000,
202		.end = 0x18fffffff,
203	};
204#else
205	const char *name = "/display@80000000";
206	const struct fdt_memory expected = {
207		.start = 0x80000000,
208		.end = 0x8fffffff,
209	};
210#endif
211	struct fdt_memory carveout;
212
213	printf("carveout: %pap-%pap na=%u ns=%u: ", &expected.start,
214	       &expected.end, address_cells, size_cells);
215
216	CHECK(fdtdec_get_carveout(fdt, name, "memory-region", 0, &carveout,
217				  NULL, NULL, NULL, NULL));
218
219	if ((carveout.start != expected.start) ||
220	    (carveout.end != expected.end)) {
221		printf("carveout: %pap-%pap, expected %pap-%pap\n",
222		       &carveout.start, &carveout.end,
223		       &expected.start, &expected.end);
224		return 1;
225	}
226
227	printf("pass\n");
228	return 0;
229}
230
231static int make_fdt_carveout(void *fdt, int size, uint32_t address_cells,
232			     uint32_t size_cells)
233{
234	fdt32_t na = cpu_to_fdt32(address_cells);
235	fdt32_t ns = cpu_to_fdt32(size_cells);
236#if defined(DEBUG) && defined(CONFIG_SANDBOX)
237	char filename[512];
238	int fd;
239#endif
240	int err;
241
242	CHECK(fdt_create(fdt, size));
243	CHECK(fdt_finish_reservemap(fdt));
244	CHECK(fdt_begin_node(fdt, ""));
245	CHECK(fdt_property(fdt, "#address-cells", &na, sizeof(na)));
246	CHECK(fdt_property(fdt, "#size-cells", &ns, sizeof(ns)));
247	CHECK(fdt_end_node(fdt));
248	CHECK(fdt_finish(fdt));
249	CHECK(fdt_pack(fdt));
250
251	CHECK(fdt_open_into(fdt, fdt, FDT_SIZE));
252
253	err = make_fdt_carveout_device(fdt, address_cells, size_cells);
254
255#if defined(DEBUG) && defined(CONFIG_SANDBOX)
256	snprintf(filename, sizeof(filename), "/tmp/fdtdec-carveout-%u-%u.dtb",
257		 address_cells, size_cells);
258
259	fd = os_open(filename, OS_O_CREAT | OS_O_WRONLY);
260	if (fd < 0) {
261		printf("could not open .dtb file to write\n");
262		goto out;
263	}
264
265	os_write(fd, fdt, size);
266	os_close(fd);
267
268out:
269#endif
270	return err;
271}
272
273static int check_carveout(void)
274{
275	void *fdt;
276
277	fdt = malloc(FDT_SIZE);
278	if (!fdt) {
279		printf("%s: out of memory\n", __func__);
280		return 1;
281	}
282
283#ifndef CONFIG_PHYS_64BIT
284	CHECKVAL(make_fdt_carveout(fdt, FDT_SIZE, 1, 1), 0);
285	CHECKOK(check_fdt_carveout(fdt, 1, 1));
286	CHECKVAL(make_fdt_carveout(fdt, FDT_SIZE, 1, 2), 0);
287	CHECKOK(check_fdt_carveout(fdt, 1, 2));
288#else
289	CHECKVAL(make_fdt_carveout(fdt, FDT_SIZE, 1, 1), -FDT_ERR_BADVALUE);
290	CHECKVAL(make_fdt_carveout(fdt, FDT_SIZE, 1, 2), -FDT_ERR_BADVALUE);
291#endif
292	CHECKVAL(make_fdt_carveout(fdt, FDT_SIZE, 2, 1), 0);
293	CHECKOK(check_fdt_carveout(fdt, 2, 1));
294	CHECKVAL(make_fdt_carveout(fdt, FDT_SIZE, 2, 2), 0);
295	CHECKOK(check_fdt_carveout(fdt, 2, 2));
296
297	free(fdt);
298	return 0;
299}
300
301static int do_test_fdtdec(struct cmd_tbl *cmdtp, int flag, int argc,
302			  char *const argv[])
303{
304	/* basic tests */
305	CHECKOK(run_test("", "", ""));
306	CHECKOK(run_test("1e 3d", "", ""));
307
308	/*
309	 * 'a' represents 0, 'b' represents 1, etc.
310	 * The first character is the alias number, the second is the node
311	 * number. So the params mean:
312	 * 0a 1b	: point alias 0 to node 0 (a), alias 1 to node 1(b)
313	 * ab		: to create nodes 0 and 1 (a and b)
314	 * ab		: we expect the function to return two nodes, in
315	 *		  the order 0, 1
316	 */
317	CHECKOK(run_test("0a 1b", "ab", "ab"));
318
319	CHECKOK(run_test("0a 1c", "ab", "ab"));
320	CHECKOK(run_test("1c", "ab", "ab"));
321	CHECKOK(run_test("1b", "ab", "ab"));
322	CHECKOK(run_test("0b", "ab", "ba"));
323	CHECKOK(run_test("0b 2d", "dbc", "bcd"));
324	CHECKOK(run_test("0d 3a 1c 2b", "dbac", "dcba"));
325
326	/* things with holes */
327	CHECKOK(run_test("1b 3d", "dbc", "cb d"));
328	CHECKOK(run_test("1e 3d", "dbc", "bc d"));
329
330	/* no aliases */
331	CHECKOK(run_test("", "dbac", "dbac"));
332
333	/* disabled nodes */
334	CHECKOK(run_test("0d 3a 1c 2b", "dBac", "dc a"));
335	CHECKOK(run_test("0b 2d", "DBc", "c"));
336	CHECKOK(run_test("0b 4d 2c", "DBc", "  c"));
337
338	/* conflicting aliases - first one gets it */
339	CHECKOK(run_test("2a 1a 0a", "a", "  a"));
340	CHECKOK(run_test("0a 1a 2a", "a", "a"));
341
342	CHECKOK(check_carveout());
343
344	printf("Test passed\n");
345	return 0;
346}
347
348U_BOOT_CMD(
349	test_fdtdec, 3, 1, do_test_fdtdec,
350	"test_fdtdec",
351	"Run tests for fdtdec library");
352