1*91f16700Schasinglulu /* 2*91f16700Schasinglulu * Copyright (c) 2018-2023, ARM Limited and Contributors. All rights reserved. 3*91f16700Schasinglulu * 4*91f16700Schasinglulu * SPDX-License-Identifier: BSD-3-Clause 5*91f16700Schasinglulu */ 6*91f16700Schasinglulu 7*91f16700Schasinglulu /* Helper functions to offer easier navigation of Device Tree Blob */ 8*91f16700Schasinglulu 9*91f16700Schasinglulu #include <assert.h> 10*91f16700Schasinglulu #include <errno.h> 11*91f16700Schasinglulu #include <inttypes.h> 12*91f16700Schasinglulu #include <stdint.h> 13*91f16700Schasinglulu #include <string.h> 14*91f16700Schasinglulu 15*91f16700Schasinglulu #include <libfdt.h> 16*91f16700Schasinglulu 17*91f16700Schasinglulu #include <common/debug.h> 18*91f16700Schasinglulu #include <common/fdt_wrappers.h> 19*91f16700Schasinglulu #include <common/uuid.h> 20*91f16700Schasinglulu 21*91f16700Schasinglulu /* 22*91f16700Schasinglulu * Read cells from a given property of the given node. Any number of 32-bit 23*91f16700Schasinglulu * cells of the property can be read. Returns 0 on success, or a negative 24*91f16700Schasinglulu * FDT error value otherwise. 25*91f16700Schasinglulu */ 26*91f16700Schasinglulu int fdt_read_uint32_array(const void *dtb, int node, const char *prop_name, 27*91f16700Schasinglulu unsigned int cells, uint32_t *value) 28*91f16700Schasinglulu { 29*91f16700Schasinglulu const fdt32_t *prop; 30*91f16700Schasinglulu int value_len; 31*91f16700Schasinglulu 32*91f16700Schasinglulu assert(dtb != NULL); 33*91f16700Schasinglulu assert(prop_name != NULL); 34*91f16700Schasinglulu assert(value != NULL); 35*91f16700Schasinglulu assert(node >= 0); 36*91f16700Schasinglulu 37*91f16700Schasinglulu /* Access property and obtain its length (in bytes) */ 38*91f16700Schasinglulu prop = fdt_getprop(dtb, node, prop_name, &value_len); 39*91f16700Schasinglulu if (prop == NULL) { 40*91f16700Schasinglulu VERBOSE("Couldn't find property %s in dtb\n", prop_name); 41*91f16700Schasinglulu return -FDT_ERR_NOTFOUND; 42*91f16700Schasinglulu } 43*91f16700Schasinglulu 44*91f16700Schasinglulu /* Verify that property length can fill the entire array. */ 45*91f16700Schasinglulu if (NCELLS((unsigned int)value_len) < cells) { 46*91f16700Schasinglulu WARN("Property length mismatch\n"); 47*91f16700Schasinglulu return -FDT_ERR_BADVALUE; 48*91f16700Schasinglulu } 49*91f16700Schasinglulu 50*91f16700Schasinglulu for (unsigned int i = 0U; i < cells; i++) { 51*91f16700Schasinglulu value[i] = fdt32_to_cpu(prop[i]); 52*91f16700Schasinglulu } 53*91f16700Schasinglulu 54*91f16700Schasinglulu return 0; 55*91f16700Schasinglulu } 56*91f16700Schasinglulu 57*91f16700Schasinglulu int fdt_read_uint32(const void *dtb, int node, const char *prop_name, 58*91f16700Schasinglulu uint32_t *value) 59*91f16700Schasinglulu { 60*91f16700Schasinglulu return fdt_read_uint32_array(dtb, node, prop_name, 1, value); 61*91f16700Schasinglulu } 62*91f16700Schasinglulu 63*91f16700Schasinglulu uint32_t fdt_read_uint32_default(const void *dtb, int node, 64*91f16700Schasinglulu const char *prop_name, uint32_t dflt_value) 65*91f16700Schasinglulu { 66*91f16700Schasinglulu uint32_t ret = dflt_value; 67*91f16700Schasinglulu int err = fdt_read_uint32(dtb, node, prop_name, &ret); 68*91f16700Schasinglulu 69*91f16700Schasinglulu if (err < 0) { 70*91f16700Schasinglulu return dflt_value; 71*91f16700Schasinglulu } 72*91f16700Schasinglulu 73*91f16700Schasinglulu return ret; 74*91f16700Schasinglulu } 75*91f16700Schasinglulu 76*91f16700Schasinglulu int fdt_read_uint64(const void *dtb, int node, const char *prop_name, 77*91f16700Schasinglulu uint64_t *value) 78*91f16700Schasinglulu { 79*91f16700Schasinglulu uint32_t array[2] = {0, 0}; 80*91f16700Schasinglulu int ret; 81*91f16700Schasinglulu 82*91f16700Schasinglulu ret = fdt_read_uint32_array(dtb, node, prop_name, 2, array); 83*91f16700Schasinglulu if (ret < 0) { 84*91f16700Schasinglulu return ret; 85*91f16700Schasinglulu } 86*91f16700Schasinglulu 87*91f16700Schasinglulu *value = ((uint64_t)array[0] << 32) | array[1]; 88*91f16700Schasinglulu return 0; 89*91f16700Schasinglulu } 90*91f16700Schasinglulu 91*91f16700Schasinglulu /* 92*91f16700Schasinglulu * Read bytes from a given property of the given node. Any number of 93*91f16700Schasinglulu * bytes of the property can be read. The fdt pointer is updated. 94*91f16700Schasinglulu * Returns 0 on success, and -1 on error. 95*91f16700Schasinglulu */ 96*91f16700Schasinglulu int fdtw_read_bytes(const void *dtb, int node, const char *prop, 97*91f16700Schasinglulu unsigned int length, void *value) 98*91f16700Schasinglulu { 99*91f16700Schasinglulu const void *ptr; 100*91f16700Schasinglulu int value_len; 101*91f16700Schasinglulu 102*91f16700Schasinglulu assert(dtb != NULL); 103*91f16700Schasinglulu assert(prop != NULL); 104*91f16700Schasinglulu assert(value != NULL); 105*91f16700Schasinglulu assert(node >= 0); 106*91f16700Schasinglulu 107*91f16700Schasinglulu /* Access property and obtain its length (in bytes) */ 108*91f16700Schasinglulu ptr = fdt_getprop_namelen(dtb, node, prop, (int)strlen(prop), 109*91f16700Schasinglulu &value_len); 110*91f16700Schasinglulu if (ptr == NULL) { 111*91f16700Schasinglulu WARN("Couldn't find property %s in dtb\n", prop); 112*91f16700Schasinglulu return -1; 113*91f16700Schasinglulu } 114*91f16700Schasinglulu 115*91f16700Schasinglulu /* Verify that property length is not less than number of bytes */ 116*91f16700Schasinglulu if ((unsigned int)value_len < length) { 117*91f16700Schasinglulu WARN("Property length mismatch\n"); 118*91f16700Schasinglulu return -1; 119*91f16700Schasinglulu } 120*91f16700Schasinglulu 121*91f16700Schasinglulu (void)memcpy(value, ptr, length); 122*91f16700Schasinglulu 123*91f16700Schasinglulu return 0; 124*91f16700Schasinglulu } 125*91f16700Schasinglulu 126*91f16700Schasinglulu /* 127*91f16700Schasinglulu * Read string from a given property of the given node. Up to 'size - 1' 128*91f16700Schasinglulu * characters are read, and a NUL terminator is added. Returns 0 on success, 129*91f16700Schasinglulu * and -1 upon error. 130*91f16700Schasinglulu */ 131*91f16700Schasinglulu int fdtw_read_string(const void *dtb, int node, const char *prop, 132*91f16700Schasinglulu char *str, size_t size) 133*91f16700Schasinglulu { 134*91f16700Schasinglulu const char *ptr; 135*91f16700Schasinglulu size_t len; 136*91f16700Schasinglulu 137*91f16700Schasinglulu assert(dtb != NULL); 138*91f16700Schasinglulu assert(node >= 0); 139*91f16700Schasinglulu assert(prop != NULL); 140*91f16700Schasinglulu assert(str != NULL); 141*91f16700Schasinglulu assert(size > 0U); 142*91f16700Schasinglulu 143*91f16700Schasinglulu ptr = fdt_getprop_namelen(dtb, node, prop, (int)strlen(prop), NULL); 144*91f16700Schasinglulu if (ptr == NULL) { 145*91f16700Schasinglulu WARN("Couldn't find property %s in dtb\n", prop); 146*91f16700Schasinglulu return -1; 147*91f16700Schasinglulu } 148*91f16700Schasinglulu 149*91f16700Schasinglulu len = strlcpy(str, ptr, size); 150*91f16700Schasinglulu if (len >= size) { 151*91f16700Schasinglulu WARN("String of property %s in dtb has been truncated\n", prop); 152*91f16700Schasinglulu return -1; 153*91f16700Schasinglulu } 154*91f16700Schasinglulu 155*91f16700Schasinglulu return 0; 156*91f16700Schasinglulu } 157*91f16700Schasinglulu 158*91f16700Schasinglulu /* 159*91f16700Schasinglulu * Read UUID from a given property of the given node. Returns 0 on success, 160*91f16700Schasinglulu * and a negative value upon error. 161*91f16700Schasinglulu */ 162*91f16700Schasinglulu int fdtw_read_uuid(const void *dtb, int node, const char *prop, 163*91f16700Schasinglulu unsigned int length, uint8_t *uuid) 164*91f16700Schasinglulu { 165*91f16700Schasinglulu /* Buffer for UUID string (plus NUL terminator) */ 166*91f16700Schasinglulu char uuid_string[UUID_STRING_LENGTH + 1U]; 167*91f16700Schasinglulu int err; 168*91f16700Schasinglulu 169*91f16700Schasinglulu assert(dtb != NULL); 170*91f16700Schasinglulu assert(prop != NULL); 171*91f16700Schasinglulu assert(uuid != NULL); 172*91f16700Schasinglulu assert(node >= 0); 173*91f16700Schasinglulu 174*91f16700Schasinglulu if (length < UUID_BYTES_LENGTH) { 175*91f16700Schasinglulu return -EINVAL; 176*91f16700Schasinglulu } 177*91f16700Schasinglulu 178*91f16700Schasinglulu err = fdtw_read_string(dtb, node, prop, uuid_string, 179*91f16700Schasinglulu UUID_STRING_LENGTH + 1U); 180*91f16700Schasinglulu if (err != 0) { 181*91f16700Schasinglulu return err; 182*91f16700Schasinglulu } 183*91f16700Schasinglulu 184*91f16700Schasinglulu if (read_uuid(uuid, uuid_string) != 0) { 185*91f16700Schasinglulu return -FDT_ERR_BADVALUE; 186*91f16700Schasinglulu } 187*91f16700Schasinglulu 188*91f16700Schasinglulu return 0; 189*91f16700Schasinglulu } 190*91f16700Schasinglulu 191*91f16700Schasinglulu /* 192*91f16700Schasinglulu * Write cells in place to a given property of the given node. At most 2 cells 193*91f16700Schasinglulu * of the property are written. Returns 0 on success, and -1 upon error. 194*91f16700Schasinglulu */ 195*91f16700Schasinglulu int fdtw_write_inplace_cells(void *dtb, int node, const char *prop, 196*91f16700Schasinglulu unsigned int cells, void *value) 197*91f16700Schasinglulu { 198*91f16700Schasinglulu int err, len; 199*91f16700Schasinglulu 200*91f16700Schasinglulu assert(dtb != NULL); 201*91f16700Schasinglulu assert(prop != NULL); 202*91f16700Schasinglulu assert(value != NULL); 203*91f16700Schasinglulu assert(node >= 0); 204*91f16700Schasinglulu 205*91f16700Schasinglulu /* We expect either 1 or 2 cell property */ 206*91f16700Schasinglulu assert(cells <= 2U); 207*91f16700Schasinglulu 208*91f16700Schasinglulu if (cells == 2U) 209*91f16700Schasinglulu *(fdt64_t *)value = cpu_to_fdt64(*(uint64_t *)value); 210*91f16700Schasinglulu else 211*91f16700Schasinglulu *(fdt32_t *)value = cpu_to_fdt32(*(uint32_t *)value); 212*91f16700Schasinglulu 213*91f16700Schasinglulu len = (int)cells * 4; 214*91f16700Schasinglulu 215*91f16700Schasinglulu /* Set property value in place */ 216*91f16700Schasinglulu err = fdt_setprop_inplace(dtb, node, prop, value, len); 217*91f16700Schasinglulu if (err != 0) { 218*91f16700Schasinglulu WARN("Modify property %s failed with error %d\n", prop, err); 219*91f16700Schasinglulu return -1; 220*91f16700Schasinglulu } 221*91f16700Schasinglulu 222*91f16700Schasinglulu return 0; 223*91f16700Schasinglulu } 224*91f16700Schasinglulu 225*91f16700Schasinglulu /* 226*91f16700Schasinglulu * Write bytes in place to a given property of the given node. 227*91f16700Schasinglulu * Any number of bytes of the property can be written. 228*91f16700Schasinglulu * Returns 0 on success, and < 0 on error. 229*91f16700Schasinglulu */ 230*91f16700Schasinglulu int fdtw_write_inplace_bytes(void *dtb, int node, const char *prop, 231*91f16700Schasinglulu unsigned int length, const void *data) 232*91f16700Schasinglulu { 233*91f16700Schasinglulu const void *ptr; 234*91f16700Schasinglulu int namelen, value_len, err; 235*91f16700Schasinglulu 236*91f16700Schasinglulu assert(dtb != NULL); 237*91f16700Schasinglulu assert(prop != NULL); 238*91f16700Schasinglulu assert(data != NULL); 239*91f16700Schasinglulu assert(node >= 0); 240*91f16700Schasinglulu 241*91f16700Schasinglulu namelen = (int)strlen(prop); 242*91f16700Schasinglulu 243*91f16700Schasinglulu /* Access property and obtain its length in bytes */ 244*91f16700Schasinglulu ptr = fdt_getprop_namelen(dtb, node, prop, namelen, &value_len); 245*91f16700Schasinglulu if (ptr == NULL) { 246*91f16700Schasinglulu WARN("Couldn't find property %s in dtb\n", prop); 247*91f16700Schasinglulu return -1; 248*91f16700Schasinglulu } 249*91f16700Schasinglulu 250*91f16700Schasinglulu /* Verify that property length is not less than number of bytes */ 251*91f16700Schasinglulu if ((unsigned int)value_len < length) { 252*91f16700Schasinglulu WARN("Property length mismatch\n"); 253*91f16700Schasinglulu return -1; 254*91f16700Schasinglulu } 255*91f16700Schasinglulu 256*91f16700Schasinglulu /* Set property value in place */ 257*91f16700Schasinglulu err = fdt_setprop_inplace_namelen_partial(dtb, node, prop, 258*91f16700Schasinglulu namelen, 0, 259*91f16700Schasinglulu data, (int)length); 260*91f16700Schasinglulu if (err != 0) { 261*91f16700Schasinglulu WARN("Set property %s failed with error %d\n", prop, err); 262*91f16700Schasinglulu } 263*91f16700Schasinglulu 264*91f16700Schasinglulu return err; 265*91f16700Schasinglulu } 266*91f16700Schasinglulu 267*91f16700Schasinglulu static uint64_t fdt_read_prop_cells(const fdt32_t *prop, int nr_cells) 268*91f16700Schasinglulu { 269*91f16700Schasinglulu uint64_t reg = fdt32_to_cpu(prop[0]); 270*91f16700Schasinglulu 271*91f16700Schasinglulu if (nr_cells > 1) { 272*91f16700Schasinglulu reg = (reg << 32) | fdt32_to_cpu(prop[1]); 273*91f16700Schasinglulu } 274*91f16700Schasinglulu 275*91f16700Schasinglulu return reg; 276*91f16700Schasinglulu } 277*91f16700Schasinglulu 278*91f16700Schasinglulu int fdt_get_reg_props_by_index(const void *dtb, int node, int index, 279*91f16700Schasinglulu uintptr_t *base, size_t *size) 280*91f16700Schasinglulu { 281*91f16700Schasinglulu const fdt32_t *prop; 282*91f16700Schasinglulu int parent, len; 283*91f16700Schasinglulu int ac, sc; 284*91f16700Schasinglulu int cell; 285*91f16700Schasinglulu 286*91f16700Schasinglulu parent = fdt_parent_offset(dtb, node); 287*91f16700Schasinglulu if (parent < 0) { 288*91f16700Schasinglulu return -FDT_ERR_BADOFFSET; 289*91f16700Schasinglulu } 290*91f16700Schasinglulu 291*91f16700Schasinglulu ac = fdt_address_cells(dtb, parent); 292*91f16700Schasinglulu sc = fdt_size_cells(dtb, parent); 293*91f16700Schasinglulu 294*91f16700Schasinglulu cell = index * (ac + sc); 295*91f16700Schasinglulu 296*91f16700Schasinglulu prop = fdt_getprop(dtb, node, "reg", &len); 297*91f16700Schasinglulu if (prop == NULL) { 298*91f16700Schasinglulu WARN("Couldn't find \"reg\" property in dtb\n"); 299*91f16700Schasinglulu return -FDT_ERR_NOTFOUND; 300*91f16700Schasinglulu } 301*91f16700Schasinglulu 302*91f16700Schasinglulu if (((cell + ac + sc) * (int)sizeof(uint32_t)) > len) { 303*91f16700Schasinglulu return -FDT_ERR_BADVALUE; 304*91f16700Schasinglulu } 305*91f16700Schasinglulu 306*91f16700Schasinglulu if (base != NULL) { 307*91f16700Schasinglulu *base = (uintptr_t)fdt_read_prop_cells(&prop[cell], ac); 308*91f16700Schasinglulu } 309*91f16700Schasinglulu 310*91f16700Schasinglulu if (size != NULL) { 311*91f16700Schasinglulu *size = (size_t)fdt_read_prop_cells(&prop[cell + ac], sc); 312*91f16700Schasinglulu } 313*91f16700Schasinglulu 314*91f16700Schasinglulu return 0; 315*91f16700Schasinglulu } 316*91f16700Schasinglulu 317*91f16700Schasinglulu /******************************************************************************* 318*91f16700Schasinglulu * This function fills reg node info (base & size) with an index found by 319*91f16700Schasinglulu * checking the reg-names node. 320*91f16700Schasinglulu * Returns 0 on success and a negative FDT error code on failure. 321*91f16700Schasinglulu ******************************************************************************/ 322*91f16700Schasinglulu int fdt_get_reg_props_by_name(const void *dtb, int node, const char *name, 323*91f16700Schasinglulu uintptr_t *base, size_t *size) 324*91f16700Schasinglulu { 325*91f16700Schasinglulu int index; 326*91f16700Schasinglulu 327*91f16700Schasinglulu index = fdt_stringlist_search(dtb, node, "reg-names", name); 328*91f16700Schasinglulu if (index < 0) { 329*91f16700Schasinglulu return index; 330*91f16700Schasinglulu } 331*91f16700Schasinglulu 332*91f16700Schasinglulu return fdt_get_reg_props_by_index(dtb, node, index, base, size); 333*91f16700Schasinglulu } 334*91f16700Schasinglulu 335*91f16700Schasinglulu /******************************************************************************* 336*91f16700Schasinglulu * This function gets the stdout path node. 337*91f16700Schasinglulu * It reads the value indicated inside the device tree. 338*91f16700Schasinglulu * Returns node offset on success and a negative FDT error code on failure. 339*91f16700Schasinglulu ******************************************************************************/ 340*91f16700Schasinglulu int fdt_get_stdout_node_offset(const void *dtb) 341*91f16700Schasinglulu { 342*91f16700Schasinglulu int node; 343*91f16700Schasinglulu const char *prop, *path; 344*91f16700Schasinglulu int len; 345*91f16700Schasinglulu 346*91f16700Schasinglulu /* The /secure-chosen node takes precedence over the standard one. */ 347*91f16700Schasinglulu node = fdt_path_offset(dtb, "/secure-chosen"); 348*91f16700Schasinglulu if (node < 0) { 349*91f16700Schasinglulu node = fdt_path_offset(dtb, "/chosen"); 350*91f16700Schasinglulu if (node < 0) { 351*91f16700Schasinglulu return -FDT_ERR_NOTFOUND; 352*91f16700Schasinglulu } 353*91f16700Schasinglulu } 354*91f16700Schasinglulu 355*91f16700Schasinglulu prop = fdt_getprop(dtb, node, "stdout-path", NULL); 356*91f16700Schasinglulu if (prop == NULL) { 357*91f16700Schasinglulu return -FDT_ERR_NOTFOUND; 358*91f16700Schasinglulu } 359*91f16700Schasinglulu 360*91f16700Schasinglulu /* Determine the actual path length, as a colon terminates the path. */ 361*91f16700Schasinglulu path = strchr(prop, ':'); 362*91f16700Schasinglulu if (path == NULL) { 363*91f16700Schasinglulu len = strlen(prop); 364*91f16700Schasinglulu } else { 365*91f16700Schasinglulu len = path - prop; 366*91f16700Schasinglulu } 367*91f16700Schasinglulu 368*91f16700Schasinglulu /* Aliases cannot start with a '/', so it must be the actual path. */ 369*91f16700Schasinglulu if (prop[0] == '/') { 370*91f16700Schasinglulu return fdt_path_offset_namelen(dtb, prop, len); 371*91f16700Schasinglulu } 372*91f16700Schasinglulu 373*91f16700Schasinglulu /* Lookup the alias, as this contains the actual path. */ 374*91f16700Schasinglulu path = fdt_get_alias_namelen(dtb, prop, len); 375*91f16700Schasinglulu if (path == NULL) { 376*91f16700Schasinglulu return -FDT_ERR_NOTFOUND; 377*91f16700Schasinglulu } 378*91f16700Schasinglulu 379*91f16700Schasinglulu return fdt_path_offset(dtb, path); 380*91f16700Schasinglulu } 381*91f16700Schasinglulu 382*91f16700Schasinglulu 383*91f16700Schasinglulu /******************************************************************************* 384*91f16700Schasinglulu * Only devices which are direct children of root node use CPU address domain. 385*91f16700Schasinglulu * All other devices use addresses that are local to the device node and cannot 386*91f16700Schasinglulu * directly used by CPU. Device tree provides an address translation mechanism 387*91f16700Schasinglulu * through "ranges" property which provides mappings from local address space to 388*91f16700Schasinglulu * parent address space. Since a device could be a child of a child node to the 389*91f16700Schasinglulu * root node, there can be more than one level of address translation needed to 390*91f16700Schasinglulu * map the device local address space to CPU address space. 391*91f16700Schasinglulu * fdtw_translate_address() API performs address translation of a local address 392*91f16700Schasinglulu * to a global address with help of various helper functions. 393*91f16700Schasinglulu ******************************************************************************/ 394*91f16700Schasinglulu 395*91f16700Schasinglulu static bool fdtw_xlat_hit(const fdt32_t *value, int child_addr_size, 396*91f16700Schasinglulu int parent_addr_size, int range_size, uint64_t base_address, 397*91f16700Schasinglulu uint64_t *translated_addr) 398*91f16700Schasinglulu { 399*91f16700Schasinglulu uint64_t local_address, parent_address, addr_range; 400*91f16700Schasinglulu 401*91f16700Schasinglulu local_address = fdt_read_prop_cells(value, child_addr_size); 402*91f16700Schasinglulu parent_address = fdt_read_prop_cells(value + child_addr_size, 403*91f16700Schasinglulu parent_addr_size); 404*91f16700Schasinglulu addr_range = fdt_read_prop_cells(value + child_addr_size + 405*91f16700Schasinglulu parent_addr_size, 406*91f16700Schasinglulu range_size); 407*91f16700Schasinglulu VERBOSE("DT: Address %" PRIx64 " mapped to %" PRIx64 " with range %" PRIx64 "\n", 408*91f16700Schasinglulu local_address, parent_address, addr_range); 409*91f16700Schasinglulu 410*91f16700Schasinglulu /* Perform range check */ 411*91f16700Schasinglulu if ((base_address < local_address) || 412*91f16700Schasinglulu (base_address >= local_address + addr_range)) { 413*91f16700Schasinglulu return false; 414*91f16700Schasinglulu } 415*91f16700Schasinglulu 416*91f16700Schasinglulu /* Found hit for the addr range that needs to be translated */ 417*91f16700Schasinglulu *translated_addr = parent_address + (base_address - local_address); 418*91f16700Schasinglulu VERBOSE("DT: child address %" PRIx64 "mapped to %" PRIx64 " in parent bus\n", 419*91f16700Schasinglulu local_address, parent_address); 420*91f16700Schasinglulu return true; 421*91f16700Schasinglulu } 422*91f16700Schasinglulu 423*91f16700Schasinglulu #define ILLEGAL_ADDR ULL(~0) 424*91f16700Schasinglulu 425*91f16700Schasinglulu static uint64_t fdtw_search_all_xlat_entries(const void *dtb, 426*91f16700Schasinglulu const struct fdt_property *ranges_prop, 427*91f16700Schasinglulu int local_bus, uint64_t base_address) 428*91f16700Schasinglulu { 429*91f16700Schasinglulu uint64_t translated_addr; 430*91f16700Schasinglulu const fdt32_t *next_entry; 431*91f16700Schasinglulu int parent_bus_node, nxlat_entries, length; 432*91f16700Schasinglulu int self_addr_cells, parent_addr_cells, self_size_cells, ncells_xlat; 433*91f16700Schasinglulu 434*91f16700Schasinglulu /* 435*91f16700Schasinglulu * The number of cells in one translation entry in ranges is the sum of 436*91f16700Schasinglulu * the following values: 437*91f16700Schasinglulu * self#address-cells + parent#address-cells + self#size-cells 438*91f16700Schasinglulu * Ex: the iofpga ranges property has one translation entry with 4 cells 439*91f16700Schasinglulu * They represent iofpga#addr-cells + motherboard#addr-cells + iofpga#size-cells 440*91f16700Schasinglulu * = 1 + 2 + 1 441*91f16700Schasinglulu */ 442*91f16700Schasinglulu 443*91f16700Schasinglulu parent_bus_node = fdt_parent_offset(dtb, local_bus); 444*91f16700Schasinglulu self_addr_cells = fdt_address_cells(dtb, local_bus); 445*91f16700Schasinglulu self_size_cells = fdt_size_cells(dtb, local_bus); 446*91f16700Schasinglulu parent_addr_cells = fdt_address_cells(dtb, parent_bus_node); 447*91f16700Schasinglulu 448*91f16700Schasinglulu /* Number of cells per translation entry i.e., mapping */ 449*91f16700Schasinglulu ncells_xlat = self_addr_cells + parent_addr_cells + self_size_cells; 450*91f16700Schasinglulu 451*91f16700Schasinglulu assert(ncells_xlat > 0); 452*91f16700Schasinglulu 453*91f16700Schasinglulu /* 454*91f16700Schasinglulu * Find the number of translations(mappings) specified in the current 455*91f16700Schasinglulu * `ranges` property. Note that length represents number of bytes and 456*91f16700Schasinglulu * is stored in big endian mode. 457*91f16700Schasinglulu */ 458*91f16700Schasinglulu length = fdt32_to_cpu(ranges_prop->len); 459*91f16700Schasinglulu nxlat_entries = (length/sizeof(uint32_t))/ncells_xlat; 460*91f16700Schasinglulu 461*91f16700Schasinglulu assert(nxlat_entries > 0); 462*91f16700Schasinglulu 463*91f16700Schasinglulu next_entry = (const fdt32_t *)ranges_prop->data; 464*91f16700Schasinglulu 465*91f16700Schasinglulu /* Iterate over the entries in the "ranges" */ 466*91f16700Schasinglulu for (int i = 0; i < nxlat_entries; i++) { 467*91f16700Schasinglulu if (fdtw_xlat_hit(next_entry, self_addr_cells, 468*91f16700Schasinglulu parent_addr_cells, self_size_cells, base_address, 469*91f16700Schasinglulu &translated_addr)){ 470*91f16700Schasinglulu return translated_addr; 471*91f16700Schasinglulu } 472*91f16700Schasinglulu next_entry = next_entry + ncells_xlat; 473*91f16700Schasinglulu } 474*91f16700Schasinglulu 475*91f16700Schasinglulu INFO("DT: No translation found for address %" PRIx64 " in node %s\n", 476*91f16700Schasinglulu base_address, fdt_get_name(dtb, local_bus, NULL)); 477*91f16700Schasinglulu return ILLEGAL_ADDR; 478*91f16700Schasinglulu } 479*91f16700Schasinglulu 480*91f16700Schasinglulu 481*91f16700Schasinglulu /******************************************************************************* 482*91f16700Schasinglulu * address mapping needs to be done recursively starting from current node to 483*91f16700Schasinglulu * root node through all intermediate parent nodes. 484*91f16700Schasinglulu * Sample device tree is shown here: 485*91f16700Schasinglulu 486*91f16700Schasinglulu smb@0,0 { 487*91f16700Schasinglulu compatible = "simple-bus"; 488*91f16700Schasinglulu 489*91f16700Schasinglulu #address-cells = <2>; 490*91f16700Schasinglulu #size-cells = <1>; 491*91f16700Schasinglulu ranges = <0 0 0 0x08000000 0x04000000>, 492*91f16700Schasinglulu <1 0 0 0x14000000 0x04000000>, 493*91f16700Schasinglulu <2 0 0 0x18000000 0x04000000>, 494*91f16700Schasinglulu <3 0 0 0x1c000000 0x04000000>, 495*91f16700Schasinglulu <4 0 0 0x0c000000 0x04000000>, 496*91f16700Schasinglulu <5 0 0 0x10000000 0x04000000>; 497*91f16700Schasinglulu 498*91f16700Schasinglulu motherboard { 499*91f16700Schasinglulu arm,v2m-memory-map = "rs1"; 500*91f16700Schasinglulu compatible = "arm,vexpress,v2m-p1", "simple-bus"; 501*91f16700Schasinglulu #address-cells = <2>; 502*91f16700Schasinglulu #size-cells = <1>; 503*91f16700Schasinglulu ranges; 504*91f16700Schasinglulu 505*91f16700Schasinglulu iofpga@3,00000000 { 506*91f16700Schasinglulu compatible = "arm,amba-bus", "simple-bus"; 507*91f16700Schasinglulu #address-cells = <1>; 508*91f16700Schasinglulu #size-cells = <1>; 509*91f16700Schasinglulu ranges = <0 3 0 0x200000>; 510*91f16700Schasinglulu v2m_serial1: uart@a0000 { 511*91f16700Schasinglulu compatible = "arm,pl011", "arm,primecell"; 512*91f16700Schasinglulu reg = <0x0a0000 0x1000>; 513*91f16700Schasinglulu interrupts = <0 6 4>; 514*91f16700Schasinglulu clocks = <&v2m_clk24mhz>, <&v2m_clk24mhz>; 515*91f16700Schasinglulu clock-names = "uartclk", "apb_pclk"; 516*91f16700Schasinglulu }; 517*91f16700Schasinglulu }; 518*91f16700Schasinglulu }; 519*91f16700Schasinglulu 520*91f16700Schasinglulu * As seen above, there are 3 levels of address translations needed. An empty 521*91f16700Schasinglulu * `ranges` property denotes identity mapping (as seen in `motherboard` node). 522*91f16700Schasinglulu * Each ranges property can map a set of child addresses to parent bus. Hence 523*91f16700Schasinglulu * there can be more than 1 (translation) entry in the ranges property as seen 524*91f16700Schasinglulu * in the `smb` node which has 6 translation entries. 525*91f16700Schasinglulu ******************************************************************************/ 526*91f16700Schasinglulu 527*91f16700Schasinglulu /* Recursive implementation */ 528*91f16700Schasinglulu uint64_t fdtw_translate_address(const void *dtb, int node, 529*91f16700Schasinglulu uint64_t base_address) 530*91f16700Schasinglulu { 531*91f16700Schasinglulu int length, local_bus_node; 532*91f16700Schasinglulu const char *node_name; 533*91f16700Schasinglulu uint64_t global_address; 534*91f16700Schasinglulu 535*91f16700Schasinglulu local_bus_node = fdt_parent_offset(dtb, node); 536*91f16700Schasinglulu node_name = fdt_get_name(dtb, local_bus_node, NULL); 537*91f16700Schasinglulu 538*91f16700Schasinglulu /* 539*91f16700Schasinglulu * In the example given above, starting from the leaf node: 540*91f16700Schasinglulu * uart@a000 represents the current node 541*91f16700Schasinglulu * iofpga@3,00000000 represents the local bus 542*91f16700Schasinglulu * motherboard represents the parent bus 543*91f16700Schasinglulu */ 544*91f16700Schasinglulu 545*91f16700Schasinglulu /* Read the ranges property */ 546*91f16700Schasinglulu const struct fdt_property *property = fdt_get_property(dtb, 547*91f16700Schasinglulu local_bus_node, "ranges", &length); 548*91f16700Schasinglulu 549*91f16700Schasinglulu if (property == NULL) { 550*91f16700Schasinglulu if (local_bus_node == 0) { 551*91f16700Schasinglulu /* 552*91f16700Schasinglulu * root node doesn't have range property as addresses 553*91f16700Schasinglulu * are in CPU address space. 554*91f16700Schasinglulu */ 555*91f16700Schasinglulu return base_address; 556*91f16700Schasinglulu } 557*91f16700Schasinglulu INFO("DT: Couldn't find ranges property in node %s\n", 558*91f16700Schasinglulu node_name); 559*91f16700Schasinglulu return ILLEGAL_ADDR; 560*91f16700Schasinglulu } else if (length == 0) { 561*91f16700Schasinglulu /* empty ranges indicates identity map to parent bus */ 562*91f16700Schasinglulu return fdtw_translate_address(dtb, local_bus_node, base_address); 563*91f16700Schasinglulu } 564*91f16700Schasinglulu 565*91f16700Schasinglulu VERBOSE("DT: Translation lookup in node %s at offset %d\n", node_name, 566*91f16700Schasinglulu local_bus_node); 567*91f16700Schasinglulu global_address = fdtw_search_all_xlat_entries(dtb, property, 568*91f16700Schasinglulu local_bus_node, base_address); 569*91f16700Schasinglulu 570*91f16700Schasinglulu if (global_address == ILLEGAL_ADDR) { 571*91f16700Schasinglulu return ILLEGAL_ADDR; 572*91f16700Schasinglulu } 573*91f16700Schasinglulu 574*91f16700Schasinglulu /* Translate the local device address recursively */ 575*91f16700Schasinglulu return fdtw_translate_address(dtb, local_bus_node, global_address); 576*91f16700Schasinglulu } 577*91f16700Schasinglulu 578*91f16700Schasinglulu /* 579*91f16700Schasinglulu * For every CPU node (`/cpus/cpu@n`) in an FDT, execute a callback passing a 580*91f16700Schasinglulu * pointer to the FDT and the offset of the CPU node. If the return value of the 581*91f16700Schasinglulu * callback is negative, it is treated as an error and the loop is aborted. In 582*91f16700Schasinglulu * this situation, the value of the callback is returned from the function. 583*91f16700Schasinglulu * 584*91f16700Schasinglulu * Returns `0` on success, or a negative integer representing an error code. 585*91f16700Schasinglulu */ 586*91f16700Schasinglulu int fdtw_for_each_cpu(const void *dtb, 587*91f16700Schasinglulu int (*callback)(const void *dtb, int node, uintptr_t mpidr)) 588*91f16700Schasinglulu { 589*91f16700Schasinglulu int ret = 0; 590*91f16700Schasinglulu int parent, node = 0; 591*91f16700Schasinglulu 592*91f16700Schasinglulu parent = fdt_path_offset(dtb, "/cpus"); 593*91f16700Schasinglulu if (parent < 0) { 594*91f16700Schasinglulu return parent; 595*91f16700Schasinglulu } 596*91f16700Schasinglulu 597*91f16700Schasinglulu fdt_for_each_subnode(node, dtb, parent) { 598*91f16700Schasinglulu const char *name; 599*91f16700Schasinglulu int len; 600*91f16700Schasinglulu 601*91f16700Schasinglulu uintptr_t mpidr = 0U; 602*91f16700Schasinglulu 603*91f16700Schasinglulu name = fdt_get_name(dtb, node, &len); 604*91f16700Schasinglulu if (strncmp(name, "cpu@", 4) != 0) { 605*91f16700Schasinglulu continue; 606*91f16700Schasinglulu } 607*91f16700Schasinglulu 608*91f16700Schasinglulu ret = fdt_get_reg_props_by_index(dtb, node, 0, &mpidr, NULL); 609*91f16700Schasinglulu if (ret < 0) { 610*91f16700Schasinglulu break; 611*91f16700Schasinglulu } 612*91f16700Schasinglulu 613*91f16700Schasinglulu ret = callback(dtb, node, mpidr); 614*91f16700Schasinglulu if (ret < 0) { 615*91f16700Schasinglulu break; 616*91f16700Schasinglulu } 617*91f16700Schasinglulu } 618*91f16700Schasinglulu 619*91f16700Schasinglulu return ret; 620*91f16700Schasinglulu } 621*91f16700Schasinglulu 622*91f16700Schasinglulu /* 623*91f16700Schasinglulu * Find a given node in device tree. If not present, add it. 624*91f16700Schasinglulu * Returns offset of node found/added on success, and < 0 on error. 625*91f16700Schasinglulu */ 626*91f16700Schasinglulu int fdtw_find_or_add_subnode(void *fdt, int parentoffset, const char *name) 627*91f16700Schasinglulu { 628*91f16700Schasinglulu int offset; 629*91f16700Schasinglulu 630*91f16700Schasinglulu offset = fdt_subnode_offset(fdt, parentoffset, name); 631*91f16700Schasinglulu 632*91f16700Schasinglulu if (offset == -FDT_ERR_NOTFOUND) { 633*91f16700Schasinglulu offset = fdt_add_subnode(fdt, parentoffset, name); 634*91f16700Schasinglulu } 635*91f16700Schasinglulu 636*91f16700Schasinglulu if (offset < 0) { 637*91f16700Schasinglulu ERROR("%s: %s: %s\n", __func__, name, fdt_strerror(offset)); 638*91f16700Schasinglulu } 639*91f16700Schasinglulu 640*91f16700Schasinglulu return offset; 641*91f16700Schasinglulu } 642