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