diff mbox

[v2,4/5] PCI: generic: Correct, and avoid overflow, in bus_max calculation.

Message ID 1442527332-1174-5-git-send-email-ddaney.cavm@gmail.com (mailing list archive)
State New, archived
Headers show

Commit Message

David Daney Sept. 17, 2015, 10:02 p.m. UTC
From: David Daney <david.daney@cavium.com>

There are two problems with the bus_max calculation:

1) The u8 data type can overflow for large config space windows.

2) The calculation is incorrect for a bus range that doesn't start at
   zero.

Since the configuration space is relative to bus zero, make bus_max
just be the size of the config window scaled by bus_shift.  Then clamp
it to a maximum of 255, per PCI.  Use a data type of int to avoid
overflow problems.

Update host-generic-pci.txt to clarify the semantics of the "reg"
property with respect to non-zero starting bus numbers.

Signed-off-by: David Daney <david.daney@cavium.com>
---
Change from v1: Added text to host-generic-pci.txt

 Documentation/devicetree/bindings/pci/host-generic-pci.txt | 4 +++-
 drivers/pci/host/pci-host-generic.c                        | 7 ++++---
 2 files changed, 7 insertions(+), 4 deletions(-)

Comments

Will Deacon Sept. 23, 2015, 6:01 p.m. UTC | #1
On Thu, Sep 17, 2015 at 11:02:11PM +0100, David Daney wrote:
> From: David Daney <david.daney@cavium.com>
> 
> There are two problems with the bus_max calculation:
> 
> 1) The u8 data type can overflow for large config space windows.
> 
> 2) The calculation is incorrect for a bus range that doesn't start at
>    zero.
> 
> Since the configuration space is relative to bus zero, make bus_max
> just be the size of the config window scaled by bus_shift.  Then clamp
> it to a maximum of 255, per PCI.  Use a data type of int to avoid
> overflow problems.
> 
> Update host-generic-pci.txt to clarify the semantics of the "reg"
> property with respect to non-zero starting bus numbers.
> 
> Signed-off-by: David Daney <david.daney@cavium.com>
> ---
> Change from v1: Added text to host-generic-pci.txt
> 
>  Documentation/devicetree/bindings/pci/host-generic-pci.txt | 4 +++-
>  drivers/pci/host/pci-host-generic.c                        | 7 ++++---
>  2 files changed, 7 insertions(+), 4 deletions(-)
> 
> diff --git a/Documentation/devicetree/bindings/pci/host-generic-pci.txt b/Documentation/devicetree/bindings/pci/host-generic-pci.txt
> index cf3e205..105a968 100644
> --- a/Documentation/devicetree/bindings/pci/host-generic-pci.txt
> +++ b/Documentation/devicetree/bindings/pci/host-generic-pci.txt
> @@ -34,7 +34,9 @@ Properties of the host controller node:
>  - #size-cells    : Must be 2.
>  
>  - reg            : The Configuration Space base address and size, as accessed
> -                   from the parent bus.
> +                   from the parent bus.  The base address corresponds to
> +                   bus zero, even though the "bus-range" property may specify
> +                   a different starting bus number.

That's a useful clarification, thanks.

>  Properties of the /chosen node:
> diff --git a/drivers/pci/host/pci-host-generic.c b/drivers/pci/host/pci-host-generic.c
> index 77cf4bd..0a9c453 100644
> --- a/drivers/pci/host/pci-host-generic.c
> +++ b/drivers/pci/host/pci-host-generic.c
> @@ -164,7 +164,7 @@ out_release_res:
>  static int gen_pci_parse_map_cfg_windows(struct gen_pci *pci)
>  {
>  	int err;
> -	u8 bus_max;
> +	int bus_max;
>  	resource_size_t busn;
>  	struct resource *bus_range;
>  	struct device *dev = pci->host.dev.parent;
> @@ -177,8 +177,9 @@ static int gen_pci_parse_map_cfg_windows(struct gen_pci *pci)
>  	}
>  
>  	/* Limit the bus-range to fit within reg */
> -	bus_max = pci->cfg.bus_range->start +
> -		  (resource_size(&pci->cfg.res) >> pci->cfg.ops.bus_shift) - 1;
> +	bus_max = (resource_size(&pci->cfg.res) >> pci->cfg.ops.bus_shift) - 1;
> +	if (bus_max > 255)
> +		bus_max = 255;

