1/*
2 * libfdt - Flat Device Tree manipulation
3 *	Testcase for DT overlays()
4 * Copyright (C) 2016 Free Electrons
5 * Copyright (C) 2016 NextThing Co.
6 *
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public License
9 * as published by the Free Software Foundation; either version 2.1 of
10 * the License, or (at your option) any later version.
11 *
12 * This library is distributed in the hope that it will be useful, but
13 * WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15 * Lesser General Public License for more details.
16 *
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with this library; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
20 */
21
22#include <stdio.h>
23
24#include <libfdt.h>
25
26#include "tests.h"
27
28#define CHECK(code) \
29	{ \
30		int err = (code); \
31		if (err) \
32			FAIL(#code ": %s", fdt_strerror(err)); \
33	}
34
35/* 4k ought to be enough for anybody */
36#define FDT_COPY_SIZE	(4 * 1024)
37
38static int fdt_getprop_u32_by_poffset(void *fdt, const char *path,
39				      const char *name, int poffset,
40				      unsigned long *out)
41{
42	const fdt32_t *val;
43	int node_off;
44	int len;
45
46	node_off = fdt_path_offset(fdt, path);
47	if (node_off < 0)
48		return node_off;
49
50	val = fdt_getprop(fdt, node_off, name, &len);
51	if (!val || (len < (sizeof(uint32_t) * (poffset + 1))))
52		return -FDT_ERR_NOTFOUND;
53
54	*out = fdt32_to_cpu(*(val + poffset));
55
56	return 0;
57}
58
59static int check_getprop_string_by_name(void *fdt, const char *path,
60					const char *name, const char *val)
61{
62	int node_off;
63
64	node_off = fdt_path_offset(fdt, path);
65	if (node_off < 0)
66		return node_off;
67
68	check_getprop_string(fdt, node_off, name, val);
69
70	return 0;
71}
72
73static int check_getprop_u32_by_name(void *fdt, const char *path,
74				     const char *name, uint32_t val)
75{
76	int node_off;
77
78	node_off = fdt_path_offset(fdt, path);
79	CHECK(node_off < 0);
80
81	check_getprop_cell(fdt, node_off, name, val);
82
83	return 0;
84}
85
86static int check_getprop_null_by_name(void *fdt, const char *path,
87				      const char *name)
88{
89	int node_off;
90
91	node_off = fdt_path_offset(fdt, path);
92	CHECK(node_off < 0);
93
94	check_property(fdt, node_off, name, 0, NULL);
95
96	return 0;
97}
98
99static int fdt_overlay_change_int_property(void *fdt)
100{
101	return check_getprop_u32_by_name(fdt, "/test-node", "test-int-property",
102					 43);
103}
104
105static int fdt_overlay_change_str_property(void *fdt)
106{
107	return check_getprop_string_by_name(fdt, "/test-node",
108					    "test-str-property", "foobar");
109}
110
111static int fdt_overlay_add_str_property(void *fdt)
112{
113	return check_getprop_string_by_name(fdt, "/test-node",
114					    "test-str-property-2", "foobar2");
115}
116
117static int fdt_overlay_add_node(void *fdt)
118{
119	return check_getprop_null_by_name(fdt, "/test-node/new-node",
120					  "new-property");
121}
122
123static int fdt_overlay_add_subnode_property(void *fdt)
124{
125	check_getprop_null_by_name(fdt, "/test-node/sub-test-node",
126				   "sub-test-property");
127	check_getprop_null_by_name(fdt, "/test-node/sub-test-node",
128				   "new-sub-test-property");
129
130	return 0;
131}
132
133static int fdt_overlay_local_phandle(void *fdt)
134{
135	uint32_t local_phandle;
136	unsigned long val = 0;
137	int off;
138
139	off = fdt_path_offset(fdt, "/test-node/new-local-node");
140	CHECK(off < 0);
141
142	local_phandle = fdt_get_phandle(fdt, off);
143	CHECK(!local_phandle);
144
145	CHECK(fdt_getprop_u32_by_poffset(fdt, "/test-node",
146					 "test-several-phandle",
147					 0, &val));
148	CHECK(val != local_phandle);
149
150	CHECK(fdt_getprop_u32_by_poffset(fdt, "/test-node",
151					 "test-several-phandle",
152					 1, &val));
153	CHECK(val != local_phandle);
154
155	return 0;
156}
157
158static int fdt_overlay_local_phandles(void *fdt)
159{
160	uint32_t local_phandle, test_phandle;
161	unsigned long val = 0;
162	int off;
163
164	off = fdt_path_offset(fdt, "/test-node/new-local-node");
165	CHECK(off < 0);
166
167	local_phandle = fdt_get_phandle(fdt, off);
168	CHECK(!local_phandle);
169
170	off = fdt_path_offset(fdt, "/test-node");
171	CHECK(off < 0);
172
173	test_phandle = fdt_get_phandle(fdt, off);
174	CHECK(!test_phandle);
175
176	CHECK(fdt_getprop_u32_by_poffset(fdt, "/test-node",
177					 "test-phandle", 0, &val));
178	CHECK(test_phandle != val);
179
180	CHECK(fdt_getprop_u32_by_poffset(fdt, "/test-node",
181					 "test-phandle", 1, &val));
182	CHECK(local_phandle != val);
183
184	return 0;
185}
186
187static void *open_dt(char *path)
188{
189	void *dt, *copy;
190
191	dt = load_blob(path);
192	copy = xmalloc(FDT_COPY_SIZE);
193
194	/*
195	 * Resize our DTs to 4k so that we have room to operate on
196	 */
197	CHECK(fdt_open_into(dt, copy, FDT_COPY_SIZE));
198
199	return copy;
200}
201
202int main(int argc, char *argv[])
203{
204	void *fdt_base, *fdt_overlay;
205
206	test_init(argc, argv);
207	if (argc != 3)
208		CONFIG("Usage: %s <base dtb> <overlay dtb>", argv[0]);
209
210	fdt_base = open_dt(argv[1]);
211	fdt_overlay = open_dt(argv[2]);
212
213	/* Apply the overlay */
214	CHECK(fdt_overlay_apply(fdt_base, fdt_overlay));
215
216	fdt_overlay_change_int_property(fdt_base);
217	fdt_overlay_change_str_property(fdt_base);
218	fdt_overlay_add_str_property(fdt_base);
219	fdt_overlay_add_node(fdt_base);
220	fdt_overlay_add_subnode_property(fdt_base);
221
222	/*
223	 * If the base tree has a __symbols__ node, do the tests that
224	 * are only successful with a proper phandle support, and thus
225	 * dtc -@
226	 */
227	if (fdt_path_offset(fdt_base, "/__symbols__") >= 0) {
228		fdt_overlay_local_phandle(fdt_base);
229		fdt_overlay_local_phandles(fdt_base);
230	}
231
232	PASS();
233}
234