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