first and last commit

This commit is contained in:
Chris Nutter committed 2023-07-08 13:03:17 -07:00
commit 1bf4efd552
2612 files changed
+636500

No files matched your search

+682
View File
@@ -0,0 +1,682 @@
/*
* 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;
}
+67
View File
@@ -0,0 +1,67 @@
#include <debug.h>
#include <string.h>
#include <stdlib.h>
#include <lib/devicetree.h>
#include <sys/menu.h>
#include "devicetree_private.h"
void print_indent(int level)
{
for (int i = 0; i < level; i++)
putchar(' ');
}
void print_property(struct dtprop *prop, bool verbose)
{
uint32_t prop_size;
bool truncated = false;
printf("%s [0x%x]:", prop->name, prop->size);
prop_size = prop->size;
if (!verbose && prop_size > 16) {
prop_size = 16;
truncated = true;
}
for (uint32_t i = 0; i < prop_size; i++)
printf(" %02x", ((uint8_t *)prop->data)[i]);
if (truncated)
printf(" ...");
putchar('\n');
}
void dump_tree_int(dt_node_t *node, int indent, bool verbose)
{
const char *node_name;
node_name = dt_get_node_name(node);
if (node_name == NULL)
node_name = "<UNNAMED>";
print_indent(indent);
printf("%s {\n", node_name);
indent += 4;
for (unsigned i = 0; i < node->nprops; i++) {
print_indent(indent);
print_property(&node->props[i], verbose);
}
for (unsigned i = 0; i < node->nchildren; i++)
dump_tree_int(&node->children[i], indent, verbose);
indent -= 4;
print_indent(indent);
printf("}\n");
}
#if DEBUG_BUILD
void dt_dump(bool verbose)
{
dump_tree_int(dt_get_root(), 0, verbose);
}
#endif
+150
View File
@@ -0,0 +1,150 @@
/*
* Copyright (C) 2010-2015 Apple Inc. All rights reserved.
* Copyright (C) 2006 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/env.h>
#include <lib/fs.h>
#include <lib/image.h>
#include <lib/mib.h>
#include <sys/menu.h>
#include <string.h>
/* load the device tree out of flash into a heap-based piece of memory */
int dt_load(void)
{
int result = 0;
addr_t dtaddr;
size_t dtsize;
/* load to default addresses */
dtaddr = mib_get_addr(kMIBTargetDefaultDeviceTreeAddress);
dtsize = mib_get_size(kMIBTargetDefaultDeviceTreeSize);
/* if we don't already have a devicetree, look for a known image containing one */
if (dt_get_size() == 0) {
if (image_load_type(&dtaddr, &dtsize, IMAGE_TYPE_DEVTREE, 0) != 0) {
dprintf(DEBUG_INFO, "load_device_tree: failed to find device tree\n");
return -1;
}
if (!dt_deserialize((void *)dtaddr, dtsize)) {
dprintf(DEBUG_CRITICAL, "Mal-formed devicetree\n");
result = -1;
}
}
/* consolidate environment */
security_consolidate_environment();
return 0;
}
int dt_load_file(const char *path)
{
int result = 0;
u_int32_t type;
addr_t dtaddr;
size_t dtsize;
/* load to default addresses */
dtaddr = mib_get_addr(kMIBTargetDefaultDeviceTreeAddress);
dtsize = mib_get_size(kMIBTargetDefaultDeviceTreeSize);
// Image3 requires the non-restore types
if (mib_get_u32(kMIBPlatformImageFormat) == 4) {
// Image4
type = IMAGE_TYPE_DEVTREE_RESTORE;
} else {
// Image3
type = IMAGE_TYPE_DEVTREE;
}
if (image_load_file(path, &dtaddr, &dtsize, &type, 1, NULL, 0) != 0) {
dprintf(DEBUG_INFO, "failed to load devicetree from %s", path);
dt_init();
return -1;
}
if (!dt_deserialize((void *)dtaddr, dtsize)) {
dprintf(DEBUG_CRITICAL, "Mal-formed devicetree\n");
result = -1;
}
/* consolidate environment */
security_consolidate_environment();
return 0;
}
int do_devicetree(int argc, struct cmd_arg *argv)
{
addr_t addr = mib_get_addr(kMIBTargetDefaultLoadAddress);
addr_t dtaddr = mib_get_addr(kMIBTargetDefaultDeviceTreeAddress);
size_t dtsize = mib_get_size(kMIBTargetDefaultDeviceTreeSize);
size_t len;
u_int32_t type;
len = env_get_uint("filesize", 0);
#if !RELEASE_BUILD
if ((argc > 3) || ((argc > 1) && !strcmp("help", argv[1].str))) {
printf("usage:\n\t%s [<len>] [<address>]\n", argv[0].str);
return -1;
}
addr = env_get_uint("loadaddr", addr);
if (argc > 1)
len = argv[1].u;
if (argc > 2)
addr = argv[2].u;
#endif
if (len == 0) {
printf("filesize variable invalid or not set, aborting\n");
return -1;
}
if (len > dtsize) {
printf("Device Tree too large\n");
return -1;
}
if (!security_allow_memory((void *)addr, len)) {
printf("Permission Denied\n");
return -1;
}
// Image3 requires the non-restore types
if (mib_get_u32(kMIBPlatformImageFormat) == 4) {
// Image4
type = IMAGE_TYPE_DEVTREE_RESTORE;
} else {
// Image3
type = IMAGE_TYPE_DEVTREE;
}
if (image_load_memory(addr, len, &dtaddr, &dtsize, &type, 1, NULL, 0) != 0) {
printf("Device Tree image not valid\n");
dt_init();
return -1;
}
dt_deserialize((void *)dtaddr, dtsize);
/* consolidate environment */
security_consolidate_environment();
printf("loaded device tree at %p of size 0x%zx, from image at %p\n", (void *)dtaddr, dtsize, (void *)addr);
return 0;
}
+32
View File
@@ -0,0 +1,32 @@
#ifndef DEVICETREE_PRIVATE_H
#define DEVICETREE_PRIVATE_H
#include <stdint.h>
#include <stdbool.h>
#define DT_PROP_FLAG_PLACEHOLDER (1 << 31)
#define DT_PROP_FLAGS_MASK (0xf0000000)
#define DT_PROP_SIZE_MASK (~DT_PROP_FLAGS_MASK)
typedef struct dtprop {
char *name;
void *data;
uint32_t flags;
uint32_t size;
bool name_malloced;
bool data_malloced;
} dtprop_t;
struct dtnode {
dtprop_t *props;
dt_node_t *children;
uint32_t nprops;
uint32_t nchildren;
// caches name property
void *name;
// caches size of name property
uint32_t name_size;
};
#endif
+105
View File
@@ -0,0 +1,105 @@
#include <unittest.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdlib.h>
#include <stdio.h>
#include <sys/stat.h>
#include <lib/env.h>
#include <lib/devicetree.h>
#include <mock_syscfg.h>
struct blockdev *create_mock_blockdev(const char *name, const char *filename, uint32_t block_size);
int do_printenv(int argc, void *args);
int fuzz_main(const char *filename)
{
void *dt_in_buffer;
void *dt_out_buffer;
size_t dt_in_size;
size_t dt_out_size;
struct stat st;
FILE *f;
int result;
dt_node_t *root;
// Set up environment
mock_syscfg_add('ABCD', "abcd", 4);
mock_syscfg_add('AAAA', "abcdabcdabcdabcd", 16);
mock_syscfg_add('AAAB', "abcdabcdabcdabc", 15);
mock_syscfg_add('AAAC', "abcdabcdabcdabcda", 17);
env_set("a", "", 0);
env_set("b", "b", 0);
env_set("c", "cc", 0);
env_set("d", "ddd", 0);
env_set("e", "eeee", 0);
env_set("f", "fffff", 0);
env_set("g", "ggggggggggggggggggggggggggggggggggggggggggg", 0);
env_set("h", "hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh", 0);
env_set("i", "01:02:03:40:50:60", 0);
// Get input size
result = stat(filename, &st);
TEST_ASSERT_EQ(result, 0);
dt_in_size = st.st_size;
// Read it
f = fopen(filename, "r");
TEST_ASSERT_NOT_NULL(f);
dt_in_buffer = malloc(dt_in_size);
result = fread(dt_in_buffer, 1, dt_in_size, f);
TEST_ASSERT_EQ(result, dt_in_size);
root = dt_deserialize(dt_in_buffer, dt_in_size);
if (root != NULL) {
// serialize the unmodified devicetree
dt_out_size = dt_get_size();
dt_out_buffer = malloc(dt_out_size);
dt_serialize(root, dt_out_buffer, dt_out_size);
free(dt_out_buffer);
// make some mods
dt_set_prop(root, "a", NULL, 0);
dt_set_prop(root, "b", "abcdefg", 7);
dt_set_prop(root, "c", "abcdefgh", 8);
dt_set_prop(root, "d", "abcdefghi", 9);
dt_remove_prop(root, "e");
dt_rename_prop(root, "f", "z");
dt_node_t *node;
dt_find_node(root, "chosen", &node);
dt_find_node(NULL, "chosen", &node);
dt_find_node(root, "product/camera", &node);
dt_find_node(NULL, "product/camera", &node);
dt_find_node(root, "product/sdfasd", &node);
// serialize with the modifications
dt_out_size = dt_get_size();
dt_out_buffer = malloc(dt_out_size);
dt_serialize(root, dt_out_buffer, dt_out_size);
free(dt_out_buffer);
// causes deserialized devicetree to be freed
dt_init();
}
free(dt_in_buffer);
return result;
}
// Mock for lib/env
bool
env_blacklist(const char *name, bool write)
{
return false;
}
bool
env_blacklist_nvram(const char *name)
{
return false;
}
+22
View File
@@ -0,0 +1,22 @@
#
# AFL fuzzing dictionary for device tree
#
# See docs/fuzzing.txt
#
# Pass to afl-fuzz with -x option
zeroes0="zeroes/0"
zeroes1="zeroes/1"
zeroes2="zeroes/2"
zeroes3="zeroes/3"
zeroes4="zeroes/4"
zeroes5="zeroes/5"
zeroes7="zeroes/7"
zeroes500="zeroes/500"
syscfgAAAA="syscfg/AAAA"
syscfgAAAB="syscfg/AAAB"
syscfgAAAC="syscfg/AAAC"
syscfgABCD="syscfg/ABCD"
macaddr="macaddr/i"
macaddrA="macaddr/a"
macaddrB="macaddr/b"
+29
View File
@@ -0,0 +1,29 @@
# Copyright (C) 2015 Apple 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.
#
LIBDEVICETREE_DIR := $(GET_LOCAL_DIR)
LIBDEVICETREE_BUILD := $(call TOLIBDIR,$(LIBDEVICETREE_DIR)/LIBDEVICETREE.a)
COMMONLIBS += LIBDEVICETREE
# Only process the remainder of the makefile if we are building libraries
#
ifeq ($(MAKEPHASE),libraries)
LIBDEVICETREE_OBJS := $(LIBDEVICETREE_DIR)/devicetree.o \
$(LIBDEVICETREE_DIR)/devicetree_load.o \
$(LIBDEVICETREE_DIR)/devicetree_debug.o
LIBDEVICETREE_OBJS := $(call TOLIBOBJDIR,$(LIBDEVICETREE_OBJS))
ALL_DEPS += $(LIBDEVICETREE_OBJS:%o=%d)
$(LIBDEVICETREE_BUILD): $(LIBDEVICETREE_OBJS)
endif
+14
View File
@@ -0,0 +1,14 @@
# Copyright (C) 2007, 2010, 2015 Apple, Inc. All rights reserved.
# Copyright (C) 2006 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.
#
OPTIONS += WITH_DEVICETREE=1
LIBRARY_MODULES += lib/devicetree
+23
View File
@@ -0,0 +1,23 @@
# Copyright (C) 2014 Apple 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.
#
LOCAL_DIR := $(GET_LOCAL_DIR)
TEST_NAME := devicetree-fuzz
TEST_OBJS := \
$(LOCAL_DIR)/devicetree.o \
lib/env/env.o
TEST_SUPPORT_OBJS += \
tests/fuzz-main.o \
tests/mocks/syscfg.o \
$(LOCAL_DIR)/fuzz.o
+876
View File
@@ -0,0 +1,876 @@
/*
* 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 <unittest.h>
#include <mock_syscfg.h>
#include <lib/devicetree.h>
#include <lib/env.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
typedef struct dt_test_data {
void *input;
const void *expected;
size_t input_size;
size_t expected_size;
uint32_t misc;
} dt_test_data;
#define DT_TESTCASE(in, out) { \
.input = (void *)in, \
.input_size = sizeof(in) - 1, \
.expected = out, \
.expected_size = sizeof(out) - 1, \
.misc = 0 \
}
#define DT_TESTCASE_MISC(in, out, misc) { \
.input = in, \
.input_size = sizeof(in) - 1, \
.expected = out, \
.expected_size = sizeof(out) - 1, \
.misc = misc \
}
#define DT_TESTCASE_FAIL(in) { \
.input = in, \
.input_size = sizeof(in) - 1, \
.expected = NULL, \
.expected_size = 0, \
.misc = 0 \
}
#define DT_TESTCASE_FAIL_MISC(in, misc) { \
.input = in, \
.input_size = sizeof(in) - 1, \
.expected = NULL, \
.expected_size = 0, \
.misc = misc \
}
// handy for dumping two buffers side-by-side when tests fail
static void hexdump2(uint8_t *buf1, uint8_t *buf2, size_t size) __unused;
static void hexdump2(uint8_t *buf1, uint8_t *buf2, size_t size) {
for (size_t i = 0; i < size; i += 8) {
printf("%04zx: ", i);
for (size_t j = 0; j < 8; j++) {
if (i + j < size)
printf(" %02x", buf1[i + j]);
else
printf(" ");
}
printf(" ");
for (size_t j = 0; j < 8; j++) {
if (i + j < size)
printf(" %02x", buf2[i + j]);
else
printf(" ");
}
printf(" ");
for (size_t j = 0; j < 8; j++) {
if (i + j < size) {
uint8_t c = buf1[i + j];
if (!isprint(c))
c = '.';
printf("%c", c);
} else {
printf(" ");
}
}
printf(" ");
for (size_t j = 0; j < 8; j++) {
if (i + j < size) {
uint8_t c = buf2[i + j];
if (!isprint(c))
c = '.';
printf("%c", c);
} else {
printf(" ");
}
}
printf("\n");
}
}
dt_node_t *test_common_deserialize(dt_test_data *data)
{
dt_init();
return dt_deserialize((void *)data->input, data->input_size);
}
void test_common_serialize(dt_test_data *data)
{
void *serialize_buf;
TEST_ASSERT_EQ(dt_get_size(), data->expected_size);
serialize_buf = calloc(data->expected_size, 1);
dt_serialize(0, serialize_buf, data->expected_size);
TEST_ASSERT_MEM_EQ(serialize_buf, data->expected, data->expected_size);
free(serialize_buf);
}
uint8_t test_dt1[] =
// root node
"\x01\x00\x00\x00" // 1 property
"\x03\x00\x00\x00" // 3 children
// 1st property of root
"name\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
"\x05\x00\x00\x00" // property length 5
"root\0\0\0\0"
// 1st child
"\x03\x00\x00\x00" // 3 properties
"\x00\x00\x00\x00" // 0 children
// 1st property of 1st child
"name\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
"\x07\x00\x00\x00" // property length 7
"child1\0\0"
// 2nd property of 1st child
"property1\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
"\x09\x00\x00\x00" // property length 9
"abcdefghi\0\0\0"
// 3rd property of 1st child
"property2\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
"\x08\x00\x00\x00" // property length 8
"01234567"
// 2nd child
"\x03\x00\x00\x00" // 3 properties
"\x01\x00\x00\x00" // 1 child
// 1st property of 2nd child
"name\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
"\x07\x00\x00\x00" // property length 7
"child2\0\0"
// 2nd property of 2nd child
"property1\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
"\x00\x00\x00\x00" // property length 0
""
// 3rd property of 2nd child
"property2\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
"\x01\x00\x00\x00" // property length 1
"X\x00\x00\x00"
// 1st grandchild
"\x02\x00\x00\x00" // 2 properties
"\x00\x00\x00\x00" // 0 children
// 1st property of 1st grandchild
"name\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
"\x0b\x00\x00\x00" // property length 11
"grandchild\x00\x00" //
// 2nd property of 1st grandchild
"theprop\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
"\x04\x00\x00\x00" // property length 4
"YYYY"
// 3rd child
"\x01\x00\x00\x00" // 1 property
"\x00\x00\x00\x00" // 0 children
// 1st property of 3rd child
"name\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
"\x07\x00\x00\x00" // property length 7
"child3\0\0";
void test_find_property(dt_node_t *node, char *propName, void *expected, uint32_t expected_len)
{
char *propNameInOut = propName;
void *propData;
uint32_t propSize;
if (expected != NULL) {
TEST_ASSERT_EQ(FindProperty(node, &propNameInOut, &propData, &propSize), true);
// result should point into the device tree's backing store
TEST_ASSERT_PTR_NEQ(propNameInOut, propName);
TEST_ASSERT_MEM_EQ(propData, expected, expected_len);
TEST_ASSERT_EQ(propSize, expected_len);
} else {
TEST_ASSERT_EQ(FindProperty(node, &propNameInOut, &propData, &propSize), false);
}
}
void test_devicetree_legacy(uintptr_t param)
{
dt_node_t *node;
dt_node_t *root;
dt_node_t *child1, *child2, *child3, *grandchild;
dt_init();
// - 1 for null terminator of the string
dt_deserialize(&test_dt1[0], sizeof(test_dt1) - 1);
// we should be able to find the root of the device tree
TEST_ASSERT_EQ(FindNode(0, "", &root), true);
// An empty path returns the same node
TEST_ASSERT_EQ(FindNode(root, "", &node), true);
TEST_ASSERT_PTR_EQ(root, node);
// none of these should be found
TEST_ASSERT_EQ(FindNode(0, "root", &node), false);
TEST_ASSERT_EQ(FindNode(0, "/", &node), false);
TEST_ASSERT_EQ(FindNode(0, "child0", &node), false);
TEST_ASSERT_EQ(FindNode(0, "child1/grandchild", &node), false);
TEST_ASSERT_EQ(FindNode(0, "child2/grandchild2", &node), false);
TEST_ASSERT_EQ(FindNode(0, "child2/grandchild/child", &node), false);
TEST_ASSERT_EQ(FindNode(0, "child1", &child1), true);
TEST_ASSERT_NOT_NULL(child1);
test_find_property(child1, "name", "child1", strlen("child1") + 1);
test_find_property(child1, "property1", "abcdefghi", 9);
test_find_property(child1, "property2", "01234567", 8);
test_find_property(child1, "someprop", NULL, 0);
TEST_ASSERT_EQ(FindNode(0, "child2", &child2), true);
TEST_ASSERT_NOT_NULL(child2);
test_find_property(child2, "name", "child2", strlen("child2") + 1);
test_find_property(child2, "property1", "", 0);
test_find_property(child2, "property2", "X", 1);
test_find_property(child2, "someprop", NULL, 0);
TEST_ASSERT_EQ(FindNode(0, "child3", &child3), true);
TEST_ASSERT_NOT_NULL(child3);
test_find_property(child3, "name", "child3", strlen("child3") + 1);
test_find_property(child3, "property1", NULL, 0);
test_find_property(child3, "property2", NULL, 0);
TEST_ASSERT_EQ(FindNode(0, "child2/grandchild", &grandchild), true);
TEST_ASSERT_NOT_NULL(grandchild);
test_find_property(grandchild, "name", "grandchild", strlen("grandchild") + 1);
test_find_property(grandchild, "theprop", "YYYY", 4);
}
void test_dt_get_prop(dt_node_t *node, char *propName, void *expected, uint32_t expected_len)
{
char *propNameInOut = propName;
void *propData;
uint32_t propSize;
if (expected != NULL) {
TEST_ASSERT_EQ(dt_get_prop(node, &propNameInOut, &propData, &propSize), true);
// result should point into the device tree's backing store
TEST_ASSERT_PTR_NEQ(propNameInOut, propName);
TEST_ASSERT_MEM_EQ(propData, expected, expected_len);
TEST_ASSERT_EQ(propSize, expected_len);
} else {
TEST_ASSERT_EQ(dt_get_prop(node, &propNameInOut, &propData, &propSize), false);
}
}
void test_dt_deserialize(uintptr_t context)
{
dt_node_t *node;
dt_node_t *root;
dt_node_t *child1, *child2, *child3, *grandchild;
dt_init();
// - 1 for null terminator of the string
root = dt_deserialize(&test_dt1[0], sizeof(test_dt1) - 1);
TEST_ASSERT_NOT_NULL(root);
// size should match the size of the source buffer
TEST_ASSERT_EQ(dt_get_size(), sizeof(test_dt1) - 1);
// we should be able to find the root of the device tree
TEST_ASSERT_PTR_EQ(dt_get_root(), root);
// An empty path returns the same node
TEST_ASSERT_EQ(dt_find_node(root, "", &node), true);
TEST_ASSERT_PTR_EQ(root, node);
// none of these should be found
TEST_ASSERT_EQ(dt_find_node(0, "root", &node), false);
TEST_ASSERT_EQ(dt_find_node(0, "/", &node), false);
TEST_ASSERT_EQ(dt_find_node(0, "child0", &node), false);
TEST_ASSERT_EQ(dt_find_node(0, "child1/grandchild", &node), false);
TEST_ASSERT_EQ(dt_find_node(0, "child2/grandchild2", &node), false);
TEST_ASSERT_EQ(dt_find_node(0, "child2/grandchild/child", &node), false);
TEST_ASSERT_EQ(dt_find_node(0, "child1", &child1), true);
TEST_ASSERT_NOT_NULL(child1);
test_dt_get_prop(child1, "name", "child1", strlen("child1") + 1);
test_dt_get_prop(child1, "property1", "abcdefghi", 9);
test_dt_get_prop(child1, "property2", "01234567", 8);
test_dt_get_prop(child1, "someprop", NULL, 0);
TEST_ASSERT_EQ(dt_find_node(0, "child2", &child2), true);
TEST_ASSERT_NOT_NULL(child2);
test_dt_get_prop(child2, "name", "child2", strlen("child2") + 1);
test_dt_get_prop(child2, "property1", "", 0);
test_dt_get_prop(child2, "property2", "X", 1);
test_dt_get_prop(child2, "someprop", NULL, 0);
TEST_ASSERT_EQ(dt_find_node(0, "child3", &child3), true);
TEST_ASSERT_NOT_NULL(child3);
test_dt_get_prop(child3, "name", "child3", strlen("child3") + 1);
test_dt_get_prop(child3, "property1", NULL, 0);
test_dt_get_prop(child3, "property2", NULL, 0);
TEST_ASSERT_EQ(dt_find_node(0, "child2/grandchild", &grandchild), true);
TEST_ASSERT_NOT_NULL(grandchild);
test_dt_get_prop(grandchild, "name", "grandchild", strlen("grandchild") + 1);
test_dt_get_prop(grandchild, "theprop", "YYYY", 4);
}
// Deserialize and then reserialize device tree,
// verify output is identical to input
void test_dt_serialize(uintptr_t context)
{
dt_test_data data = DT_TESTCASE(test_dt1, test_dt1);
test_common_deserialize(&data);
test_common_serialize(&data);
}
// Deserialize and then reserialize device tree using a buffer
// too small for the result, verify that this results in a panic
void test_dt_serialize_too_small(uintptr_t context)
{
size_t out_buffer_size;
void *serialize_buf;
dt_test_data data = DT_TESTCASE(test_dt1, test_dt1);
test_common_deserialize(&data);
dt_init();
if ((int)context >= 0) {
out_buffer_size = context;
} else {
out_buffer_size = data.expected_size - (size_t)(-1 * (int)context);
}
serialize_buf = calloc(out_buffer_size, 1);
TEST_EXPECT_PANIC();
dt_serialize(0, serialize_buf, out_buffer_size);
free(serialize_buf);
TEST_EXPECT_PANICKED();
}
uint8_t test_dt2[] =
// root node
"\x02\x00\x00\x00" // 2 properties
"\x01\x00\x00\x00" // 1 child
// 1st property of root
"name\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
"\x05\x00\x00\x00" // property length 5
"root\0\0\0\0"
// 2nd property of root
"prop\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
"\x04\x00\x00\x00" // property length 4
"QWER"
// 1st child
"\x03\x00\x00\x00" // 3 properties
"\x00\x00\x00\x00" // 0 children
// 1st property of 1st child
"name\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
"\x07\x00\x00\x00" // property length 7
"child1\0\0"
// 2nd property of 1st child
"prop\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
"\x04\x00\x00\x00" // property length 4
"ASDF"
// 3rd property of 1st child
"prop2\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
"\x00\x00\x00\x00"; // property length 0
uint8_t test_dt2_out[] =
// root node
"\x01\x00\x00\x00" // 1 property
"\x01\x00\x00\x00" // 1 child
// 1st property of root
"name\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
"\x05\x00\x00\x00" // property length 5
"root\0\0\0\0"
// 1st child
"\x09\x00\x00\x00" // 9 properties
"\x00\x00\x00\x00" // 0 children
// 1st property of 1st child
"name\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
"\x0e\x00\x00\x00" // property length 14
"renamed-child\0\0\0"
// 2nd property of 1st child
"renamed-prop\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
"\x04\x00\x00\x00" // property length 4
"ASDF"
// 3rd property of 1st child
"prop2\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
"\x00\x00\x00\x00" // property length 0
// 4th property of 1st child
"newprop32\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
"\x04\x00\x00\x00" // property length 4
"\xcd\xab\x34\x12"
// 5th property of 1st child
"newprop64\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
"\x08\x00\x00\x00" // property length 8
"\xef\xcd\xab\x90\x78\x56\x34\x12"
// 6th property of 1st child
"newpropaddr\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
"\x08\x00\x00\x00" // property length 8
"\x88\x77\x66\x55\x44\x33\x22\x11"
// 7th property of 1st child
"newpropstr\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
"\x08\x00\x00\x00" // property length 8
"strprop\0"
// 8th property of 1st child
"newpropempty\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
"\x00\x00\x00\x00" // property length 0
// 9th property of 1st child
"newpropdata\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
"\x0b\x00\x00\x00" // property length 11
"somedata\0xy\0";
void test_dt_update(uintptr_t context)
{
char *prop_name;
dt_node_t *root;
dt_node_t *node;
dt_test_data data = DT_TESTCASE(test_dt2, test_dt2_out);
root = test_common_deserialize(&data);
TEST_ASSERT_NOT_NULL(root);
// removing a property returns true if it existed
TEST_ASSERT(dt_remove_prop(root, "prop"));
// and false if not
TEST_ASSERT(!dt_remove_prop(root, "prop"));
TEST_ASSERT(!dt_remove_prop(root, "prop2"));
TEST_ASSERT(dt_find_node(root, "child1", &node));
dt_set_prop_32(node, "newprop32", 0x1234abcd);
dt_set_prop_64(node, "newprop64", 0x1234567890abcdefULL);
dt_set_prop_addr(node, "newpropaddr", 0x1122334455667788ULL);
dt_set_prop_str(node, "newpropstr", "strprop");
dt_set_prop(node, "newpropempty", "", 0);
dt_set_prop(node, "newpropdata", "somedata\0xy", 11);
// After changing the node's name, it should be findable under
// the new name, and not under the old one
dt_set_prop_str(node, "name", "renamed-child");
TEST_ASSERT(!dt_find_node(root, "child1", &node));
TEST_ASSERT(dt_find_node(root, "renamed-child", &node));
// After changing the property's name, it should be findable under
// the new name, and not under the old one
prop_name = "prop";
TEST_ASSERT(dt_get_prop(node, &prop_name, NULL, NULL));
TEST_ASSERT(dt_rename_prop(node, "prop", "renamed-prop"));
prop_name = "prop";
TEST_ASSERT(!dt_get_prop(node, &prop_name, NULL, NULL));
prop_name = "renamed-prop";
TEST_ASSERT(dt_get_prop(node, &prop_name, NULL, NULL));
test_common_serialize(&data);
}
uint8_t test_dt_update_shorter_in[] =
// root node
"\x04\x00\x00\x00" // 4 properties
"\x00\x00\x00\x00" // 0 children
// 1st property of root
"name\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
"\x05\x00\x00\x00" // property length 5
"root\0\0\0\0"
// 2nd property of root
"prop1\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
"\x08\x00\x00\x00" // property length 8
"12345678"
// 3rd property of root
"prop2\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
"\x07\x00\x00\x00" // property length 7
"1234567\0"
// 4th property of root
"prop3\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
"\x09\x00\x00\x00" // property length 9
"123456789\0\0\0"
// 5th property of root
"prop4\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
"\x08\x00\x00\x00" // property length 8
"12345678";
uint8_t test_dt_update_shorter_out[] =
// root node
"\x05\x00\x00\x00" // 5 properties
"\x00\x00\x00\x00" // 0 children
// 1st property of root
"name\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
"\x05\x00\x00\x00" // property length 5
"root\0\0\0\0"
// 2nd property of root
"prop1\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
"\x04\x00\x00\x00" // property length 4
"abcd"
// 3rd property of root
"prop2\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
"\x04\x00\x00\x00" // property length 4
"ABCD"
// 4th property of root
"prop3\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
"\x04\x00\x00\x00" // property length 4
"QWER"
// 5th property of root
"prop4\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
"\x05\x00\x00\x00" // property length 5
"ASDFG\0\0\0";
void test_dt_update_shorter(uintptr_t context)
{
dt_node_t *root;
dt_test_data data = DT_TESTCASE(test_dt_update_shorter_in, test_dt_update_shorter_out);
root = test_common_deserialize(&data);
TEST_ASSERT_NOT_NULL(root);
dt_set_prop(root, "prop1", "abcd", 4);
dt_set_prop(root, "prop2", "ABCD", 4);
dt_set_prop(root, "prop3", "QWER", 4);
dt_set_prop(root, "prop4", "ASDFG", 5);
test_common_serialize(&data);
}
uint8_t test_dt_placeholders_data[] =
// root node
"\x09\x00\x00\x00" // num properties
"\x00\x00\x00\x00" // num children
// 1st property of root
"name\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
"\x05\x00\x00\x00" // property length 5
"root\0\0\0\0"
// 2nd property of root (valid keys should get filled in)
"prop1\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
"\x0c\x00\x00\x80" // placeholder property length 12
"syscfg/ABCD\0"
// 3rd property of root (invalid keys should get elided)
"prop2\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
"\x0c\x00\x00\x80" // placeholder property length 12
"syscfg/NNNN\0"
// 4th property of root (valid key after invalid key should get filled in)
"prop3\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
"\x18\x00\x00\x80" // placeholder property length 24
"syscfg/NNNN,syscfg/ABCD\0"
// 5th property of root (1st value found should get copied)
"prop4\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
"\x18\x00\x00\x80" // placeholder property length 24
"syscfg/FFFF,syscfg/ABCD\0"
// 6th property of root (syscg value longer than placeholder)
"prop5\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
"\x0c\x00\x00\x80" // placeholder property length 12
"syscfg/BigE\0"
// 7th property of root (syscg value truncated)
"prop6\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
"\x0e\x00\x00\x80" // placeholder property length 14
"syscfg/BigE/6\0\0\0"
// 8th property of root (syscg value padded)
"prop7\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
"\x0f\x00\x00\x80" // placeholder property length 15
"syscfg/ABCD/12\0\0"
// 9th property of root (macaddr from environment)
"prop8\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
"\x10\x00\x00\x80" // placeholder property length 16
"macaddr/ethaddr\0";
uint8_t test_dt_placeholders_out[] =
// root node
"\x08\x00\x00\x00" // num properties
"\x00\x00\x00\x00" // num children
// 1st property of root
"name\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
"\x05\x00\x00\x00" // property length 5
"root\0\0\0\0"
// 2nd property of root (valid keys should get filled in)
"prop1\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
"\x06\x00\x00\x00" // property length 6
"found!\0\0"
// 3rd property of root (valid key after invalid key should get filled in)
"prop3\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
"\x06\x00\x00\x00" // property length 6
"found!\0\0"
// 4th property of root (1st value found should get copied)
"prop4\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
"\x04\x00\x00\x00" // property length 4
"yess"
// 5th property of root (syscfg value longer than placeholder)
"prop5\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
"\x1a\x00\x00\x00" // property length 26
"ABCDEFGHIJKLMNOPQRSTUVWXYZ\0\0"
// 6th property of root (syscfg value truncated)
"prop6\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
"\x06\x00\x00\x00" // property length 6
"ABCDEF\0\0"
// 7th property of root (syscfg value padded)
"prop7\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
"\x0c\x00\x00\x00" // property length 12
"found!\0\0\0\0\0\0"
// 8th property of root (mac address)
"prop8\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
"\x06\x00\x00\x00" // property length 6
"\x01\x02\x03\x40\x50\x60\0\0";
void test_dt_placeholders(uintptr_t context)
{
dt_node_t *root;
mock_syscfg_reset();
mock_syscfg_add('ABCD', "found!", strlen("found!"));
mock_syscfg_add('FFFF', "yess", strlen("yess"));
mock_syscfg_add('BigE', "ABCDEFGHIJKLMNOPQRSTUVWXYZ", 26);
env_set("ethaddr", "01:02:03:40:50:60", 0);
dt_test_data data = DT_TESTCASE(test_dt_placeholders_data, test_dt_placeholders_out);
root = test_common_deserialize(&data);
TEST_ASSERT_NOT_NULL(root);
test_common_serialize(&data);
}
uint8_t test_dt_placeholders_zeroes_data[] =
// root node
"\x04\x00\x00\x00" // num properties
"\x00\x00\x00\x00" // num children
// 1st property of root
"name\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
"\x05\x00\x00\x00" // property length 5
"root\0\0\0\0"
// 2nd property of root
"prop1\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
"\x09\x00\x00\x80" // property length
"zeroes/2\0\0\0\0"
// 3nd property of root
"prop2\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
"\x09\x00\x00\x80" // property length
"zeroes/4\0\0\0\0"
// 4th property of root
"prop3\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
"\x0c\x00\x00\x80" // property length
"zeroes/0x20\0";
uint8_t test_dt_placeholders_zeroes_out[] =
// root node
"\x04\x00\x00\x00" // num properties
"\x00\x00\x00\x00" // num children
// 1st property of root
"name\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
"\x05\x00\x00\x00" // property length 5
"root\0\0\0\0"
// 2nd property of root
"prop1\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
"\x02\x00\x00\x00" // property length
"\0\0\0\0"
// 3nd property of root
"prop2\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
"\x04\x00\x00\x00" // property length
"\0\0\0\0"
// 4th property of root
"prop3\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
"\x20\x00\x00\x00" // property length
"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00";
void test_dt_placeholders_zeroes(uintptr_t context)
{
dt_node_t *root;
dt_test_data data = DT_TESTCASE(test_dt_placeholders_zeroes_data, test_dt_placeholders_zeroes_out);
root = test_common_deserialize(&data);
TEST_ASSERT_NOT_NULL(root);
test_common_serialize(&data);
}
void test_common_short(dt_test_data *data)
{
dt_node_t *root;
// Start by making sure the data is well-formed
root = test_common_deserialize(data);
TEST_ASSERT_NOT_NULL(root);
test_common_serialize(data);
// Now chop off a byte at a time, verifying we get a failure each time
for (size_t i = 0; i < data->input_size; i++) {
dt_init();
root = dt_deserialize((void *)data->input, i);
TEST_ASSERT_NULL(root);
}
}
void test_dt_short1(uintptr_t context)
{
uint8_t input_data[] =
"\x00\x00\x00\x00\00\x00\x00\x00" // no properties, no children
;
dt_test_data data = DT_TESTCASE(input_data, input_data);
test_common_short(&data);
}
void test_dt_short2(uintptr_t context)
{
uint8_t input_data[] =
"\x01\x00\x00\x00\x00\x00\x00\x00" // one property, no children
"abcdefghijklmnopqrstuvqxzy01234\0" // 31-char name
"\x03\x00\x00\x00" // property len 3
"abc\0" // property value
;
dt_test_data data = DT_TESTCASE(input_data, input_data);
test_common_short(&data);
}
void test_dt_short3(uintptr_t context)
{
uint8_t input_data[] =
"\x01\x00\x00\x00\x01\x00\x00\x00" // one property, one child
"abcdefghijklmnopqrstuvqxzy01234\0" // 31-char name
"\x03\x00\x00\x00" // property len 3
"abc\0" // property value
"\x00\x00\x00\x00\x00\x00\x00\x00" // no properties, no children
;
dt_test_data data = DT_TESTCASE(input_data, input_data);
test_common_short(&data);
}
void test_dt_bad_name(uintptr_t context)
{
uint8_t input_data[] =
"\x01\x00\x00\x00\x00\x00\x00\x00" // one property, no children
"abcdefghijklmnopqrstuvqxzy012345" // 32-char name
"\x03\x00\x00\x00" // property len 3
"abc\0" // property value
;
dt_node_t *root;
dt_test_data data = DT_TESTCASE_FAIL(input_data);
dt_init();
root = dt_deserialize(data.input, data.input_size);
TEST_ASSERT_NULL(root);
}
static struct test_suite devicetree_test_suite = {
.name = "devicetree",
.description = "tests the devicetree parser",
.test_cases = {
{ "devicetree_legacy", test_devicetree_legacy, 0 },
{ "dt_deserialize", test_dt_deserialize, 0 },
{ "dt_serialize", test_dt_serialize, 0 },
{ "dt_serialize_too_small0", test_dt_serialize, 0 },
{ "dt_serialize_too_small1", test_dt_serialize, 1 },
{ "dt_serialize_too_small-1", test_dt_serialize, (uintptr_t)-1LL },
{ "dt_update", test_dt_update, 0 },
{ "dt_update_shorter", test_dt_update_shorter, 0 },
{ "dt_placeholders", test_dt_placeholders, 0 },
{ "dt_placeholders_zeroes", test_dt_placeholders_zeroes, 0 },
{ "dt_short1", test_dt_short1, 0 },
{ "dt_short2", test_dt_short2, 0 },
{ "dt_short3", test_dt_short3, 0 },
{ "dt_bad_name", test_dt_bad_name, 0 },
TEST_CASE_LAST
}
};
TEST_SUITE(devicetree_test_suite);
bool env_blacklist(const char *name, bool write)
{
return false;
}
bool env_blacklist_nvram(const char *name)
{
return false;
}
+22
View File
@@ -0,0 +1,22 @@
# Copyright (C) 2014 Apple 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.
#
LOCAL_DIR := $(GET_LOCAL_DIR)
TEST_NAME := devicetree
TEST_OBJS := \
$(LOCAL_DIR)/devicetree.o \
lib/env/env.o
TEST_SUPPORT_OBJS := \
tests/unittest-main.o \
tests/mocks/syscfg.o \
$(LOCAL_DIR)/tests.o