Re: [PATCH v3 1/1] libfdt: add fdt_append_addrrange()

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



On Tue, Mar 12, 2019 at 08:20:51PM +1100, David Gibson wrote:
> On Fri, Mar 08, 2019 at 02:05:43PM +0900, AKASHI Takahiro wrote:
> > This function will append an address range property using parent node's
> > "#address-cells" and "#size-cells" properties.
> > 
> > It will be used in implementing kdump with kexec_file_load system call
> > at linux kernel for arm64 once it is merged into kernel tree.
> > 
> > Signed-off-by: AKASHI Takahiro <takahiro.akashi@xxxxxxxxxx>
> > ---
> >  libfdt/fdt_addresses.c       |  47 +++++++++++++++
> >  libfdt/libfdt.h              |  37 ++++++++++++
> >  libfdt/libfdt_internal.h     |   9 +++
> >  tests/.gitignore             |   1 +
> >  tests/Makefile.tests         |   1 +
> >  tests/appendprop_addrrange.c | 108 +++++++++++++++++++++++++++++++++++
> >  tests/run_tests.sh           |   5 ++
> >  tests/testdata.h             |   6 ++
> >  tests/tests.h                |   3 +
> >  tests/testutils.c            |  58 +++++++++++++++++++
> >  10 files changed, 275 insertions(+)
> >  create mode 100644 tests/appendprop_addrrange.c
> > 
> > diff --git a/libfdt/fdt_addresses.c b/libfdt/fdt_addresses.c
> > index f13a87dfa068..7a00c6197872 100644
> > --- a/libfdt/fdt_addresses.c
> > +++ b/libfdt/fdt_addresses.c
> > @@ -95,3 +95,50 @@ int fdt_size_cells(const void *fdt, int nodeoffset)
> >  		return 1;
> >  	return val;
> >  }
> > +
> > +/* This function assumes that [address|size]_cells is 1 or 2 */
> > +int fdt_appendprop_addrrange(void *fdt, int parent, int nodeoffset,
> > +			     const char *name, uint64_t addr, uint64_t size)
> > +{
> > +	int addr_cells, size_cells, ret;
> > +	uint8_t data[sizeof(fdt64_t) * 2], *prop;
> > +
> > +	ret = fdt_address_cells(fdt, parent);
> > +	if (ret < 0)
> > +		return ret;
> > +	addr_cells = ret;
> > +
> > +	ret = fdt_size_cells(fdt, parent);
> > +	if (ret < 0)
> > +		return ret;
> > +	size_cells = ret;
> > +
> > +	/* check validity of address */
> > +	prop = data;
> > +	if (addr_cells == 1) {
> > +		if ((addr > UINT32_MAX) || ((addr + size) > UINT32_MAX))
> > +			return -FDT_ERR_BADVALUE;
> 
> This test could fail to detect bad values in the case of 64-bit
> overflow - probably best to check for that first.

Okay, but we already know that addr <= UINT32_MAX here and
so can simplify the check against addr + size:

                if ((addr > UINT32_MAX) || ((UINT32_MAX + 1 - addr) < size))
                        return -FDT_ERR_BADVALUE;

> > +		fdt32_st(prop, cpu_to_fdt32((uint32_t)addr));
> 
> fdtXX_st() should, like fdtXX_ld() do its own byteswapping.
> 
> > +	} else if (addr_cells == 2) {
> > +		fdt64_st(prop, cpu_to_fdt64(addr));
> > +	} else {
> > +		return -FDT_ERR_BADNCELLS;
> > +	}
> > +
> > +	/* check validity of size */
> > +	prop += addr_cells * sizeof(fdt32_t);
> > +	if (size_cells == 1) {
> > +		if (size > UINT32_MAX)
> > +			return -FDT_ERR_BADVALUE;
> > +
> > +		fdt32_st(prop, cpu_to_fdt32((uint32_t)size));
> > +	} else if (size_cells == 2) {
> > +		fdt64_st(prop, cpu_to_fdt64(size));
> > +	} else {
> > +		return -FDT_ERR_BADNCELLS;
> > +	}
> > +
> > +	return fdt_appendprop(fdt, nodeoffset, name, data,
> > +			      (addr_cells + size_cells) * sizeof(fdt32_t));
> > +}
> > diff --git a/libfdt/libfdt.h b/libfdt/libfdt.h
> > index a470d1df6d2a..c25d980c2547 100644
> > --- a/libfdt/libfdt.h
> > +++ b/libfdt/libfdt.h
> > @@ -1831,6 +1831,43 @@ static inline int fdt_appendprop_cell(void *fdt, int nodeoffset,
> >  #define fdt_appendprop_string(fdt, nodeoffset, name, str) \
> >  	fdt_appendprop((fdt), (nodeoffset), (name), (str), strlen(str)+1)
> >  
> > +/**
> > + * fdt_appendprop_addrrange - append a address range property
> > + * @fdt: pointer to the device tree blob
> > + * @parent: offset of the parent node
> > + * @nodeoffset: offset of the node to add a property at
> > + * @name: name of property
> > + * @addr: start address of a given range
> > + * @size: size of a given range
> > + *
> > + * fdt_appendprop_addrrange() appends an address range value (start
> > + * address and size) to the value of the named property in the given
> > + * node, or creates a new property with that value if it does not
> > + * already exist.
> > + * If "name" is not specified, a default "reg" is used.
> > + * Cell sizes are determined by parent's #address-cells and #size-cells.
> > + *
> > + * This function may insert data into the blob, and will therefore
> > + * change the offsets of some existing nodes.
> > + *
> > + * returns:
> > + *	0, on success
> > + *	-FDT_ERR_BADLAYOUT,
> > + *	-FDT_ERR_BADMAGIC,
> > + *	-FDT_ERR_BADNCELLS, if the node has a badly formatted or invalid
> > + *		#address-cells property
> > + *	-FDT_ERR_BADOFFSET, nodeoffset did not point to FDT_BEGIN_NODE tag
> > + *	-FDT_ERR_BADSTATE,
> > + *	-FDT_ERR_BADSTRUCTURE,
> > + *	-FDT_ERR_BADVERSION,
> > + *	-FDT_ERR_BADVALUE, addr or size doesn't fit to respective cells size
> > + *	-FDT_ERR_NOSPACE, there is insufficient free space in the blob to
> > + *		contain a new property
> > + *	-FDT_ERR_TRUNCATED, standard meanings
> > + */
> > +int fdt_appendprop_addrrange(void *fdt, int parent, int nodeoffset,
> > +			     const char *name, uint64_t addr, uint64_t size);
> > +
> >  /**
> >   * fdt_delprop - delete a property
> >   * @fdt: pointer to the device tree blob
> > diff --git a/libfdt/libfdt_internal.h b/libfdt/libfdt_internal.h
> > index 4109f890ae60..f1ce1d84f7f0 100644
> > --- a/libfdt/libfdt_internal.h
> > +++ b/libfdt/libfdt_internal.h
> > @@ -93,4 +93,13 @@ static inline struct fdt_reserve_entry *fdt_mem_rsv_w_(void *fdt, int n)
> >  
> >  #define FDT_SW_MAGIC		(~FDT_MAGIC)
> >  
> > +static inline void *fdt32_st(void *property, fdt32_t value)
> > +{
> > +	return memcpy(property, &value, sizeof(value));
> 
> First, as above, this should do its own byteswapping (because it knows
> the data size).
> 
> Second, I've been told that a memcpy() like this can be surprisingly
> expensive on certain ARM chips, so doing it with byte accesses and
> shift arithmetic like fdtXX_ld() would be preferable.

Okay.

Thank you for your review.
-Takahiro Akashi

> > +}
> > +
> > +static inline void *fdt64_st(void *property, fdt64_t value)
> > +{
> > +	return memcpy(property, &value, sizeof(value));
> > +}
> >  #endif /* LIBFDT_INTERNAL_H */
> > diff --git a/tests/.gitignore b/tests/.gitignore
> > index 12af43868e09..0bc78aa5842b 100644
> > --- a/tests/.gitignore
> > +++ b/tests/.gitignore
> > @@ -8,6 +8,7 @@ tmp.*
> >  /addr_size_cells
> >  /addr_size_cells2
> >  /appendprop[12]
> > +/appendprop_addrrange
> >  /asm_tree_dump
> >  /boot-cpuid
> >  /char_literal
> > diff --git a/tests/Makefile.tests b/tests/Makefile.tests
> > index b02d8bf3d15b..b77f121f0ba6 100644
> > --- a/tests/Makefile.tests
> > +++ b/tests/Makefile.tests
> > @@ -10,6 +10,7 @@ LIB_TESTS_L = get_mem_rsv \
> >  	notfound \
> >  	addr_size_cells \
> >  	addr_size_cells2 \
> > +	appendprop_addrrange \
> >  	stringlist \
> >  	setprop_inplace nop_property nop_node \
> >  	sw_tree1 sw_states \
> > diff --git a/tests/appendprop_addrrange.c b/tests/appendprop_addrrange.c
> > new file mode 100644
> > index 000000000000..b54f9049ff6a
> > --- /dev/null
> > +++ b/tests/appendprop_addrrange.c
> > @@ -0,0 +1,108 @@
> > +/*
> > + * libfdt - Flat Device Tree manipulation
> > + *	Testcase for fdt_appendprop_addrrange()
> > + * Copyright (C) 2018 AKASHI Takahiro, Linaro Limited
> > + *
> > + * This library is free software; you can redistribute it and/or
> > + * modify it under the terms of the GNU Lesser General Public License
> > + * as published by the Free Software Foundation; either version 2.1 of
> > + * the License, or (at your option) any later version.
> > + *
> > + * This library 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
> > + * Lesser General Public License for more details.
> > + *
> > + * You should have received a copy of the GNU Lesser General Public
> > + * License along with this library; if not, write to the Free Software
> > + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
> > + */
> > +#include <stdlib.h>
> > +#include <stdio.h>
> > +#include <string.h>
> > +#include <stdint.h>
> > +
> > +#include <libfdt.h>
> > +
> > +#include "tests.h"
> > +#include "testdata.h"
> > +
> > +int main(int argc, char *argv[])
> > +{
> > +	void *fdt, *buf;
> > +	int offset, xac, xsc, num, i, err;
> > +	uint64_t addr, size;
> > +
> > +	if (argc != 5)
> > +		CONFIG("Usage: %s <dtb file> <address-cells> <size-cells> <num>\n",
> > +		       argv[0]);
> > +
> > +	test_init(argc, argv);
> > +	fdt = load_blob(argv[1]);
> > +	xac = strtol(argv[2], NULL, 10);
> > +	xsc = strtol(argv[3], NULL, 10);
> > +	num = strtol(argv[4], NULL, 10);
> > +
> > +	buf = xmalloc(0x1000);
> > +	if (!buf)
> > +		FAIL("Couldn't allocate temporary buffer");
> > +	err = fdt_open_into(fdt, buf, 0x1000);
> > +	if (err)
> > +		FAIL("fdt_open_into(): %s", fdt_strerror(err));
> > +
> > +	fdt = buf;
> > +
> > +	/* Set up */
> > +	err = fdt_setprop_cell(fdt, 0, "#address-cells", xac);
> > +	if (err)
> > +		FAIL("fdt_setprop_cell(\"#address-cells\"): %s",
> > +		     fdt_strerror(err));
> > +	err = fdt_setprop_cell(fdt, 0, "#size-cells", xsc);
> > +	if (err)
> > +		FAIL("fdt_setprop_cell(\"#size-cells\"): %s",
> > +		     fdt_strerror(err));
> > +
> > +	offset = fdt_path_offset(fdt, "/node@1");
> > +	if (offset < 0)
> > +		FAIL("Couldn't find path %s", "/node@1");
> > +
> > +	addr = TEST_MEMREGION_ADDR;
> > +	if (xac > 1)
> > +		addr += TEST_MEMREGION_ADDR_HI;
> > +	size = TEST_MEMREGION_SIZE;
> > +	if (xsc > 1)
> > +		size += TEST_MEMREGION_SIZE_HI;
> > +
> > +	/*
> > +	 * Do test
> > +	 */
> > +	/* 1. repeat append's */
> > +	for (i = 0; i < num; i++) {
> > +		err = fdt_appendprop_addrrange(fdt, 0, offset,
> > +					       "prop-memregion", addr, size);
> > +		if (err)
> > +			FAIL("Failed to append[%d] \"prop-memregion\": %s",
> > +			     i, fdt_strerror(err));
> > +
> > +		check_getprop_addrrange(fdt, 0, offset, "prop-memregion",
> > +					i + 1);
> > +
> > +		addr += size;
> > +		size += TEST_MEMREGION_SIZE_INC;
> > +	}
> > +
> > +	/* 2. default property name */
> > +	addr = TEST_MEMREGION_ADDR;
> > +	if (xac > 1)
> > +		addr += TEST_MEMREGION_ADDR_HI;
> > +	size = TEST_MEMREGION_SIZE;
> > +	if (xsc > 1)
> > +		size += TEST_MEMREGION_SIZE_HI;
> > +
> > +	err = fdt_appendprop_addrrange(fdt, 0, offset, NULL, addr, size);
> > +	if (err)
> > +		FAIL("Failed to set \"reg\"(default): %s", fdt_strerror(err));
> > +	check_getprop_addrrange(fdt, 0, offset, "reg", 1);
> > +
> > +	PASS();
> > +}
> > diff --git a/tests/run_tests.sh b/tests/run_tests.sh
> > index 04fc1e8a2bf7..f6b308f045e2 100755
> > --- a/tests/run_tests.sh
> > +++ b/tests/run_tests.sh
> > @@ -424,6 +424,11 @@ libfdt_tests () {
> >      run_dtc_test -I dts -O dtb -o property_iterate.dtb property_iterate.dts
> >      run_test property_iterate property_iterate.dtb
> >  
> > +    run_dtc_test -I dts -O dtb -o unit-addr-without-reg.dtb unit-addr-without-reg.dts
> > +    run_test appendprop_addrrange unit-addr-without-reg.dtb 1 1 1
> > +    run_test appendprop_addrrange unit-addr-without-reg.dtb 2 2 2
> > +    run_test appendprop_addrrange unit-addr-without-reg.dtb 2 1 3
> > +
> >      # Tests for behaviour on various sorts of corrupted trees
> >      run_test truncated_property
> >      run_test truncated_string
> > diff --git a/tests/testdata.h b/tests/testdata.h
> > index 68dcbaceada4..0d08efb20d52 100644
> > --- a/tests/testdata.h
> > +++ b/tests/testdata.h
> > @@ -40,6 +40,12 @@
> >  #define TEST_CHAR4	'\''
> >  #define TEST_CHAR5	'\xff'
> >  
> > +#define TEST_MEMREGION_ADDR	0x12345678
> > +#define TEST_MEMREGION_ADDR_HI	0x8765432100000000
> > +#define TEST_MEMREGION_SIZE	0x9abcdef0
> > +#define TEST_MEMREGION_SIZE_HI	0x0fedcba900000000
> > +#define TEST_MEMREGION_SIZE_INC	0x1000
> > +
> >  #ifndef __ASSEMBLY__
> >  extern struct fdt_header test_tree1;
> >  extern struct fdt_header truncated_property;
> > diff --git a/tests/tests.h b/tests/tests.h
> > index dc8120eb6d9d..75735d63ee4c 100644
> > --- a/tests/tests.h
> > +++ b/tests/tests.h
> > @@ -128,6 +128,9 @@ const void *check_get_prop_offset(void *fdt, int poffset, const char *in_name,
> >  		check_get_prop_offset(fdt, poffset, name, sizeof(x), &x); \
> >  	})
> >  
> > +const void *check_getprop_addrrange(void *fdt, int parent, int nodeoffset,
> > +				    const char *name, int num);
> > +
> >  int nodename_eq(const char *s1, const char *s2);
> >  void vg_prepare_blob(void *fdt, size_t bufsize);
> >  void *load_blob(const char *filename);
> > diff --git a/tests/testutils.c b/tests/testutils.c
> > index bbfda90b9cdd..a250d5a5d7f9 100644
> > --- a/tests/testutils.c
> > +++ b/tests/testutils.c
> > @@ -45,6 +45,7 @@ static inline void VALGRIND_MAKE_MEM_DEFINED(void *p, size_t len)
> >  #include <libfdt.h>
> >  
> >  #include "tests.h"
> > +#include "testdata.h"
> >  
> >  /* For FDT_SW_MAGIC */
> >  #include "libfdt_internal.h"
> > @@ -184,6 +185,63 @@ const void *check_get_prop_offset(void *fdt, int poffset, const char *exp_name,
> >  	return propval;
> >  }
> >  
> > +const void *check_getprop_addrrange(void *fdt, int parent, int nodeoffset,
> > +				    const char *name, int num)
> > +{
> > +	const void *propval;
> > +	int xac, xsc, buf_size, cells, i;
> > +	char *buf, *p;
> > +	uint64_t addr, size;
> > +	fdt32_t val;
> > +
> > +	xac = fdt_address_cells(fdt, parent);
> > +	xsc = fdt_size_cells(fdt, parent);
> > +
> > +	if (xac <= 0)
> > +		FAIL("Couldn't identify #address-cells: %s",
> > +		     fdt_strerror(xac));
> > +	if (xsc <= 0)
> > +		FAIL("Couldn't identify #size-cells: %s",
> > +		     fdt_strerror(xsc));
> > +
> > +	buf_size = (xac + xsc) * sizeof(fdt32_t) * num;
> > +	buf = malloc(buf_size);
> > +	if (!buf)
> > +		FAIL("Couldn't allocate temporary buffer");
> > +
> > +	/* expected value */
> > +	addr = TEST_MEMREGION_ADDR;
> > +	if (xac > 1)
> > +		addr += TEST_MEMREGION_ADDR_HI;
> > +	size = TEST_MEMREGION_SIZE;
> > +	if (xsc > 1)
> > +		size += TEST_MEMREGION_SIZE_HI;
> > +	for (p = buf, i = 0; i < num; i++) {
> > +		cells = xac;
> > +		while (cells) {
> > +			val = cpu_to_fdt32(addr >> (32 * (--cells)));
> > +			memcpy(p, &val, sizeof(val));
> > +			p += sizeof(val);
> > +		}
> > +		cells = xsc;
> > +		while (cells) {
> > +			val = cpu_to_fdt32(size >> (32 * (--cells)));
> > +			memcpy(p, &val, sizeof(val));
> > +			p += sizeof(val);
> > +		}
> > +
> > +		addr += size;
> > +		size += TEST_MEMREGION_SIZE_INC;
> > +	}
> > +
> > +	/* check */
> > +	propval = check_getprop(fdt, nodeoffset, name, buf_size,
> > +				(const void *)buf);
> > +
> > +	free(buf);
> > +
> > +	return propval;
> > +}
> >  
> >  int nodename_eq(const char *s1, const char *s2)
> >  {
> 
> -- 
> 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





[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