1 /*-
2 * SPDX-License-Identifier: BSD-2-Clause
3 *
4 * Copyright (c) 2009-2010 The FreeBSD Foundation
5 *
6 * This software was developed by Semihalf under sponsorship from
7 * the FreeBSD Foundation.
8 *
9 * Redistribution and use in source and binary forms, with or without
10 * modification, are permitted provided that the following conditions
11 * are met:
12 * 1. Redistributions of source code must retain the above copyright
13 * notice, this list of conditions and the following disclaimer.
14 * 2. Redistributions in binary form must reproduce the above copyright
15 * notice, this list of conditions and the following disclaimer in the
16 * documentation and/or other materials provided with the distribution.
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
19 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
22 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
24 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
26 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
27 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
28 * SUCH DAMAGE.
29 */
30
31 #include <sys/param.h>
32 #include <sys/ctype.h>
33 #include <sys/kernel.h>
34 #include <sys/malloc.h>
35 #include <sys/systm.h>
36
37 #include <contrib/libfdt/libfdt.h>
38
39 #include <machine/stdarg.h>
40
41 #include <dev/fdt/fdt_common.h>
42 #include <dev/ofw/ofwvar.h>
43 #include <dev/ofw/openfirm.h>
44 #include <dev/ofw/ofw_bus_subr.h>
45
46 #include "ofw_if.h"
47
48 #ifdef DEBUG
49 #define debugf(fmt, args...) do { printf("%s(): ", __func__); \
50 printf(fmt,##args); } while (0)
51 #else
52 #define debugf(fmt, args...)
53 #endif
54
55 #if defined(__arm__)
56 #if defined(SOC_MV_ARMADAXP) || defined(SOC_MV_ARMADA38X) || \
57 defined(SOC_MV_DISCOVERY) || defined(SOC_MV_DOVE) || \
58 defined(SOC_MV_FREY) || defined(SOC_MV_KIRKWOOD) || \
59 defined(SOC_MV_LOKIPLUS) || defined(SOC_MV_ORION)
60 #define FDT_MARVELL
61 #endif
62 #endif
63
64 static int ofw_fdt_init(ofw_t, void *);
65 static phandle_t ofw_fdt_peer(ofw_t, phandle_t);
66 static phandle_t ofw_fdt_child(ofw_t, phandle_t);
67 static phandle_t ofw_fdt_parent(ofw_t, phandle_t);
68 static phandle_t ofw_fdt_instance_to_package(ofw_t, ihandle_t);
69 static ssize_t ofw_fdt_getproplen(ofw_t, phandle_t, const char *);
70 static ssize_t ofw_fdt_getprop(ofw_t, phandle_t, const char *, void *, size_t);
71 static int ofw_fdt_nextprop(ofw_t, phandle_t, const char *, char *, size_t);
72 static int ofw_fdt_setprop(ofw_t, phandle_t, const char *, const void *,
73 size_t);
74 static ssize_t ofw_fdt_canon(ofw_t, const char *, char *, size_t);
75 static phandle_t ofw_fdt_finddevice(ofw_t, const char *);
76 static ssize_t ofw_fdt_instance_to_path(ofw_t, ihandle_t, char *, size_t);
77 static ssize_t ofw_fdt_package_to_path(ofw_t, phandle_t, char *, size_t);
78 static int ofw_fdt_interpret(ofw_t, const char *, int, cell_t *);
79
80 static ofw_method_t ofw_fdt_methods[] = {
81 OFWMETHOD(ofw_init, ofw_fdt_init),
82 OFWMETHOD(ofw_peer, ofw_fdt_peer),
83 OFWMETHOD(ofw_child, ofw_fdt_child),
84 OFWMETHOD(ofw_parent, ofw_fdt_parent),
85 OFWMETHOD(ofw_instance_to_package, ofw_fdt_instance_to_package),
86 OFWMETHOD(ofw_getproplen, ofw_fdt_getproplen),
87 OFWMETHOD(ofw_getprop, ofw_fdt_getprop),
88 OFWMETHOD(ofw_nextprop, ofw_fdt_nextprop),
89 OFWMETHOD(ofw_setprop, ofw_fdt_setprop),
90 OFWMETHOD(ofw_canon, ofw_fdt_canon),
91 OFWMETHOD(ofw_finddevice, ofw_fdt_finddevice),
92 OFWMETHOD(ofw_instance_to_path, ofw_fdt_instance_to_path),
93 OFWMETHOD(ofw_package_to_path, ofw_fdt_package_to_path),
94 OFWMETHOD(ofw_interpret, ofw_fdt_interpret),
95 { 0, 0 }
96 };
97
98 static ofw_def_t ofw_fdt = {
99 OFW_FDT,
100 ofw_fdt_methods,
101 0
102 };
103 OFW_DEF(ofw_fdt);
104
105 #define FDT_FBSDVER_LEN 16
106 #define FDT_MODEL_LEN 80
107 #define FDT_COMPAT_LEN 255
108 #define FDT_SERIAL_LEN 32
109
110 static void *fdtp = NULL;
111 static char fdt_model[FDT_MODEL_LEN];
112 static char fdt_compatible[FDT_COMPAT_LEN];
113 static char fdt_fbsd_version[FDT_FBSDVER_LEN];
114 static char fdt_serial[FDT_SERIAL_LEN];
115
116 static int
sysctl_handle_dtb(SYSCTL_HANDLER_ARGS)117 sysctl_handle_dtb(SYSCTL_HANDLER_ARGS)
118 {
119
120 return (sysctl_handle_opaque(oidp, fdtp, fdt_totalsize(fdtp), req));
121 }
122
123 static void
sysctl_register_fdt_oid(void * arg)124 sysctl_register_fdt_oid(void *arg)
125 {
126
127 /* If there is no FDT registered, skip adding the sysctl */
128 if (fdtp == NULL)
129 return;
130
131 SYSCTL_ADD_PROC(NULL, SYSCTL_STATIC_CHILDREN(_hw_fdt), OID_AUTO, "dtb",
132 CTLTYPE_OPAQUE | CTLFLAG_RD | CTLFLAG_MPSAFE, NULL, 0,
133 sysctl_handle_dtb, "", "Device Tree Blob");
134 if (fdt_model[0] != '\0')
135 SYSCTL_ADD_STRING(NULL, SYSCTL_STATIC_CHILDREN(_hw_fdt),
136 OID_AUTO, "model", CTLFLAG_RD, fdt_model,
137 FDT_MODEL_LEN, "System board model");
138 if (fdt_compatible[0] != '\0')
139 SYSCTL_ADD_STRING(NULL, SYSCTL_STATIC_CHILDREN(_hw_fdt),
140 OID_AUTO, "compatible", CTLFLAG_RD, fdt_compatible,
141 FDT_COMPAT_LEN, "Compatible platforms");
142 if (fdt_fbsd_version[0] != '\0')
143 SYSCTL_ADD_STRING(NULL, SYSCTL_STATIC_CHILDREN(_hw_fdt),
144 OID_AUTO, "freebsd-version", CTLFLAG_RD, fdt_fbsd_version,
145 FDT_FBSDVER_LEN, "FreeBSD DTS branding version");
146 if (fdt_serial[0] != '\0')
147 SYSCTL_ADD_STRING(NULL, SYSCTL_STATIC_CHILDREN(_hw_fdt),
148 OID_AUTO, "serial-number", CTLFLAG_RD, fdt_serial,
149 FDT_SERIAL_LEN, "Serial number");
150 }
151 SYSINIT(dtb_oid, SI_SUB_KMEM, SI_ORDER_ANY, sysctl_register_fdt_oid, NULL);
152
153 static int
ofw_fdt_init(ofw_t ofw,void * data)154 ofw_fdt_init(ofw_t ofw, void *data)
155 {
156 phandle_t root;
157 ssize_t len;
158 int err;
159 int i;
160
161 /* Check FDT blob integrity */
162 if ((err = fdt_check_header(data)) != 0)
163 return (err);
164
165 fdtp = data;
166 root = ofw_fdt_finddevice(NULL, "/");
167 len = ofw_fdt_getproplen(NULL, root, "model");
168 if (len > 0 && len <= FDT_MODEL_LEN) {
169 bzero(fdt_model, FDT_MODEL_LEN);
170 ofw_fdt_getprop(NULL, root, "model", fdt_model, FDT_MODEL_LEN);
171 }
172 len = ofw_fdt_getproplen(NULL, root, "compatible");
173 if (len > 0 && len <= FDT_COMPAT_LEN) {
174 bzero(fdt_compatible, FDT_COMPAT_LEN);
175 ofw_fdt_getprop(NULL, root, "compatible", fdt_compatible,
176 FDT_COMPAT_LEN);
177 /* Replace the middle '\0' with ' ' */
178 for (i = 0; i < len - 1; i++)
179 if (fdt_compatible[i] == '\0')
180 fdt_compatible[i] = ' ';
181 }
182 len = ofw_fdt_getproplen(NULL, root, "freebsd,dts-version");
183 if (len > 0 && len <= FDT_FBSDVER_LEN) {
184 bzero(fdt_fbsd_version, FDT_FBSDVER_LEN);
185 ofw_fdt_getprop(NULL, root, "freebsd,dts-version",
186 fdt_fbsd_version, FDT_FBSDVER_LEN);
187 }
188 len = ofw_fdt_getproplen(NULL, root, "serial-number");
189 if (len > 0 && len <= FDT_SERIAL_LEN) {
190 bzero(fdt_serial, FDT_SERIAL_LEN);
191 ofw_fdt_getprop(NULL, root, "serial-number",
192 fdt_serial, FDT_SERIAL_LEN);
193 /*
194 * Non-standard property; check for NUL-terminated
195 * printable string.
196 */
197 for (i = 0; i < len - 1; i++) {
198 if (!isprint(fdt_serial[i])) {
199 fdt_serial[0] = '\0';
200 break;
201 }
202 }
203 if (fdt_serial[len - 1] != '\0')
204 fdt_serial[0] = '\0';
205 }
206 return (0);
207 }
208
209 /*
210 * Device tree functions.
211 *
212 * We use the offset from fdtp to the node as the 'phandle' in OF interface.
213 *
214 * phandle is a u32 value, therefore we cannot use the pointer to node as
215 * phandle in 64 bit. We also do not use the usual fdt offset as phandle,
216 * as it can be 0, and the OF interface has special meaning for phandle 0.
217 */
218
219 static phandle_t
fdt_offset_phandle(int offset)220 fdt_offset_phandle(int offset)
221 {
222 if (offset < 0)
223 return (0);
224 return ((phandle_t)offset + fdt_off_dt_struct(fdtp));
225 }
226
227 static int
fdt_phandle_offset(phandle_t p)228 fdt_phandle_offset(phandle_t p)
229 {
230 int pint = (int)p;
231 int dtoff = fdt_off_dt_struct(fdtp);
232
233 if (pint < dtoff)
234 return (-1);
235 return (pint - dtoff);
236 }
237
238 /* Return the next sibling of this node or 0. */
239 static phandle_t
ofw_fdt_peer(ofw_t ofw,phandle_t node)240 ofw_fdt_peer(ofw_t ofw, phandle_t node)
241 {
242 int offset;
243
244 if (node == 0) {
245 /* Find root node */
246 offset = fdt_path_offset(fdtp, "/");
247
248 return (fdt_offset_phandle(offset));
249 }
250
251 offset = fdt_phandle_offset(node);
252 if (offset < 0)
253 return (0);
254 offset = fdt_next_subnode(fdtp, offset);
255 return (fdt_offset_phandle(offset));
256 }
257
258 /* Return the first child of this node or 0. */
259 static phandle_t
ofw_fdt_child(ofw_t ofw,phandle_t node)260 ofw_fdt_child(ofw_t ofw, phandle_t node)
261 {
262 int offset;
263
264 offset = fdt_phandle_offset(node);
265 if (offset < 0)
266 return (0);
267 offset = fdt_first_subnode(fdtp, offset);
268 return (fdt_offset_phandle(offset));
269 }
270
271 /* Return the parent of this node or 0. */
272 static phandle_t
ofw_fdt_parent(ofw_t ofw,phandle_t node)273 ofw_fdt_parent(ofw_t ofw, phandle_t node)
274 {
275 int offset, paroffset;
276
277 offset = fdt_phandle_offset(node);
278 if (offset < 0)
279 return (0);
280
281 paroffset = fdt_parent_offset(fdtp, offset);
282 return (fdt_offset_phandle(paroffset));
283 }
284
285 /* Return the package handle that corresponds to an instance handle. */
286 static phandle_t
ofw_fdt_instance_to_package(ofw_t ofw,ihandle_t instance)287 ofw_fdt_instance_to_package(ofw_t ofw, ihandle_t instance)
288 {
289
290 /* Where real OF uses ihandles in the tree, FDT uses xref phandles */
291 return (OF_node_from_xref(instance));
292 }
293
294 /* Get the length of a property of a package. */
295 static ssize_t
ofw_fdt_getproplen(ofw_t ofw,phandle_t package,const char * propname)296 ofw_fdt_getproplen(ofw_t ofw, phandle_t package, const char *propname)
297 {
298 const void *prop;
299 int offset, len;
300
301 offset = fdt_phandle_offset(package);
302 if (offset < 0)
303 return (-1);
304
305 len = -1;
306 prop = fdt_getprop(fdtp, offset, propname, &len);
307
308 if (prop == NULL && strcmp(propname, "name") == 0) {
309 /* Emulate the 'name' property */
310 fdt_get_name(fdtp, offset, &len);
311 return (len + 1);
312 }
313
314 if (prop == NULL && offset == fdt_path_offset(fdtp, "/chosen")) {
315 if (strcmp(propname, "fdtbootcpu") == 0)
316 return (sizeof(cell_t));
317 if (strcmp(propname, "fdtmemreserv") == 0)
318 return (sizeof(uint64_t)*2*fdt_num_mem_rsv(fdtp));
319 }
320
321 if (prop == NULL)
322 return (-1);
323
324 return (len);
325 }
326
327 /* Get the value of a property of a package. */
328 static ssize_t
ofw_fdt_getprop(ofw_t ofw,phandle_t package,const char * propname,void * buf,size_t buflen)329 ofw_fdt_getprop(ofw_t ofw, phandle_t package, const char *propname, void *buf,
330 size_t buflen)
331 {
332 const void *prop;
333 const char *name;
334 int len, offset;
335 uint32_t cpuid;
336
337 offset = fdt_phandle_offset(package);
338 if (offset < 0)
339 return (-1);
340
341 prop = fdt_getprop(fdtp, offset, propname, &len);
342
343 if (prop == NULL && strcmp(propname, "name") == 0) {
344 /* Emulate the 'name' property */
345 name = fdt_get_name(fdtp, offset, &len);
346 strncpy(buf, name, buflen);
347 return (len + 1);
348 }
349
350 if (prop == NULL && offset == fdt_path_offset(fdtp, "/chosen")) {
351 if (strcmp(propname, "fdtbootcpu") == 0) {
352 cpuid = cpu_to_fdt32(fdt_boot_cpuid_phys(fdtp));
353 len = sizeof(cpuid);
354 prop = &cpuid;
355 }
356 if (strcmp(propname, "fdtmemreserv") == 0) {
357 prop = (char *)fdtp + fdt_off_mem_rsvmap(fdtp);
358 len = sizeof(uint64_t)*2*fdt_num_mem_rsv(fdtp);
359 }
360 }
361
362 if (prop == NULL)
363 return (-1);
364
365 bcopy(prop, buf, min(len, buflen));
366
367 return (len);
368 }
369
370 /*
371 * Get the next property of a package. Return values:
372 * -1: package or previous property does not exist
373 * 0: no more properties
374 * 1: success
375 */
376 static int
ofw_fdt_nextprop(ofw_t ofw,phandle_t package,const char * previous,char * buf,size_t size)377 ofw_fdt_nextprop(ofw_t ofw, phandle_t package, const char *previous, char *buf,
378 size_t size)
379 {
380 const void *prop;
381 const char *name;
382 int offset;
383
384 offset = fdt_phandle_offset(package);
385 if (offset < 0)
386 return (-1);
387
388 if (previous == NULL)
389 /* Find the first prop in the node */
390 offset = fdt_first_property_offset(fdtp, offset);
391 else {
392 fdt_for_each_property_offset(offset, fdtp, offset) {
393 prop = fdt_getprop_by_offset(fdtp, offset, &name, NULL);
394 if (prop == NULL)
395 return (-1); /* Internal error */
396 /* Skip until we find 'previous', then bail out */
397 if (strcmp(name, previous) != 0)
398 continue;
399 offset = fdt_next_property_offset(fdtp, offset);
400 break;
401 }
402 }
403
404 if (offset < 0)
405 return (0); /* No properties */
406
407 prop = fdt_getprop_by_offset(fdtp, offset, &name, &offset);
408 if (prop == NULL)
409 return (-1); /* Internal error */
410
411 strncpy(buf, name, size);
412
413 return (1);
414 }
415
416 /* Set the value of a property of a package. */
417 static int
ofw_fdt_setprop(ofw_t ofw,phandle_t package,const char * propname,const void * buf,size_t len)418 ofw_fdt_setprop(ofw_t ofw, phandle_t package, const char *propname,
419 const void *buf, size_t len)
420 {
421 int offset;
422
423 offset = fdt_phandle_offset(package);
424 if (offset < 0)
425 return (-1);
426
427 if (fdt_setprop_inplace(fdtp, offset, propname, buf, len) != 0)
428 /* Try to add property, when setting value inplace failed */
429 return (fdt_setprop(fdtp, offset, propname, buf, len));
430
431 return (0);
432 }
433
434 /* Convert a device specifier to a fully qualified pathname. */
435 static ssize_t
ofw_fdt_canon(ofw_t ofw,const char * device,char * buf,size_t len)436 ofw_fdt_canon(ofw_t ofw, const char *device, char *buf, size_t len)
437 {
438
439 return (-1);
440 }
441
442 /* Return a package handle for the specified device. */
443 static phandle_t
ofw_fdt_finddevice(ofw_t ofw,const char * device)444 ofw_fdt_finddevice(ofw_t ofw, const char *device)
445 {
446 int offset;
447
448 offset = fdt_path_offset(fdtp, device);
449 if (offset < 0)
450 return (-1);
451 return (fdt_offset_phandle(offset));
452 }
453
454 /* Return the fully qualified pathname corresponding to an instance. */
455 static ssize_t
ofw_fdt_instance_to_path(ofw_t ofw,ihandle_t instance,char * buf,size_t len)456 ofw_fdt_instance_to_path(ofw_t ofw, ihandle_t instance, char *buf, size_t len)
457 {
458 phandle_t phandle;
459
460 phandle = OF_instance_to_package(instance);
461 if (phandle == -1)
462 return (-1);
463
464 return (OF_package_to_path(phandle, buf, len));
465 }
466
467 /* Return the fully qualified pathname corresponding to a package. */
468 static ssize_t
ofw_fdt_package_to_path(ofw_t ofw,phandle_t package,char * buf,size_t len)469 ofw_fdt_package_to_path(ofw_t ofw, phandle_t package, char *buf, size_t len)
470 {
471
472 return (-1);
473 }
474
475 #if defined(FDT_MARVELL)
476 static int
ofw_fdt_fixup(ofw_t ofw)477 ofw_fdt_fixup(ofw_t ofw)
478 {
479 char model[FDT_MODEL_LEN];
480 phandle_t root;
481 ssize_t len;
482 int i;
483
484 if ((root = ofw_fdt_finddevice(ofw, "/")) == -1)
485 return (ENODEV);
486
487 if ((len = ofw_fdt_getproplen(ofw, root, "model")) <= 0)
488 return (0);
489
490 bzero(model, FDT_MODEL_LEN);
491 if (ofw_fdt_getprop(ofw, root, "model", model, FDT_MODEL_LEN) <= 0)
492 return (0);
493
494 /*
495 * Search fixup table and call handler if appropriate.
496 */
497 for (i = 0; fdt_fixup_table[i].model != NULL; i++) {
498 if (strncmp(model, fdt_fixup_table[i].model,
499 FDT_MODEL_LEN) != 0)
500 /*
501 * Sometimes it's convenient to provide one
502 * fixup entry that refers to many boards.
503 * To handle this case, simply check if model
504 * is compatible parameter
505 */
506 if(!ofw_bus_node_is_compatible(root,
507 fdt_fixup_table[i].model))
508 continue;
509
510 if (fdt_fixup_table[i].handler != NULL)
511 (*fdt_fixup_table[i].handler)(root);
512 }
513
514 return (0);
515 }
516 #endif
517
518 static int
ofw_fdt_interpret(ofw_t ofw,const char * cmd,int nret,cell_t * retvals)519 ofw_fdt_interpret(ofw_t ofw, const char *cmd, int nret, cell_t *retvals)
520 {
521 #if defined(FDT_MARVELL)
522 int rv;
523
524 /*
525 * Note: FDT does not have the possibility to 'interpret' commands,
526 * but we abuse the interface a bit to use it for doing non-standard
527 * operations on the device tree blob.
528 *
529 * Currently the only supported 'command' is to trigger performing
530 * fixups.
531 */
532 if (strncmp("perform-fixup", cmd, 13) != 0)
533 return (0);
534
535 rv = ofw_fdt_fixup(ofw);
536 if (nret > 0)
537 retvals[0] = rv;
538
539 return (rv);
540 #else
541 return (0);
542 #endif
543 }
544