I still don't understand the need for this part. If the cfg space is bigger
than bus_max, isn't that simply an invalid resource? Given that the resource
could be broken in other ways too, this check feels more like a specific
workaround rather than generally useful code.

Will
David Daney Sept. 23, 2015, 6:21 p.m. UTC | #2
On 09/23/2015 11:01 AM, Will Deacon wrote:
> On Thu, Sep 17, 2015 at 11:02:11PM +0100, David Daney wrote:
[...]
>
>>   Properties of the /chosen node:
>> diff --git a/drivers/pci/host/pci-host-generic.c b/drivers/pci/host/pci-host-generic.c
>> index 77cf4bd..0a9c453 100644
>> --- a/drivers/pci/host/pci-host-generic.c
>> +++ b/drivers/pci/host/pci-host-generic.c
>> @@ -164,7 +164,7 @@ out_release_res:
>>   static int gen_pci_parse_map_cfg_windows(struct gen_pci *pci)
>>   {
>>   	int err;
>> -	u8 bus_max;
>> +	int bus_max;
>>   	resource_size_t busn;
>>   	struct resource *bus_range;
>>   	struct device *dev = pci->host.dev.parent;
>> @@ -177,8 +177,9 @@ static int gen_pci_parse_map_cfg_windows(struct gen_pci *pci)
>>   	}
>>
>>   	/* Limit the bus-range to fit within reg */
>> -	bus_max = pci->cfg.bus_range->start +
>> -		  (resource_size(&pci->cfg.res) >> pci->cfg.ops.bus_shift) - 1;
>> +	bus_max = (resource_size(&pci->cfg.res) >> pci->cfg.ops.bus_shift) - 1;
>> +	if (bus_max > 255)
>> +		bus_max = 255;
>
> I still don't understand the need for this part. If the cfg space is bigger
> than bus_max, isn't that simply an invalid resource? Given that the resource
> could be broken in other ways too, this check feels more like a specific
> workaround rather than generally useful code.

Imagine...

   bus-range [0x80 .. 0xff], this requires a cfg.res that will cover the 
entire range of 0..0xff.

   according to the calculations above, (resource_size(&pci->cfg.res) >> 
pci->cfg.ops.bus_shift) - 1 will have a value of 0xff, so...

   bus_max = 0x80 + 0xff -> OVERFLOW of u8!

That is not useful.  bus_max should represent the largest bus number 
that can be covered by cfg.res.  That is what my patch is attempting to 
accomplish.  Calculate the largest bus number that can be accommodated 
by cfg.res, and then clamp it to 0xff.

David Daney.
Arnd Bergmann Sept. 23, 2015, 7:27 p.m. UTC | #3
On Wednesday 23 September 2015 11:21:56 David Daney wrote:
> >>
> >>      /* Limit the bus-range to fit within reg */
> >> -    bus_max = pci->cfg.bus_range->start +
> >> -              (resource_size(&pci->cfg.res) >> pci->cfg.ops.bus_shift) - 1;
> >> +    bus_max = (resource_size(&pci->cfg.res) >> pci->cfg.ops.bus_shift) - 1;
> >> +    if (bus_max > 255)
> >> +            bus_max = 255;
> >
> > I still don't understand the need for this part. If the cfg space is bigger
> > than bus_max, isn't that simply an invalid resource? Given that the resource
> > could be broken in other ways too, this check feels more like a specific
> > workaround rather than generally useful code.
> 
> Imagine...
> 
>    bus-range [0x80 .. 0xff], this requires a cfg.res that will cover the 
> entire range of 0..0xff.
> 
>    according to the calculations above, (resource_size(&pci->cfg.res) >> 
> pci->cfg.ops.bus_shift) - 1 will have a value of 0xff, so...

