From: Andrew Jones <drjones@redhat.com>
To: Christoffer Dall <christoffer.dall@linaro.org>
Cc: kvmarm@lists.cs.columbia.edu, kvm@vger.kernel.org
Subject: Re: [PATCH 11/17] add support for device trees
Date: Mon, 3 Feb 2014 16:31:24 +0100 [thread overview]
Message-ID: <20140203153120.GE6832@hawk.usersys.redhat.com> (raw)
In-Reply-To: <20140202022715.GU3570@cbox>
On Sat, Feb 01, 2014 at 06:27:15PM -0800, Christoffer Dall wrote:
> On Tue, Jan 21, 2014 at 05:21:57PM +0100, Andrew Jones wrote:
> > Add some device tree functions, built on libfdt, to the common
> > code in order to facilitate the extraction of boot info and device
> > base addresses. These functions are generic and arch-neutral. It's
> > expected that more functions will be added as more information
> > from the device tree is needed.
>
> You're doing a bit more than that, you're introducing a couple of
> concepts such as a bus and reg structures, which are concepts above DT,
> which could use a bit of clarification. In particular this patch is a
> bit hard to review on its own, because you cannot see how things are
> supposed to be used without looking at subsequent patches...
True, but I'm not sure how I can improve things, other than add some
better documentation to the code in this patch, which I'll do for v4.
>
> I'm not a DT expert, but here goes the best I can do...
>
> >
> > Signed-off-by: Andrew Jones <drjones@redhat.com>
> > ---
> > lib/devicetree.c | 257 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
> > lib/devicetree.h | 95 ++++++++++++++++++++
> > lib/libcflat.h | 1 +
> > 3 files changed, 353 insertions(+)
> > create mode 100644 lib/devicetree.c
> > create mode 100644 lib/devicetree.h
> >
> > diff --git a/lib/devicetree.c b/lib/devicetree.c
> > new file mode 100644
> > index 0000000000000..a9ab7ed367704
> > --- /dev/null
> > +++ b/lib/devicetree.c
> > @@ -0,0 +1,257 @@
> > +#include "libcflat.h"
> > +#include "libfdt/libfdt.h"
> > +#include "devicetree.h"
> > +
> > +static const void *fdt;
> > +
> > +const char *dt_strerror(int errval)
> > +{
> > + if (errval == -EINVAL)
> > + return "Invalid argument";
> > + return fdt_strerror(errval);
> > +}
> > +
> > +int dt_set(const void *fdt_ptr)
> > +{
> > + int ret = fdt_check_header(fdt_ptr);
> > + if (ret == 0)
> > + fdt = fdt_ptr;
> > + return ret;
> > +}
> > +
> > +const void *dt_get(void)
> > +{
> > + return fdt;
> > +}
> > +
> > +int dt_get_bootargs_ptr(char **bootargs)
> > +{
> > + const struct fdt_property *prop;
> > + int node, err;
> > +
> > + node = fdt_path_offset(fdt, "/chosen");
> > + if (node < 0)
> > + return node;
> > +
> > + prop = fdt_get_property(fdt, node, "bootargs", &err);
> > + if (prop)
> > + *bootargs = (char *)prop->data;
> > + else if (err != -FDT_ERR_NOTFOUND)
> > + return err;
> > +
> > + return 0;
> > +}
> > +
> > +int dt_bus_default_match(const struct dt_bus *bus __unused,
> > + int nodeoffset __unused)
> > +{
> > + /* just select first node found */
> > + return 1;
> > +}
> > +
> > +int dt_bus_default_translate(const struct dt_bus *bus __unused,
> > + struct dt_reg *reg, void **addr, size_t *size)
> > +{
>
> I don't think you want addr to be a void **, but a phys_addr_t, see note
> about ARM 32-bit LPAE below.
Oh right. I'll get this straightened out.
>
> > + u64 temp64;
> > +
> > + if (!reg || !addr)
> > + return -EINVAL;
>
> defensive programming?
yeah, on second thought, it doesn't make sense to worry about this.
I'll rip all of it out.
>
> > +
> > + /*
> > + * default translate only understands u32 (<1> <1>) and
> > + * u64 (<2> <1>|<2>) addresses
> > + */
> > + if (reg->nr_address_cells < 1
> > + || reg->nr_address_cells > 2
> > + || reg->nr_size_cells < 1
> > + || reg->nr_size_cells > 2)
> > + return -EINVAL;
> > +
> > + if (reg->nr_address_cells == 2)
> > + temp64 = ((u64)reg->address_cells[0] << 32)
> > + | reg->address_cells[1];
> > + else
> > + temp64 = reg->address_cells[0];
>
> I would use braces on these statements given the multi-line first
> statement.
>
> > +
> > + /*
> > + * If we're 32-bit, then the upper word of a two word
> > + * address better be zero.
> > + */
>
> not necessarily, on ARM 32-bit LPAE you have 40-bit physical addresses
> but 32-bit virtual address pointers...
>
> > + if (sizeof(void *) == sizeof(u32) && reg->nr_address_cells > 1
> > + && reg->address_cells[0] != 0)
> > + return -EINVAL;
> > +
> > + *addr = (void *)(unsigned long)temp64;
> > +
> > + if (size) {
> > + if (reg->nr_size_cells == 2)
> > + temp64 = ((u64)reg->size_cells[0] << 32)
> > + | reg->size_cells[1];
> > + else
> > + temp64 = reg->size_cells[0];
> > +
> > + if (sizeof(size_t) == sizeof(u32) && reg->nr_size_cells > 1
> > + && reg->size_cells[0] != 0)
> > + return -EINVAL;
>
> same as above.
>
> > +
> > + *size = (size_t)temp64;
>
> hmmm, I feel like I just read this code. Maybe you can have a static
> little helper funciton to actually read your values.
ok
>
> > + }
> > +
> > + return 0;
> > +}
> > +
> > +const struct dt_bus dt_default_bus = {
> > + .name = "default",
> > + .match = dt_bus_default_match,
> > + .translate = dt_bus_default_translate,
> > +};
> > +
> > +void dt_bus_init_defaults(struct dt_bus *bus, const char *name)
> > +{
> > + *bus = dt_default_bus;
> > + bus->name = name;
> > +}
> > +
> > +int dt_bus_find_device_compatible(const struct dt_bus *bus,
> > + const char *compatible)
> > +{
> > + int node, ret;
> > +
> > + if (!bus || !bus->match)
> > + return -EINVAL;
>
> see __dt_bus_translate_reg below
>
> > +
> > + node = fdt_node_offset_by_compatible(fdt, -1, compatible);
> > +
> > + while (node >= 0) {
> > + if ((ret = bus->match(bus, node)) < 0)
> > + return ret;
> > + else if (ret)
> > + break;
> > + node = fdt_node_offset_by_compatible(fdt, node, compatible);
> > + }
> > +
> > + return node;
> > +}
> > +
> > +static int __dt_get_num_cells(int node, u32 *address_cells, u32 *size_cells)
> > +{
> > + const struct fdt_property *prop;
> > + u32 *data;
> > + int err;
> > +
> > + prop = fdt_get_property(fdt, node, "#address-cells", &err);
> > + if (!prop && err == -FDT_ERR_NOTFOUND) {
> > +
> > + node = fdt_parent_offset(fdt, node);
> > + if (node < 0)
> > + return node;
> > +
> > + return __dt_get_num_cells(node, address_cells, size_cells);
>
> you're doing recursive calls with a severly limited stack space, you
> should probably at least check your depth and catch errors at a
> reasonable level.
Good point on the stack use. I'll get rid of the recursion.
>
> It's probably cleaner to just have an iterative lookup of the node with
> #address_cells in it (in which you can also verify that is also has a
> #size-cells in it) and then pass that to the function that actually
> reads it.
>
> > +
> > + } else if (!prop) {
> > + return err;
> > + }
> > +
> > + data = (u32 *)prop->data;
> > + *address_cells = fdt32_to_cpu(*data);
> > +
> > + prop = fdt_get_property(fdt, node, "#size-cells", &err);
> > + if (!prop) {
> > + printf("we can read #address-cells, but not #size-cells?\n");
> > + return err;
> > + }
> > +
> > + data = (u32 *)prop->data;
> > + *size_cells = fdt32_to_cpu(*data);
> > +
> > + return 0;
> > +}
> > +
> > +int dt_get_num_cells(int nodeoffset, u32 *address_cells, u32 *size_cells)
> > +{
> > + if (!address_cells || !size_cells)
> > + return -EINVAL;
>
> defensive programming?
>
> > + return __dt_get_num_cells(nodeoffset, address_cells, size_cells);
> > +}
> > +
> > +int dt_get_reg(int nodeoffset, int regidx, u32 *address_cells,
> > + u32 *size_cells, struct dt_reg *reg)
> > +{
> > + const struct fdt_property *prop;
> > + u32 *data, regsz, i;
> > + int err;
> > +
> > + if (!address_cells || !size_cells || !reg)
> > + return -EINVAL;
>
> defensive programming?
>
> > +
> > + memset(reg, 0, sizeof(struct dt_reg));
>
> sizeof(*reg)
>
> > +
> > + /*
> > + * We assume #size-cells == 0 means translation is impossible,
>
> hmm, this won't work for the cpus' reg properties for example. I would
> just assume (and document) that this function always takes valid
> address_cells and size_cells (as u32 instead of u32 *). I get that
> you're trying to do some caching (from reading later patches), but that
> should still be possible to do externally.
cpus will need their own 'dt_get_cpuids', built on a 'dt_for_each_cpu',
or some such, and wouldn't use this function. Other than cpus, I think
this is a safe convention. Although, that said, I'll look into cleaning
up the interface a bit, while maintaining ease of use and the ability to
cache.
>
> If you really really want to, then you should use address_cells, but I'm
> not sure if it's actually valid for any bindings to have address_cells
> == 0 and size_cells >0. If you have conventions like this, it should be
> clearly documented on the function itself!
>
> > + * reserving it to indicate that we don't know what #address-cells
> > + * and #size-cells are yet, and thus must try to get them from the
> > + * parent.
> > + */
> > + if (*size_cells == 0 && (err = dt_get_num_cells(nodeoffset,
> > + address_cells, size_cells)) < 0)
> > + return err;
> > +
> > + prop = fdt_get_property(fdt, nodeoffset, "reg", &err);
> > + if (prop == NULL)
> > + return err;
> > +
> > + regsz = (*address_cells + *size_cells) * sizeof(u32);
> > +
> > + if ((regidx + 1) * regsz > prop->len)
> > + return -EINVAL;
> > +
> > + data = (u32 *)(prop->data + regidx * regsz);
> > +
> > + for (i = 0; i < *address_cells; ++i, ++data)
> > + reg->address_cells[i] = fdt32_to_cpu(*data);
> > + for (i = 0; i < *size_cells; ++i, ++data)
> > + reg->size_cells[i] = fdt32_to_cpu(*data);
> > +
> > + reg->nr_address_cells = *address_cells;
> > + reg->nr_size_cells = *size_cells;
> > +
> > + return 0;
> > +}
> > +
> > +int __dt_bus_translate_reg(int nodeoffset, const struct dt_bus *bus,
> > + int regidx, u32 *address_cells, u32 *size_cells,
> > + void **addr, size_t *size)
> > +{
> > + struct dt_reg reg;
> > + int ret;
> > +
> > + if (!bus || !bus->translate)
> > + return -EINVAL;
>
> The !bus seems overly defensive again.
>
> The !bus->translate may be helpful here, but since we're returning a
> bunch of -EINVALs you should probably put a debug statement in the error
> catching part, so devs can easily enable debugging and figure out where
> things are going wrong.
>
> That is, if anyone would ever call this with a bus without translate on
> there...
>
> > +
> > + ret = dt_get_reg(nodeoffset, regidx, address_cells, size_cells, ®);
> > + if (ret < 0)
> > + return ret;
> > +
> > + return bus->translate(bus, ®, addr, size);
> > +}
> > +
> > +int dt_bus_translate_reg(int nodeoffset, const struct dt_bus *bus,
> > + int regidx, void **addr, size_t *size)
> > +{
> > + /*
> > + * size_cells == 0 tells dt_get_reg to get address_cells
> > + * and size_cells from the parent node
> > + */
> > + u32 address_cells, size_cells = 0;
> > + return __dt_bus_translate_reg(nodeoffset, bus, regidx,
> > + &address_cells, &size_cells, addr, size);
> > +}
> > +
> > +int dt_get_memory_params(void **start, size_t *size)
>
> again, memory can exit beyond the 32-bit mark on ARM LPAE systems.
>
> > +{
> > + int node = fdt_path_offset(fdt, "/memory");
> > + if (node < 0)
> > + return node;
> > +
> > + return dt_bus_translate_reg(node, &dt_default_bus, 0, start, size);
> > +}
> > diff --git a/lib/devicetree.h b/lib/devicetree.h
> > new file mode 100644
> > index 0000000000000..1f2d61b46a308
> > --- /dev/null
> > +++ b/lib/devicetree.h
> > @@ -0,0 +1,95 @@
> > +#ifndef _DEVICETREE_H_
> > +#define _DEVICETREE_H_
> > +#include "libcflat.h"
> > +
> > +/*
> > + * set/get the fdt pointer
> > + */
> > +extern int dt_set(const void *fdt_ptr);
> > +extern const void *dt_get(void);
> > +
> > +/*
> > + * bootinfo accessors
> > + */
> > +extern int dt_get_bootargs_ptr(char **bootargs);
> > +extern int dt_get_memory_params(void **start, size_t *size);
>
> There's no documentation to these functions?
I was thinking the names were self-documenting, but can add comments
above them with a couple more details.
>
> > +
> > +#define MAX_ADDRESS_CELLS 4
> > +#define MAX_SIZE_CELLS 4
> > +struct dt_reg {
> > + u32 nr_address_cells;
> > + u32 nr_size_cells;
> > + u32 address_cells[MAX_ADDRESS_CELLS];
> > + u32 size_cells[MAX_SIZE_CELLS];
> > +};
>
> dt_reg? Is this for dt-bindings that specify the reg property? A
> comment would be nice.
I would have liked this to be private to devicetree.c, but as
translate is bus specific we need a way for buses to get "raw"
regs from the dt. I'll comment this better.
>
> > +
> > +struct dt_bus {
> > + const char *name;
> > + int (*match)(const struct dt_bus *bus, int nodeoffset);
> > + /*
> > + * match() returns
> > + * - a positive value on match
> > + * - zero on no match
> > + * - a negative value on error
> > + */
>
> what does match do? (I know by reading the code, but it should be
> documented)
>
> consider using defines for the return values, so your client code can
> be:
>
> if (bus->match(bus, node) == BUS_MATCH_SUCCESS)
> break;
ok
>
> > + int (*translate)(const struct dt_bus *bus, struct dt_reg *reg,
> > + void **addr, size_t *size);
> > + /*
> > + * translate() returns
> > + * - zero on success
> > + * - a negative value on error
> > + */
>
> ditto
>
> > + void *private;
>
> what is this field meant to contain? A hint about drivers passing
> information to translate() and match() would be helpful.
ok
>
> > +};
> > +
> > +extern const struct dt_bus dt_default_bus;
> > +extern void dt_bus_init_defaults(struct dt_bus *bus, const char *name);
> > +extern int dt_bus_default_match(const struct dt_bus *bus, int nodeoffset);
> > +extern int dt_bus_default_translate(const struct dt_bus *bus,
> > + struct dt_reg *reg, void **addr,
> > + size_t *size);
> > +
> > +/*
> > + * find an fdt device node compatible with @compatible using match()
> > + * from the given bus @bus.
> > + */
> > +extern int dt_bus_find_device_compatible(const struct dt_bus *bus,
> > + const char *compatible);
> > +
> > +/*
> > + * translate the reg indexed by @regidx of the "reg" property of the
> > + * device node at @nodeoffset using translate() from the given bus @bus.
> > + * returns the translation in @addr and @size
> > + */
>
> define 'translate' in the comment (into an address).
ok
>
> > +extern int dt_bus_translate_reg(int nodeoffset, const struct dt_bus *bus,
> > + int regidx, void **addr, size_t *size);
> > +
> > +/*
> > + * same as dt_bus_translate_reg, but uses the given @address_cells and
> > + * @size_cells rather than pulling them from the parent of @nodeoffset
> > + */
> > +extern int __dt_bus_translate_reg(int nodeoffset, const struct dt_bus *bus,
> > + int regidx, u32 *address_cells,
> > + u32 *size_cells, void **addr,
> > + size_t *size);
> > +
> > +/*
> > + * read the "reg" property of @nodeoffset, which is defined by @address_cells
> > + * and @size_cells, and store the reg indexed by @regidx into @reg
> > + */
> > +extern int dt_get_reg(int nodeoffset, int regidx, u32 *address_cells,
> > + u32 *size_cells, struct dt_reg *reg);
> > +
> > +/*
> > + * searches up the devicetree for @address_cells and @size_cells,
> > + * starting from @nodeoffset
> > + */
> > +extern int dt_find_num_cells(int nodeoffset, u32 *address_cells,
> > + u32 *size_cells);
>
> not defined?
Oops, s/_find_/_get_/
>
> > +
> > +/*
> > + * convert devicetree errors to strings
> > + */
> > +extern const char *dt_strerror(int errval);
> > +
> > +#endif
> > diff --git a/lib/libcflat.h b/lib/libcflat.h
> > index 2cde64a560956..fdaaf2a8ab31d 100644
> > --- a/lib/libcflat.h
> > +++ b/lib/libcflat.h
> > @@ -61,6 +61,7 @@ extern long atol(const char *ptr);
> >
> > #define offsetof(TYPE, MEMBER) __builtin_offsetof (TYPE, MEMBER)
> >
> > +#define __unused __attribute__((__unused__))
> > #define NULL ((void *)0UL)
> > #include "errno.h"
> > #endif
> > --
> > 1.8.1.4
> >
>
> Thanks,
> --
> Christoffer
next prev parent reply other threads:[~2014-02-03 15:31 UTC|newest]
Thread overview: 47+ messages / expand[flat|nested] mbox.gz Atom feed top
2014-01-21 16:21 [PATCH 00/17] kvm-unit-tests/arm: initial drop Andrew Jones
2014-01-21 16:21 ` [PATCH 01/17] remove unused files Andrew Jones
2014-01-21 16:21 ` [PATCH 02/17] makefile and run_tests tweaks Andrew Jones
2014-01-21 16:21 ` [PATCH 03/17] clean root dir of all x86-ness Andrew Jones
2014-01-21 16:21 ` [PATCH 04/17] gitignore: Ignore more Andrew Jones
2014-01-21 16:21 ` [PATCH 05/17] add 'make cscope' support Andrew Jones
2014-02-02 2:22 ` Christoffer Dall
2014-02-03 13:25 ` Andrew Jones
2014-01-21 16:21 ` [PATCH 06/17] Add halt() and some error codes Andrew Jones
2014-02-02 2:23 ` Christoffer Dall
2014-01-21 16:21 ` [PATCH 07/17] move x86's simple heap management to common code Andrew Jones
2014-02-02 2:23 ` Christoffer Dall
2014-01-21 16:21 ` [PATCH 08/17] Introduce libio to common code for io read/write Andrew Jones
2014-02-02 2:24 ` Christoffer Dall
2014-02-03 13:51 ` Andrew Jones
2014-02-03 16:30 ` Christoffer Dall
2014-01-21 16:21 ` [PATCH 10/17] libfdt: get libfdt to build Andrew Jones
2014-02-02 2:25 ` Christoffer Dall
2014-02-03 13:57 ` Andrew Jones
2014-01-21 16:21 ` [PATCH 11/17] add support for device trees Andrew Jones
2014-02-02 2:27 ` Christoffer Dall
2014-02-03 15:31 ` Andrew Jones [this message]
2014-02-03 16:36 ` Christoffer Dall
2014-01-21 16:21 ` [PATCH 12/17] Introduce virtio-testdev Andrew Jones
2014-02-02 2:27 ` Christoffer Dall
2014-02-03 15:44 ` Andrew Jones
2014-02-03 16:41 ` Christoffer Dall
2014-01-21 16:21 ` [PATCH 13/17] arm: initial drop Andrew Jones
2014-02-02 2:28 ` Christoffer Dall
2014-02-03 15:55 ` Andrew Jones
2014-01-21 16:22 ` [PATCH 14/17] arm: Add IO accessors to avoid register-writeback Andrew Jones
2014-01-21 16:22 ` [PATCH 15/17] printf: support field padding Andrew Jones
2014-02-02 2:28 ` Christoffer Dall
2014-01-21 16:22 ` [PATCH 16/17] arm: add useful headers from the linux kernel Andrew Jones
2014-02-02 2:29 ` Christoffer Dall
2014-02-03 16:46 ` Andrew Jones
2014-02-03 17:38 ` Christoffer Dall
2014-01-21 16:22 ` [PATCH 17/17] arm: vectors support Andrew Jones
2014-02-02 2:29 ` Christoffer Dall
2014-02-03 16:50 ` Andrew Jones
2014-02-03 21:19 ` Christoffer Dall
2014-02-04 7:12 ` Andrew Jones
[not found] ` <CABWnSnPMc_CrH8N28TScBVvQmCk+XD-bVWvdmJAxxVczHsVx_g@mail.gmail.com>
2014-01-29 15:35 ` [PATCH 00/17] kvm-unit-tests/arm: initial drop Andrew Jones
2014-02-02 2:22 ` Christoffer Dall
2014-02-03 13:24 ` Andrew Jones
[not found] ` <CALxX4v-h+gOCZDukCnGK_GUQepu07KYw4BGjzjGNgA0SdDcLNw@mail.gmail.com>
2014-02-04 8:33 ` Andrew Jones
[not found] ` <1390321323-1855-10-git-send-email-drjones@redhat.com>
2014-02-02 2:25 ` [PATCH 09/17] libfdt: Import libfdt source Christoffer Dall
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20140203153120.GE6832@hawk.usersys.redhat.com \
--to=drjones@redhat.com \
--cc=christoffer.dall@linaro.org \
--cc=kvm@vger.kernel.org \
--cc=kvmarm@lists.cs.columbia.edu \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox