757 lines
15 KiB
C
757 lines
15 KiB
C
// SPDX-License-Identifier: GPL-2.0-or-later
|
|
/*
|
|
* treegen - generate test device tree blobs
|
|
*
|
|
* Build example device tree blobs for test. Note that some of the generated
|
|
* blobs are deliberately malformed, in order to test error conditions.
|
|
*/
|
|
#include <assert.h>
|
|
#include <stdarg.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <stdint.h>
|
|
#include <unistd.h>
|
|
#include <fcntl.h>
|
|
|
|
#include <libfdt_env.h>
|
|
#include <fdt.h>
|
|
|
|
#include "tests.h"
|
|
#include "testdata.h"
|
|
|
|
/*
|
|
* Buffer with write cursor. Emit helpers write at the cursor, advance it, and
|
|
* return the offset at which data was written.
|
|
*/
|
|
struct buf {
|
|
char *data;
|
|
size_t size, cursor;
|
|
};
|
|
|
|
#define CHUNKSIZE 1024
|
|
|
|
static struct buf buf_init(void)
|
|
{
|
|
struct buf b = { NULL, 0, 0 };
|
|
|
|
return b;
|
|
}
|
|
|
|
/* Low-level helpers */
|
|
|
|
static size_t emit_bytes(struct buf *b, const void *data, int len)
|
|
{
|
|
size_t off = b->cursor;
|
|
|
|
if (!len)
|
|
return off;
|
|
|
|
if (b->cursor + len > b->size) {
|
|
size_t newsize = b->cursor + len + CHUNKSIZE;
|
|
|
|
b->data = xrealloc(b->data, newsize);
|
|
memset(b->data + b->size, 0, newsize - b->size);
|
|
b->size = newsize;
|
|
}
|
|
memcpy(b->data + b->cursor, data, len);
|
|
b->cursor += len;
|
|
return off;
|
|
}
|
|
|
|
static size_t emit_u32(struct buf *b, uint32_t val)
|
|
{
|
|
fdt32_t beval = cpu_to_fdt32(val);
|
|
|
|
return emit_bytes(b, &beval, sizeof(beval));
|
|
}
|
|
|
|
static size_t emit_u64(struct buf *b, uint64_t val)
|
|
{
|
|
fdt64_t beval = cpu_to_fdt64(val);
|
|
|
|
return emit_bytes(b, &beval, sizeof(beval));
|
|
}
|
|
|
|
static size_t emit_string(struct buf *b, const char *str)
|
|
{
|
|
return emit_bytes(b, str, strlen(str) + 1);
|
|
}
|
|
|
|
#define MAX_ALIGNMENT 8
|
|
|
|
static size_t emit_align(struct buf *b, size_t alignment)
|
|
{
|
|
size_t pad = ((b->cursor + alignment - 1) & ~(alignment - 1)) - b->cursor;
|
|
static const char zeros[MAX_ALIGNMENT];
|
|
|
|
assert(alignment <= MAX_ALIGNMENT);
|
|
assert(pad < alignment);
|
|
|
|
return emit_bytes(b, zeros, pad);
|
|
}
|
|
|
|
static size_t start_block(struct buf *b)
|
|
{
|
|
return b->cursor;
|
|
}
|
|
|
|
static void fill_prop_name(struct buf *b, size_t strs, size_t prop, size_t stroff)
|
|
{
|
|
struct fdt_property *fp = (struct fdt_property *)(b->data + prop);
|
|
|
|
fp->nameoff = cpu_to_fdt32(stroff - strs);
|
|
}
|
|
|
|
/* FDT structure helpers */
|
|
|
|
static size_t emit_fdt_header(struct buf *b)
|
|
{
|
|
size_t off = emit_u32(b, FDT_MAGIC);
|
|
|
|
emit_u32(b, 0); /* totalsize */
|
|
emit_u32(b, 0); /* off_dt_struct */
|
|
emit_u32(b, 0); /* off_dt_strings */
|
|
emit_u32(b, 0); /* off_mem_rsvmap */
|
|
emit_u32(b, 0x11); /* version */
|
|
emit_u32(b, 0x10); /* last_comp_version */
|
|
emit_u32(b, 0); /* boot_cpuid_phys */
|
|
emit_u32(b, 0); /* size_dt_strings */
|
|
emit_u32(b, 0); /* size_dt_struct */
|
|
return off;
|
|
}
|
|
|
|
static void finish_rsvmap(struct buf *b, size_t hdr, size_t rsvmap)
|
|
{
|
|
struct fdt_header *fh = (struct fdt_header *)(b->data + hdr);
|
|
|
|
fh->off_mem_rsvmap = cpu_to_fdt32(rsvmap - hdr);
|
|
}
|
|
|
|
static void finish_struct_block(struct buf *b, size_t hdr, size_t start)
|
|
{
|
|
struct fdt_header *fh = (struct fdt_header *)(b->data + hdr);
|
|
|
|
fh->off_dt_struct = cpu_to_fdt32(start - hdr);
|
|
fh->size_dt_struct = cpu_to_fdt32(b->cursor - start);
|
|
}
|
|
|
|
static void finish_strings_block(struct buf *b, size_t hdr, size_t start)
|
|
{
|
|
struct fdt_header *fh = (struct fdt_header *)(b->data + hdr);
|
|
|
|
fh->off_dt_strings = cpu_to_fdt32(start - hdr);
|
|
fh->size_dt_strings = cpu_to_fdt32(b->cursor - start);
|
|
}
|
|
|
|
static void finish_totalsize(struct buf *b, size_t hdr)
|
|
{
|
|
struct fdt_header *fh = (struct fdt_header *)(b->data + hdr);
|
|
|
|
fh->totalsize = cpu_to_fdt32(b->cursor - hdr);
|
|
}
|
|
|
|
static size_t emit_rsvmap_entry(struct buf *b, uint64_t addr, uint64_t size)
|
|
{
|
|
size_t off = emit_u64(b, addr);
|
|
|
|
emit_u64(b, size);
|
|
return off;
|
|
}
|
|
|
|
static size_t emit_rsvmap_term(struct buf *b)
|
|
{
|
|
return emit_rsvmap_entry(b, 0, 0);
|
|
}
|
|
|
|
static void emit_rsvmap_empty(struct buf *b, size_t hdr)
|
|
{
|
|
size_t rsvmap = start_block(b);
|
|
|
|
emit_rsvmap_term(b);
|
|
finish_rsvmap(b, hdr, rsvmap);
|
|
}
|
|
|
|
static size_t emit_begin_node(struct buf *b, const char *name)
|
|
{
|
|
size_t off = emit_u32(b, FDT_BEGIN_NODE);
|
|
|
|
emit_string(b, name);
|
|
emit_align(b, FDT_TAGSIZE);
|
|
return off;
|
|
}
|
|
|
|
static size_t emit_end_node(struct buf *b)
|
|
{
|
|
return emit_u32(b, FDT_END_NODE);
|
|
}
|
|
|
|
static size_t emit_fdt_end(struct buf *b)
|
|
{
|
|
return emit_u32(b, FDT_END);
|
|
}
|
|
|
|
static size_t emit_prop_header(struct buf *b, int nameoff, int len)
|
|
{
|
|
size_t off = emit_u32(b, FDT_PROP);
|
|
|
|
emit_u32(b, len);
|
|
emit_u32(b, nameoff);
|
|
return off;
|
|
}
|
|
|
|
static size_t emit_prop_nil(struct buf *b, int nameoff)
|
|
{
|
|
return emit_prop_header(b, nameoff, 0);
|
|
}
|
|
|
|
static size_t emit_prop_u32(struct buf *b, int nameoff, uint32_t val)
|
|
{
|
|
size_t off = emit_prop_header(b, nameoff, 4);
|
|
|
|
emit_u32(b, val);
|
|
return off;
|
|
}
|
|
|
|
static size_t emit_prop_u64(struct buf *b, int nameoff, uint64_t val)
|
|
{
|
|
size_t off = emit_prop_header(b, nameoff, 8);
|
|
|
|
emit_u64(b, val);
|
|
return off;
|
|
}
|
|
|
|
static size_t emit_prop_str(struct buf *b, int nameoff, const char *str)
|
|
{
|
|
int len = strlen(str) + 1;
|
|
size_t off = emit_prop_header(b, nameoff, len);
|
|
|
|
emit_bytes(b, str, len);
|
|
emit_align(b, FDT_TAGSIZE);
|
|
return off;
|
|
}
|
|
|
|
#define emit_prop_strings(b, nameoff, ...) \
|
|
emit_prop_strings_(b, nameoff, __VA_ARGS__, NULL)
|
|
|
|
static size_t emit_prop_strings_(struct buf *b, int nameoff, ...)
|
|
{
|
|
size_t off = emit_prop_header(b, nameoff, 0);
|
|
size_t start = b->cursor;
|
|
va_list ap;
|
|
const char *s;
|
|
|
|
va_start(ap, nameoff);
|
|
while ((s = va_arg(ap, const char *)) != NULL)
|
|
emit_string(b, s);
|
|
va_end(ap);
|
|
|
|
((struct fdt_property *)(b->data + off))->len =
|
|
cpu_to_fdt32(b->cursor - start);
|
|
emit_align(b, FDT_TAGSIZE);
|
|
return off;
|
|
}
|
|
|
|
/*
|
|
* Tree generators
|
|
*
|
|
* Each function builds a complete DTB in a buffer.
|
|
*/
|
|
|
|
static struct buf make_test_tree1(void)
|
|
{
|
|
struct buf b = buf_init();
|
|
size_t hdr;
|
|
size_t p_compatible, p_prop_int, p_prop_int64, p_prop_str,
|
|
p_address_cells, p_size_cells;
|
|
size_t p_s1_compatible, p_s1_reg, p_s1_prop_int;
|
|
size_t p_s1ss_compatible, p_s1ss_placeholder, p_s1ss_prop_int;
|
|
size_t p_s2_reg, p_s2_linux_phandle, p_s2_prop_int,
|
|
p_s2_address_cells, p_s2_size_cells;
|
|
size_t p_s2ss0_reg, p_s2ss0_phandle, p_s2ss0_compatible,
|
|
p_s2ss0_prop_int;
|
|
|
|
hdr = emit_fdt_header(&b);
|
|
emit_align(&b, 8);
|
|
|
|
{
|
|
size_t rsvmap = start_block(&b);
|
|
|
|
emit_rsvmap_entry(&b, TEST_ADDR_1, TEST_SIZE_1);
|
|
emit_rsvmap_entry(&b, TEST_ADDR_2, TEST_SIZE_2);
|
|
emit_rsvmap_term(&b);
|
|
finish_rsvmap(&b, hdr, rsvmap);
|
|
}
|
|
|
|
{
|
|
size_t ss = start_block(&b);
|
|
|
|
emit_begin_node(&b, "");
|
|
p_compatible = emit_prop_str(&b, 0, "test_tree1");
|
|
p_prop_int = emit_prop_u32(&b, 0, TEST_VALUE_1);
|
|
p_prop_int64 = emit_prop_u64(&b, 0, TEST_VALUE64_1);
|
|
p_prop_str = emit_prop_str(&b, 0, TEST_STRING_1);
|
|
p_address_cells = emit_prop_u32(&b, 0, 1);
|
|
p_size_cells = emit_prop_u32(&b, 0, 0);
|
|
|
|
emit_begin_node(&b, "subnode@1");
|
|
p_s1_compatible = emit_prop_str(&b, 0, "subnode1");
|
|
p_s1_reg = emit_prop_u32(&b, 0, 1);
|
|
p_s1_prop_int = emit_prop_u32(&b, 0, TEST_VALUE_1);
|
|
|
|
emit_begin_node(&b, "subsubnode");
|
|
p_s1ss_compatible = emit_prop_strings(&b, 0,
|
|
"subsubnode1", "subsubnode");
|
|
p_s1ss_placeholder = emit_prop_strings(&b, 0,
|
|
"this is a placeholder string",
|
|
"string2");
|
|
p_s1ss_prop_int = emit_prop_u32(&b, 0, TEST_VALUE_1);
|
|
emit_end_node(&b);
|
|
|
|
emit_begin_node(&b, "ss1");
|
|
emit_end_node(&b);
|
|
|
|
emit_end_node(&b);
|
|
|
|
emit_begin_node(&b, "subnode@2");
|
|
p_s2_reg = emit_prop_u32(&b, 0, 2);
|
|
p_s2_linux_phandle = emit_prop_u32(&b, 0, PHANDLE_1);
|
|
p_s2_prop_int = emit_prop_u32(&b, 0, TEST_VALUE_2);
|
|
p_s2_address_cells = emit_prop_u32(&b, 0, 1);
|
|
p_s2_size_cells = emit_prop_u32(&b, 0, 0);
|
|
|
|
emit_begin_node(&b, "subsubnode@0");
|
|
p_s2ss0_reg = emit_prop_u32(&b, 0, 0);
|
|
p_s2ss0_phandle = emit_prop_u32(&b, 0, PHANDLE_2);
|
|
p_s2ss0_compatible = emit_prop_strings(&b, 0,
|
|
"subsubnode2", "subsubnode");
|
|
p_s2ss0_prop_int = emit_prop_u32(&b, 0, TEST_VALUE_2);
|
|
emit_end_node(&b);
|
|
|
|
emit_begin_node(&b, "ss2");
|
|
emit_end_node(&b);
|
|
|
|
emit_end_node(&b);
|
|
|
|
emit_end_node(&b);
|
|
emit_fdt_end(&b);
|
|
finish_struct_block(&b, hdr, ss);
|
|
}
|
|
|
|
{
|
|
size_t strs = start_block(&b);
|
|
size_t s;
|
|
|
|
s = emit_string(&b, "compatible");
|
|
fill_prop_name(&b, strs, p_compatible, s);
|
|
fill_prop_name(&b, strs, p_s1_compatible, s);
|
|
fill_prop_name(&b, strs, p_s1ss_compatible, s);
|
|
fill_prop_name(&b, strs, p_s2ss0_compatible, s);
|
|
|
|
s = emit_string(&b, "prop-int");
|
|
fill_prop_name(&b, strs, p_prop_int, s);
|
|
fill_prop_name(&b, strs, p_s1_prop_int, s);
|
|
fill_prop_name(&b, strs, p_s1ss_prop_int, s);
|
|
fill_prop_name(&b, strs, p_s2_prop_int, s);
|
|
fill_prop_name(&b, strs, p_s2ss0_prop_int, s);
|
|
|
|
fill_prop_name(&b, strs, p_prop_int64,
|
|
emit_string(&b, "prop-int64"));
|
|
|
|
fill_prop_name(&b, strs, p_prop_str,
|
|
emit_string(&b, "prop-str"));
|
|
|
|
fill_prop_name(&b, strs, p_s2_linux_phandle,
|
|
emit_string(&b, "linux,phandle"));
|
|
|
|
fill_prop_name(&b, strs, p_s2ss0_phandle,
|
|
emit_string(&b, "phandle"));
|
|
|
|
s = emit_string(&b, "reg");
|
|
fill_prop_name(&b, strs, p_s1_reg, s);
|
|
fill_prop_name(&b, strs, p_s2_reg, s);
|
|
fill_prop_name(&b, strs, p_s2ss0_reg, s);
|
|
|
|
fill_prop_name(&b, strs, p_s1ss_placeholder,
|
|
emit_string(&b, "placeholder"));
|
|
|
|
s = emit_string(&b, "#address-cells");
|
|
fill_prop_name(&b, strs, p_address_cells, s);
|
|
fill_prop_name(&b, strs, p_s2_address_cells, s);
|
|
|
|
s = emit_string(&b, "#size-cells");
|
|
fill_prop_name(&b, strs, p_size_cells, s);
|
|
fill_prop_name(&b, strs, p_s2_size_cells, s);
|
|
|
|
finish_strings_block(&b, hdr, strs);
|
|
}
|
|
|
|
finish_totalsize(&b, hdr);
|
|
|
|
return b;
|
|
}
|
|
|
|
static struct buf make_truncated_property(void)
|
|
{
|
|
struct buf b = buf_init();
|
|
size_t hdr;
|
|
size_t p;
|
|
|
|
hdr = emit_fdt_header(&b);
|
|
emit_align(&b, 8);
|
|
|
|
emit_rsvmap_empty(&b, hdr);
|
|
|
|
{
|
|
size_t ss = start_block(&b);
|
|
|
|
emit_begin_node(&b, "");
|
|
p = emit_prop_header(&b, 0, 4);
|
|
finish_struct_block(&b, hdr, ss);
|
|
}
|
|
|
|
{
|
|
size_t strs = start_block(&b);
|
|
|
|
emit_string(&b, "truncated");
|
|
fill_prop_name(&b, strs, p, strs);
|
|
finish_strings_block(&b, hdr, strs);
|
|
}
|
|
|
|
finish_totalsize(&b, hdr);
|
|
|
|
return b;
|
|
}
|
|
|
|
static struct buf make_bad_node_char(void)
|
|
{
|
|
struct buf b = buf_init();
|
|
size_t hdr;
|
|
|
|
hdr = emit_fdt_header(&b);
|
|
emit_align(&b, 8);
|
|
|
|
emit_rsvmap_empty(&b, hdr);
|
|
|
|
{
|
|
size_t ss = start_block(&b);
|
|
|
|
emit_begin_node(&b, "");
|
|
emit_begin_node(&b, "sub$node");
|
|
emit_end_node(&b);
|
|
emit_end_node(&b);
|
|
emit_fdt_end(&b);
|
|
finish_struct_block(&b, hdr, ss);
|
|
}
|
|
|
|
finish_strings_block(&b, hdr, start_block(&b));
|
|
|
|
finish_totalsize(&b, hdr);
|
|
|
|
return b;
|
|
}
|
|
|
|
static struct buf make_bad_node_format(void)
|
|
{
|
|
struct buf b = buf_init();
|
|
size_t hdr;
|
|
|
|
hdr = emit_fdt_header(&b);
|
|
emit_align(&b, 8);
|
|
|
|
emit_rsvmap_empty(&b, hdr);
|
|
|
|
{
|
|
size_t ss = start_block(&b);
|
|
|
|
emit_begin_node(&b, "");
|
|
emit_begin_node(&b, "subnode@1@2");
|
|
emit_end_node(&b);
|
|
emit_end_node(&b);
|
|
emit_fdt_end(&b);
|
|
finish_struct_block(&b, hdr, ss);
|
|
}
|
|
|
|
finish_strings_block(&b, hdr, start_block(&b));
|
|
|
|
finish_totalsize(&b, hdr);
|
|
|
|
return b;
|
|
}
|
|
|
|
static struct buf make_bad_prop_char(void)
|
|
{
|
|
struct buf b = buf_init();
|
|
size_t hdr;
|
|
size_t p;
|
|
|
|
hdr = emit_fdt_header(&b);
|
|
emit_align(&b, 8);
|
|
|
|
emit_rsvmap_empty(&b, hdr);
|
|
|
|
{
|
|
size_t ss = start_block(&b);
|
|
|
|
emit_begin_node(&b, "");
|
|
p = emit_prop_u32(&b, 0, TEST_VALUE_1);
|
|
emit_end_node(&b);
|
|
emit_fdt_end(&b);
|
|
finish_struct_block(&b, hdr, ss);
|
|
}
|
|
|
|
{
|
|
size_t strs = start_block(&b);
|
|
|
|
fill_prop_name(&b, strs, p,
|
|
emit_string(&b, "prop$erty"));
|
|
finish_strings_block(&b, hdr, strs);
|
|
}
|
|
|
|
finish_totalsize(&b, hdr);
|
|
|
|
return b;
|
|
}
|
|
|
|
static struct buf make_ovf_size_strings(void)
|
|
{
|
|
struct buf b = buf_init();
|
|
size_t hdr;
|
|
|
|
hdr = emit_fdt_header(&b);
|
|
emit_align(&b, 8);
|
|
|
|
emit_rsvmap_empty(&b, hdr);
|
|
|
|
{
|
|
size_t ss = start_block(&b);
|
|
|
|
emit_begin_node(&b, "");
|
|
emit_prop_u32(&b, 0x10000000, 0);
|
|
emit_end_node(&b);
|
|
emit_fdt_end(&b);
|
|
finish_struct_block(&b, hdr, ss);
|
|
}
|
|
|
|
{
|
|
size_t strs = start_block(&b);
|
|
|
|
emit_string(&b, "x");
|
|
finish_strings_block(&b, hdr, strs);
|
|
}
|
|
|
|
((struct fdt_header *)(b.data + hdr))->size_dt_strings =
|
|
cpu_to_fdt32(0xffffffff);
|
|
|
|
finish_totalsize(&b, hdr);
|
|
|
|
return b;
|
|
}
|
|
|
|
static struct buf make_truncated_string(void)
|
|
{
|
|
struct buf b = buf_init();
|
|
size_t hdr;
|
|
size_t p_good, p_bad;
|
|
|
|
hdr = emit_fdt_header(&b);
|
|
emit_align(&b, 8);
|
|
|
|
emit_rsvmap_empty(&b, hdr);
|
|
|
|
{
|
|
size_t ss = start_block(&b);
|
|
|
|
emit_begin_node(&b, "");
|
|
p_good = emit_prop_nil(&b, 0);
|
|
p_bad = emit_prop_nil(&b, 0);
|
|
emit_end_node(&b);
|
|
emit_fdt_end(&b);
|
|
finish_struct_block(&b, hdr, ss);
|
|
}
|
|
|
|
{
|
|
size_t strs = start_block(&b);
|
|
|
|
fill_prop_name(&b, strs, p_good, emit_string(&b, "good"));
|
|
fill_prop_name(&b, strs, p_bad, emit_bytes(&b, "bad", 3));
|
|
finish_strings_block(&b, hdr, strs);
|
|
}
|
|
|
|
finish_totalsize(&b, hdr);
|
|
|
|
return b;
|
|
}
|
|
|
|
static struct buf make_truncated_memrsv(void)
|
|
{
|
|
struct buf b = buf_init();
|
|
size_t hdr;
|
|
|
|
hdr = emit_fdt_header(&b);
|
|
|
|
{
|
|
size_t ss = start_block(&b);
|
|
|
|
emit_begin_node(&b, "");
|
|
emit_end_node(&b);
|
|
emit_fdt_end(&b);
|
|
finish_struct_block(&b, hdr, ss);
|
|
}
|
|
|
|
finish_strings_block(&b, hdr, start_block(&b));
|
|
|
|
{
|
|
size_t rsvmap;
|
|
|
|
emit_align(&b, 8);
|
|
rsvmap = start_block(&b);
|
|
emit_rsvmap_entry(&b, TEST_ADDR_1, TEST_SIZE_1);
|
|
finish_rsvmap(&b, hdr, rsvmap);
|
|
}
|
|
|
|
finish_totalsize(&b, hdr);
|
|
|
|
return b;
|
|
}
|
|
|
|
static struct buf make_unterminated_memrsv(void)
|
|
{
|
|
struct buf b = buf_init();
|
|
size_t hdr;
|
|
|
|
hdr = emit_fdt_header(&b);
|
|
|
|
{
|
|
size_t rsvmap;
|
|
|
|
emit_align(&b, 8);
|
|
rsvmap = start_block(&b);
|
|
emit_rsvmap_entry(&b, TEST_ADDR_1, TEST_SIZE_1);
|
|
finish_rsvmap(&b, hdr, rsvmap);
|
|
}
|
|
|
|
{
|
|
size_t ss = start_block(&b);
|
|
|
|
emit_begin_node(&b, "");
|
|
emit_end_node(&b);
|
|
emit_fdt_end(&b);
|
|
finish_struct_block(&b, hdr, ss);
|
|
}
|
|
|
|
finish_strings_block(&b, hdr, start_block(&b));
|
|
|
|
finish_totalsize(&b, hdr);
|
|
|
|
return b;
|
|
}
|
|
|
|
static struct buf make_two_roots(void)
|
|
{
|
|
struct buf b = buf_init();
|
|
size_t hdr;
|
|
|
|
hdr = emit_fdt_header(&b);
|
|
emit_align(&b, 8);
|
|
|
|
emit_rsvmap_empty(&b, hdr);
|
|
|
|
{
|
|
size_t ss = start_block(&b);
|
|
|
|
emit_begin_node(&b, "");
|
|
emit_end_node(&b);
|
|
emit_begin_node(&b, "");
|
|
emit_end_node(&b);
|
|
emit_fdt_end(&b);
|
|
finish_struct_block(&b, hdr, ss);
|
|
}
|
|
|
|
finish_strings_block(&b, hdr, start_block(&b));
|
|
|
|
finish_totalsize(&b, hdr);
|
|
|
|
return b;
|
|
}
|
|
|
|
static struct buf make_named_root(void)
|
|
{
|
|
struct buf b = buf_init();
|
|
size_t hdr;
|
|
|
|
hdr = emit_fdt_header(&b);
|
|
emit_align(&b, 8);
|
|
|
|
emit_rsvmap_empty(&b, hdr);
|
|
|
|
{
|
|
size_t ss = start_block(&b);
|
|
|
|
emit_begin_node(&b, "fake");
|
|
emit_end_node(&b);
|
|
emit_fdt_end(&b);
|
|
finish_struct_block(&b, hdr, ss);
|
|
}
|
|
|
|
finish_strings_block(&b, hdr, start_block(&b));
|
|
|
|
finish_totalsize(&b, hdr);
|
|
|
|
return b;
|
|
}
|
|
|
|
/* Tree table and main */
|
|
|
|
static struct {
|
|
struct buf (*generator)(void);
|
|
const char *filename;
|
|
} trees[] = {
|
|
#define TREE(name) { make_##name, #name ".dtb" }
|
|
TREE(test_tree1),
|
|
TREE(bad_node_char), TREE(bad_node_format), TREE(bad_prop_char),
|
|
TREE(ovf_size_strings),
|
|
TREE(truncated_property), TREE(truncated_string),
|
|
TREE(truncated_memrsv),
|
|
TREE(unterminated_memrsv),
|
|
TREE(two_roots),
|
|
TREE(named_root),
|
|
};
|
|
|
|
int main(int argc, char *argv[])
|
|
{
|
|
unsigned int i;
|
|
|
|
if (argc != 2) {
|
|
fprintf(stderr, "Usage: %s <output-dir>\n", argv[0]);
|
|
return 1;
|
|
}
|
|
|
|
if (chdir(argv[1]) != 0) {
|
|
perror("chdir()");
|
|
return 1;
|
|
}
|
|
|
|
for (i = 0; i < ARRAY_SIZE(trees); i++) {
|
|
struct buf b = trees[i].generator();
|
|
const char *filename = trees[i].filename;
|
|
int fd;
|
|
ssize_t ret;
|
|
|
|
printf("Tree \"%s\", %zu bytes\n", filename, b.cursor);
|
|
|
|
fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC, 0666);
|
|
if (fd < 0)
|
|
perror("open()");
|
|
|
|
ret = write(fd, b.data, b.cursor);
|
|
if (ret < 0 || (size_t)ret != b.cursor)
|
|
perror("write()");
|
|
|
|
close(fd);
|
|
free(b.data);
|
|
}
|
|
|
|
return 0;
|
|
}
|