Re: [PATCH] libfdt: add helpers to read address and size from reg

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



On Wed, Nov 30, 2016 at 01:35:04PM -0600, Benjamin Fair wrote:
> On 11/25/2016 04:51 AM, David Gibson wrote:
> > On Wed, Nov 09, 2016 at 10:58:32AM -0600, Benjamin Fair wrote:
> [...]
> > 
> > I like the concept of a helper to read entries from reg, but there
> > some things about the execution of it I think need some more thought.
> > 
> 
> Thanks for the review.
> 
> [...]
> > > diff --git a/libfdt/fdt_addresses.c b/libfdt/fdt_addresses.c
> > > index eff4dbc..92cbed9 100644
> > > --- a/libfdt/fdt_addresses.c
> > > +++ b/libfdt/fdt_addresses.c
> > > @@ -55,6 +55,9 @@
> > > 
> > >  #include "libfdt_internal.h"
> > > 
> > > +#define BYTES_PER_CELL 4
> > > +#define BITS_PER_CELL 32
> > 
> > You shouldn't need these.  BYTES_PER_CELL == sizeof(fdt32_t).
> > 
> 
> Of course. Thanks. I'll get rid of them.
> 
> > > +
> > >  int fdt_address_cells(const void *fdt, int nodeoffset)
> > >  {
> > >  	const fdt32_t *ac;
> > > @@ -94,3 +97,62 @@ int fdt_size_cells(const void *fdt, int nodeoffset)
> > > 
> > >  	return val;
> > >  }
> > > +
> > > +static uint64_t _fdt_read_cells(const fdt32_t *cells, int n)
> > 
> > This is a reasonable helper, but the name is bad.  "read_cells"
> > suggests it can read some arbitrary number of cells, but in fact all
> > it can do is read a 32-bit int or a 64-bit int.  Plus everything is
> > made up of cells, but more specifically what you're doing here is
> > interpreting several cells as an integer in the usual encoding.
> > 
> 
> Would "cells_to_integer" be a better name? Or would you recommend something
> else for this?

cells_to_integer would be ok.  Or just fdt_read_integer().

> 
> > > +{
> > > +	int i;
> > > +	uint64_t res;
> > > +
> > > +	/* TODO: Support values larger than 2 cells */
> > 
> > I don't really see any way you could support >2 cells without
> > completely changing the interface.
> > 
> 
> True. I wanted to have the result be a 128 bit integer, but couldn't find a
> portable way to do so. Is there a better way to go about this? Or is it fine
> to only support at most 2 cells, even though the rest of libfdt supports 4?

I think just supporting 2 cells is ok - it's useful in enough
practical cases.  However, I'd suggest thinking of this in a more
layered way.

First, create a helper which just locates the relevant pieces of reg
entries, returning addresses and sizes as (fdt32_t *).  That's at
least somewhat useful for the >2 cells case, even though you have to
then parse the address/size yourself.  In the most common case for
this, PCI (address-cells == 3), you probably want to do that anyway.
This may also be useful for cases which are <= 2 cells, but the
encoding of the address is not just a plain integer.

You can then polish up fdt_read_integer() a bit and export it.

Finally you can add another helper which combines these to directly
get you the addr+size as u64 for buses where that's suitable.  Make
sure to return an error if #a or #s > 2, of course.

> > > +	if (n > 2)
> > > +		return -FDT_ERR_BADNCELLS;
> > > +
> > > +	res = 0;
> > > +	for (i = 0; i < n; i++) {
> > > +		res <<= BITS_PER_CELL;
> > > +		res |= fdt32_to_cpu(cells[i]);
> > > +	}
> > > +
> > > +	return res;
> > > +}
> > > +
> > > +int fdt_simple_addr_size(const void *fdt, int nodeoffset, int idx,
> > > +			 uintptr_t *addr, size_t *size)
> > > +{
> > > +	int parent;
> > > +	int ac, sc, reg_stride;
> > > +	int res;
> > > +	const fdt32_t *reg;
> > > +
> > > +	reg = fdt_getprop(fdt, nodeoffset, "reg", &res);
> > > +	if (res < 0)
> > > +		return res;
> > > +
> > > +	parent = fdt_parent_offset(fdt, nodeoffset);
> > > +	if (parent < 0)
> > > +		return res;
> > 
> > So, fdt_parent_offset() is very expensive, I wouldn't recommend it in
> > a function that's likely to be called a lot like this.  Instead I'd
> > suggest a function which takes the parent offset as a parameter, and
> > optionally a wrapper that uses fdt_parent_offset().
> 
> Great idea, I'll do this in the next revision once we have a solution for
> the rest of the comments.
> 
> > 
> > > +
> > > +	ac = fdt_address_cells(fdt, parent);
> > > +	if (ac < 0)
> > > +		return ac;
> > > +
> > > +	sc = fdt_size_cells(fdt, parent);
> > > +	if (sc < 0)
> > > +		return sc;
> > > +
> > > +	reg_stride = ac + sc;
> > > +	/*
> > > +	 * res is the number of bytes read and must be an even multiple of the
> > > +	 * sum of ac and sc.
> > > +	 */
> > > +	if ((res % (reg_stride * BYTES_PER_CELL)) != 0)
> > > +		return -FDT_ERR_BADVALUE;
> > > +
> > > +	if (addr)
> > > +		*addr = (uintptr_t) _fdt_read_cells(&reg[reg_stride * idx], ac);
> > 
> > I don't think uintptr_t makes sense here.  The addresses in the device
> > tree are in whatever bus they're in, and there are a whole stack of
> > reasons that could be unrelated to the pointer size of environment
> > libfdt is running in:
> >     - The device may be on a subordinate bus whose addresses need
> >       to be translated
> >     - Even at the top-level, the reg properties represent
> >       *physical* addresses, which may not be the same as virtual
> >       addresses  in code running on the system
> >     - libfdt may be running on a completely different system from the
> >       one the device tree in question is aimed at (bootloaders are
> >       only one use case for libfdt).
> > 
> > > +	if (size)
> > > +		*size = (size_t) _fdt_read_cells(&reg[ac + reg_stride * idx],
> > > +						 sc);
> > 
> > Likewise size_t isn't necessarily right here, although I suspect it's
> > less likely to break in practice.
> > 
> 
> Hmm... Is it fine to use uint64_t for both of these instead then?

Yes, I think that's reasonable.  Or you could even use unsigned long
or unsigned long long - as long as you error whenever #cells is too
big for the size of that type.  uint64_t is probably clearer and more
consistent, though.

-- 
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