Extending the computation to 32 bit seems fine, but I'd rather warn loudly
if the bus range does not fit within the registers.

Also note that the computation is already correct with my interpretation
of the reg property.

	Arnd
Will Deacon Sept. 23, 2015, 7:33 p.m. UTC | #4
On Wed, Sep 23, 2015 at 07:21:56PM +0100, David Daney wrote:
> On 09/23/2015 11:01 AM, Will Deacon wrote:
> > On Thu, Sep 17, 2015 at 11:02:11PM +0100, David Daney wrote:
> [...]
> >
> >>   Properties of the /chosen node:
> >> diff --git a/drivers/pci/host/pci-host-generic.c b/drivers/pci/host/pci-host-generic.c
> >> index 77cf4bd..0a9c453 100644
> >> --- a/drivers/pci/host/pci-host-generic.c
> >> +++ b/drivers/pci/host/pci-host-generic.c
> >> @@ -164,7 +164,7 @@ out_release_res:
> >>   static int gen_pci_parse_map_cfg_windows(struct gen_pci *pci)
> >>   {
> >>   	int err;
> >> -	u8 bus_max;
> >> +	int bus_max;
> >>   	resource_size_t busn;
> >>   	struct resource *bus_range;
> >>   	struct device *dev = pci->host.dev.parent;
> >> @@ -177,8 +177,9 @@ static int gen_pci_parse_map_cfg_windows(struct gen_pci *pci)
> >>   	}
> >>
> >>   	/* Limit the bus-range to fit within reg */
> >> -	bus_max = pci->cfg.bus_range->start +
> >> -		  (resource_size(&pci->cfg.res) >> pci->cfg.ops.bus_shift) - 1;
> >> +	bus_max = (resource_size(&pci->cfg.res) >> pci->cfg.ops.bus_shift) - 1;
> >> +	if (bus_max > 255)
> >> +		bus_max = 255;
> >
> > I still don't understand the need for this part. If the cfg space is bigger
> > than bus_max, isn't that simply an invalid resource? Given that the resource
> > could be broken in other ways too, this check feels more like a specific
> > workaround rather than generally useful code.
> 
> Imagine...
> 
>    bus-range [0x80 .. 0xff], this requires a cfg.res that will cover the 
> entire range of 0..0xff.
> 
>    according to the calculations above, (resource_size(&pci->cfg.res) >> 
> pci->cfg.ops.bus_shift) - 1 will have a value of 0xff, so...
> 
>    bus_max = 0x80 + 0xff -> OVERFLOW of u8!
> 
> That is not useful.  bus_max should represent the largest bus number 
> that can be covered by cfg.res.  That is what my patch is attempting to 
> accomplish.  Calculate the largest bus number that can be accommodated 
> by cfg.res, and then clamp it to 0xff.

Sorry, I should've been more specific. The only part I don't like is the
'if (bus_max > 255)' check.

Will
Will Deacon Sept. 23, 2015, 7:35 p.m. UTC | #5
On Wed, Sep 23, 2015 at 08:27:41PM +0100, Arnd Bergmann wrote:
> On Wednesday 23 September 2015 11:21:56 David Daney wrote:
> > >>
> > >>      /* Limit the bus-range to fit within reg */
> > >> -    bus_max = pci->cfg.bus_range->start +
> > >> -              (resource_size(&pci->cfg.res) >> pci->cfg.ops.bus_shift) - 1;
> > >> +    bus_max = (resource_size(&pci->cfg.res) >> pci->cfg.ops.bus_shift) - 1;
> > >> +    if (bus_max > 255)
> > >> +            bus_max = 255;
> > >
> > > I still don't understand the need for this part. If the cfg space is bigger
> > > than bus_max, isn't that simply an invalid resource? Given that the resource
> > > could be broken in other ways too, this check feels more like a specific
> > > workaround rather than generally useful code.
> > 
> > Imagine...
> > 
> >    bus-range [0x80 .. 0xff], this requires a cfg.res that will cover the 
> > entire range of 0..0xff.
> > 
> >    according to the calculations above, (resource_size(&pci->cfg.res) >> 
> > pci->cfg.ops.bus_shift) - 1 will have a value of 0xff, so...
> 
> Extending the computation to 32 bit seems fine, but I'd rather warn loudly
> if the bus range does not fit within the registers.
> 
> Also note that the computation is already correct with my interpretation
> of the reg property.

