683 lines
17 KiB
C
683 lines
17 KiB
C
|
/*
|
||
|
* Copyright (C) 2014-2015 Apple Computer, Inc. All rights reserved.
|
||
|
*
|
||
|
* This document is the property of Apple Inc.
|
||
|
* It is considered confidential and proprietary.
|
||
|
*
|
||
|
* This document may not be reproduced or transmitted in any form,
|
||
|
* in whole or in part, without the express written permission of
|
||
|
* Apple Inc.
|
||
|
*/
|
||
|
#include <debug.h>
|
||
|
#include <lib/devicetree.h>
|
||
|
#include <lib/syscfg.h>
|
||
|
#include <lib/env.h>
|
||
|
#include <stdint.h>
|
||
|
#include <stdlib.h>
|
||
|
#include <string.h>
|
||
|
#include "devicetree_private.h"
|
||
|
|
||
|
/*
|
||
|
* Device Tree Parsing Library
|
||
|
*
|
||
|
* This library implements the device tree serializer/updater/deserializer.
|
||
|
*
|
||
|
* There should be more stuff here, but for now a quick note.
|
||
|
*
|
||
|
* The dt_size variable contains the size the device tree will have upon
|
||
|
* serialization. Any modification to the deserialized device tree must
|
||
|
* update this variable so that it continues to contain the size the device
|
||
|
* tree will have on re-serialization. This is because other modules need
|
||
|
* to know the size of the device tree in order to properly lay out memory
|
||
|
* (for example, in order to allocate a region of the kernel's memory map)
|
||
|
*/
|
||
|
|
||
|
static void free_node(dt_node_t *node);
|
||
|
void dt_free(dt_node_t *dt);
|
||
|
|
||
|
static size_t dt_size;
|
||
|
static dt_node_t *dt_root;
|
||
|
static void *dt_data;
|
||
|
static bool dt_sealed;
|
||
|
|
||
|
#define ROUND_SIZE(s) (((s) + 3) & ~3)
|
||
|
|
||
|
void dt_init(void)
|
||
|
{
|
||
|
dt_size = 0;
|
||
|
if (dt_root != NULL) {
|
||
|
dt_free(dt_root);
|
||
|
dt_root = NULL;
|
||
|
}
|
||
|
dt_data = NULL;
|
||
|
dt_sealed = false;
|
||
|
}
|
||
|
|
||
|
size_t dt_get_size(void)
|
||
|
{
|
||
|
return dt_size;
|
||
|
}
|
||
|
|
||
|
static void free_node(dt_node_t *node)
|
||
|
{
|
||
|
if (node->props != NULL) {
|
||
|
for (uint32_t i = 0; i < node->nprops; i++) {
|
||
|
if (node->props[i].name_malloced)
|
||
|
free(node->props[i].name);
|
||
|
if (node->props[i].data_malloced)
|
||
|
free(node->props[i].data);
|
||
|
}
|
||
|
free(node->props);
|
||
|
node->props = NULL;
|
||
|
}
|
||
|
if (node->children != NULL) {
|
||
|
for (uint32_t i = 0; i < node->nchildren; i++) {
|
||
|
free_node(&node->children[i]);
|
||
|
}
|
||
|
free(node->children);
|
||
|
node->children = NULL;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/* Replaces placeholder values during deserialization. The placeholders are normal strings,
|
||
|
* but the size field has bit 31 set to indicate it's a placeholder. If iBoot successfully
|
||
|
* finds the requested data, it replaces the placeholder value with the requested data
|
||
|
* (and sets bit 31 of the size back to 0). If iBoot doesn't find the requested data,
|
||
|
* it removes the property from the device tree.
|
||
|
*
|
||
|
* Two kinds of placeholders are currently supported:
|
||
|
* syscfg/<SKey> - looks up the key 'SKey' in syscfg
|
||
|
* syscfg/<SKey>/<len> - looks up the key 'SKey' in syscfg, and pads/truncates to specified
|
||
|
* length (for compatibility with old-style devicetree placeholders)
|
||
|
* macaddr/<envname> - looks up the mac address envname in the environment using env_get_ethaddr
|
||
|
* zeroes/<bytes> - creates a property with bytes zero bytes
|
||
|
*
|
||
|
* Multiple placeholders can be specified, with options separated by commas. The first
|
||
|
* placeholder found is used.
|
||
|
*/
|
||
|
static bool expand_placeholder_prop(dtprop_t *prop)
|
||
|
{
|
||
|
uint32_t alloc_size;
|
||
|
uint8_t ethaddr[6];
|
||
|
uint8_t dummy = 0;
|
||
|
void *data = NULL;
|
||
|
uint32_t data_size = 0;
|
||
|
uint32_t data_pad_size = 0;
|
||
|
char *placeholder;
|
||
|
char *next_placeholder;
|
||
|
// make sure placeholder is null-terminated
|
||
|
if (memchr(prop->data, '\0', prop->size) == NULL) {
|
||
|
panic("devicetree placeholder property '%s' missing null terminator", prop->name);
|
||
|
}
|
||
|
|
||
|
placeholder = prop->data;
|
||
|
next_placeholder = placeholder;
|
||
|
|
||
|
while ((placeholder = strsep(&next_placeholder, ",")) != NULL) {
|
||
|
if (memcmp(placeholder, "syscfg/", 7) == 0) {
|
||
|
char *details = placeholder + 7;
|
||
|
uint8_t *tag_str;
|
||
|
char *len_str;
|
||
|
uint32_t len;
|
||
|
uint32_t tag;
|
||
|
|
||
|
tag_str = (uint8_t *)details;
|
||
|
|
||
|
len_str = details;
|
||
|
strsep(&len_str, "/");
|
||
|
|
||
|
if (strlen(details) != 4) {
|
||
|
panic("dt: wrong length on placeholder %s", placeholder);
|
||
|
}
|
||
|
|
||
|
tag = tag_str[0] << 24 | tag_str[1] << 16 | tag_str[2] << 8 | tag_str[3];
|
||
|
|
||
|
if (syscfg_find_tag(tag, &data, &data_size)) {
|
||
|
// For compatibility with old-style devicetree placeholders, a length
|
||
|
// can be specified to truncate/pad the syscfg data to
|
||
|
if (len_str != NULL) {
|
||
|
len = (uint32_t)strtoul(len_str, NULL, 0);
|
||
|
if (len > data_size) {
|
||
|
data_pad_size = len - data_size;
|
||
|
} else {
|
||
|
data_size = len;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
break;
|
||
|
}
|
||
|
} else if (memcmp(placeholder, "macaddr/", 8) == 0) {
|
||
|
char *details = placeholder + 8;
|
||
|
|
||
|
if (env_get_ethaddr(details, ethaddr) == 0) {
|
||
|
data = ethaddr;
|
||
|
data_size = sizeof(ethaddr);
|
||
|
break;
|
||
|
}
|
||
|
} else if (memcmp(placeholder, "zeroes/", 7) == 0) {
|
||
|
char *details = placeholder + 7;
|
||
|
|
||
|
// Set data to a dummy pointer so that data != NULL but request
|
||
|
// copying 0 bytes. The padding will all get zeroed, resulting
|
||
|
// in a property value with the requested number of zeroes
|
||
|
data = &dummy;
|
||
|
data_size = 0;
|
||
|
data_pad_size = (uint32_t)strtoul(details, NULL, 0);
|
||
|
}
|
||
|
|
||
|
placeholder = next_placeholder;
|
||
|
}
|
||
|
|
||
|
uint32_t original_size = ROUND_SIZE(prop->size);
|
||
|
|
||
|
if (data != NULL) {
|
||
|
alloc_size = ROUND_SIZE(data_size + data_pad_size);
|
||
|
|
||
|
// Since this function is called during initial parsing, we know
|
||
|
// that the storage for the placeholder was in the original devicetree
|
||
|
// buffer and not malloced in a new buffer. We only need a new buffer
|
||
|
// if the new data is longer than the placeholder
|
||
|
if (alloc_size > original_size) {
|
||
|
prop->data = malloc(alloc_size);
|
||
|
prop->data_malloced = true;
|
||
|
}
|
||
|
memcpy(prop->data, data, data_size);
|
||
|
// zero-fill any padding bytes
|
||
|
memset((uint8_t *)prop->data + data_size, 0, alloc_size - data_size);
|
||
|
|
||
|
prop->size = data_size + data_pad_size;
|
||
|
|
||
|
return true;
|
||
|
} else {
|
||
|
return false;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
static bool parse_node(dt_node_t *node, uint8_t **addrptr, const uint8_t *endaddr)
|
||
|
{
|
||
|
uint8_t *addr = *addrptr;
|
||
|
|
||
|
if ((size_t)(endaddr - addr) < 2 * sizeof(uint32_t))
|
||
|
goto error;
|
||
|
|
||
|
memcpy(&node->nprops, addr, 4);
|
||
|
memcpy(&node->nchildren, addr + 4, 4);
|
||
|
addr += 2 * sizeof(uint32_t);
|
||
|
dt_size += 2 * sizeof(uint32_t);
|
||
|
|
||
|
// Sanity checks
|
||
|
if (node->nprops > 1024) {
|
||
|
dprintf(DEBUG_INFO, "dt: too many properties\n");
|
||
|
goto error;
|
||
|
}
|
||
|
if (node->nchildren > 1024) {
|
||
|
dprintf(DEBUG_INFO, "dt: too many children\n");
|
||
|
goto error;
|
||
|
}
|
||
|
|
||
|
if (node->nprops > 0)
|
||
|
node->props = calloc(sizeof(*node->props), node->nprops);
|
||
|
if (node->nchildren > 0)
|
||
|
node->children = calloc(sizeof(*node->children), node->nchildren);
|
||
|
|
||
|
// Parse each property, recording its size, the address of the name,
|
||
|
// and the address of its data.
|
||
|
// We could find the "name" property and cache it here, but instead
|
||
|
// we'll do that if we ever traverse this node looking for a node
|
||
|
// by path.
|
||
|
for(uint32_t i = 0; i < node->nprops; i++) {
|
||
|
dtprop_t *prop;
|
||
|
uint32_t size_and_flags;
|
||
|
bool prop_removed = false;
|
||
|
|
||
|
ASSERT(addr <= endaddr);
|
||
|
|
||
|
prop = &node->props[i];
|
||
|
|
||
|
if((size_t)(endaddr - addr) < kPropNameLength + sizeof(uint32_t)) {
|
||
|
dprintf(DEBUG_INFO, "dt: not enough bytes for property name '%s'\n", (char *)addr);
|
||
|
goto error;
|
||
|
}
|
||
|
if (memchr(addr, 0, kPropNameLength) == NULL) {
|
||
|
dprintf(DEBUG_INFO, "dt: unterminated property name '%s'\n", (char *)addr);
|
||
|
goto error;
|
||
|
}
|
||
|
|
||
|
prop->name = (char *)addr;
|
||
|
|
||
|
memcpy(&size_and_flags, addr + kPropNameLength, sizeof(uint32_t));
|
||
|
addr += kPropNameLength + sizeof(uint32_t);
|
||
|
|
||
|
prop->size = size_and_flags & DT_PROP_SIZE_MASK;
|
||
|
|
||
|
if ((size_t)(endaddr - addr) < ROUND_SIZE(prop->size)) {
|
||
|
dprintf(DEBUG_INFO, "dt: not enough bytes for property '%s'\n", prop->name);
|
||
|
goto error;
|
||
|
}
|
||
|
|
||
|
prop->data = addr;
|
||
|
addr += ROUND_SIZE(prop->size);
|
||
|
|
||
|
if (size_and_flags & DT_PROP_FLAG_PLACEHOLDER) {
|
||
|
// if placeholder expansion fails, then recycle the property slot
|
||
|
// we used for the placeholder
|
||
|
prop_removed = !expand_placeholder_prop(prop);
|
||
|
if (prop_removed) {
|
||
|
node->nprops--;
|
||
|
i--;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Account for the size of the property after any placeholder expansion has occurred
|
||
|
if (!prop_removed)
|
||
|
dt_size += kPropNameLength + sizeof(uint32_t) + ROUND_SIZE(prop->size);
|
||
|
}
|
||
|
|
||
|
for (uint32_t i = 0; i < node->nchildren; i++) {
|
||
|
if (!parse_node(&node->children[i], &addr, endaddr))
|
||
|
goto error;
|
||
|
}
|
||
|
|
||
|
*addrptr = addr;
|
||
|
|
||
|
return true;
|
||
|
error:
|
||
|
free_node(node);
|
||
|
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
dt_node_t *dt_deserialize(void *dtaddr, size_t dtlength)
|
||
|
{
|
||
|
uint8_t *startptr;
|
||
|
uint8_t *endptr;
|
||
|
dt_node_t *node;
|
||
|
|
||
|
dt_init();
|
||
|
|
||
|
node = calloc(sizeof(*node), 1);
|
||
|
startptr = dtaddr;
|
||
|
endptr = dtaddr + dtlength;
|
||
|
|
||
|
if (!parse_node(node, &startptr, endptr)) {
|
||
|
dt_size = 0;
|
||
|
free(node);
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
dt_root = node;
|
||
|
|
||
|
return node;
|
||
|
}
|
||
|
|
||
|
void dt_free(dt_node_t *dt)
|
||
|
{
|
||
|
free_node(dt);
|
||
|
free(dt);
|
||
|
}
|
||
|
|
||
|
static void write_prop(char *name, void *data, uint32_t data_size, uint8_t **bufptr, uint8_t *end)
|
||
|
{
|
||
|
uint8_t *buf = *bufptr;
|
||
|
uint32_t prop_buf_size = ROUND_SIZE(data_size);
|
||
|
uint32_t total_size = kPropNameLength + sizeof(uint32_t) + prop_buf_size;
|
||
|
|
||
|
// no overflow
|
||
|
if (prop_buf_size < data_size || total_size < prop_buf_size) {
|
||
|
panic("devicetree integer overflow");
|
||
|
}
|
||
|
|
||
|
// Make sure there's room for the property name and value
|
||
|
if ((size_t)(end - buf) < total_size) {
|
||
|
panic("devicetree buffer overflow");
|
||
|
}
|
||
|
|
||
|
// Write property name, padding with zeroes to 32 bytes
|
||
|
memset(buf, 0, kPropNameLength);
|
||
|
strlcpy((char *)buf, name, kPropNameLength);
|
||
|
buf += kPropNameLength;
|
||
|
|
||
|
// Then property size
|
||
|
memcpy(buf, &data_size, sizeof(uint32_t));
|
||
|
buf += sizeof(uint32_t);
|
||
|
|
||
|
// And property value, which won't be present if size is 0
|
||
|
if (prop_buf_size != 0) {
|
||
|
memcpy(buf, data, data_size);
|
||
|
// Pad if needed to multiple of 4 bytes
|
||
|
if (data_size != prop_buf_size)
|
||
|
memset(buf + data_size, 0, prop_buf_size - data_size);
|
||
|
buf += prop_buf_size;
|
||
|
}
|
||
|
|
||
|
RELEASE_ASSERT(buf <= end);
|
||
|
|
||
|
// let caller know how far we wrote
|
||
|
*bufptr = buf;
|
||
|
}
|
||
|
|
||
|
static void write_node(dt_node_t *node, uint8_t **bufptr, uint8_t *end)
|
||
|
{
|
||
|
uint8_t *buf = *bufptr;
|
||
|
|
||
|
RELEASE_ASSERT(*bufptr < end);
|
||
|
|
||
|
// Make sure there's room for the node header
|
||
|
if ((size_t)(end - buf) < 2 * sizeof(uint32_t))
|
||
|
panic("devicetree buffer overflow");
|
||
|
// Write the node header (number of properties, then number of children)
|
||
|
memcpy(buf, &node->nprops, 4);
|
||
|
memcpy(buf + 4, &node->nchildren, 4);
|
||
|
buf += 8;
|
||
|
|
||
|
// Write out each property
|
||
|
for (uint32_t i = 0; i < node->nprops; i++) {
|
||
|
dtprop_t *prop = &node->props[i];
|
||
|
|
||
|
write_prop(prop->name, prop->data, prop->size, &buf, end);
|
||
|
}
|
||
|
|
||
|
// Write out all of this node's children
|
||
|
for (uint32_t i = 0; i < node->nchildren; i++) {
|
||
|
write_node(&node->children[i], &buf, end);
|
||
|
}
|
||
|
|
||
|
// Let the caller know how far we wrote
|
||
|
*bufptr = buf;
|
||
|
|
||
|
RELEASE_ASSERT(*bufptr <= end);
|
||
|
}
|
||
|
|
||
|
void dt_serialize(dt_node_t *dt, void *buffer, size_t bufferlen)
|
||
|
{
|
||
|
uint8_t *startptr = buffer;
|
||
|
uint8_t *endptr = startptr + bufferlen;
|
||
|
|
||
|
if (dt == NULL)
|
||
|
dt = dt_root;
|
||
|
|
||
|
RELEASE_ASSERT(dt != NULL);
|
||
|
|
||
|
if (bufferlen < dt_size)
|
||
|
panic("devicetree length %zu > buffer size %zu", dt_size, bufferlen);
|
||
|
|
||
|
write_node(dt, &startptr, endptr);
|
||
|
|
||
|
// Make sure dt_size is accurate
|
||
|
ASSERT(startptr == (uint8_t *)buffer + dt_size);
|
||
|
}
|
||
|
|
||
|
dt_node_t *dt_get_root(void)
|
||
|
{
|
||
|
return dt_root;
|
||
|
}
|
||
|
|
||
|
bool dt_find_node(dt_node_t *root, const char *path, dt_node_t **node)
|
||
|
{
|
||
|
uint32_t path_len;
|
||
|
const char *rest;
|
||
|
|
||
|
if (root == NULL) {
|
||
|
if (dt_root == NULL)
|
||
|
return false;
|
||
|
root = dt_root;
|
||
|
}
|
||
|
|
||
|
if(path[0] == '\0') {
|
||
|
*node = root;
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
rest = strchr(path, '/');
|
||
|
if (rest == NULL) {
|
||
|
rest = "";
|
||
|
path_len = strlen(path);
|
||
|
} else {
|
||
|
path_len = rest - path;
|
||
|
rest++;
|
||
|
}
|
||
|
|
||
|
for (uint32_t i = 0; i < root->nchildren; i++) {
|
||
|
dt_node_t *child = &root->children[i];
|
||
|
if (child->name == NULL) {
|
||
|
char *prop_name = "name";
|
||
|
if (!dt_get_prop(child, &prop_name, &child->name, &child->name_size)) {
|
||
|
dprintf(DEBUG_CRITICAL, "dt: found node with no name: %p\n", child);
|
||
|
continue;
|
||
|
}
|
||
|
}
|
||
|
if (child->name_size == path_len + 1 && memcmp(path, child->name, path_len) == 0) {
|
||
|
return dt_find_node(child, rest, node);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
static bool find_prop(dt_node_t *node, const char *name, dtprop_t **prop_out)
|
||
|
{
|
||
|
ASSERT(node != NULL);
|
||
|
ASSERT(name != NULL);
|
||
|
|
||
|
for (uint32_t i = 0; i < node->nprops; i++) {
|
||
|
dtprop_t *prop = &node->props[i];
|
||
|
|
||
|
if (strncmp(name, prop->name, kPropNameLength) == 0) {
|
||
|
if (prop_out != NULL)
|
||
|
*prop_out = prop;
|
||
|
return true;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
bool dt_get_prop(dt_node_t *node, char **prop_name, void **prop_data, uint32_t *prop_size)
|
||
|
{
|
||
|
dtprop_t *prop;
|
||
|
|
||
|
ASSERT(node != NULL);
|
||
|
ASSERT(prop_name != NULL && *prop_name != NULL);
|
||
|
|
||
|
if(find_prop(node, *prop_name, &prop)) {
|
||
|
*prop_name = prop->name;
|
||
|
if (prop_size != NULL)
|
||
|
*prop_size = prop->size;
|
||
|
if (prop_data != NULL)
|
||
|
*prop_data = prop->data;
|
||
|
return true;
|
||
|
} else {
|
||
|
return false;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Like dt_find_prop, but with a less obnoxious interface on prop_name
|
||
|
bool dt_find_prop(dt_node_t *node, const char *prop_name, void **prop_data, uint32_t *prop_size)
|
||
|
{
|
||
|
dtprop_t *prop;
|
||
|
|
||
|
ASSERT(node != NULL);
|
||
|
ASSERT(prop_name != NULL);
|
||
|
|
||
|
if(find_prop(node, prop_name, &prop)) {
|
||
|
if (prop_size != NULL)
|
||
|
*prop_size = prop->size;
|
||
|
if (prop_data != NULL)
|
||
|
*prop_data = prop->data;
|
||
|
return true;
|
||
|
} else {
|
||
|
return false;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
bool dt_has_prop(dt_node_t *node, const char *prop_name)
|
||
|
{
|
||
|
return dt_find_prop(node, prop_name, NULL, NULL);
|
||
|
}
|
||
|
|
||
|
const char *dt_get_node_name(dt_node_t *node)
|
||
|
{
|
||
|
if (node->name == NULL) {
|
||
|
char *prop_name = "name";
|
||
|
dt_get_prop(node, &prop_name, &node->name, &node->name_size);
|
||
|
}
|
||
|
|
||
|
return node->name;
|
||
|
}
|
||
|
|
||
|
static dtprop_t *add_prop(dt_node_t *node, const char *name)
|
||
|
{
|
||
|
uint32_t nprops = node->nprops + 1;
|
||
|
dtprop_t *prop;
|
||
|
|
||
|
ASSERT(strlen(name) < kPropNameLength);
|
||
|
|
||
|
node->props = realloc(node->props, nprops * sizeof(*node->props));
|
||
|
|
||
|
prop = &node->props[nprops - 1];
|
||
|
memset(prop, 0, sizeof(*prop));
|
||
|
|
||
|
prop->name = calloc(kPropNameLength, 1);
|
||
|
strlcpy(prop->name, name, kPropNameLength);
|
||
|
prop->name_malloced = true;
|
||
|
|
||
|
node->nprops = nprops;
|
||
|
|
||
|
// The device tree will grow by the size of the property name and length fields
|
||
|
// The caller of this function will account for the growth from the value
|
||
|
dt_size += 4 + kPropNameLength;
|
||
|
|
||
|
return prop;
|
||
|
}
|
||
|
|
||
|
void dt_set_prop(dt_node_t *node, const char *name, const void *data, uint32_t size)
|
||
|
{
|
||
|
dtprop_t *prop;
|
||
|
size_t cur_alloc_size;
|
||
|
size_t new_alloc_size;
|
||
|
|
||
|
ASSERT(node != NULL);
|
||
|
ASSERT(name != NULL);
|
||
|
ASSERT(size == 0 || data != NULL);
|
||
|
ASSERT(!dt_sealed);
|
||
|
|
||
|
if (!find_prop(node, name, &prop))
|
||
|
prop = add_prop(node, name);
|
||
|
|
||
|
// if we're changing the name, get rid of the cache
|
||
|
if (prop->data == node->name) {
|
||
|
node->name = NULL;
|
||
|
node->name_size = 0;
|
||
|
}
|
||
|
|
||
|
// Data is always serialized with length padded to 4 bytes
|
||
|
new_alloc_size = ROUND_SIZE(size);
|
||
|
cur_alloc_size = ROUND_SIZE(prop->size);
|
||
|
|
||
|
// zero-length properties are legal, but malloc(0) isn't
|
||
|
// rest of this file handles prop.data == NULL and prop.size == 0 properly
|
||
|
if (new_alloc_size > 0) {
|
||
|
// Only re-allocate if needed. data_malloced will be false for
|
||
|
// newly created properties because add_prop zeroes the struct
|
||
|
// before returning it
|
||
|
if (cur_alloc_size < new_alloc_size) {
|
||
|
if (!prop->data_malloced)
|
||
|
prop->data = malloc(new_alloc_size);
|
||
|
else
|
||
|
prop->data = realloc(prop->data, new_alloc_size);
|
||
|
prop->data_malloced = true;
|
||
|
}
|
||
|
memcpy(prop->data, data, size);
|
||
|
// zero-fill any padding bytes
|
||
|
memset((uint8_t *)prop->data + size, 0, new_alloc_size - size);
|
||
|
}
|
||
|
|
||
|
prop->size = size;
|
||
|
|
||
|
// add_prop accounted for the name and length fields, we just need
|
||
|
// to fgure out the difference from the value. Since add_prop zeroes
|
||
|
// out the struct, for new properties, cur_alloc_size will be 0
|
||
|
dt_size += new_alloc_size - cur_alloc_size;
|
||
|
}
|
||
|
|
||
|
void dt_set_prop_32(dt_node_t *node, const char *name, uint32_t value)
|
||
|
{
|
||
|
dt_set_prop(node, name, &value, sizeof(value));
|
||
|
}
|
||
|
|
||
|
void dt_set_prop_64(dt_node_t *node, const char *name, uint64_t value)
|
||
|
{
|
||
|
dt_set_prop(node, name, &value, sizeof(value));
|
||
|
}
|
||
|
|
||
|
void dt_set_prop_addr(dt_node_t *node, const char *name, uintptr_t value)
|
||
|
{
|
||
|
dt_set_prop(node, name, &value, sizeof(value));
|
||
|
}
|
||
|
|
||
|
void dt_set_prop_str(dt_node_t *node, const char *name, const char *str)
|
||
|
{
|
||
|
dt_set_prop(node, name, str, strlen(str) + 1);
|
||
|
}
|
||
|
|
||
|
bool dt_remove_prop(dt_node_t *node, const char *name)
|
||
|
{
|
||
|
ASSERT(node != NULL);
|
||
|
ASSERT(name != NULL);
|
||
|
ASSERT(!dt_sealed);
|
||
|
|
||
|
for (uint32_t i = 0; i < node->nprops; i++) {
|
||
|
dtprop_t *prop = &node->props[i];
|
||
|
|
||
|
if (strncmp(name, prop->name, kPropNameLength) == 0) {
|
||
|
// unlikely, but if we're removing the name, get rid of the cache
|
||
|
if (prop->data == node->name) {
|
||
|
node->name = NULL;
|
||
|
node->name_size = 0;
|
||
|
}
|
||
|
|
||
|
dt_size -= ROUND_SIZE(prop->size) + 4 + kPropNameLength;
|
||
|
|
||
|
memmove(prop, prop + 1, (node->nprops - i - 1) * sizeof(*prop));
|
||
|
node->nprops--;
|
||
|
|
||
|
return true;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
bool dt_rename_prop(dt_node_t *node, const char *name, const char *new_name)
|
||
|
{
|
||
|
ASSERT(node != NULL);
|
||
|
ASSERT(name != NULL);
|
||
|
ASSERT(new_name != NULL);
|
||
|
ASSERT(!dt_sealed);
|
||
|
|
||
|
dtprop_t *prop;
|
||
|
size_t new_name_len = strlen(new_name);
|
||
|
|
||
|
ASSERT(new_name_len < kPropNameLength - 1);
|
||
|
|
||
|
if (find_prop(node, name, &prop)) {
|
||
|
// unlikely, but if we're renaming the name property, get rid of the cache
|
||
|
if (prop->data == node->name) {
|
||
|
node->name = NULL;
|
||
|
node->name_size = 0;
|
||
|
}
|
||
|
|
||
|
memset(prop->name, 0, kPropNameLength);
|
||
|
memcpy(prop->name, new_name, new_name_len);
|
||
|
|
||
|
return true;
|
||
|
} else {
|
||
|
return false;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void dt_seal(void)
|
||
|
{
|
||
|
dt_sealed = true;
|
||
|
}
|