Back to home page

LXR

 
 

    


File indexing completed on 2025-05-11 08:24:08

0001 /* SPDX-License-Identifier: BSD-2-Clause */
0002 
0003 /**
0004  * @file
0005  *
0006  * @ingroup ofw
0007  */
0008 
0009 /*
0010  * Copyright (C) 2020 Niteesh Babu G S <niteesh.gs@gmail.com>
0011  *
0012  * Redistribution and use in source and binary forms, with or without
0013  * modification, are permitted provided that the following conditions
0014  * are met:
0015  * 1. Redistributions of source code must retain the above copyright
0016  *    notice, this list of conditions and the following disclaimer.
0017  * 2. Redistributions in binary form must reproduce the above copyright
0018  *    notice, this list of conditions and the following disclaimer in the
0019  *    documentation and/or other materials provided with the distribution.
0020  *
0021  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
0022  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
0023  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
0024  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
0025  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
0026  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
0027  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
0028  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
0029  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
0030  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
0031  * POSSIBILITY OF SUCH DAMAGE.
0032  */
0033 
0034 #ifdef HAVE_CONFIG_H
0035 #include "config.h"
0036 #endif
0037 
0038 #include <bsp/fdt.h>
0039 #include <sys/param.h>
0040 #include <ofw/ofw.h>
0041 #include <libfdt.h>
0042 #include <assert.h>
0043 #include <rtems/sysinit.h>
0044 #include <ofw/ofw_test.h>
0045 #include <rtems/score/assert.h>
0046 
0047 static void *fdtp = NULL;
0048 
0049 static phandle_t rtems_fdt_offset_to_phandle( int offset )
0050 {
0051   if (offset < 0) {
0052     return 0;
0053   }
0054 
0055   return (phandle_t)offset + fdt_off_dt_struct(fdtp);
0056 }
0057 
0058 static int rtems_fdt_phandle_to_offset( phandle_t handle )
0059 {
0060   int off;
0061   int fdt_off;
0062 
0063   off = (int) handle;
0064   fdt_off = fdt_off_dt_struct(fdtp);
0065 
0066   if (off < fdt_off) {
0067     return -1;
0068 
0069   }
0070 
0071   return off - fdt_off;
0072 }
0073 
0074 void rtems_ofw_init( void ) {
0075   int rv;
0076   const void *fdt;
0077   
0078   fdt = bsp_fdt_get();
0079 
0080   rv = fdt_check_header(fdt);
0081 
0082   /*
0083    * If the FDT is invalid exit through fatal.
0084    */
0085   if (rv != 0) {
0086     rtems_fatal_error_occurred(RTEMS_NOT_CONFIGURED);
0087   }
0088 
0089   fdtp = (void *)fdt;
0090 }
0091 
0092 RTEMS_SYSINIT_ITEM(
0093   rtems_ofw_init,
0094   RTEMS_SYSINIT_BSP_PRE_DRIVERS,
0095   RTEMS_SYSINIT_ORDER_FIRST
0096 );
0097 
0098 phandle_t rtems_ofw_peer( phandle_t node )
0099 {
0100   int offset;
0101 
0102   if (node == 0) {
0103     int root = fdt_path_offset(fdtp, "/");
0104     return rtems_fdt_offset_to_phandle(root);
0105   }
0106 
0107   offset = rtems_fdt_phandle_to_offset(node);
0108   if (offset < 0) {
0109     return 0;
0110   }
0111 
0112   offset = fdt_next_subnode(fdtp, offset);
0113   return rtems_fdt_offset_to_phandle(offset);
0114 }
0115 
0116 phandle_t rtems_ofw_child( phandle_t node )
0117 {
0118   int offset;
0119 
0120   offset = rtems_fdt_phandle_to_offset(node);
0121 
0122   if (offset < 0) {
0123     return 0;
0124   }
0125 
0126   offset = fdt_first_subnode(fdtp, offset);
0127   return rtems_fdt_offset_to_phandle(offset);
0128 }
0129 
0130 phandle_t rtems_ofw_parent( phandle_t node )
0131 {
0132   int offset;
0133 
0134   offset = rtems_fdt_phandle_to_offset(node);
0135 
0136   if (offset < 0) {
0137     return 0;
0138   }
0139 
0140   offset = fdt_parent_offset(fdtp, offset);
0141   return rtems_fdt_offset_to_phandle(offset);
0142 }
0143 
0144 ssize_t rtems_ofw_get_prop_len(
0145   phandle_t node,
0146   const char *propname
0147 )
0148 {
0149   int offset;
0150   int len;
0151   const void *prop;
0152 
0153   offset = rtems_fdt_phandle_to_offset(node);
0154 
0155   if (offset < 0) {
0156     return -1;
0157   }
0158 
0159   prop = fdt_getprop(fdtp, offset, propname, &len);
0160 
0161   if (prop == NULL && strcmp(propname, "name") == 0) {
0162     fdt_get_name(fdtp, offset, &len);
0163     return len + 1;
0164   }
0165 
0166   if (prop == NULL && offset == fdt_path_offset(fdtp, "/chosen")) {
0167     if (strcmp(propname, "fdtbootcpu") == 0)
0168       return sizeof(pcell_t);
0169     if (strcmp(propname, "fdtmemreserv") == 0)
0170       return 2 * sizeof(uint64_t) * fdt_num_mem_rsv(fdtp);
0171   }
0172 
0173   if (prop == NULL) {
0174     return -1;
0175   }
0176 
0177   return len;
0178 }
0179 
0180 ssize_t rtems_ofw_get_prop(
0181   phandle_t    node,
0182   const char  *propname,
0183   void        *buf,
0184   size_t       bufsize
0185 )
0186 {
0187   const void *prop;
0188   int offset;
0189   int len;
0190   int copy_len;
0191   uint32_t cpuid;
0192 
0193   offset = rtems_fdt_phandle_to_offset(node);
0194 
0195   if (offset < 0) {
0196     return -1;
0197   }
0198 
0199   prop = fdt_getprop(fdtp, offset, propname, &len);
0200 
0201   if (prop == NULL && strcmp(propname, "name") == 0) {
0202     prop = fdt_get_name(fdtp, offset, &len);
0203 
0204     /* Node name's are 1-31 chars in length consisting of only
0205      * ascii chars and are null terminated */
0206     strlcpy(buf, prop, bufsize);
0207 
0208     /* Return the length of the name including the null byte
0209      * rather than the amount copied.
0210      * This is the behaviour in libBSD ofw_fdt_getprop
0211      */
0212     return len + 1;
0213   }
0214 
0215   if (prop == NULL && offset == fdt_path_offset(fdtp, "/chosen")) {
0216     if (strcmp(propname, "fdtbootcpu") == 0) {
0217       cpuid = cpu_to_fdt32(fdt_boot_cpuid_phys(fdtp));
0218       len = sizeof(cpuid);
0219       prop = &cpuid;
0220     }
0221     if (strcmp(propname, "fdtmemreserv") == 0) {
0222       prop = (char *)fdtp + fdt_off_mem_rsvmap(fdtp);
0223       len = sizeof(uint64_t)*2*fdt_num_mem_rsv(fdtp);
0224     }
0225   }
0226 
0227   if (prop == NULL) {
0228     return -1;
0229   }
0230 
0231   copy_len = MIN(len, bufsize);
0232   _Assert(copy_len <= bufsize);
0233   memmove(buf, prop, copy_len);
0234 
0235   return len;
0236 }
0237 
0238 ssize_t rtems_ofw_get_enc_prop(
0239   phandle_t    node,
0240   const char  *prop,
0241   pcell_t     *buf,
0242   size_t       len
0243 )
0244 {
0245   ssize_t rv;
0246 
0247   assert(len % sizeof(pcell_t) == 0);
0248   rv = rtems_ofw_get_prop(node, prop, buf, len);
0249 
0250   if (rv < 0) {
0251     return rv;
0252   }
0253 
0254   for (int i = 0; i < (len / 4); i++) {
0255     buf[i] = fdt32_to_cpu(buf[i]);
0256   }
0257 
0258   return rv;
0259 }
0260 
0261 int rtems_ofw_has_prop(
0262   phandle_t    node,
0263   const char  *propname
0264 )
0265 {
0266   ssize_t rv;
0267 
0268   rv = rtems_ofw_get_prop_len(node, propname);
0269   return rv >= 0 ? 1 : 0;
0270 }
0271 
0272 ssize_t rtems_ofw_search_prop(
0273   phandle_t    node,
0274   const char  *propname,
0275   void        *buf,
0276   size_t       len
0277 )
0278 {
0279   ssize_t rv;
0280 
0281   for (; node != 0; node = rtems_ofw_parent(node)) {
0282     if ((rv = rtems_ofw_get_prop(node, propname, buf, len) != -1)) {
0283       return rv;
0284     }
0285   }
0286 
0287   return -1;
0288 }
0289 
0290 ssize_t rtems_ofw_search_enc_prop(
0291   phandle_t    node,
0292   const char  *propname,
0293   pcell_t     *buf,
0294   size_t       len
0295 )
0296 {
0297   ssize_t rv;
0298 
0299   for (; node != 0; node = rtems_ofw_parent(node)) {
0300     if ((rv = rtems_ofw_get_enc_prop(node, propname, buf, len) != -1)) {
0301       return rv;
0302     }
0303   }
0304 
0305   return -1;
0306 }
0307 
0308 ssize_t rtems_ofw_get_prop_alloc(
0309   phandle_t    node,
0310   const char  *propname,
0311   void       **buf
0312 )
0313 {
0314   ssize_t len;
0315 
0316   *buf = NULL;
0317   if ((len = rtems_ofw_get_prop_len(node, propname)) == -1) {
0318     return -1;
0319   }
0320 
0321   if (len > 0) {
0322     *buf = malloc(len);
0323     if (*buf == NULL) {
0324       return -1;
0325     }
0326 
0327     if (rtems_ofw_get_prop(node, propname, *buf, len) == -1) {
0328       rtems_ofw_free(*buf);
0329       *buf = NULL;
0330       return -1;
0331     }
0332   }
0333 
0334   return len;
0335 }
0336 
0337 ssize_t rtems_ofw_get_prop_alloc_multi(
0338   phandle_t    node,
0339   const char  *propname,
0340   int          elsz,
0341   void       **buf
0342 )
0343 {
0344   ssize_t len;
0345 
0346   *buf = NULL;
0347   if ((len = rtems_ofw_get_prop_len(node, propname)) == -1 ||
0348       (len % elsz != 0)) {
0349     return -1;
0350   }
0351 
0352   if (len > 0) {
0353     *buf = malloc(len);
0354     if (*buf == NULL) {
0355       return -1;
0356     }
0357 
0358     if (rtems_ofw_get_prop(node, propname, *buf, len) == -1) {
0359       rtems_ofw_free(*buf);
0360       *buf = NULL;
0361       return -1;
0362     }
0363   }
0364 
0365   return (len / elsz);
0366 }
0367 
0368 ssize_t rtems_ofw_get_enc_prop_alloc(
0369   phandle_t    node,
0370   const char  *propname,
0371   void       **buf
0372 )
0373 {
0374   ssize_t len;
0375 
0376   *buf = NULL;
0377   if ((len = rtems_ofw_get_prop_len(node, propname)) == -1) {
0378     return -1;
0379   }
0380 
0381   if (len > 0) {
0382     *buf = malloc(len);
0383     if (*buf == NULL) {
0384       return -1;
0385     }
0386 
0387     if (rtems_ofw_get_enc_prop(node, propname, *buf, len) == -1) {
0388       rtems_ofw_free(*buf);
0389       *buf = NULL;
0390       return -1;
0391     }
0392   }
0393 
0394   return len;
0395 }
0396 
0397 ssize_t rtems_ofw_get_enc_prop_alloc_multi(
0398   phandle_t     node,
0399   const char   *propname,
0400   int           elsz,
0401   void        **buf
0402 )
0403 {
0404   ssize_t len;
0405 
0406   *buf = NULL;
0407   if ((len = rtems_ofw_get_prop_len(node, propname)) == -1 ||
0408       (len % elsz != 0)) {
0409     return -1;
0410   }
0411 
0412   if (len > 0) {
0413     *buf = malloc(len);
0414     if (*buf == NULL) {
0415       return -1;
0416     }
0417 
0418     if (rtems_ofw_get_enc_prop(node, propname, *buf, len) == -1) {
0419       rtems_ofw_free(*buf);
0420       *buf = NULL;
0421       return -1;
0422     }
0423   }
0424 
0425   return (len / elsz);
0426 }
0427 
0428 void rtems_ofw_free( void *buf )
0429 {
0430   free(buf);
0431 }
0432 
0433 int rtems_ofw_next_prop(
0434   phandle_t    node,
0435   const char  *previous,
0436   char        *buf,
0437   size_t       len
0438 )
0439 {
0440   const void *name;
0441   const void *prop;
0442   int offset;
0443 
0444   offset = rtems_fdt_phandle_to_offset(node);
0445 
0446   if (offset < 0) {
0447     return -1;
0448   }
0449 
0450   if (previous == NULL) {
0451     offset = fdt_first_property_offset(fdtp, offset);
0452   } else {
0453     fdt_for_each_property_offset(offset, fdtp, offset) {
0454       prop = fdt_getprop_by_offset(fdtp, offset, (const char **)&name, NULL);
0455       if (prop == NULL)
0456         return -1;
0457 
0458       if (strcmp(previous, name) != 0)
0459         continue;
0460 
0461       offset = fdt_next_property_offset(fdtp, offset);
0462       break;
0463     }
0464   }
0465 
0466   if (offset < 0)
0467     return 0;
0468 
0469   prop = fdt_getprop_by_offset(fdtp, offset, (const char **)&name, &offset);
0470   if (prop == NULL)
0471     return -1;
0472 
0473   strncpy(buf, name, len);
0474 
0475   return 1;
0476 }
0477 
0478 int rtems_ofw_set_prop(
0479   phandle_t    node,
0480   const char  *name,
0481   const void  *buf,
0482   size_t       len
0483 )
0484 {
0485   int offset;
0486 
0487   offset = rtems_fdt_phandle_to_offset(node);
0488 
0489   if (offset < 0)
0490     return -1;
0491 
0492   if (fdt_setprop_inplace(fdtp, offset, name, buf, len) != 0)
0493     return (fdt_setprop(fdtp, offset, name, buf, len));
0494 
0495   return 0;
0496 }
0497 
0498 phandle_t rtems_ofw_find_device( const char *path )
0499 {
0500   int offset;
0501 
0502   offset = fdt_path_offset(fdtp, path);
0503   if (offset < 0)
0504     return -1;
0505 
0506   return rtems_fdt_offset_to_phandle(offset);
0507 }
0508 
0509 static phandle_t rtems_ofw_get_effective_phandle(
0510   phandle_t node,
0511   phandle_t xref
0512 )
0513 {
0514   phandle_t child;
0515   phandle_t ref;
0516   int node_offset;
0517 
0518   node_offset = fdt_path_offset(fdtp, "/");
0519 
0520   while ((node_offset = fdt_next_node(fdtp, node_offset, NULL)) > 0) {
0521     child = rtems_fdt_offset_to_phandle(node_offset);
0522 
0523     if (rtems_ofw_get_enc_prop(child, "phandle", &ref, sizeof(ref)) == -1 &&
0524         rtems_ofw_get_enc_prop(child, "ibm,phandle", &ref, sizeof(ref)) == -1 &&
0525         rtems_ofw_get_enc_prop(child, "linux,phandle", &ref, sizeof(ref)) == -1
0526     ) {
0527       continue;
0528     }
0529 
0530     if (ref == xref)
0531       return child;
0532   }
0533 
0534   return -1;
0535 }
0536 
0537 phandle_t rtems_ofw_node_from_xref( phandle_t xref )
0538 {
0539   phandle_t node;
0540 
0541   if ((node = rtems_ofw_get_effective_phandle(rtems_ofw_peer(0), xref)) == -1)
0542     return xref;
0543 
0544   return node;
0545 }
0546 
0547 phandle_t rtems_ofw_xref_from_node( phandle_t node )
0548 {
0549   phandle_t ref;
0550 
0551     if (rtems_ofw_get_enc_prop(node, "phandle", &ref, sizeof(ref)) == -1 &&
0552         rtems_ofw_get_enc_prop(node, "ibm,phandle", &ref, sizeof(ref)) == -1 &&
0553         rtems_ofw_get_enc_prop(node, "linux,phandle", &ref, sizeof(ref)) == -1)
0554     {
0555       return node;
0556     }
0557 
0558     return ref;
0559 }
0560 
0561 phandle_t rtems_ofw_instance_to_package( ihandle_t instance )
0562 {
0563   return rtems_ofw_node_from_xref(instance);
0564 }
0565 
0566 ssize_t rtems_ofw_package_to_path(
0567   phandle_t   node,
0568   char       *buf,
0569   size_t      len
0570 )
0571 {
0572   int offset;
0573   int rv;
0574 
0575   offset = rtems_fdt_phandle_to_offset(node);
0576 
0577   rv = fdt_get_path(fdtp, offset, buf, len);
0578   if (rv != 0)
0579     return -1;
0580 
0581   return rv;
0582 }
0583 
0584 ssize_t rtems_ofw_instance_to_path(
0585   ihandle_t  instance,
0586   char      *buf,
0587   size_t     len
0588 )
0589 {
0590   int offset;
0591   int rv;
0592 
0593   offset = rtems_ofw_instance_to_package(instance);
0594   offset = rtems_fdt_phandle_to_offset(offset);
0595 
0596   rv = fdt_get_path(fdtp, offset, buf, len);
0597   if (rv != 0)
0598     return -1;
0599 
0600   return rv;
0601 }
0602 
0603 int rtems_ofw_get_reg(
0604   phandle_t              node,
0605   rtems_ofw_memory_area *buf,
0606   size_t                 size
0607 )
0608 {
0609   int len;
0610   int offset;
0611   int nranges;
0612   int nregs;
0613   phandle_t parent;
0614   rtems_ofw_ranges range;
0615   const rtems_ofw_ranges *ptr;
0616 
0617   len = rtems_ofw_get_enc_prop(node, "reg", (pcell_t *)buf, size);
0618   if (len <= 0) {
0619     return len;
0620   }
0621 
0622   nregs = MIN(len, size) / sizeof(rtems_ofw_memory_area);
0623 
0624   for (parent = rtems_ofw_parent(node); parent > 0;
0625       parent = rtems_ofw_parent(parent)) {
0626 
0627     offset = rtems_fdt_phandle_to_offset(parent);
0628     ptr = fdt_getprop(fdtp, offset, "ranges", &len);
0629 
0630     if (ptr == NULL) {
0631       break;
0632     }
0633 
0634     nranges = len / sizeof(rtems_ofw_ranges);
0635 
0636     offset = 0;
0637     for (int i=0; i < nregs; i++) {
0638       for (int j=0; j < nranges; j++) {
0639         
0640         range.parent_bus = fdt32_to_cpu(ptr[j].parent_bus);
0641         range.child_bus = fdt32_to_cpu(ptr[j].child_bus);
0642         range.size = fdt32_to_cpu(ptr[j].size);
0643 
0644         /**
0645          * (buf + size - (sizeof(buf[0]) - 1) is the last valid
0646          * address for buf[i]. If buf[i] points to any address larger
0647          * than this, it will be an out of bound access
0648          */
0649         _Assert(&buf[i] < (buf + size - (sizeof(buf[0]) - 1)));
0650         if (buf[i].start >= range.child_bus &&
0651             buf[i].start < range.child_bus + range.size) {
0652           offset = range.parent_bus - range.child_bus;
0653           break;
0654         }
0655 
0656       }
0657       buf[i].start += offset;
0658     }
0659   }
0660 
0661   return nregs;
0662 }
0663 
0664 int rtems_ofw_get_interrupts(
0665   phandle_t            node,
0666   rtems_vector_number *buf,
0667   size_t               size
0668 )
0669 {
0670   int rv;
0671 
0672   rv = rtems_ofw_get_enc_prop(node, "interrupts", buf, size);
0673 
0674   if (rv <= 0) {
0675     return rv;
0676   }
0677 
0678   return MIN(size, rv) / sizeof(rtems_vector_number);
0679 }
0680 
0681 bool rtems_ofw_node_status( phandle_t node )
0682 {
0683   int len;
0684   const char buf[10];
0685 
0686   len = rtems_ofw_get_prop(node, "status", (void *)&buf[0], sizeof(buf));
0687   if ((len == -1) ||
0688       (strncmp(buf, "okay", MIN(5, len)) == 0) ||
0689       (strncmp(buf, "ok", MIN(3, len)) == 0)) {
0690       return true;
0691   }
0692 
0693   return false;
0694 }
0695 
0696 phandle_t rtems_ofw_find_device_by_compat( const char *compat )
0697 {
0698   int offset;
0699 
0700   offset = fdt_node_offset_by_compatible(fdtp, -1, compat);
0701   return rtems_fdt_offset_to_phandle(offset);
0702 }
0703 
0704 bool rtems_ofw_is_node_compatible(
0705   phandle_t node,
0706   const char *compat
0707 )
0708 {
0709   int offset;
0710 
0711   offset = rtems_fdt_phandle_to_offset(node);
0712 
0713   return fdt_node_check_compatible(fdtp, offset, compat) == 0;
0714 }