From what Lorenzo was saying, ACPI shares the interpretation that David is
implementing here and, given that the DT version seems to be subjective,
aligning this reg property with MMCFG seems to make sense.

Will
Arnd Bergmann Sept. 23, 2015, 7:39 p.m. UTC | #6
On Wednesday 23 September 2015 20:35:45 Will Deacon wrote:
> On Wed, Sep 23, 2015 at 08:27:41PM +0100, Arnd Bergmann wrote:
> > On Wednesday 23 September 2015 11:21:56 David Daney wrote:
> > > >>
> > > >>      /* Limit the bus-range to fit within reg */
> > > >> -    bus_max = pci->cfg.bus_range->start +
> > > >> -              (resource_size(&pci->cfg.res) >> pci->cfg.ops.bus_shift) - 1;
> > > >> +    bus_max = (resource_size(&pci->cfg.res) >> pci->cfg.ops.bus_shift) - 1;
> > > >> +    if (bus_max > 255)
> > > >> +            bus_max = 255;
> > > >
> > > > I still don't understand the need for this part. If the cfg space is bigger
> > > > than bus_max, isn't that simply an invalid resource? Given that the resource
> > > > could be broken in other ways too, this check feels more like a specific
> > > > workaround rather than generally useful code.
> > > 
> > > Imagine...
> > > 
> > >    bus-range [0x80 .. 0xff], this requires a cfg.res that will cover the 
> > > entire range of 0..0xff.
> > > 
> > >    according to the calculations above, (resource_size(&pci->cfg.res) >> 
> > > pci->cfg.ops.bus_shift) - 1 will have a value of 0xff, so...
> > 
> > Extending the computation to 32 bit seems fine, but I'd rather warn loudly
> > if the bus range does not fit within the registers.
> > 
> > Also note that the computation is already correct with my interpretation
> > of the reg property.
> 
> From what Lorenzo was saying, ACPI shares the interpretation that David is
> implementing here and, given that the DT version seems to be subjective,
> aligning this reg property with MMCFG seems to make sense.

We should then make it very clear in the binding that the driver
is not allowed to actually map the registers for the buses outside
of the bus-range, as that is highly unusual.

We would also need a special exception for this if we ever get to
implement the DT source checker that we have been talking about for
years, as the reg property might then overlap with a property from
another device.

	Arnd
Will Deacon Sept. 23, 2015, 7:47 p.m. UTC | #7
On Wed, Sep 23, 2015 at 08:39:27PM +0100, Arnd Bergmann wrote:
> On Wednesday 23 September 2015 20:35:45 Will Deacon wrote:
> > On Wed, Sep 23, 2015 at 08:27:41PM +0100, Arnd Bergmann wrote:
> > > On Wednesday 23 September 2015 11:21:56 David Daney wrote:
> > > > >>
> > > > >>      /* Limit the bus-range to fit within reg */
> > > > >> -    bus_max = pci->cfg.bus_range->start +
> > > > >> -              (resource_size(&pci->cfg.res) >> pci->cfg.ops.bus_shift) - 1;
> > > > >> +    bus_max = (resource_size(&pci->cfg.res) >> pci->cfg.ops.bus_shift) - 1;
> > > > >> +    if (bus_max > 255)
> > > > >> +            bus_max = 255;
> > > > >
> > > > > I still don't understand the need for this part. If the cfg space is bigger
> > > > > than bus_max, isn't that simply an invalid resource? Given that the resource
> > > > > could be broken in other ways too, this check feels more like a specific
> > > > > workaround rather than generally useful code.
> > > > 
> > > > Imagine...
> > > > 
> > > >    bus-range [0x80 .. 0xff], this requires a cfg.res that will cover the 
> > > > entire range of 0..0xff.
> > > > 
> > > >    according to the calculations above, (resource_size(&pci->cfg.res) >> 
> > > > pci->cfg.ops.bus_shift) - 1 will have a value of 0xff, so...
> > > 
> > > Extending the computation to 32 bit seems fine, but I'd rather warn loudly
> > > if the bus range does not fit within the registers.
> > > 
> > > Also note that the computation is already correct with my interpretation
> > > of the reg property.
> > 
> > From what Lorenzo was saying, ACPI shares the interpretation that David is
> > implementing here and, given that the DT version seems to be subjective,
> > aligning this reg property with MMCFG seems to make sense.
> 
> We should then make it very clear in the binding that the driver
> is not allowed to actually map the registers for the buses outside
> of the bus-range, as that is highly unusual.
> 
> We would also need a special exception for this if we ever get to
> implement the DT source checker that we have been talking about for
> years, as the reg property might then overlap with a property from
> another device.

