LCOV - code coverage report
Current view: top level - libfdt - fdt_sw.c (source / functions) Hit Total Coverage
Test: skiboot.info Lines: 126 178 70.8 %
Date: 2024-09-10 18:37:41 Functions: 15 18 83.3 %
Branches: 0 0 -

           Branch data     Line data    Source code
       1                 :            : // SPDX-License-Identifier: (GPL-2.0-or-later OR BSD-2-Clause)
       2                 :            : /*
       3                 :            :  * libfdt - Flat Device Tree manipulation
       4                 :            :  * Copyright (C) 2006 David Gibson, IBM Corporation.
       5                 :            :  */
       6                 :            : #include "libfdt_env.h"
       7                 :            : 
       8                 :            : #include <fdt.h>
       9                 :            : #include <libfdt.h>
      10                 :            : 
      11                 :            : #include "libfdt_internal.h"
      12                 :            : 
      13                 :       1011 : static int fdt_sw_probe_(void *fdt)
      14                 :            : {
      15                 :       1011 :         if (!can_assume(VALID_INPUT)) {
      16                 :       1011 :                 if (fdt_magic(fdt) == FDT_MAGIC)
      17                 :          0 :                         return -FDT_ERR_BADSTATE;
      18                 :       1011 :                 else if (fdt_magic(fdt) != FDT_SW_MAGIC)
      19                 :          0 :                         return -FDT_ERR_BADMAGIC;
      20                 :            :         }
      21                 :            : 
      22                 :       1011 :         return 0;
      23                 :            : }
      24                 :            : 
      25                 :            : #define FDT_SW_PROBE(fdt) \
      26                 :            :         { \
      27                 :            :                 int err; \
      28                 :            :                 if ((err = fdt_sw_probe_(fdt)) != 0) \
      29                 :            :                         return err; \
      30                 :            :         }
      31                 :            : 
      32                 :            : /* 'memrsv' state:      Initial state after fdt_create()
      33                 :            :  *
      34                 :            :  * Allowed functions:
      35                 :            :  *      fdt_add_reservemap_entry()
      36                 :            :  *      fdt_finish_reservemap()         [moves to 'struct' state]
      37                 :            :  */
      38                 :          4 : static int fdt_sw_probe_memrsv_(void *fdt)
      39                 :            : {
      40                 :          4 :         int err = fdt_sw_probe_(fdt);
      41                 :          4 :         if (err)
      42                 :          0 :                 return err;
      43                 :            : 
      44                 :          4 :         if (!can_assume(VALID_INPUT) && fdt_off_dt_strings(fdt) != 0)
      45                 :          0 :                 return -FDT_ERR_BADSTATE;
      46                 :          4 :         return 0;
      47                 :            : }
      48                 :            : 
      49                 :            : #define FDT_SW_PROBE_MEMRSV(fdt) \
      50                 :            :         { \
      51                 :            :                 int err; \
      52                 :            :                 if ((err = fdt_sw_probe_memrsv_(fdt)) != 0) \
      53                 :            :                         return err; \
      54                 :            :         }
      55                 :            : 
      56                 :            : /* 'struct' state:      Enter this state after fdt_finish_reservemap()
      57                 :            :  *
      58                 :            :  * Allowed functions:
      59                 :            :  *      fdt_begin_node()
      60                 :            :  *      fdt_end_node()
      61                 :            :  *      fdt_property*()
      62                 :            :  *      fdt_finish()                    [moves to 'complete' state]
      63                 :            :  */
      64                 :       1007 : static int fdt_sw_probe_struct_(void *fdt)
      65                 :            : {
      66                 :       1007 :         int err = fdt_sw_probe_(fdt);
      67                 :       1007 :         if (err)
      68                 :          0 :                 return err;
      69                 :            : 
      70                 :       2014 :         if (!can_assume(VALID_INPUT) &&
      71                 :       1007 :             fdt_off_dt_strings(fdt) != fdt_totalsize(fdt))
      72                 :          0 :                 return -FDT_ERR_BADSTATE;
      73                 :       1007 :         return 0;
      74                 :            : }
      75                 :            : 
      76                 :            : #define FDT_SW_PROBE_STRUCT(fdt) \
      77                 :            :         { \
      78                 :            :                 int err; \
      79                 :            :                 if ((err = fdt_sw_probe_struct_(fdt)) != 0) \
      80                 :            :                         return err; \
      81                 :            :         }
      82                 :            : 
      83                 :        846 : static inline uint32_t sw_flags(void *fdt)
      84                 :            : {
      85                 :            :         /* assert: (fdt_magic(fdt) == FDT_SW_MAGIC) */
      86                 :        846 :         return fdt_last_comp_version(fdt);
      87                 :            : }
      88                 :            : 
      89                 :            : /* 'complete' state:    Enter this state after fdt_finish()
      90                 :            :  *
      91                 :            :  * Allowed functions: none
      92                 :            :  */
      93                 :            : 
      94                 :       1007 : static void *fdt_grab_space_(void *fdt, size_t len)
      95                 :            : {
      96                 :       1007 :         unsigned int offset = fdt_size_dt_struct(fdt);
      97                 :            :         unsigned int spaceleft;
      98                 :            : 
      99                 :       1007 :         spaceleft = fdt_totalsize(fdt) - fdt_off_dt_struct(fdt)
     100                 :       1007 :                 - fdt_size_dt_strings(fdt);
     101                 :            : 
     102                 :       1007 :         if ((offset + len < offset) || (offset + len > spaceleft))
     103                 :          0 :                 return NULL;
     104                 :            : 
     105                 :       1007 :         fdt_set_size_dt_struct(fdt, offset + len);
     106                 :       1007 :         return fdt_offset_ptr_w_(fdt, offset);
     107                 :            : }
     108                 :            : 
     109                 :          1 : int fdt_create_with_flags(void *buf, int bufsize, uint32_t flags)
     110                 :            : {
     111                 :          1 :         const int hdrsize = FDT_ALIGN(sizeof(struct fdt_header),
     112                 :            :                                       sizeof(struct fdt_reserve_entry));
     113                 :          1 :         void *fdt = buf;
     114                 :            : 
     115                 :          1 :         if (bufsize < hdrsize)
     116                 :          0 :                 return -FDT_ERR_NOSPACE;
     117                 :            : 
     118                 :          1 :         if (flags & ~FDT_CREATE_FLAGS_ALL)
     119                 :          0 :                 return -FDT_ERR_BADFLAGS;
     120                 :            : 
     121                 :          1 :         memset(buf, 0, bufsize);
     122                 :            : 
     123                 :            :         /*
     124                 :            :          * magic and last_comp_version keep intermediate state during the fdt
     125                 :            :          * creation process, which is replaced with the proper FDT format by
     126                 :            :          * fdt_finish().
     127                 :            :          *
     128                 :            :          * flags should be accessed with sw_flags().
     129                 :            :          */
     130                 :          1 :         fdt_set_magic(fdt, FDT_SW_MAGIC);
     131                 :          1 :         fdt_set_version(fdt, FDT_LAST_SUPPORTED_VERSION);
     132                 :          1 :         fdt_set_last_comp_version(fdt, flags);
     133                 :            : 
     134                 :          1 :         fdt_set_totalsize(fdt,  bufsize);
     135                 :            : 
     136                 :          1 :         fdt_set_off_mem_rsvmap(fdt, hdrsize);
     137                 :          1 :         fdt_set_off_dt_struct(fdt, fdt_off_mem_rsvmap(fdt));
     138                 :          1 :         fdt_set_off_dt_strings(fdt, 0);
     139                 :            : 
     140                 :          1 :         return 0;
     141                 :            : }
     142                 :            : 
     143                 :          0 : int fdt_create(void *buf, int bufsize)
     144                 :            : {
     145                 :          0 :         return fdt_create_with_flags(buf, bufsize, 0);
     146                 :            : }
     147                 :            : 
     148                 :          0 : int fdt_resize(void *fdt, void *buf, int bufsize)
     149                 :            : {
     150                 :            :         size_t headsize, tailsize;
     151                 :            :         char *oldtail, *newtail;
     152                 :            : 
     153                 :          0 :         FDT_SW_PROBE(fdt);
     154                 :            : 
     155                 :          0 :         if (bufsize < 0)
     156                 :          0 :                 return -FDT_ERR_NOSPACE;
     157                 :            : 
     158                 :          0 :         headsize = fdt_off_dt_struct(fdt) + fdt_size_dt_struct(fdt);
     159                 :          0 :         tailsize = fdt_size_dt_strings(fdt);
     160                 :            : 
     161                 :          0 :         if (!can_assume(VALID_DTB) &&
     162                 :          0 :             headsize + tailsize > fdt_totalsize(fdt))
     163                 :          0 :                 return -FDT_ERR_INTERNAL;
     164                 :            : 
     165                 :          0 :         if ((headsize + tailsize) > (unsigned)bufsize)
     166                 :          0 :                 return -FDT_ERR_NOSPACE;
     167                 :            : 
     168                 :          0 :         oldtail = (char *)fdt + fdt_totalsize(fdt) - tailsize;
     169                 :          0 :         newtail = (char *)buf + bufsize - tailsize;
     170                 :            : 
     171                 :            :         /* Two cases to avoid clobbering data if the old and new
     172                 :            :          * buffers partially overlap */
     173                 :          0 :         if (buf <= fdt) {
     174                 :          0 :                 memmove(buf, fdt, headsize);
     175                 :          0 :                 memmove(newtail, oldtail, tailsize);
     176                 :            :         } else {
     177                 :          0 :                 memmove(newtail, oldtail, tailsize);
     178                 :          0 :                 memmove(buf, fdt, headsize);
     179                 :            :         }
     180                 :            : 
     181                 :          0 :         fdt_set_totalsize(buf, bufsize);
     182                 :          0 :         if (fdt_off_dt_strings(buf))
     183                 :          0 :                 fdt_set_off_dt_strings(buf, bufsize);
     184                 :            : 
     185                 :          0 :         return 0;
     186                 :            : }
     187                 :            : 
     188                 :          4 : int fdt_add_reservemap_entry(void *fdt, uint64_t addr, uint64_t size)
     189                 :            : {
     190                 :            :         struct fdt_reserve_entry *re;
     191                 :            :         int offset;
     192                 :            : 
     193                 :          4 :         FDT_SW_PROBE_MEMRSV(fdt);
     194                 :            : 
     195                 :          4 :         offset = fdt_off_dt_struct(fdt);
     196                 :          4 :         if ((offset + sizeof(*re)) > fdt_totalsize(fdt))
     197                 :          0 :                 return -FDT_ERR_NOSPACE;
     198                 :            : 
     199                 :          4 :         re = (struct fdt_reserve_entry *)((char *)fdt + offset);
     200                 :          4 :         re->address = cpu_to_fdt64(addr);
     201                 :          4 :         re->size = cpu_to_fdt64(size);
     202                 :            : 
     203                 :          4 :         fdt_set_off_dt_struct(fdt, offset + sizeof(*re));
     204                 :            : 
     205                 :          4 :         return 0;
     206                 :            : }
     207                 :            : 
     208                 :          1 : int fdt_finish_reservemap(void *fdt)
     209                 :            : {
     210                 :          1 :         int err = fdt_add_reservemap_entry(fdt, 0, 0);
     211                 :            : 
     212                 :          1 :         if (err)
     213                 :          0 :                 return err;
     214                 :            : 
     215                 :          1 :         fdt_set_off_dt_strings(fdt, fdt_totalsize(fdt));
     216                 :          1 :         return 0;
     217                 :            : }
     218                 :            : 
     219                 :         80 : int fdt_begin_node(void *fdt, const char *name)
     220                 :            : {
     221                 :            :         struct fdt_node_header *nh;
     222                 :            :         int namelen;
     223                 :            : 
     224                 :         80 :         FDT_SW_PROBE_STRUCT(fdt);
     225                 :            : 
     226                 :         80 :         namelen = strlen(name) + 1;
     227                 :         80 :         nh = fdt_grab_space_(fdt, sizeof(*nh) + FDT_TAGALIGN(namelen));
     228                 :         80 :         if (! nh)
     229                 :          0 :                 return -FDT_ERR_NOSPACE;
     230                 :            : 
     231                 :         80 :         nh->tag = cpu_to_fdt32(FDT_BEGIN_NODE);
     232                 :         80 :         memcpy(nh->name, name, namelen);
     233                 :         80 :         return 0;
     234                 :            : }
     235                 :            : 
     236                 :         80 : int fdt_end_node(void *fdt)
     237                 :            : {
     238                 :            :         fdt32_t *en;
     239                 :            : 
     240                 :         80 :         FDT_SW_PROBE_STRUCT(fdt);
     241                 :            : 
     242                 :         80 :         en = fdt_grab_space_(fdt, FDT_TAGSIZE);
     243                 :         80 :         if (! en)
     244                 :          0 :                 return -FDT_ERR_NOSPACE;
     245                 :            : 
     246                 :         80 :         *en = cpu_to_fdt32(FDT_END_NODE);
     247                 :         80 :         return 0;
     248                 :            : }
     249                 :            : 
     250                 :         99 : static int fdt_add_string_(void *fdt, const char *s)
     251                 :            : {
     252                 :         99 :         char *strtab = (char *)fdt + fdt_totalsize(fdt);
     253                 :         99 :         unsigned int strtabsize = fdt_size_dt_strings(fdt);
     254                 :         99 :         unsigned int len = strlen(s) + 1;
     255                 :            :         unsigned int struct_top, offset;
     256                 :            : 
     257                 :         99 :         offset = strtabsize + len;
     258                 :         99 :         struct_top = fdt_off_dt_struct(fdt) + fdt_size_dt_struct(fdt);
     259                 :         99 :         if (fdt_totalsize(fdt) - offset < struct_top)
     260                 :          0 :                 return 0; /* no more room :( */
     261                 :            : 
     262                 :         99 :         memcpy(strtab - offset, s, len);
     263                 :         99 :         fdt_set_size_dt_strings(fdt, strtabsize + len);
     264                 :         99 :         return -offset;
     265                 :            : }
     266                 :            : 
     267                 :            : /* Must only be used to roll back in case of error */
     268                 :          0 : static void fdt_del_last_string_(void *fdt, const char *s)
     269                 :            : {
     270                 :          0 :         int strtabsize = fdt_size_dt_strings(fdt);
     271                 :          0 :         int len = strlen(s) + 1;
     272                 :            : 
     273                 :          0 :         fdt_set_size_dt_strings(fdt, strtabsize - len);
     274                 :          0 : }
     275                 :            : 
     276                 :        846 : static int fdt_find_add_string_(void *fdt, const char *s, int *allocated)
     277                 :            : {
     278                 :        846 :         char *strtab = (char *)fdt + fdt_totalsize(fdt);
     279                 :        846 :         int strtabsize = fdt_size_dt_strings(fdt);
     280                 :            :         const char *p;
     281                 :            : 
     282                 :        846 :         *allocated = 0;
     283                 :            : 
     284                 :        846 :         p = fdt_find_string_(strtab - strtabsize, strtabsize, s);
     285                 :        846 :         if (p)
     286                 :        747 :                 return p - strtab;
     287                 :            : 
     288                 :         99 :         *allocated = 1;
     289                 :            : 
     290                 :         99 :         return fdt_add_string_(fdt, s);
     291                 :            : }
     292                 :            : 
     293                 :        846 : int fdt_property_placeholder(void *fdt, const char *name, int len, void **valp)
     294                 :            : {
     295                 :            :         struct fdt_property *prop;
     296                 :            :         int nameoff;
     297                 :            :         int allocated;
     298                 :            : 
     299                 :        846 :         FDT_SW_PROBE_STRUCT(fdt);
     300                 :            : 
     301                 :            :         /* String de-duplication can be slow, _NO_NAME_DEDUP skips it */
     302                 :        846 :         if (sw_flags(fdt) & FDT_CREATE_FLAG_NO_NAME_DEDUP) {
     303                 :          0 :                 allocated = 1;
     304                 :          0 :                 nameoff = fdt_add_string_(fdt, name);
     305                 :            :         } else {
     306                 :        846 :                 nameoff = fdt_find_add_string_(fdt, name, &allocated);
     307                 :            :         }
     308                 :        846 :         if (nameoff == 0)
     309                 :          0 :                 return -FDT_ERR_NOSPACE;
     310                 :            : 
     311                 :        846 :         prop = fdt_grab_space_(fdt, sizeof(*prop) + FDT_TAGALIGN(len));
     312                 :        846 :         if (! prop) {
     313                 :          0 :                 if (allocated)
     314                 :          0 :                         fdt_del_last_string_(fdt, name);
     315                 :          0 :                 return -FDT_ERR_NOSPACE;
     316                 :            :         }
     317                 :            : 
     318                 :        846 :         prop->tag = cpu_to_fdt32(FDT_PROP);
     319                 :        846 :         prop->nameoff = cpu_to_fdt32(nameoff);
     320                 :        846 :         prop->len = cpu_to_fdt32(len);
     321                 :        846 :         *valp = prop->data;
     322                 :        846 :         return 0;
     323                 :            : }
     324                 :            : 
     325                 :        846 : int fdt_property(void *fdt, const char *name, const void *val, int len)
     326                 :            : {
     327                 :            :         void *ptr;
     328                 :            :         int ret;
     329                 :            : 
     330                 :        846 :         ret = fdt_property_placeholder(fdt, name, len, &ptr);
     331                 :        846 :         if (ret)
     332                 :          0 :                 return ret;
     333                 :        846 :         memcpy(ptr, val, len);
     334                 :        846 :         return 0;
     335                 :            : }
     336                 :            : 
     337                 :          1 : int fdt_finish(void *fdt)
     338                 :            : {
     339                 :          1 :         char *p = (char *)fdt;
     340                 :            :         fdt32_t *end;
     341                 :            :         int oldstroffset, newstroffset;
     342                 :            :         uint32_t tag;
     343                 :            :         int offset, nextoffset;
     344                 :            : 
     345                 :          1 :         FDT_SW_PROBE_STRUCT(fdt);
     346                 :            : 
     347                 :            :         /* Add terminator */
     348                 :          1 :         end = fdt_grab_space_(fdt, sizeof(*end));
     349                 :          1 :         if (! end)
     350                 :          0 :                 return -FDT_ERR_NOSPACE;
     351                 :          1 :         *end = cpu_to_fdt32(FDT_END);
     352                 :            : 
     353                 :            :         /* Relocate the string table */
     354                 :          1 :         oldstroffset = fdt_totalsize(fdt) - fdt_size_dt_strings(fdt);
     355                 :          1 :         newstroffset = fdt_off_dt_struct(fdt) + fdt_size_dt_struct(fdt);
     356                 :          1 :         memmove(p + newstroffset, p + oldstroffset, fdt_size_dt_strings(fdt));
     357                 :          1 :         fdt_set_off_dt_strings(fdt, newstroffset);
     358                 :            : 
     359                 :            :         /* Walk the structure, correcting string offsets */
     360                 :          1 :         offset = 0;
     361                 :       1007 :         while ((tag = fdt_next_tag(fdt, offset, &nextoffset)) != FDT_END) {
     362                 :       1006 :                 if (tag == FDT_PROP) {
     363                 :            :                         struct fdt_property *prop =
     364                 :        846 :                                 fdt_offset_ptr_w_(fdt, offset);
     365                 :            :                         int nameoff;
     366                 :            : 
     367                 :        846 :                         nameoff = fdt32_to_cpu(prop->nameoff);
     368                 :        846 :                         nameoff += fdt_size_dt_strings(fdt);
     369                 :        846 :                         prop->nameoff = cpu_to_fdt32(nameoff);
     370                 :            :                 }
     371                 :       1006 :                 offset = nextoffset;
     372                 :            :         }
     373                 :          1 :         if (nextoffset < 0)
     374                 :          0 :                 return nextoffset;
     375                 :            : 
     376                 :            :         /* Finally, adjust the header */
     377                 :          1 :         fdt_set_totalsize(fdt, newstroffset + fdt_size_dt_strings(fdt));
     378                 :            : 
     379                 :            :         /* And fix up fields that were keeping intermediate state. */
     380                 :          1 :         fdt_set_last_comp_version(fdt, FDT_LAST_COMPATIBLE_VERSION);
     381                 :          1 :         fdt_set_magic(fdt, FDT_MAGIC);
     382                 :            : 
     383                 :          1 :         return 0;
     384                 :            : }

Generated by: LCOV version 1.14