1/* 2 * libfdt - Flat Device Tree manipulation 3 * Copyright (C) 2006 David Gibson, IBM Corporation. 4 * 5 * Redistribution and use in source and binary forms, with or 6 * without modification, are permitted provided that the following 7 * conditions are met: 8 * 9 * 1. Redistributions of source code must retain the above 10 * copyright notice, this list of conditions and the following 11 * disclaimer. 12 * 2. Redistributions in binary form must reproduce the above 13 * copyright notice, this list of conditions and the following 14 * disclaimer in the documentation and/or other materials 15 * provided with the distribution. 16 * 17 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND 18 * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, 19 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 20 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 21 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR 22 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 23 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 24 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 25 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 26 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR 28 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, 29 * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 */ 31#include <fdt.h> 32#include <libfdt.h> 33 34#include "libfdt_env.h" 35#include "libfdt_internal.h" 36 37static int _fdt_sw_check_header(void *fdt) 38{ 39 if (fdt_magic(fdt) != FDT_SW_MAGIC) 40 return -FDT_ERR_BADMAGIC; 41 /* FIXME: should check more details about the header state */ 42 return 0; 43} 44 45#define FDT_SW_CHECK_HEADER(fdt) \ 46 { \ 47 int err; \ 48 if ((err = _fdt_sw_check_header(fdt)) != 0) \ 49 return err; \ 50 } 51 52static void *_fdt_grab_space(void *fdt, size_t len) 53{ 54 int offset = fdt_size_dt_struct(fdt); 55 int spaceleft; 56 57 spaceleft = fdt_totalsize(fdt) - fdt_off_dt_struct(fdt) 58 - fdt_size_dt_strings(fdt); 59 60 if ((offset + len < offset) || (offset + len > spaceleft)) 61 return NULL; 62 63 fdt_set_size_dt_struct(fdt, offset + len); 64 return _fdt_offset_ptr_w(fdt, offset); 65} 66 67int fdt_create(void *buf, int bufsize) 68{ 69 void *fdt = buf; 70 71 if (bufsize < sizeof(struct fdt_header)) 72 return -FDT_ERR_NOSPACE; 73 74 memset(buf, 0, bufsize); 75 76 fdt_set_magic(fdt, FDT_SW_MAGIC); 77 fdt_set_version(fdt, FDT_LAST_SUPPORTED_VERSION); 78 fdt_set_last_comp_version(fdt, FDT_FIRST_SUPPORTED_VERSION); 79 fdt_set_totalsize(fdt, bufsize); 80 81 fdt_set_off_mem_rsvmap(fdt, FDT_ALIGN(sizeof(struct fdt_header), 82 sizeof(struct fdt_reserve_entry))); 83 fdt_set_off_dt_struct(fdt, fdt_off_mem_rsvmap(fdt)); 84 fdt_set_off_dt_strings(fdt, bufsize); 85 86 return 0; 87} 88 89int fdt_add_reservemap_entry(void *fdt, uint64_t addr, uint64_t size) 90{ 91 struct fdt_reserve_entry *re; 92 int offset; 93 94 FDT_SW_CHECK_HEADER(fdt); 95 96 if (fdt_size_dt_struct(fdt)) 97 return -FDT_ERR_BADSTATE; 98 99 offset = fdt_off_dt_struct(fdt); 100 if ((offset + sizeof(*re)) > fdt_totalsize(fdt)) 101 return -FDT_ERR_NOSPACE; 102 103 re = (struct fdt_reserve_entry *)((char *)fdt + offset); 104 re->address = cpu_to_fdt64(addr); 105 re->size = cpu_to_fdt64(size); 106 107 fdt_set_off_dt_struct(fdt, offset + sizeof(*re)); 108 109 return 0; 110} 111 112int fdt_finish_reservemap(void *fdt) 113{ 114 return fdt_add_reservemap_entry(fdt, 0, 0); 115} 116 117int fdt_begin_node(void *fdt, const char *name) 118{ 119 struct fdt_node_header *nh; 120 int namelen = strlen(name) + 1; 121 122 FDT_SW_CHECK_HEADER(fdt); 123 124 nh = _fdt_grab_space(fdt, sizeof(*nh) + FDT_TAGALIGN(namelen)); 125 if (! nh) 126 return -FDT_ERR_NOSPACE; 127 128 nh->tag = cpu_to_fdt32(FDT_BEGIN_NODE); 129 memcpy(nh->name, name, namelen); 130 return 0; 131} 132 133int fdt_end_node(void *fdt) 134{ 135 fdt32_t *en; 136 137 FDT_SW_CHECK_HEADER(fdt); 138 139 en = _fdt_grab_space(fdt, FDT_TAGSIZE); 140 if (! en) 141 return -FDT_ERR_NOSPACE; 142 143 *en = cpu_to_fdt32(FDT_END_NODE); 144 return 0; 145} 146 147static int _fdt_find_add_string(void *fdt, const char *s) 148{ 149 char *strtab = (char *)fdt + fdt_totalsize(fdt); 150 const char *p; 151 int strtabsize = fdt_size_dt_strings(fdt); 152 int len = strlen(s) + 1; 153 int struct_top, offset; 154 155 p = _fdt_find_string(strtab - strtabsize, strtabsize, s); 156 if (p) 157 return p - strtab; 158 159 /* Add it */ 160 offset = -strtabsize - len; 161 struct_top = fdt_off_dt_struct(fdt) + fdt_size_dt_struct(fdt); 162 if (fdt_totalsize(fdt) + offset < struct_top) 163 return 0; /* no more room :( */ 164 165 memcpy(strtab + offset, s, len); 166 fdt_set_size_dt_strings(fdt, strtabsize + len); 167 return offset; 168} 169 170int fdt_property(void *fdt, const char *name, const void *val, int len) 171{ 172 struct fdt_property *prop; 173 int nameoff; 174 175 FDT_SW_CHECK_HEADER(fdt); 176 177 nameoff = _fdt_find_add_string(fdt, name); 178 if (nameoff == 0) 179 return -FDT_ERR_NOSPACE; 180 181 prop = _fdt_grab_space(fdt, sizeof(*prop) + FDT_TAGALIGN(len)); 182 if (! prop) 183 return -FDT_ERR_NOSPACE; 184 185 prop->tag = cpu_to_fdt32(FDT_PROP); 186 prop->nameoff = cpu_to_fdt32(nameoff); 187 prop->len = cpu_to_fdt32(len); 188 memcpy(prop->data, val, len); 189 return 0; 190} 191 192int fdt_finish(void *fdt) 193{ 194 char *p = (char *)fdt; 195 fdt32_t *end; 196 int oldstroffset, newstroffset; 197 uint32_t tag; 198 int offset, nextoffset; 199 200 FDT_SW_CHECK_HEADER(fdt); 201 202 /* Add terminator */ 203 end = _fdt_grab_space(fdt, sizeof(*end)); 204 if (! end) 205 return -FDT_ERR_NOSPACE; 206 *end = cpu_to_fdt32(FDT_END); 207 208 /* Relocate the string table */ 209 oldstroffset = fdt_totalsize(fdt) - fdt_size_dt_strings(fdt); 210 newstroffset = fdt_off_dt_struct(fdt) + fdt_size_dt_struct(fdt); 211 memmove(p + newstroffset, p + oldstroffset, fdt_size_dt_strings(fdt)); 212 fdt_set_off_dt_strings(fdt, newstroffset); 213 214 /* Walk the structure, correcting string offsets */ 215 offset = 0; 216 while ((tag = fdt_next_tag(fdt, offset, &nextoffset)) != FDT_END) { 217 if (tag == FDT_PROP) { 218 struct fdt_property *prop = 219 _fdt_offset_ptr_w(fdt, offset); 220 int nameoff; 221 222 nameoff = fdt32_to_cpu(prop->nameoff); 223 nameoff += fdt_size_dt_strings(fdt); 224 prop->nameoff = cpu_to_fdt32(nameoff); 225 } 226 offset = nextoffset; 227 } 228 if (nextoffset < 0) 229 return nextoffset; 230 231 /* Finally, adjust the header */ 232 fdt_set_totalsize(fdt, newstroffset + fdt_size_dt_strings(fdt)); 233 fdt_set_magic(fdt, FDT_MAGIC); 234 return 0; 235} 236