1 /* Copyright 2013-2014 IBM Corp.
2  *
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at
6  *
7  * 	http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
12  * implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #include <skiboot.h>
18 #include <stdarg.h>
19 #include <libfdt.h>
20 #include <device.h>
21 #include <cpu.h>
22 #include <opal.h>
23 #include <interrupts.h>
24 #include <fsp.h>
25 #include <cec.h>
26 #include <vpd.h>
27 #include <ccan/str/str.h>
28 
29 static int fdt_error;
30 
31 #undef DEBUG_FDT
32 #ifdef DEBUG_FDT
33 #define FDT_DBG(fmt, a...)	prlog(PR_DEBUG, "FDT: " fmt, ##a)
34 #else
35 #define FDT_DBG(fmt, a...)
36 #endif
37 
__save_err(int err,const char * str)38 static void __save_err(int err, const char *str)
39 {
40 	FDT_DBG("rc: %d from \"%s\"\n", err, str);
41 	if (err && !fdt_error) {
42 		prerror("FDT: Error %d from \"%s\"\n", err, str);
43 		fdt_error = err;
44 	}
45 }
46 
47 #define save_err(...) __save_err(__VA_ARGS__, #__VA_ARGS__)
48 
dt_property_cell(void * fdt,const char * name,u32 cell)49 static void dt_property_cell(void *fdt, const char *name, u32 cell)
50 {
51 	save_err(fdt_property_cell(fdt, name, cell));
52 }
53 
dt_begin_node(void * fdt,const struct dt_node * dn)54 static void dt_begin_node(void *fdt, const struct dt_node *dn)
55 {
56 	save_err(fdt_begin_node(fdt, dn->name));
57 
58 	dt_property_cell(fdt, "phandle", dn->phandle);
59 }
60 
dt_property(void * fdt,const struct dt_property * p)61 static void dt_property(void *fdt, const struct dt_property *p)
62 {
63 	save_err(fdt_property(fdt, p->name, p->prop, p->len));
64 }
65 
dt_end_node(void * fdt)66 static void dt_end_node(void *fdt)
67 {
68 	save_err(fdt_end_node(fdt));
69 }
70 
71 #ifdef DEBUG_FDT
dump_fdt(void * fdt)72 static void dump_fdt(void *fdt)
73 {
74 	int i, off, depth, err;
75 
76 	prlog(PR_INFO, "Device tree %u@%p\n", fdt_totalsize(fdt), fdt);
77 	err = fdt_check_header(fdt);
78 	if (err) {
79 		prerror("fdt_check_header: %s\n", fdt_strerror(err));
80 		return;
81 	}
82 	prlog(PR_INFO, "fdt_check_header passed\n");
83 
84 	prlog(PR_INFO, "fdt_num_mem_rsv = %u\n", fdt_num_mem_rsv(fdt));
85 	for (i = 0; i < fdt_num_mem_rsv(fdt); i++) {
86 		u64 addr, size;
87 
88 		err = fdt_get_mem_rsv(fdt, i, &addr, &size);
89 		if (err) {
90 			prlog(PR_INFO, " ERR %s\n", fdt_strerror(err));
91 			return;
92 		}
93 		prlog(PR_INFO, "  mem_rsv[%i] = %lu@%#lx\n",
94 		      i, (long)addr, (long)size);
95 	}
96 
97 	for (off = fdt_next_node(fdt, 0, &depth);
98 	     off > 0;
99 	     off = fdt_next_node(fdt, off, &depth)) {
100 		int len;
101 		const char *name;
102 
103 		name = fdt_get_name(fdt, off, &len);
104 		if (!name) {
105 			prerror("fdt: offset %i no name!\n", off);
106 			return;
107 		}
108 		prlog(PR_INFO, "name: %s [%u]\n", name, off);
109 	}
110 }
111 #else
dump_fdt(void * fdt __unused)112 static inline void dump_fdt(void *fdt __unused) { }
113 #endif
114 
flatten_dt_properties(void * fdt,const struct dt_node * dn)115 static void flatten_dt_properties(void *fdt, const struct dt_node *dn)
116 {
117 	const struct dt_property *p;
118 
119 	list_for_each(&dn->properties, p, list) {
120 		if (strstarts(p->name, DT_PRIVATE))
121 			continue;
122 
123 		FDT_DBG("  prop: %s size: %ld\n", p->name, p->len);
124 		dt_property(fdt, p);
125 	}
126 }
127 
flatten_dt_node(void * fdt,const struct dt_node * root,bool exclusive)128 static void flatten_dt_node(void *fdt, const struct dt_node *root,
129 			    bool exclusive)
130 {
131 	const struct dt_node *i;
132 
133 	if (!exclusive) {
134 		FDT_DBG("node: %s\n", root->name);
135 		dt_begin_node(fdt, root);
136 		flatten_dt_properties(fdt, root);
137 	}
138 
139 	list_for_each(&root->children, i, list)
140 		flatten_dt_node(fdt, i, false);
141 
142 	if (!exclusive)
143 		dt_end_node(fdt);
144 }
145 
create_dtb_reservemap(void * fdt,const struct dt_node * root)146 static void create_dtb_reservemap(void *fdt, const struct dt_node *root)
147 {
148 	uint64_t base, size;
149 	const uint64_t *ranges;
150 	const struct dt_property *prop;
151 	int i;
152 
153 	/* Duplicate the reserved-ranges property into the fdt reservemap */
154 	prop = dt_find_property(root, "reserved-ranges");
155 	if (prop) {
156 		ranges = (const void *)prop->prop;
157 
158 		for (i = 0; i < prop->len / (sizeof(uint64_t) * 2); i++) {
159 			base = *(ranges++);
160 			size = *(ranges++);
161 			save_err(fdt_add_reservemap_entry(fdt, base, size));
162 		}
163 	}
164 
165 	save_err(fdt_finish_reservemap(fdt));
166 }
167 
__create_dtb(void * fdt,size_t len,const struct dt_node * root,bool exclusive)168 static int __create_dtb(void *fdt, size_t len,
169 			const struct dt_node *root,
170 			bool exclusive)
171 {
172 	fdt_create(fdt, len);
173 	if (root == dt_root && !exclusive)
174 		create_dtb_reservemap(fdt, root);
175 	else
176 		fdt_finish_reservemap(fdt);
177 
178 	flatten_dt_node(fdt, root, exclusive);
179 
180 	save_err(fdt_finish(fdt));
181 	if (fdt_error) {
182 		prerror("dtb: error %s\n", fdt_strerror(fdt_error));
183 		return fdt_error;
184 	}
185 
186 	dump_fdt(fdt);
187 	return 0;
188 }
189 
create_dtb(const struct dt_node * root,bool exclusive)190 void *create_dtb(const struct dt_node *root, bool exclusive)
191 {
192 	void *fdt = NULL;
193 	size_t len = DEVICE_TREE_MAX_SIZE;
194 	uint32_t old_last_phandle = get_last_phandle();
195 	int ret;
196 
197 	do {
198 		set_last_phandle(old_last_phandle);
199 		fdt_error = 0;
200 		fdt = malloc(len);
201 		if (!fdt) {
202 			prerror("dtb: could not malloc %lu\n", (long)len);
203 			return NULL;
204 		}
205 
206 		ret = __create_dtb(fdt, len, root, exclusive);
207 		if (ret) {
208 			free(fdt);
209 			fdt = NULL;
210 		}
211 
212 		len *= 2;
213 	} while (ret == -FDT_ERR_NOSPACE);
214 
215 	return fdt;
216 }
217 
opal_get_device_tree(uint32_t phandle,uint64_t buf,uint64_t len)218 static int64_t opal_get_device_tree(uint32_t phandle,
219 				    uint64_t buf, uint64_t len)
220 {
221 	struct dt_node *root;
222 	void *fdt = (void *)buf;
223 	uint32_t old_last_phandle;
224 	int64_t totalsize;
225 	int ret;
226 
227 	if (!opal_addr_valid(fdt))
228 		return OPAL_PARAMETER;
229 
230 	root = dt_find_by_phandle(dt_root, phandle);
231 	if (!root)
232 		return OPAL_PARAMETER;
233 
234 	if (!fdt) {
235 		fdt = create_dtb(root, true);
236 		if (!fdt)
237 			return OPAL_INTERNAL_ERROR;
238 		totalsize = fdt_totalsize(fdt);
239 		free(fdt);
240 		return totalsize;
241 	}
242 
243 	if (!len)
244 		return OPAL_PARAMETER;
245 
246 	fdt_error = 0;
247 	old_last_phandle = get_last_phandle();
248 	ret = __create_dtb(fdt, len, root, true);
249 	if (ret) {
250 		set_last_phandle(old_last_phandle);
251 		if (ret == -FDT_ERR_NOSPACE)
252 			return OPAL_NO_MEM;
253 
254 		return OPAL_EMPTY;
255 	}
256 
257 	return OPAL_SUCCESS;
258 }
259 opal_call(OPAL_GET_DEVICE_TREE, opal_get_device_tree, 3);
260