Re: [PATCH v3 2/2] Add support for YAML encoded output

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]



On Tue, Sep 11, 2018 at 03:41:31PM -0500, Rob Herring wrote:
> From: Grant Likely <grant.likely@xxxxxxx>
> 
> 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@xxxxxxx>
> [robh: make YAML support optional, build fixes, Travis CI test,
>  preserve type information in paths and phandles]
> Signed-off-by: Rob Herring <robh@xxxxxxxxxx>

Applied, thanks.

> ---
> v3: 
> - Add a test case
> - Drop a leftover debug print
> - Rebase on current master
> 
> v2:
> - Use NO_YAML instead of HAS_YAML
> - Replace stderr prints with asserts
> - Rework phandle marker handling
> - Consolidate yaml_emitter_emit plus yaml_die 
>   into yaml_emitter_emit_or_die
> 
>  .travis.yml                     |   3 +-
>  Documentation/manual.txt        |   3 +
>  Makefile                        |   9 +-
>  Makefile.dtc                    |   4 +
>  dtc.c                           |  11 ++
>  dtc.h                           |   4 +
>  tests/run_tests.sh              |   8 ++
>  tests/type-preservation.dt.yaml |  20 +++
>  yamltree.c                      | 247 ++++++++++++++++++++++++++++++++
>  9 files changed, 307 insertions(+), 2 deletions(-)
>  create mode 100644 tests/type-preservation.dt.yaml
>  create mode 100644 yamltree.c
> 
> diff --git a/.travis.yml b/.travis.yml
> index ecdef0d49234..114932a23cc6 100644
> --- a/.travis.yml
> +++ b/.travis.yml
> @@ -14,6 +14,7 @@ matrix:
>              - swig
>              - python-dev
>              - valgrind
> +            - libyaml-0-2
>          coverity_scan:
>            project:
>              name: dtc
> @@ -26,7 +27,7 @@ matrix:
>          - make check && make checkm
>  
>      # Check it builds properly without optional packages:
> -    #     python, valgrind
> +    #     python, valgrind, libyaml
>      - script:
>          - make
>          - make check
> diff --git a/Documentation/manual.txt b/Documentation/manual.txt
> index 6898caa5b40f..db32dd72a0fc 100644
> --- a/Documentation/manual.txt
> +++ b/Documentation/manual.txt
> @@ -78,6 +78,9 @@ The currently supported Output Formats are:
>          then simply be added to your Makefile.  Additionally, the
>          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
>  
> diff --git a/Makefile b/Makefile
> index c4bfae603067..7a472b747af4 100644
> --- a/Makefile
> +++ b/Makefile
> @@ -46,6 +46,13 @@ else
>  	CFLAGS += $(shell $(PKG_CONFIG) --cflags valgrind)
>  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)
>  SHAREDLIB_EXT     = dylib
>  SHAREDLIB_CFLAGS  = -fPIC
> @@ -329,7 +336,7 @@ clean: libfdt_clean pylibfdt_clean tests_clean
>  #
>  %: %.o
>  	@$(VECHO) LD $@
> -	$(LINK.c) -o $@ $^
> +	$(LINK.c) -o $@ $^ $(LDLIBS)
>  
>  %.o: %.c
>  	@$(VECHO) CC $@
> diff --git a/Makefile.dtc b/Makefile.dtc
> index bece49b35535..d4375630a7f7 100644
> --- a/Makefile.dtc
> +++ b/Makefile.dtc
> @@ -14,5 +14,9 @@ DTC_SRCS = \
>  	treesource.c \
>  	util.c
>  
> +ifneq ($(NO_YAML),1)
> +DTC_SRCS += yamltree.c
> +endif
> +
>  DTC_GEN_SRCS = dtc-lexer.lex.c dtc-parser.tab.c
>  DTC_OBJS = $(DTC_SRCS:%.c=%.o) $(DTC_GEN_SRCS:%.c=%.o)
> diff --git a/dtc.c b/dtc.c
> index c36994e6eac5..64134aadb997 100644
> --- a/dtc.c
> +++ b/dtc.c
> @@ -95,6 +95,9 @@ static const char * const usage_opts_help[] = {
>  	"\n\tOutput formats are:\n"
>  	 "\t\tdts - device tree source text\n"
>  	 "\t\tdtb - device tree blob\n"
> +#ifndef NO_YAML
> +	 "\t\tyaml - device tree encoded as YAML\n"
> +#endif
>  	 "\t\tasm - assembler source",
>  	"\n\tBlob version to produce, defaults to "stringify(DEFAULT_FDT_VERSION)" (for dtb and asm output)",
>  	"\n\tOutput dependency file",
> @@ -128,6 +131,8 @@ static const char *guess_type_by_name(const char *fname, const char *fallback)
>  		return fallback;
>  	if (!strcasecmp(s, ".dts"))
>  		return "dts";
> +	if (!strcasecmp(s, ".yaml"))
> +		return "yaml";
>  	if (!strcasecmp(s, ".dtb"))
>  		return "dtb";
>  	return fallback;
> @@ -350,6 +355,12 @@ int main(int argc, char *argv[])
>  
>  	if (streq(outform, "dts")) {
>  		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")) {
>  		dt_to_blob(outf, dti, outversion);
>  	} else if (streq(outform, "asm")) {
> diff --git a/dtc.h b/dtc.h
> index bb769d2a8e56..cbe541525c2c 100644
> --- a/dtc.h
> +++ b/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);
>  struct dt_info *dt_from_source(const char *f);
>  
> +/* YAML source */
> +
> +void dt_to_yaml(FILE *f, struct dt_info *dti);
> +
>  /* FS trees */
>  
>  struct dt_info *dt_from_fs(const char *dirname);
> diff --git a/tests/run_tests.sh b/tests/run_tests.sh
> index bbdc5c8724bf..adc4daebd945 100755
> --- a/tests/run_tests.sh
> +++ b/tests/run_tests.sh
> @@ -568,6 +568,14 @@ dtc_tests () {
>          run_wrap_test cmp $tree $tree.test.dts
>      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
>      for tree in test_tree1.dtb ; do
>  	 for aver in 1 2 3 16 17; do
> diff --git a/tests/type-preservation.dt.yaml b/tests/type-preservation.dt.yaml
> new file mode 100644
> index 000000000000..ee8cfdebe9be
> --- /dev/null
> +++ b/tests/type-preservation.dt.yaml
> @@ -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"]
> +...
> diff --git a/yamltree.c b/yamltree.c
> new file mode 100644
> index 000000000000..a00285a5a9ec
> --- /dev/null
> +++ b/yamltree.c
> @@ -0,0 +1,247 @@
> +/*
> + * (C) Copyright Linaro, Ltd. 2018
> + * (C) Copyright Arm Holdings.  2017
> + * (C) Copyright David Gibson <dwg@xxxxxxxxxxx>, 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);
> +}

-- 
David Gibson			| I'll have my music baroque, and my code
david AT gibson.dropbear.id.au	| minimalist, thank you.  NOT _the_ _other_
				| _way_ _around_!
http://www.ozlabs.org/~dgibson

Attachment: signature.asc
Description: PGP signature


[Index of Archives]     [Device Tree]     [Device Tree Spec]     [Linux Driver Backports]     [Video for Linux]     [Linux USB Devel]     [Linux Audio Users]     [Linux Kernel]     [Linux SCSI]     [Yosemite Backpacking]

  Powered by Linux