Completely agreed. Having a base that isn't actually safe to map is horrible
and should be called out.

Will
Arnd Bergmann Sept. 23, 2015, 8:45 p.m. UTC | #8
On Wednesday 23 September 2015 21:39:27 Arnd Bergmann wrote:
> On Wednesday 23 September 2015 20:35:45 Will Deacon wrote:
> > 
> > From what Lorenzo was saying, ACPI shares the interpretation that David is
> > implementing here and, given that the DT version seems to be subjective,
> > aligning this reg property with MMCFG seems to make sense.
> 
> We should then make it very clear in the binding that the driver
> is not allowed to actually map the registers for the buses outside
> of the bus-range, as that is highly unusual.

One more point here: I think ACPI does something different here,
it lists the base address of mmconfig space of the the PCI domain
that the host bridge belongs to. In contrast, in DT the 'reg'
property is defined as the registers belonging to the host bridge
itself, and the DT does not say anything about domains at all
(aside from the linux,pci-domain property that is not mandatory
and a Linux-specific extension).

	Arnd
diff mbox

Patch

diff --git a/Documentation/devicetree/bindings/pci/host-generic-pci.txt b/Documentation/devicetree/bindings/pci/host-generic-pci.txt
index cf3e205..105a968 100644
--- a/Documentation/devicetree/bindings/pci/host-generic-pci.txt
+++ b/Documentation/devicetree/bindings/pci/host-generic-pci.txt
@@ -34,7 +34,9 @@  Properties of the host controller node:
 - #size-cells    : Must be 2.
 
 - reg            : The Configuration Space base address and size, as accessed
-                   from the parent bus.
+                   from the parent bus.  The base address corresponds to
+                   bus zero, even though the "bus-range" property may specify
+                   a different starting bus number.
 
 
 Properties of the /chosen node:
diff --git a/drivers/pci/host/pci-host-generic.c b/drivers/pci/host/pci-host-generic.c
index 77cf4bd..0a9c453 100644
--- a/drivers/pci/host/pci-host-generic.c
+++ b/drivers/pci/host/pci-host-generic.c
@@ -164,7 +164,7 @@  out_release_res:
 static int gen_pci_parse_map_cfg_windows(struct gen_pci *pci)
 {
 	int err;
-	u8 bus_max;
+	int bus_max;
 	resource_size_t busn;
 	struct resource *bus_range;
 	struct device *dev = pci->host.dev.parent;
@@ -177,8 +177,9 @@  static int gen_pci_parse_map_cfg_windows(struct gen_pci *pci)
 	}
 
 	/* Limit the bus-range to fit within reg */
-	bus_max = pci->cfg.bus_range->start +
-		  (resource_size(&pci->cfg.res) >> pci->cfg.ops.bus_shift) - 1;
+	bus_max = (resource_size(&pci->cfg.res) >> pci->cfg.ops.bus_shift) - 1;
+	if (bus_max > 255)
+		bus_max = 255;
 	pci->cfg.bus_range->end = min_t(resource_size_t,
 					pci->cfg.bus_range->end, bus_max);