Add support for YAML encoded output
YAML encoded DT is useful for validation of DTs using binding schemas. The YAML encoding is an intermediate format used for validation and is therefore subject to change as needed. The YAML output is dependent on DTS input with type information preserved. Signed-off-by: Grant Likely <grant.likely@arm.com> [robh: make YAML support optional, build fixes, Travis CI test, preserve type information in paths and phandles] Signed-off-by: Rob Herring <robh@kernel.org> Signed-off-by: David Gibson <david@gibson.dropbear.id.au>
This commit is contained in:
parent
361b5e7d80
commit
c86da84d30
9 changed files with 307 additions and 2 deletions
|
@ -14,6 +14,7 @@ matrix:
|
||||||
- swig
|
- swig
|
||||||
- python-dev
|
- python-dev
|
||||||
- valgrind
|
- valgrind
|
||||||
|
- libyaml-0-2
|
||||||
coverity_scan:
|
coverity_scan:
|
||||||
project:
|
project:
|
||||||
name: dtc
|
name: dtc
|
||||||
|
@ -26,7 +27,7 @@ matrix:
|
||||||
- make check && make checkm
|
- make check && make checkm
|
||||||
|
|
||||||
# Check it builds properly without optional packages:
|
# Check it builds properly without optional packages:
|
||||||
# python, valgrind
|
# python, valgrind, libyaml
|
||||||
- script:
|
- script:
|
||||||
- make
|
- make
|
||||||
- make check
|
- make check
|
||||||
|
|
|
@ -78,6 +78,9 @@ The currently supported Output Formats are:
|
||||||
then simply be added to your Makefile. Additionally, the
|
then simply be added to your Makefile. Additionally, the
|
||||||
assembly file exports some symbols that can be used.
|
assembly file exports some symbols that can be used.
|
||||||
|
|
||||||
|
- "yaml": DT encoded in YAML format. This representation is an
|
||||||
|
intermediate format used for validation tools.
|
||||||
|
|
||||||
|
|
||||||
3) Command Line
|
3) Command Line
|
||||||
|
|
||||||
|
|
9
Makefile
9
Makefile
|
@ -46,6 +46,13 @@ else
|
||||||
CFLAGS += $(shell $(PKG_CONFIG) --cflags valgrind)
|
CFLAGS += $(shell $(PKG_CONFIG) --cflags valgrind)
|
||||||
endif
|
endif
|
||||||
|
|
||||||
|
NO_YAML := $(shell $(PKG_CONFIG) --exists yaml-0.1; echo $$?)
|
||||||
|
ifeq ($(NO_YAML),1)
|
||||||
|
CFLAGS += -DNO_YAML
|
||||||
|
else
|
||||||
|
LDLIBS += $(shell $(PKG_CONFIG) --libs yaml-0.1)
|
||||||
|
endif
|
||||||
|
|
||||||
ifeq ($(HOSTOS),darwin)
|
ifeq ($(HOSTOS),darwin)
|
||||||
SHAREDLIB_EXT = dylib
|
SHAREDLIB_EXT = dylib
|
||||||
SHAREDLIB_CFLAGS = -fPIC
|
SHAREDLIB_CFLAGS = -fPIC
|
||||||
|
@ -329,7 +336,7 @@ clean: libfdt_clean pylibfdt_clean tests_clean
|
||||||
#
|
#
|
||||||
%: %.o
|
%: %.o
|
||||||
@$(VECHO) LD $@
|
@$(VECHO) LD $@
|
||||||
$(LINK.c) -o $@ $^
|
$(LINK.c) -o $@ $^ $(LDLIBS)
|
||||||
|
|
||||||
%.o: %.c
|
%.o: %.c
|
||||||
@$(VECHO) CC $@
|
@$(VECHO) CC $@
|
||||||
|
|
|
@ -14,5 +14,9 @@ DTC_SRCS = \
|
||||||
treesource.c \
|
treesource.c \
|
||||||
util.c
|
util.c
|
||||||
|
|
||||||
|
ifneq ($(NO_YAML),1)
|
||||||
|
DTC_SRCS += yamltree.c
|
||||||
|
endif
|
||||||
|
|
||||||
DTC_GEN_SRCS = dtc-lexer.lex.c dtc-parser.tab.c
|
DTC_GEN_SRCS = dtc-lexer.lex.c dtc-parser.tab.c
|
||||||
DTC_OBJS = $(DTC_SRCS:%.c=%.o) $(DTC_GEN_SRCS:%.c=%.o)
|
DTC_OBJS = $(DTC_SRCS:%.c=%.o) $(DTC_GEN_SRCS:%.c=%.o)
|
||||||
|
|
11
dtc.c
11
dtc.c
|
@ -95,6 +95,9 @@ static const char * const usage_opts_help[] = {
|
||||||
"\n\tOutput formats are:\n"
|
"\n\tOutput formats are:\n"
|
||||||
"\t\tdts - device tree source text\n"
|
"\t\tdts - device tree source text\n"
|
||||||
"\t\tdtb - device tree blob\n"
|
"\t\tdtb - device tree blob\n"
|
||||||
|
#ifndef NO_YAML
|
||||||
|
"\t\tyaml - device tree encoded as YAML\n"
|
||||||
|
#endif
|
||||||
"\t\tasm - assembler source",
|
"\t\tasm - assembler source",
|
||||||
"\n\tBlob version to produce, defaults to "stringify(DEFAULT_FDT_VERSION)" (for dtb and asm output)",
|
"\n\tBlob version to produce, defaults to "stringify(DEFAULT_FDT_VERSION)" (for dtb and asm output)",
|
||||||
"\n\tOutput dependency file",
|
"\n\tOutput dependency file",
|
||||||
|
@ -128,6 +131,8 @@ static const char *guess_type_by_name(const char *fname, const char *fallback)
|
||||||
return fallback;
|
return fallback;
|
||||||
if (!strcasecmp(s, ".dts"))
|
if (!strcasecmp(s, ".dts"))
|
||||||
return "dts";
|
return "dts";
|
||||||
|
if (!strcasecmp(s, ".yaml"))
|
||||||
|
return "yaml";
|
||||||
if (!strcasecmp(s, ".dtb"))
|
if (!strcasecmp(s, ".dtb"))
|
||||||
return "dtb";
|
return "dtb";
|
||||||
return fallback;
|
return fallback;
|
||||||
|
@ -350,6 +355,12 @@ int main(int argc, char *argv[])
|
||||||
|
|
||||||
if (streq(outform, "dts")) {
|
if (streq(outform, "dts")) {
|
||||||
dt_to_source(outf, dti);
|
dt_to_source(outf, dti);
|
||||||
|
#ifndef NO_YAML
|
||||||
|
} else if (streq(outform, "yaml")) {
|
||||||
|
if (!streq(inform, "dts"))
|
||||||
|
die("YAML output format requires dts input format\n");
|
||||||
|
dt_to_yaml(outf, dti);
|
||||||
|
#endif
|
||||||
} else if (streq(outform, "dtb")) {
|
} else if (streq(outform, "dtb")) {
|
||||||
dt_to_blob(outf, dti, outversion);
|
dt_to_blob(outf, dti, outversion);
|
||||||
} else if (streq(outform, "asm")) {
|
} else if (streq(outform, "asm")) {
|
||||||
|
|
4
dtc.h
4
dtc.h
|
@ -299,6 +299,10 @@ struct dt_info *dt_from_blob(const char *fname);
|
||||||
void dt_to_source(FILE *f, struct dt_info *dti);
|
void dt_to_source(FILE *f, struct dt_info *dti);
|
||||||
struct dt_info *dt_from_source(const char *f);
|
struct dt_info *dt_from_source(const char *f);
|
||||||
|
|
||||||
|
/* YAML source */
|
||||||
|
|
||||||
|
void dt_to_yaml(FILE *f, struct dt_info *dti);
|
||||||
|
|
||||||
/* FS trees */
|
/* FS trees */
|
||||||
|
|
||||||
struct dt_info *dt_from_fs(const char *dirname);
|
struct dt_info *dt_from_fs(const char *dirname);
|
||||||
|
|
|
@ -568,6 +568,14 @@ dtc_tests () {
|
||||||
run_wrap_test cmp $tree $tree.test.dts
|
run_wrap_test cmp $tree $tree.test.dts
|
||||||
done
|
done
|
||||||
|
|
||||||
|
# Check -Oyaml output
|
||||||
|
if pkg-config --exists yaml-0.1; then
|
||||||
|
for tree in type-preservation; do
|
||||||
|
run_dtc_test -I dts -O yaml -o $tree.test.dt.yaml $tree.dts
|
||||||
|
run_wrap_test cmp $tree.dt.yaml $tree.test.dt.yaml
|
||||||
|
done
|
||||||
|
fi
|
||||||
|
|
||||||
# Check version conversions
|
# Check version conversions
|
||||||
for tree in test_tree1.dtb ; do
|
for tree in test_tree1.dtb ; do
|
||||||
for aver in 1 2 3 16 17; do
|
for aver in 1 2 3 16 17; do
|
||||||
|
|
20
tests/type-preservation.dt.yaml
Normal file
20
tests/type-preservation.dt.yaml
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
---
|
||||||
|
- '#address-cells': [[0x1]]
|
||||||
|
'#size-cells': [[0x0]]
|
||||||
|
subnode@1:
|
||||||
|
compatible: ["subnode1"]
|
||||||
|
reg: [[0x1]]
|
||||||
|
int-array: [[0x0, 0x1], [0x2, 0x3]]
|
||||||
|
int8: [!u8 [0x56]]
|
||||||
|
int8-array: [!u8 [0x0, 0x12, 0x34, 0x56]]
|
||||||
|
int16: [!u16 [0x3210]]
|
||||||
|
int16-array: [!u16 [0x1234, 0x5678, 0x90ab, 0xcdef]]
|
||||||
|
int16-matrix: [!u16 [0x1234, 0x5678], [0x90ab, 0xcdef]]
|
||||||
|
int64: [!u64 [0x200000000]]
|
||||||
|
int64-array: [!u64 [0x100000000, 0x0]]
|
||||||
|
a-string-with-nulls: ["foo\0bar", "baz"]
|
||||||
|
subsubnode:
|
||||||
|
compatible: ["subsubnode1", "subsubnode"]
|
||||||
|
subsubsubnode:
|
||||||
|
compatible: ["subsubsubnode1", [0x1234], "subsubsubnode"]
|
||||||
|
...
|
247
yamltree.c
Normal file
247
yamltree.c
Normal file
|
@ -0,0 +1,247 @@
|
||||||
|
/*
|
||||||
|
* (C) Copyright Linaro, Ltd. 2018
|
||||||
|
* (C) Copyright Arm Holdings. 2017
|
||||||
|
* (C) Copyright David Gibson <dwg@au1.ibm.com>, IBM Corporation. 2005.
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU General Public License as
|
||||||
|
* published by the Free Software Foundation; either version 2 of the
|
||||||
|
* License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
* General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
|
||||||
|
* USA
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <yaml.h>
|
||||||
|
#include "dtc.h"
|
||||||
|
#include "srcpos.h"
|
||||||
|
|
||||||
|
char *yaml_error_name[] = {
|
||||||
|
[YAML_NO_ERROR] = "no error",
|
||||||
|
[YAML_MEMORY_ERROR] = "memory error",
|
||||||
|
[YAML_READER_ERROR] = "reader error",
|
||||||
|
[YAML_SCANNER_ERROR] = "scanner error",
|
||||||
|
[YAML_PARSER_ERROR] = "parser error",
|
||||||
|
[YAML_COMPOSER_ERROR] = "composer error",
|
||||||
|
[YAML_WRITER_ERROR] = "writer error",
|
||||||
|
[YAML_EMITTER_ERROR] = "emitter error",
|
||||||
|
};
|
||||||
|
|
||||||
|
#define yaml_emitter_emit_or_die(emitter, event) ( \
|
||||||
|
{ \
|
||||||
|
if (!yaml_emitter_emit(emitter, event)) \
|
||||||
|
die("yaml '%s': %s in %s, line %i\n", \
|
||||||
|
yaml_error_name[(emitter)->error], \
|
||||||
|
(emitter)->problem, __func__, __LINE__); \
|
||||||
|
})
|
||||||
|
|
||||||
|
static void yaml_propval_int(yaml_emitter_t *emitter, struct marker *markers, char *data, int len, int width)
|
||||||
|
{
|
||||||
|
yaml_event_t event;
|
||||||
|
void *tag;
|
||||||
|
int off, start_offset = markers->offset;
|
||||||
|
|
||||||
|
switch(width) {
|
||||||
|
case 1: tag = "!u8"; break;
|
||||||
|
case 2: tag = "!u16"; break;
|
||||||
|
case 4: tag = "!u32"; break;
|
||||||
|
case 8: tag = "!u64"; break;
|
||||||
|
default:
|
||||||
|
die("Invalid width %i", width);
|
||||||
|
}
|
||||||
|
assert(len % width == 0);
|
||||||
|
|
||||||
|
yaml_sequence_start_event_initialize(&event, NULL,
|
||||||
|
(yaml_char_t *)tag, width == 4, YAML_FLOW_SEQUENCE_STYLE);
|
||||||
|
yaml_emitter_emit_or_die(emitter, &event);
|
||||||
|
|
||||||
|
for (off = 0; off < len; off += width) {
|
||||||
|
char buf[32];
|
||||||
|
struct marker *m;
|
||||||
|
bool is_phandle = false;
|
||||||
|
|
||||||
|
switch(width) {
|
||||||
|
case 1:
|
||||||
|
sprintf(buf, "0x%"PRIx8, *(uint8_t*)(data + off));
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
sprintf(buf, "0x%"PRIx16, fdt16_to_cpu(*(fdt16_t*)(data + off)));
|
||||||
|
break;
|
||||||
|
case 4:
|
||||||
|
sprintf(buf, "0x%"PRIx32, fdt32_to_cpu(*(fdt32_t*)(data + off)));
|
||||||
|
m = markers;
|
||||||
|
is_phandle = false;
|
||||||
|
for_each_marker_of_type(m, REF_PHANDLE) {
|
||||||
|
if (m->offset == (start_offset + off)) {
|
||||||
|
is_phandle = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 8:
|
||||||
|
sprintf(buf, "0x%"PRIx64, fdt64_to_cpu(*(fdt64_t*)(data + off)));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (is_phandle)
|
||||||
|
yaml_scalar_event_initialize(&event, NULL,
|
||||||
|
(yaml_char_t*)"!phandle", (yaml_char_t *)buf,
|
||||||
|
strlen(buf), 0, 0, YAML_PLAIN_SCALAR_STYLE);
|
||||||
|
else
|
||||||
|
yaml_scalar_event_initialize(&event, NULL,
|
||||||
|
(yaml_char_t*)YAML_INT_TAG, (yaml_char_t *)buf,
|
||||||
|
strlen(buf), 1, 1, YAML_PLAIN_SCALAR_STYLE);
|
||||||
|
yaml_emitter_emit_or_die(emitter, &event);
|
||||||
|
}
|
||||||
|
|
||||||
|
yaml_sequence_end_event_initialize(&event);
|
||||||
|
yaml_emitter_emit_or_die(emitter, &event);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void yaml_propval_string(yaml_emitter_t *emitter, char *str, int len)
|
||||||
|
{
|
||||||
|
yaml_event_t event;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
assert(str[len-1] == '\0');
|
||||||
|
|
||||||
|
/* Make sure the entire string is in the lower 7-bit ascii range */
|
||||||
|
for (i = 0; i < len; i++)
|
||||||
|
assert(isascii(str[i]));
|
||||||
|
|
||||||
|
yaml_scalar_event_initialize(&event, NULL,
|
||||||
|
(yaml_char_t *)YAML_STR_TAG, (yaml_char_t*)str,
|
||||||
|
len-1, 0, 1, YAML_DOUBLE_QUOTED_SCALAR_STYLE);
|
||||||
|
yaml_emitter_emit_or_die(emitter, &event);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void yaml_propval(yaml_emitter_t *emitter, struct property *prop)
|
||||||
|
{
|
||||||
|
yaml_event_t event;
|
||||||
|
int len = prop->val.len;
|
||||||
|
struct marker *m = prop->val.markers;
|
||||||
|
|
||||||
|
/* Emit the property name */
|
||||||
|
yaml_scalar_event_initialize(&event, NULL,
|
||||||
|
(yaml_char_t *)YAML_STR_TAG, (yaml_char_t*)prop->name,
|
||||||
|
strlen(prop->name), 1, 1, YAML_PLAIN_SCALAR_STYLE);
|
||||||
|
yaml_emitter_emit_or_die(emitter, &event);
|
||||||
|
|
||||||
|
/* Boolean properties are easiest to deal with. Length is zero, so just emit 'true' */
|
||||||
|
if (len == 0) {
|
||||||
|
yaml_scalar_event_initialize(&event, NULL,
|
||||||
|
(yaml_char_t *)YAML_BOOL_TAG,
|
||||||
|
(yaml_char_t*)"true",
|
||||||
|
strlen("true"), 1, 0, YAML_PLAIN_SCALAR_STYLE);
|
||||||
|
yaml_emitter_emit_or_die(emitter, &event);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!m)
|
||||||
|
die("No markers present in property '%s' value\n", prop->name);
|
||||||
|
|
||||||
|
yaml_sequence_start_event_initialize(&event, NULL,
|
||||||
|
(yaml_char_t *)YAML_SEQ_TAG, 1, YAML_FLOW_SEQUENCE_STYLE);
|
||||||
|
yaml_emitter_emit_or_die(emitter, &event);
|
||||||
|
|
||||||
|
for_each_marker(m) {
|
||||||
|
int chunk_len;
|
||||||
|
char *data = &prop->val.val[m->offset];
|
||||||
|
|
||||||
|
if (m->type < TYPE_UINT8)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
chunk_len = type_marker_length(m) ? : len;
|
||||||
|
assert(chunk_len > 0);
|
||||||
|
len -= chunk_len;
|
||||||
|
|
||||||
|
switch(m->type) {
|
||||||
|
case TYPE_UINT16:
|
||||||
|
yaml_propval_int(emitter, m, data, chunk_len, 2);
|
||||||
|
break;
|
||||||
|
case TYPE_UINT32:
|
||||||
|
yaml_propval_int(emitter, m, data, chunk_len, 4);
|
||||||
|
break;
|
||||||
|
case TYPE_UINT64:
|
||||||
|
yaml_propval_int(emitter, m, data, chunk_len, 8);
|
||||||
|
break;
|
||||||
|
case TYPE_STRING:
|
||||||
|
yaml_propval_string(emitter, data, chunk_len);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
yaml_propval_int(emitter, m, data, chunk_len, 1);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
yaml_sequence_end_event_initialize(&event);
|
||||||
|
yaml_emitter_emit_or_die(emitter, &event);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void yaml_tree(struct node *tree, yaml_emitter_t *emitter)
|
||||||
|
{
|
||||||
|
struct property *prop;
|
||||||
|
struct node *child;
|
||||||
|
yaml_event_t event;
|
||||||
|
|
||||||
|
if (tree->deleted)
|
||||||
|
return;
|
||||||
|
|
||||||
|
yaml_mapping_start_event_initialize(&event, NULL,
|
||||||
|
(yaml_char_t *)YAML_MAP_TAG, 1, YAML_ANY_MAPPING_STYLE);
|
||||||
|
yaml_emitter_emit_or_die(emitter, &event);
|
||||||
|
|
||||||
|
for_each_property(tree, prop)
|
||||||
|
yaml_propval(emitter, prop);
|
||||||
|
|
||||||
|
/* Loop over all the children, emitting them into the map */
|
||||||
|
for_each_child(tree, child) {
|
||||||
|
yaml_scalar_event_initialize(&event, NULL,
|
||||||
|
(yaml_char_t *)YAML_STR_TAG, (yaml_char_t*)child->name,
|
||||||
|
strlen(child->name), 1, 0, YAML_PLAIN_SCALAR_STYLE);
|
||||||
|
yaml_emitter_emit_or_die(emitter, &event);
|
||||||
|
yaml_tree(child, emitter);
|
||||||
|
}
|
||||||
|
|
||||||
|
yaml_mapping_end_event_initialize(&event);
|
||||||
|
yaml_emitter_emit_or_die(emitter, &event);
|
||||||
|
}
|
||||||
|
|
||||||
|
void dt_to_yaml(FILE *f, struct dt_info *dti)
|
||||||
|
{
|
||||||
|
yaml_emitter_t emitter;
|
||||||
|
yaml_event_t event;
|
||||||
|
|
||||||
|
yaml_emitter_initialize(&emitter);
|
||||||
|
yaml_emitter_set_output_file(&emitter, f);
|
||||||
|
yaml_stream_start_event_initialize(&event, YAML_UTF8_ENCODING);
|
||||||
|
yaml_emitter_emit_or_die(&emitter, &event);
|
||||||
|
|
||||||
|
yaml_document_start_event_initialize(&event, NULL, NULL, NULL, 0);
|
||||||
|
yaml_emitter_emit_or_die(&emitter, &event);
|
||||||
|
|
||||||
|
yaml_sequence_start_event_initialize(&event, NULL, (yaml_char_t *)YAML_SEQ_TAG, 1, YAML_ANY_SEQUENCE_STYLE);
|
||||||
|
yaml_emitter_emit_or_die(&emitter, &event);
|
||||||
|
|
||||||
|
yaml_tree(dti->dt, &emitter);
|
||||||
|
|
||||||
|
yaml_sequence_end_event_initialize(&event);
|
||||||
|
yaml_emitter_emit_or_die(&emitter, &event);
|
||||||
|
|
||||||
|
yaml_document_end_event_initialize(&event, 0);
|
||||||
|
yaml_emitter_emit_or_die(&emitter, &event);
|
||||||
|
|
||||||
|
yaml_stream_end_event_initialize(&event);
|
||||||
|
yaml_emitter_emit_or_die(&emitter, &event);
|
||||||
|
|
||||||
|
yaml_emitter_delete(&emitter);
|
||||||
|
}
|
Loading…
Reference in a new issue