1/*	$NetBSD: dtbs_equal_unordered.c,v 1.1.1.3 2019/12/22 12:34:06 skrll Exp $	*/
2
3// SPDX-License-Identifier: LGPL-2.1-or-later
4/*
5 * libfdt - Flat Device Tree manipulation
6 *	Tests if two given dtbs are structurally equal (including order)
7 * Copyright (C) 2007 David Gibson, IBM Corporation.
8 */
9
10#include <stdlib.h>
11#include <stdio.h>
12#include <string.h>
13#include <stdint.h>
14#include <limits.h>
15
16#include <libfdt.h>
17
18#include "tests.h"
19#include "testdata.h"
20
21static int notequal; /* = 0 */
22static int ignore_memrsv; /* = 0 */
23
24#define MISMATCH(fmt, ...)			\
25	do { \
26		if (notequal) \
27			PASS(); \
28		else \
29			FAIL(fmt, ##__VA_ARGS__);	\
30	} while (0)
31
32#define MATCH()			\
33	do { \
34		if (!notequal) \
35			PASS(); \
36		else \
37			FAIL("Trees match which shouldn't");	\
38	} while (0)
39
40#define CHECK(code) \
41	{ \
42		err = (code); \
43		if (err) \
44			FAIL(#code ": %s", fdt_strerror(err)); \
45	}
46
47static int mem_rsv_cmp(const void *p1, const void *p2)
48{
49	const struct fdt_reserve_entry *re1 = p1;
50	const struct fdt_reserve_entry *re2 = p2;
51
52	if (fdt64_to_cpu(re1->address) < fdt64_to_cpu(re2->address))
53		return -1;
54	else if (fdt64_to_cpu(re1->address) > fdt64_to_cpu(re2->address))
55		return 1;
56
57	if (fdt64_to_cpu(re1->size) < fdt64_to_cpu(re2->size))
58		return -1;
59	else if (fdt64_to_cpu(re1->size) > fdt64_to_cpu(re2->size))
60		return 1;
61
62	return 0;
63}
64
65static void compare_mem_rsv(void *fdt1, void *fdt2)
66{
67	int i;
68	uint64_t addr1, size1, addr2, size2;
69	int err;
70
71	if (fdt_num_mem_rsv(fdt1) != fdt_num_mem_rsv(fdt2))
72		MISMATCH("Trees have different number of reserve entries");
73
74	qsort((char *)fdt1 + fdt_off_mem_rsvmap(fdt1), fdt_num_mem_rsv(fdt1),
75	      sizeof(struct fdt_reserve_entry), mem_rsv_cmp);
76	qsort((char *)fdt2 + fdt_off_mem_rsvmap(fdt2), fdt_num_mem_rsv(fdt2),
77	      sizeof(struct fdt_reserve_entry), mem_rsv_cmp);
78
79	for (i = 0; i < fdt_num_mem_rsv(fdt1); i++) {
80		CHECK(fdt_get_mem_rsv(fdt1, i, &addr1, &size1));
81		CHECK(fdt_get_mem_rsv(fdt2, i, &addr2, &size2));
82
83		if ((addr1 != addr2) || (size1 != size2))
84			MISMATCH("Mismatch in reserve entry %d: "
85			     "(0x%llx, 0x%llx) != (0x%llx, 0x%llx)", i,
86			     (unsigned long long)addr1,
87			     (unsigned long long)size1,
88			     (unsigned long long)addr2,
89			     (unsigned long long)size2);
90	}
91}
92
93static void compare_properties(const void *fdt1, int offset1,
94			       const void *fdt2, int offset2)
95{
96	int offset = offset1;
97
98	/* Check the properties */
99	for (offset = fdt_first_property_offset(fdt1, offset1);
100	     offset >= 0;
101	     offset = fdt_next_property_offset(fdt1, offset)) {
102		const char *name;
103		int len1, len2;
104		const void *data1, *data2;
105		int i;
106
107		data1 = fdt_getprop_by_offset(fdt1, offset, &name, &len1);
108		if (!data1)
109			FAIL("fdt_getprop_by_offset(): %s\n",
110			     fdt_strerror(len1));
111
112		verbose_printf("Property '%s'\n", name);
113
114		data2 = fdt_getprop(fdt2, offset2, name, &len2);
115		if (!data2) {
116			if (len2 == -FDT_ERR_NOTFOUND)
117				MISMATCH("Property '%s' missing\n", name);
118			else
119				FAIL("fdt_get_property(): %s\n",
120				     fdt_strerror(len2));
121		}
122
123		verbose_printf("len1=%d data1=", len1);
124		for (i = 0; i < len1; i++)
125			verbose_printf(" %02x", ((const char *)data1)[i]);
126		verbose_printf("\nlen2=%d data2=", len2);
127		for (i = 0; i < len1; i++)
128			verbose_printf(" %02x", ((const char *)data2)[i]);
129		verbose_printf("\n");
130
131		if (len1 != len2)
132			MISMATCH("Property '%s' mismatched length %d vs. %d\n",
133			     name, len1, len2);
134		else if (memcmp(data1, data2, len1) != 0)
135			MISMATCH("Property '%s' mismatched value\n", name);
136	}
137}
138
139static void compare_node(const void *fdt1, int offset1,
140			 const void *fdt2, int offset2);
141
142static void compare_subnodes(const void *fdt1, int offset1,
143			     const void *fdt2, int offset2,
144			     int recurse)
145{
146	int coffset1, coffset2, depth;
147
148	for (depth = 0, coffset1 = offset1;
149	     (coffset1 >= 0) && (depth >= 0);
150	      coffset1 = fdt_next_node(fdt1, coffset1, &depth))
151		if (depth == 1) {
152			const char *name = fdt_get_name(fdt1, coffset1, NULL);
153
154			verbose_printf("Subnode %s\n", name);
155			coffset2 = fdt_subnode_offset(fdt2, offset2, name);
156			if (coffset2 == -FDT_ERR_NOTFOUND)
157				MISMATCH("Subnode %s missing\n", name);
158			else if (coffset2 < 0)
159				FAIL("fdt_subnode_offset(): %s\n",
160				     fdt_strerror(coffset2));
161
162			if (recurse)
163				compare_node(fdt1, coffset1, fdt2, coffset2);
164		}
165}
166
167static void compare_node(const void *fdt1, int offset1,
168			 const void *fdt2, int offset2)
169{
170	int err;
171	char path1[PATH_MAX], path2[PATH_MAX];
172
173	CHECK(fdt_get_path(fdt1, offset1, path1, sizeof(path1)));
174	CHECK(fdt_get_path(fdt2, offset2, path2, sizeof(path2)));
175
176	if (!streq(path1, path2))
177		TEST_BUG("Path mismatch %s vs. %s\n", path1, path2);
178
179	verbose_printf("Checking %s\n", path1);
180
181	compare_properties(fdt1, offset1, fdt2, offset2);
182	compare_properties(fdt2, offset2, fdt1, offset1);
183
184	compare_subnodes(fdt1, offset1, fdt2, offset2, 1);
185	compare_subnodes(fdt2, offset2, fdt1, offset1, 0);
186}
187
188static void badargs(char **argv)
189{
190	CONFIG("Usage: %s [-n] [-m] <dtb file> <dtb file>", argv[0]);
191}
192
193int main(int argc, char *argv[])
194{
195	void *fdt1, *fdt2;
196	uint32_t cpuid1, cpuid2;
197	char **args;
198	int argsleft;
199
200	test_init(argc, argv);
201
202	args = &argv[1];
203	argsleft = argc - 1;
204
205	while (argsleft > 2) {
206		if (streq(args[0], "-n"))
207			notequal = 1;
208		else if (streq(args[0], "-m"))
209			ignore_memrsv = 1;
210		else
211			badargs(argv);
212		args++;
213		argsleft--;
214	}
215	if (argsleft != 2)
216		badargs(argv);
217
218	fdt1 = load_blob(args[0]);
219	fdt2 = load_blob(args[1]);
220
221	if (!ignore_memrsv)
222		compare_mem_rsv(fdt1, fdt2);
223	compare_node(fdt1, 0, fdt2, 0);
224
225	cpuid1 = fdt_boot_cpuid_phys(fdt1);
226	cpuid2 = fdt_boot_cpuid_phys(fdt2);
227	if (cpuid1 != cpuid2)
228		MISMATCH("boot_cpuid_phys mismatch 0x%x != 0x%x",
229		     cpuid1, cpuid2);
230
231	MATCH();
232}
233