fdt: Fix fdtdec_get_addr_size() for 64-bit
PATCH 1/1 fdt: Fix fdtdec_get_addr_size() for 64-bit
From: Thierry Reding <[email protected]>
---
Simon,
When Thierry first posted this patch, you responded:
Quoted 4 lines
Quoted 1 line
+ parent =
fdt_parent_offset(blob, node);This function is very slow as it must scan the whole tree. Can we instead pass in the parent node?
I don't think that's possible in general. This function is called from fdtdec_get_addr(), and it's easy to find call sites of that function that don't have the parent node available. IIRC, the first couple of example I found scan the DT for a node with a certain compatible value, or look up nodes via aliases, rather than being called while parsing the DT in a top-down tree-like fashion, where the parent node is easily available. I didn't do an exhaustive search after I found a few problematic cases.
Quoted 6 lines
Also, how about (in addition) a
version of this function that works for devices? Like:
device_get_addr_size(struct udevice *dev, ...)so that it can handle this for you.
That sounds like a separate patch?
Equally, I see that struct udevice contains an of_offset field, but no parent_of_offset or similar. There is a struct udevice *parent though; is the struct udevice hierarchy guaranteed to 100% match the DT hierarchy? I know this isn't necessarily guaranteed in Linux's device model for example.
As such, this patch seems OK to me as-is.
--- lib/fdtdec.c | 56 ++++++++++++++++++++++++++++++++++++-------------------- 1 file changed, 36 insertions(+), 20 deletions(-)
lib/fdtdec.c +36 -20
@@ -88,29 +88,45 @@ const char *fdtdec_get_compatible(enum fdt_compat_id id)fdt_addr_t fdtdec_get_addr_size (const void *blob,int node,const char *prop_name,fdt_size_t *sizep) { -const fdt_addr_t *cell; -int len; +const fdt32_t *ptr, *end; +int parent, na, ns, len; +fdt_addr_t addr;debug ("%s: %s: " , __func__, prop_name); - cell =fdt_getprop (blob, node, prop_name, &len); -if (cell && ((!sizep && len ==sizeof (fdt_addr_t)) || - len ==sizeof (fdt_addr_t) *2 )) { -fdt_addr_t addr =fdt_addr_to_cpu (*cell); -if (sizep) { -const fdt_size_t *size; - - size = (fdt_size_t *)((char *)cell + -sizeof (fdt_addr_t)); - *sizep =fdt_size_to_cpu (*size); -debug ("addr=%08lx, size=%llx\n" , - (ulong )addr, (u64)*sizep); - }else { -debug ("%08lx\n" , (ulong )addr); - } -return addr; + + parent =fdt_parent_offset (blob, node); +if (parent <0 ) { +debug ("(no parent found)\n" ); +return FDT_ADDR_T_NONE ; } -debug ("(not found)\n" ); -return FDT_ADDR_T_NONE ; + + na =fdt_address_cells (blob, parent); + ns =fdt_size_cells (blob, parent); + + ptr =fdt_getprop (blob, node, prop_name, &len); +if (!ptr) { +debug ("(not found)\n" ); +return FDT_ADDR_T_NONE ; + } + + end = ptr + len /sizeof (*ptr); + +if (ptr + na + ns > end) { +debug ("(not enough data: expected %d bytes, got %d bytes)\n" , + (na + ns) *4 , len); +return FDT_ADDR_T_NONE ; + } + + addr =fdtdec_get_number (ptr, na); + +if (sizep) { + *sizep =fdtdec_get_number (ptr + na, ns); +debug ("addr=%pa, size=%pa\n" , &addr, sizep); + }else { +debug ("%pa\n" , &addr); + } + +return addr; } fdt_addr_t fdtdec_get_addr(const void *blob, int node,
Signature · 1 line
1.9.1
PATCH 1/1 fdt: Fix fdtdec_get_addr_size() for 64-bit
Hi,
Quoted 23 lines · Stephen Warren
From: Thierry Reding <[email protected]>
Signed-off-by Thierry Reding <[email protected]>Signed-off-by Tom Warren <[email protected]>Signed-off-by Stephen Warren <[email protected]>---
Simon,When Thierry first posted this patch, you responded:
Quoted 4 lines
Quoted 1 line
+ parent =
fdt_parent_offset(blob, node);This function is very slow as it must scan the whole tree. Can we instead pass in the parent node?
I don't think that's possible in general. This function is called from
fdtdec_get_addr(), and it's easy to find call sites of that function that don't have the parent node available. IIRC, the first couple of example I found scan the DT for a node with a certain compatible value, or look up nodes via aliases, rather than being called while parsing the DT in a top-down tree-like fashion, where the parent node is easily available. I didn't do an exhaustive search after I found a few problematic cases.
Quoted 8 lines
Quoted 6 lines
Also, how about (in addition) a
version of this function that works for devices? Like:
device_get_addr_size(struct udevice *dev, ...)so that it can handle this for you.
That sounds like a separate patch?
Yes, but I think we should get it in there so that people don't start using this (wildly inefficient) function when they don't need to. I think by passing in the parent node we force people to think about the cost.
Yes the driver model function can be a separate patch, but let's get it in at about the same time. We have dev_get_addr() so could have dev_get_addr_size().
Quoted 6 lines
Equally, I see that struct udevice contains an
of_offsetfield, but noparent_of_offsetor similar. There is a struct udevice *parent though; is the struct udevice hierarchy guaranteed to 100% match the DT hierarchy? I know this isn't necessarily guaranteed in Linux's device model for example.
Yes it is 100% guaranteed, so dev->parent->of_offset will do the right thing.
Quoted 77 lines
As such, this patch seems OK to me as-is.
--- lib/fdtdec.c | 56 ++++++++++++++++++++++++++++++++++++-------------------- 1 file changed, 36 insertions(+), 20 deletions(-)lib/fdtdec.c +36
-21@@ -88,29 +88,45 @@ const char *fdtdec_get_compatible(enum fdt_compat_id id)fdt_addr_t fdtdec_get_addr_size (const void *blob,int node,const char *prop_name,fdt_size_t *sizep) { -const fdt_addr_t *cell; -int len; +const fdt32_t *ptr, *end; +int parent, na, ns, len; +fdt_addr_t addr;debug ("%s: %s: " , __func__, prop_name); - cell =fdt_getprop (blob, node, prop_name, &len); -if (cell && ((!sizep && len ==sizeof (fdt_addr_t)) || - len ==sizeof (fdt_addr_t) *2 )) { -fdt_addr_t addr =fdt_addr_to_cpu (*cell); -if (sizep) { -const fdt_size_t *size; - - size = (fdt_size_t *)((char *)cell + -sizeof (fdt_addr_t)); - *sizep =fdt_size_to_cpu (*size); -debug ("addr=%08lx, size=%llx\n" , - (ulong )addr, (u64)*sizep); - }else { -debug ("%08lx\n" , (ulong )addr); - } -return addr; + + parent =fdt_parent_offset (blob, node); +if (parent <0 ) { +debug ("(no parent found)\n" ); +return FDT_ADDR_T_NONE ; } -debug ("(not found)\n" ); -return FDT_ADDR_T_NONE ; + + na =fdt_address_cells (blob, parent); + ns =fdt_size_cells (blob, parent); + + ptr =fdt_getprop (blob, node, prop_name, &len); +if (!ptr) { +debug ("(not found)\n" ); +return FDT_ADDR_T_NONE ; + } + + end = ptr + len /sizeof (*ptr); + +if (ptr + na + ns > end) { +debug ("(not enough data: expected %d bytes, got %d bytes)\n" , + (na + ns) *4 , len); +return FDT_ADDR_T_NONE ; + } + + addr =fdtdec_get_number (ptr, na); + +if (sizep) { + *sizep =fdtdec_get_number (ptr + na, ns); +debug ("addr=%pa, size=%pa\n" , &addr, sizep); + }else { +debug ("%pa\n" , &addr); + } + +return addr; } fdt_addr_t fdtdec_get_addr(const void *blob, int node, --
Quoted 2 lines
1.9.1
Regards,
SImon
PATCH 1/1 fdt: Fix fdtdec_get_addr_size() for 64-bit
Hi,
Quoted 45 lines · Simon Glass
Hi,
Quoted 23 lines · Stephen Warren
From: Thierry Reding <[email protected]>
Signed-off-by Thierry Reding <[email protected]>Signed-off-by Tom Warren <[email protected]>Signed-off-by Stephen Warren <[email protected]>---
Simon,When Thierry first posted this patch, you responded:
Quoted 4 lines
Quoted 1 line
+ parent =
fdt_parent_offset(blob, node);This function is very slow as it must scan the whole tree. Can we instead pass in the parent node?
I don't think that's possible in general. This function is called from
fdtdec_get_addr(), and it's easy to find call sites of that function that don't have the parent node available. IIRC, the first couple of example I found scan the DT for a node with a certain compatible value, or look up nodes via aliases, rather than being called while parsing the DT in a top-down tree-like fashion, where the parent node is easily available. I didn't do an exhaustive search after I found a few problematic cases.Quoted 8 lines
Quoted 6 lines
Also, how about (in addition) a
version of this function that works for devices? Like:
device_get_addr_size(struct udevice *dev, ...)so that it can handle this for you.
That sounds like a separate patch?
Yes, but I think we should get it in there so that people don't start using this (wildly inefficient) function when they don't need to. I think by passing in the parent node we force people to think about the cost.
Yes the driver model function can be a separate patch, but let's get it in at about the same time. We have
dev_get_addr()so could havedev_get_addr_size().Quoted 1 line
Quoted 14 lines
Quoted 5 lines
Equally, I see that struct udevice contains an
of_offsetfield, but noparent_of_offsetor similar. There is a struct udevice *parent though; is the struct udevice hierarchy guaranteed to 100% match the DT hierarchy? I know this isn't necessarily guaranteed in Linux's device model for example.Yes it is 100% guaranteed, so
dev->parent->of_offsetwill do the right thing.Quoted 6 lines
As such, this patch seems OK to me as-is.
--- lib/fdtdec.c | 56 ++++++++++++++++++++++++++++++++++++-------------------- 1 file changed, 36 insertions(+), 20 deletions(-)
This patch has been applied. I'm going to post a revert of this patch. Please can you take a look at the comments above? In particular this function is called from dev_get_addr() which is a core driver model function. It needs to be fast - and in fact dev_get_addr() already has access to the parent node.
Also looking a bit closer this patch does a lot more than 'fix it for 64-bit'. A commit message would be useful to explain what problems it is fixing, etc.
Another point is that fdt_addr_t and fdt_size_t are supposed to match the address size used in the device tree. Is that not the case with Tegra210?
Quoted 69 lines
Quoted 69 lines
lib/fdtdec.c +36
-20@@ -88,29 +88,45 @@ const char *fdtdec_get_compatible(enum fdt_compat_id id)fdt_addr_t fdtdec_get_addr_size (const void *blob,int node,const char *prop_name,fdt_size_t *sizep) { -const fdt_addr_t *cell; -int len; +const fdt32_t *ptr, *end; +int parent, na, ns, len; +fdt_addr_t addr;debug ("%s: %s: " , __func__, prop_name); - cell =fdt_getprop (blob, node, prop_name, &len); -if (cell && ((!sizep && len ==sizeof (fdt_addr_t)) || - len ==sizeof (fdt_addr_t) *2 )) { -fdt_addr_t addr =fdt_addr_to_cpu (*cell); -if (sizep) { -const fdt_size_t *size; - - size = (fdt_size_t *)((char *)cell + -sizeof (fdt_addr_t)); - *sizep =fdt_size_to_cpu (*size); -debug ("addr=%08lx, size=%llx\n" , - (ulong )addr, (u64)*sizep); - }else { -debug ("%08lx\n" , (ulong )addr); - } -return addr; + + parent =fdt_parent_offset (blob, node); +if (parent <0 ) { +debug ("(no parent found)\n" ); +return FDT_ADDR_T_NONE ; } -debug ("(not found)\n" ); -return FDT_ADDR_T_NONE ; + + na =fdt_address_cells (blob, parent); + ns =fdt_size_cells (blob, parent); + + ptr =fdt_getprop (blob, node, prop_name, &len); +if (!ptr) { +debug ("(not found)\n" ); +return FDT_ADDR_T_NONE ; + } + + end = ptr + len /sizeof (*ptr); + +if (ptr + na + ns > end) { +debug ("(not enough data: expected %d bytes, got %d bytes)\n" , + (na + ns) *4 , len); +return FDT_ADDR_T_NONE ; + } + + addr =fdtdec_get_number (ptr, na); + +if (sizep) { + *sizep =fdtdec_get_number (ptr + na, ns); +debug ("addr=%pa, size=%pa\n" , &addr, sizep); + }else { +debug ("%pa\n" , &addr); + } + +return addr; }
Quoted 4 lines
Quoted 4 lines
lib/fdtdec.c +0
-1fdt_addr_t fdtdec_get_addr(const void *blob, int node, --1.9.1
Regards,
Simon
PATCH 1/1 fdt: Fix fdtdec_get_addr_size() for 64-bit
Quoted 63 lines · Simon Glass
Hi,
Quoted 45 lines · Simon Glass
Hi,
Quoted 23 lines · Stephen Warren
From: Thierry Reding <[email protected]>
Signed-off-by Thierry Reding <[email protected]>Signed-off-by Tom Warren <[email protected]>Signed-off-by Stephen Warren <[email protected]>---
Simon,When Thierry first posted this patch, you responded:
Quoted 4 lines
Quoted 1 line
+ parent =
fdt_parent_offset(blob, node);This function is very slow as it must scan the whole tree. Can we instead pass in the parent node?
I don't think that's possible in general. This function is called from
fdtdec_get_addr(), and it's easy to find call sites of that function that don't have the parent node available. IIRC, the first couple of example I found scan the DT for a node with a certain compatible value, or look up nodes via aliases, rather than being called while parsing the DT in a top-down tree-like fashion, where the parent node is easily available. I didn't do an exhaustive search after I found a few problematic cases.Quoted 8 lines
Quoted 6 lines
Also, how about (in addition) a
version of this function that works for devices? Like:
device_get_addr_size(struct udevice *dev, ...)so that it can handle this for you.
That sounds like a separate patch?
Yes, but I think we should get it in there so that people don't start using this (wildly inefficient) function when they don't need to. I think by passing in the parent node we force people to think about the cost.
Yes the driver model function can be a separate patch, but let's get it in at about the same time. We have
dev_get_addr()so could havedev_get_addr_size().Quoted 1 line
Quoted 14 lines
Quoted 5 lines
Equally, I see that struct udevice contains an
of_offsetfield, but noparent_of_offsetor similar. There is a struct udevice *parent though; is the struct udevice hierarchy guaranteed to 100% match the DT hierarchy? I know this isn't necessarily guaranteed in Linux's device model for example.Yes it is 100% guaranteed, so
dev->parent->of_offsetwill do the right thing.Quoted 6 lines
As such, this patch seems OK to me as-is.
--- lib/fdtdec.c | 56 ++++++++++++++++++++++++++++++++++++-------------------- 1 file changed, 36 insertions(+), 20 deletions(-)
Quoted 5 lines
This patch has been applied. I'm going to post a revert of this patch. Please can you take a look at the comments above? In particular this function is called from
dev_get_addr()which is a core driver model function. It needs to be fast - and in factdev_get_addr()already has access to the parent node.
Perhaps this could be fixed by doing passing in the parent as an optional argument and then do something like this:
if (parent < 0) {
parent = fdt_parent_offset(blob, node);
if (parent < 0) {
...
}
}In that case callers that have access to the parent node already can pass it in, but others can simply pass in -1 and have the function do the lookup.
Quoted 7 lines
Also looking a bit closer this patch does a lot more than 'fix it for 64-bit'. A commit message would be useful to explain what problems it is fixing, etc.
Another point is that
fdt_addr_tandfdt_size_tare supposed to match the address size used in the device tree. Is that not the case with Tegra210?
You can't assume that #address-cells and #size-cells will be 2 for all 64-bit platforms. Some may well go with #address-cells = 1 and #size- cells = 1, and I've seen others do #address-cells = 2 and #size-cells =
1. All of these combinations are perfectly valid.
As such, fdt_addr_t and fdt_size_t make sense only if they are the maximum that the architecture can support. Even so an address could technically be larger than that, if it's behind a translated bus, for example.
So what this does is really fix parsing of address and size cells in the general case, though it would still fail for values of #address-cells or #size-cells bigger than 2 (because we don't have a datatype that would be able to contain such large values).
Note that there's also still a corner case that this doesn't handle. The DT specification states, if I remember correctly, that #address-cells and #size-cells are inherited. That means with the current code we will wrongly parse something like this:
/ {
...
#address-cells = <1>;
#size-cells = <1>;
...
bus at XXXXXXXX {
...
device at XXXXXXXX {
...
reg = <0xXXXXXXXX 0x1000>;
...
};
...
};
...
};According to the DT specification the bus at XXXXXXXX node would inherit #address-cells = <1> and #size-cells = <1> from the root node. However with libfdt what really happens is that since bus at XXXXXXXX does not have either property it will default to 2 in both cases. I'm not sure if this really is a problem. Typically nodes are not nested that deeply, or if they are then, typically, they explicitly contain #address-cells and #size-cells properties.
Thierry
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 819 bytes
Desc: not available
URL: <http://lists.denx.de/pipermail/u-boot/attachments/20150804/20eeb628/attachment.sig>
PATCH 1/1 fdt: Fix fdtdec_get_addr_size() for 64-bit
On 08/04/2015 08:26 AM, Thierry Reding wrote:
... [ discussion of new fdtdec_get_addr_size() implementation]
Quoted 27 lines
So what this does is really fix parsing of address and size cells in the general case, though it would still fail for values of #address-cells or #size-cells bigger than 2 (because we don't have a datatype that would be able to contain such large values).
Note that there's also still a corner case that this doesn't handle. The DT specification states, if I remember correctly, that #address-cells and #size-cells are inherited. That means with the current code we will wrongly parse something like this:
/ { ... #address-cells = <1>; #size-cells = <1>; ... bus at XXXXXXXX { ... device at XXXXXXXX { ... reg = <0xXXXXXXXX 0x1000>; ... }; ... }; ... };
Quoted 7 lines
According to the DT specification the bus at XXXXXXXX node would inherit #address-cells = <1> and #size-cells = <1> from the root node. However with libfdt what really happens is that since bus at XXXXXXXX does not have either property it will default to 2 in both cases. I'm not sure if this really is a problem. Typically nodes are not nested that deeply, or if they are then, typically, they explicitly contain #address-cells and #size-cells properties.
I don't think #address-cells/#size-cells do actually get inherited. Admittedly some other properties (e.g. interrupt-parent) do, but according to:
https://lists.ozlabs.org/pipermail/linuxppc-dev/2008-January/049113.html
[PATCH] powerpc: #address-cells & #size-cells properties not inherited
... and my vague memory, these two don't.
You can search Google for e.g. "#address-cells inherited" and find a number of similar assertions.
PATCH 1/1 fdt: Fix fdtdec_get_addr_size() for 64-bit
Quoted 41 lines · Stephen Warren
On 08/04/2015 08:26 AM, Thierry Reding wrote:
... [ discussion of newfdtdec_get_addr_size()implementation]Quoted 27 lines
So what this does is really fix parsing of address and size cells in the general case, though it would still fail for values of #address-cells or #size-cells bigger than 2 (because we don't have a datatype that would be able to contain such large values).
Note that there's also still a corner case that this doesn't handle. The DT specification states, if I remember correctly, that #address-cells and #size-cells are inherited. That means with the current code we will wrongly parse something like this:
/ { ... #address-cells = <1>; #size-cells = <1>; ... bus at XXXXXXXX { ... device at XXXXXXXX { ... reg = <0xXXXXXXXX 0x1000>; ... }; ... }; ... };Quoted 7 lines
According to the DT specification the bus at XXXXXXXX node would inherit #address-cells = <1> and #size-cells = <1> from the root node. However with libfdt what really happens is that since bus at XXXXXXXX does not have either property it will default to 2 in both cases. I'm not sure if this really is a problem. Typically nodes are not nested that deeply, or if they are then, typically, they explicitly contain #address-cells and #size-cells properties.
I don't think #address-cells/#size-cells do actually get inherited. Admittedly some other properties (e.g. interrupt-parent) do, but according to:
Quoted 7 lines
https://lists.ozlabs.org/pipermail/linuxppc-dev/2008-January/049113.html
[PATCH] powerpc: #address-cells & #size-cells properties not inherited... and my vague memory, these two don't.
You can search Google for e.g. "#address-cells inherited" and find a number of similar assertions.
Okay, that's good. It means there's not even a corner case. =)
Thierry
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 819 bytes
Desc: not available
URL: <http://lists.denx.de/pipermail/u-boot/attachments/20150804/e48d0499/attachment.sig>