1/*	$NetBSD: testutils.c,v 1.1.1.3 2019/12/22 12:34:07 skrll Exp $	*/
2
3// SPDX-License-Identifier: LGPL-2.1-or-later
4/*
5 * libfdt - Flat Device Tree manipulation
6 *	Testcase common utility functions
7 * Copyright (C) 2006 David Gibson, IBM Corporation.
8 */
9
10#define _GNU_SOURCE /* for strsignal() in glibc.  FreeBSD has it either way */
11
12#include <stdio.h>
13#include <stdlib.h>
14#include <stdint.h>
15#include <limits.h>
16#include <string.h>
17#include <errno.h>
18#include <signal.h>
19#include <unistd.h>
20#include <fcntl.h>
21
22#if NO_VALGRIND
23static inline void VALGRIND_MAKE_MEM_UNDEFINED(void *p, size_t len)
24{
25}
26
27static inline void VALGRIND_MAKE_MEM_DEFINED(void *p, size_t len)
28{
29}
30#else
31#include <valgrind/memcheck.h>
32#endif
33
34#include <libfdt.h>
35
36#include "tests.h"
37#include "testdata.h"
38
39/* For FDT_SW_MAGIC */
40#include "libfdt_internal.h"
41
42int verbose_test = 1;
43char *test_name;
44
45void  __attribute__((weak)) cleanup(void)
46{
47}
48
49static void sigint_handler(int signum, siginfo_t *si, void *uc)
50{
51	cleanup();
52	fprintf(stderr, "%s: %s (pid=%d)\n", test_name,
53		strsignal(signum), getpid());
54	exit(RC_BUG);
55}
56
57void test_init(int argc, char *argv[])
58{
59	int err;
60	struct sigaction sa_int = {
61		.sa_sigaction = sigint_handler,
62	};
63
64	test_name = argv[0];
65
66	err = sigaction(SIGINT, &sa_int, NULL);
67	if (err)
68		FAIL("Can't install SIGINT handler");
69
70	if (getenv("QUIET_TEST"))
71		verbose_test = 0;
72
73	verbose_printf("Starting testcase \"%s\", pid %d\n",
74		       test_name, getpid());
75}
76
77void check_mem_rsv(void *fdt, int n, uint64_t addr, uint64_t size)
78{
79	int err;
80	uint64_t addr_v, size_v;
81
82	err = fdt_get_mem_rsv(fdt, n, &addr_v, &size_v);
83	if (err < 0)
84		FAIL("fdt_get_mem_rsv(%d): %s", n, fdt_strerror(err));
85	if ((addr_v != addr) || (size_v != size))
86		FAIL("fdt_get_mem_rsv() returned (0x%llx,0x%llx) "
87		     "instead of (0x%llx,0x%llx)",
88		     (unsigned long long)addr_v, (unsigned long long)size_v,
89		     (unsigned long long)addr, (unsigned long long)size);
90}
91
92void check_property(void *fdt, int nodeoffset, const char *name,
93		    int len, const void *val)
94{
95	const struct fdt_property *prop;
96	int retlen, namelen;
97	uint32_t tag, nameoff, proplen;
98	const char *propname;
99
100	verbose_printf("Checking property \"%s\"...", name);
101	prop = fdt_get_property(fdt, nodeoffset, name, &retlen);
102	verbose_printf("pointer %p\n", prop);
103	if (! prop)
104		FAIL("Error retrieving \"%s\" pointer: %s", name,
105		     fdt_strerror(retlen));
106
107	tag = fdt32_to_cpu(prop->tag);
108	nameoff = fdt32_to_cpu(prop->nameoff);
109	proplen = fdt32_to_cpu(prop->len);
110
111	if (tag != FDT_PROP)
112		FAIL("Incorrect tag 0x%08x on property \"%s\"", tag, name);
113
114	propname = fdt_get_string(fdt, nameoff, &namelen);
115	if (!propname)
116		FAIL("Couldn't get property name: %s", fdt_strerror(namelen));
117	if (namelen != strlen(propname))
118		FAIL("Incorrect prop name length: %d instead of %zd",
119		     namelen, strlen(propname));
120	if (!streq(propname, name))
121		FAIL("Property name mismatch \"%s\" instead of \"%s\"",
122		     propname, name);
123	if (proplen != retlen)
124		FAIL("Length retrieved for \"%s\" by fdt_get_property()"
125		     " differs from stored length (%d != %d)",
126		     name, retlen, proplen);
127	if (proplen != len)
128		FAIL("Size mismatch on property \"%s\": %d insead of %d",
129		     name, proplen, len);
130	if (len && memcmp(val, prop->data, len) != 0)
131		FAIL("Data mismatch on property \"%s\"", name);
132}
133
134const void *check_getprop(void *fdt, int nodeoffset, const char *name,
135			  int len, const void *val)
136{
137	const void *propval;
138	int proplen;
139
140	propval = fdt_getprop(fdt, nodeoffset, name, &proplen);
141	if (! propval)
142		FAIL("fdt_getprop(\"%s\"): %s", name, fdt_strerror(proplen));
143
144	if (proplen != len)
145		FAIL("Size mismatch on property \"%s\": %d insead of %d",
146		     name, proplen, len);
147	if (len && memcmp(val, propval, len) != 0)
148		FAIL("Data mismatch on property \"%s\"", name);
149
150	return propval;
151}
152
153const void *check_get_prop_offset(void *fdt, int poffset, const char *exp_name,
154				  int exp_len, const void *exp_val)
155{
156	const void *propval;
157	const char *name;
158	int proplen;
159
160	propval = fdt_getprop_by_offset(fdt, poffset, &name, &proplen);
161	if (!propval)
162		FAIL("fdt_getprop(\"%s\"): %s", name, fdt_strerror(proplen));
163
164	/* Not testing for this field, so ignore */
165	if (strcmp(name, exp_name))
166		return NULL;
167
168	if (proplen != exp_len)
169		FAIL("Size mismatch on property \"%s\": %d insead of %d",
170		     name, proplen, exp_len);
171	if (exp_len && memcmp(exp_val, propval, exp_len))
172		FAIL("Data mismatch on property \"%s\"", name);
173
174	return propval;
175}
176
177const void *check_getprop_addrrange(void *fdt, int parent, int nodeoffset,
178				    const char *name, int num)
179{
180	const void *propval;
181	int xac, xsc, buf_size, cells, i;
182	char *buf, *p;
183	uint64_t addr, size;
184	fdt32_t val;
185
186	xac = fdt_address_cells(fdt, parent);
187	xsc = fdt_size_cells(fdt, parent);
188
189	if (xac <= 0)
190		FAIL("Couldn't identify #address-cells: %s",
191		     fdt_strerror(xac));
192	if (xsc <= 0)
193		FAIL("Couldn't identify #size-cells: %s",
194		     fdt_strerror(xsc));
195
196	buf_size = (xac + xsc) * sizeof(fdt32_t) * num;
197	buf = malloc(buf_size);
198	if (!buf)
199		FAIL("Couldn't allocate temporary buffer");
200
201	/* expected value */
202	addr = TEST_MEMREGION_ADDR;
203	if (xac > 1)
204		addr += TEST_MEMREGION_ADDR_HI;
205	size = TEST_MEMREGION_SIZE;
206	if (xsc > 1)
207		size += TEST_MEMREGION_SIZE_HI;
208	for (p = buf, i = 0; i < num; i++) {
209		cells = xac;
210		while (cells) {
211			val = cpu_to_fdt32(addr >> (32 * (--cells)));
212			memcpy(p, &val, sizeof(val));
213			p += sizeof(val);
214		}
215		cells = xsc;
216		while (cells) {
217			val = cpu_to_fdt32(size >> (32 * (--cells)));
218			memcpy(p, &val, sizeof(val));
219			p += sizeof(val);
220		}
221
222		addr += size;
223		size += TEST_MEMREGION_SIZE_INC;
224	}
225
226	/* check */
227	propval = check_getprop(fdt, nodeoffset, name, buf_size,
228				(const void *)buf);
229
230	free(buf);
231
232	return propval;
233}
234
235int nodename_eq(const char *s1, const char *s2)
236{
237	int len = strlen(s2);
238
239	if (strncmp(s1, s2, len) != 0)
240		return 0;
241	if (s1[len] == '\0')
242		return 1;
243	else if (!memchr(s2, '@', len) && (s1[len] == '@'))
244		return 1;
245	else
246		return 0;
247}
248
249void vg_prepare_blob(void *fdt, size_t bufsize)
250{
251	char *blob = fdt;
252	int off_memrsv, off_strings, off_struct;
253	int num_memrsv;
254	size_t size_memrsv, size_strings, size_struct;
255
256	off_memrsv = fdt_off_mem_rsvmap(fdt);
257	num_memrsv = fdt_num_mem_rsv(fdt);
258	if (num_memrsv < 0)
259		size_memrsv = fdt_totalsize(fdt) - off_memrsv;
260	else
261		size_memrsv = (num_memrsv + 1)
262			* sizeof(struct fdt_reserve_entry);
263
264	VALGRIND_MAKE_MEM_UNDEFINED(blob, bufsize);
265	VALGRIND_MAKE_MEM_DEFINED(blob, FDT_V1_SIZE);
266	VALGRIND_MAKE_MEM_DEFINED(blob, fdt_header_size(fdt));
267
268	if (fdt_magic(fdt) == FDT_MAGIC) {
269		off_strings = fdt_off_dt_strings(fdt);
270		if (fdt_version(fdt) >= 3)
271			size_strings = fdt_size_dt_strings(fdt);
272		else
273			size_strings = fdt_totalsize(fdt) - off_strings;
274
275		off_struct = fdt_off_dt_struct(fdt);
276		if (fdt_version(fdt) >= 17)
277			size_struct = fdt_size_dt_struct(fdt);
278		else
279			size_struct = fdt_totalsize(fdt) - off_struct;
280	} else if (fdt_magic(fdt) == FDT_SW_MAGIC) {
281		size_strings = fdt_size_dt_strings(fdt);
282		off_strings = fdt_off_dt_strings(fdt) - size_strings;
283
284		off_struct = fdt_off_dt_struct(fdt);
285		size_struct = fdt_size_dt_struct(fdt);
286		size_struct = fdt_totalsize(fdt) - off_struct;
287
288	} else {
289		CONFIG("Bad magic on vg_prepare_blob()");
290	}
291
292	VALGRIND_MAKE_MEM_DEFINED(blob + off_memrsv, size_memrsv);
293	VALGRIND_MAKE_MEM_DEFINED(blob + off_strings, size_strings);
294	VALGRIND_MAKE_MEM_DEFINED(blob + off_struct, size_struct);
295}
296
297void *load_blob(const char *filename)
298{
299	char *blob;
300	size_t len;
301	int ret = utilfdt_read_err(filename, &blob, &len);
302
303	if (ret)
304		CONFIG("Couldn't open blob from \"%s\": %s", filename,
305		       strerror(ret));
306
307	vg_prepare_blob(blob, len);
308
309	return blob;
310}
311
312void *load_blob_arg(int argc, char *argv[])
313{
314	if (argc != 2)
315		CONFIG("Usage: %s <dtb file>", argv[0]);
316	return load_blob(argv[1]);
317}
318
319void save_blob(const char *filename, void *fdt)
320{
321	size_t size = fdt_totalsize(fdt);
322	void *tmp;
323	int ret;
324
325	/* Make a temp copy of the blob so that valgrind won't check
326	 * about uninitialized bits in the pieces between blocks */
327	tmp = xmalloc(size);
328	fdt_move(fdt, tmp, size);
329	VALGRIND_MAKE_MEM_DEFINED(tmp, size);
330	ret = utilfdt_write_err(filename, tmp);
331	if (ret)
332		CONFIG("Couldn't write blob to \"%s\": %s", filename,
333		       strerror(ret));
334	free(tmp);
335}
336
337void *open_blob_rw(void *blob)
338{
339	int err;
340	void *buf = blob;
341
342	err = fdt_open_into(blob, buf, fdt_totalsize(blob));
343	if (err == -FDT_ERR_NOSPACE) {
344		/* Ran out of space converting to v17 */
345		int newsize = fdt_totalsize(blob) + 8;
346
347		buf = xmalloc(newsize);
348		err = fdt_open_into(blob, buf, newsize);
349	}
350	if (err)
351		FAIL("fdt_open_into(): %s", fdt_strerror(err));
352	return buf;
353}
354