Re: [PATCH v5] PCI/ACS: Enable PCI_ACS_TB and disable only when needed for ATS

2020-09-16 Thread Bjorn Helgaas
On Tue, Jul 14, 2020 at 01:15:40PM -0700, Rajat Jain wrote:
> The ACS "Translation Blocking" bit blocks the translated addresses from
> the devices. We don't expect such traffic from devices unless ATS is
> enabled on them. A device sending such traffic without ATS enabled,
> indicates malicious intent, and thus should be blocked.
> 
> Enable PCI_ACS_TB by default for all devices, and it stays enabled until
> atleast one of the devices downstream wants to enable ATS. It gets
> disabled to enable ATS on a device downstream it, and then gets enabled
> back on once all the downstream devices don't need ATS.
> 
> Signed-off-by: Rajat Jain 

I applied v4 of this patch instead because I think the complexity of
this one, where we have to walk up the tree and disable TB in upstream
bridges, is too high.  It's always tricky to modify the state of
device Y when we're doing something for device X.

> ---
> Note that I'm ignoring the devices that require quirks to enable or
> disable ACS, instead of using the standard way for ACS configuration.
> The reason is that it would require adding yet another quirk table or
> quirk function pointer, that I don't know how to implement for those
> devices, and will neither have the devices to test that code.
> 
> v5: Enable TB and disable ATS for all devices on boot. Disable TB later
> only if needed to enable ATS on downstream devices.
> v4: Add braces to avoid warning from kernel robot
> print warning for only external-facing devices.
> v3: print warning if ACS_TB not supported on external-facing/untrusted ports.
> Minor code comments fixes.
> v2: Commit log change
> 
>  drivers/pci/ats.c   |  5 
>  drivers/pci/pci.c   | 57 +
>  drivers/pci/pci.h   |  2 ++
>  drivers/pci/probe.c |  2 +-
>  include/linux/pci.h |  2 ++
>  5 files changed, 67 insertions(+), 1 deletion(-)
> 
> diff --git a/drivers/pci/ats.c b/drivers/pci/ats.c
> index b761c1f72f67..e2ea9083f30f 100644
> --- a/drivers/pci/ats.c
> +++ b/drivers/pci/ats.c
> @@ -28,6 +28,9 @@ void pci_ats_init(struct pci_dev *dev)
>   return;
>  
>   dev->ats_cap = pos;
> +
> + dev->ats_enabled = 1; /* To avoid WARN_ON from pci_disable_ats() */
> + pci_disable_ats(dev);
>  }
>  
>  /**
> @@ -82,6 +85,7 @@ int pci_enable_ats(struct pci_dev *dev, int ps)
>   }
>   pci_write_config_word(dev, dev->ats_cap + PCI_ATS_CTRL, ctrl);
>  
> + pci_disable_acs_trans_blocking(dev);
>   dev->ats_enabled = 1;
>   return 0;
>  }
> @@ -102,6 +106,7 @@ void pci_disable_ats(struct pci_dev *dev)
>   ctrl &= ~PCI_ATS_CTRL_ENABLE;
>   pci_write_config_word(dev, dev->ats_cap + PCI_ATS_CTRL, ctrl);
>  
> + pci_enable_acs_trans_blocking(dev);
>   dev->ats_enabled = 0;
>  }
>  EXPORT_SYMBOL_GPL(pci_disable_ats);
> diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c
> index 73a862782214..614e3c1e8c56 100644
> --- a/drivers/pci/pci.c
> +++ b/drivers/pci/pci.c
> @@ -876,6 +876,9 @@ static void pci_std_enable_acs(struct pci_dev *dev)
>   /* Upstream Forwarding */
>   ctrl |= (cap & PCI_ACS_UF);
>  
> + /* Translation Blocking */
> + ctrl |= (cap & PCI_ACS_TB);
> +
>   pci_write_config_word(dev, pos + PCI_ACS_CTRL, ctrl);
>  }
>  
> @@ -904,6 +907,60 @@ static void pci_enable_acs(struct pci_dev *dev)
>   pci_disable_acs_redir(dev);
>  }
>  
> +void pci_disable_acs_trans_blocking(struct pci_dev *pdev)
> +{
> + u16 cap, ctrl, pos;
> + struct pci_dev *dev;
> +
> + if (!pci_acs_enable)
> + return;
> +
> + for (dev = pdev; dev; dev = pci_upstream_bridge(pdev)) {
> +
> + pos = dev->acs_cap;
> + if (!pos)
> + continue;
> +
> + /*
> +  * Disable translation blocking when first downstream
> +  * device that needs it (for ATS) wants to enable ATS
> +  */
> + if (++dev->ats_dependencies == 1) {
> + pci_read_config_word(dev, pos + PCI_ACS_CAP, );
> + pci_read_config_word(dev, pos + PCI_ACS_CTRL, );
> + ctrl &= ~(cap & PCI_ACS_TB);
> + pci_write_config_word(dev, pos + PCI_ACS_CTRL, ctrl);
> + }
> + }
> +}
> +
> +void pci_enable_acs_trans_blocking(struct pci_dev *pdev)
> +{
> + u16 cap, ctrl, pos;
> + struct pci_dev *dev;
> +
> + if (!pci_acs_enable)
> + return;
> +
> + for (dev = pdev; dev; dev = pci_upstream_bridge(pdev)) {
> +
> + pos = dev->acs_cap;
> + if (!pos)
> + continue;
> +
> + /*
> +  * Enable translation blocking when last downstream device
> +  * that depends on it (for ATS), doesn't need ATS anymore
> +  */
> + if (--dev->ats_dependencies == 0) {
> + pci_read_config_word(dev, pos + PCI_ACS_CAP, );
> + pci_read_config_word(dev, pos 

Re: [PATCH v5] PCI/ACS: Enable PCI_ACS_TB and disable only when needed for ATS

2020-09-10 Thread Rajat Jain
Hello Bjorn,


On Mon, Aug 17, 2020 at 3:50 PM Rajat Jain  wrote:
>
> Hello Bjorn,
>
>
> On Sat, Aug 1, 2020 at 5:30 PM Rajat Jain  wrote:
> >
> > Hi Bjorn,
> >
> >
> > On Tue, Jul 14, 2020 at 1:24 PM Rajat Jain  wrote:
> > >
> > > On Tue, Jul 14, 2020 at 1:15 PM Rajat Jain  wrote:
> > > >
> > > > The ACS "Translation Blocking" bit blocks the translated addresses from
> > > > the devices. We don't expect such traffic from devices unless ATS is
> > > > enabled on them. A device sending such traffic without ATS enabled,
> > > > indicates malicious intent, and thus should be blocked.
> > > >
> > > > Enable PCI_ACS_TB by default for all devices, and it stays enabled until
> > > > atleast one of the devices downstream wants to enable ATS. It gets
> > > > disabled to enable ATS on a device downstream it, and then gets enabled
> > > > back on once all the downstream devices don't need ATS.
> > > >
> > > > Signed-off-by: Rajat Jain 
> >
> > Just checking to see if you got a chance to look at this V5 patch.
>
> Any feedback on this patch?

Gentle reminder?

Thanks & Best Regards,

Rajat


>
> Thanks & Best Regards,
>
> Rajat
>
> >
> > Thanks & Best Regards,
> >
> > Rajat
> >
> > > > ---
> > > > Note that I'm ignoring the devices that require quirks to enable or
> > > > disable ACS, instead of using the standard way for ACS configuration.
> > > > The reason is that it would require adding yet another quirk table or
> > > > quirk function pointer, that I don't know how to implement for those
> > > > devices, and will neither have the devices to test that code.
> > > >
> > > > v5: Enable TB and disable ATS for all devices on boot. Disable TB later
> > > > only if needed to enable ATS on downstream devices.
> > > > v4: Add braces to avoid warning from kernel robot
> > > > print warning for only external-facing devices.
> > > > v3: print warning if ACS_TB not supported on external-facing/untrusted 
> > > > ports.
> > > > Minor code comments fixes.
> > > > v2: Commit log change
> > > >
> > > >  drivers/pci/ats.c   |  5 
> > > >  drivers/pci/pci.c   | 57 +
> > > >  drivers/pci/pci.h   |  2 ++
> > > >  drivers/pci/probe.c |  2 +-
> > > >  include/linux/pci.h |  2 ++
> > > >  5 files changed, 67 insertions(+), 1 deletion(-)
> > > >
> > > > diff --git a/drivers/pci/ats.c b/drivers/pci/ats.c
> > > > index b761c1f72f67..e2ea9083f30f 100644
> > > > --- a/drivers/pci/ats.c
> > > > +++ b/drivers/pci/ats.c
> > > > @@ -28,6 +28,9 @@ void pci_ats_init(struct pci_dev *dev)
> > > > return;
> > > >
> > > > dev->ats_cap = pos;
> > > > +
> > > > +   dev->ats_enabled = 1; /* To avoid WARN_ON from 
> > > > pci_disable_ats() */
> > > > +   pci_disable_ats(dev);
> > > >  }
> > > >
> > > >  /**
> > > > @@ -82,6 +85,7 @@ int pci_enable_ats(struct pci_dev *dev, int ps)
> > > > }
> > > > pci_write_config_word(dev, dev->ats_cap + PCI_ATS_CTRL, ctrl);
> > > >
> > > > +   pci_disable_acs_trans_blocking(dev);
> > > > dev->ats_enabled = 1;
> > > > return 0;
> > > >  }
> > > > @@ -102,6 +106,7 @@ void pci_disable_ats(struct pci_dev *dev)
> > > > ctrl &= ~PCI_ATS_CTRL_ENABLE;
> > > > pci_write_config_word(dev, dev->ats_cap + PCI_ATS_CTRL, ctrl);
> > > >
> > > > +   pci_enable_acs_trans_blocking(dev);
> > > > dev->ats_enabled = 0;
> > > >  }
> > > >  EXPORT_SYMBOL_GPL(pci_disable_ats);
> > > > diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c
> > > > index 73a862782214..614e3c1e8c56 100644
> > > > --- a/drivers/pci/pci.c
> > > > +++ b/drivers/pci/pci.c
> > > > @@ -876,6 +876,9 @@ static void pci_std_enable_acs(struct pci_dev *dev)
> > > > /* Upstream Forwarding */
> > > > ctrl |= (cap & PCI_ACS_UF);
> > > >
> > > > +   /* Translation Blocking */
> > > > +   ctrl |= (cap & PCI_ACS_TB);
> > > > +
> > > > pci_write_config_word(dev, pos + PCI_ACS_CTRL, ctrl);
> > > >  }
> > > >
> > > > @@ -904,6 +907,60 @@ static void pci_enable_acs(struct pci_dev *dev)
> > > > pci_disable_acs_redir(dev);
> > > >  }
> > > >
> > > > +void pci_disable_acs_trans_blocking(struct pci_dev *pdev)
> > > > +{
> > > > +   u16 cap, ctrl, pos;
> > > > +   struct pci_dev *dev;
> > > > +
> > > > +   if (!pci_acs_enable)
> > > > +   return;
> > > > +
> > > > +   for (dev = pdev; dev; dev = pci_upstream_bridge(pdev)) {
> > > > +
> > > > +   pos = dev->acs_cap;
> > > > +   if (!pos)
> > > > +   continue;
> > > > +
> > > > +   /*
> > > > +* Disable translation blocking when first downstream
> > > > +* device that needs it (for ATS) wants to enable ATS
> > > > +*/
> > > > +   if (++dev->ats_dependencies == 1) {
> > >
> > > I am a little worried about a potential race condition here. I know
> > > that 2 PCI devices cannot be enumerating at the same time. Do 

Re: [PATCH v5] PCI/ACS: Enable PCI_ACS_TB and disable only when needed for ATS

2020-08-17 Thread Rajat Jain
Hello Bjorn,


On Sat, Aug 1, 2020 at 5:30 PM Rajat Jain  wrote:
>
> Hi Bjorn,
>
>
> On Tue, Jul 14, 2020 at 1:24 PM Rajat Jain  wrote:
> >
> > On Tue, Jul 14, 2020 at 1:15 PM Rajat Jain  wrote:
> > >
> > > The ACS "Translation Blocking" bit blocks the translated addresses from
> > > the devices. We don't expect such traffic from devices unless ATS is
> > > enabled on them. A device sending such traffic without ATS enabled,
> > > indicates malicious intent, and thus should be blocked.
> > >
> > > Enable PCI_ACS_TB by default for all devices, and it stays enabled until
> > > atleast one of the devices downstream wants to enable ATS. It gets
> > > disabled to enable ATS on a device downstream it, and then gets enabled
> > > back on once all the downstream devices don't need ATS.
> > >
> > > Signed-off-by: Rajat Jain 
>
> Just checking to see if you got a chance to look at this V5 patch.

Any feedback on this patch?

Thanks & Best Regards,

Rajat

>
> Thanks & Best Regards,
>
> Rajat
>
> > > ---
> > > Note that I'm ignoring the devices that require quirks to enable or
> > > disable ACS, instead of using the standard way for ACS configuration.
> > > The reason is that it would require adding yet another quirk table or
> > > quirk function pointer, that I don't know how to implement for those
> > > devices, and will neither have the devices to test that code.
> > >
> > > v5: Enable TB and disable ATS for all devices on boot. Disable TB later
> > > only if needed to enable ATS on downstream devices.
> > > v4: Add braces to avoid warning from kernel robot
> > > print warning for only external-facing devices.
> > > v3: print warning if ACS_TB not supported on external-facing/untrusted 
> > > ports.
> > > Minor code comments fixes.
> > > v2: Commit log change
> > >
> > >  drivers/pci/ats.c   |  5 
> > >  drivers/pci/pci.c   | 57 +
> > >  drivers/pci/pci.h   |  2 ++
> > >  drivers/pci/probe.c |  2 +-
> > >  include/linux/pci.h |  2 ++
> > >  5 files changed, 67 insertions(+), 1 deletion(-)
> > >
> > > diff --git a/drivers/pci/ats.c b/drivers/pci/ats.c
> > > index b761c1f72f67..e2ea9083f30f 100644
> > > --- a/drivers/pci/ats.c
> > > +++ b/drivers/pci/ats.c
> > > @@ -28,6 +28,9 @@ void pci_ats_init(struct pci_dev *dev)
> > > return;
> > >
> > > dev->ats_cap = pos;
> > > +
> > > +   dev->ats_enabled = 1; /* To avoid WARN_ON from pci_disable_ats() 
> > > */
> > > +   pci_disable_ats(dev);
> > >  }
> > >
> > >  /**
> > > @@ -82,6 +85,7 @@ int pci_enable_ats(struct pci_dev *dev, int ps)
> > > }
> > > pci_write_config_word(dev, dev->ats_cap + PCI_ATS_CTRL, ctrl);
> > >
> > > +   pci_disable_acs_trans_blocking(dev);
> > > dev->ats_enabled = 1;
> > > return 0;
> > >  }
> > > @@ -102,6 +106,7 @@ void pci_disable_ats(struct pci_dev *dev)
> > > ctrl &= ~PCI_ATS_CTRL_ENABLE;
> > > pci_write_config_word(dev, dev->ats_cap + PCI_ATS_CTRL, ctrl);
> > >
> > > +   pci_enable_acs_trans_blocking(dev);
> > > dev->ats_enabled = 0;
> > >  }
> > >  EXPORT_SYMBOL_GPL(pci_disable_ats);
> > > diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c
> > > index 73a862782214..614e3c1e8c56 100644
> > > --- a/drivers/pci/pci.c
> > > +++ b/drivers/pci/pci.c
> > > @@ -876,6 +876,9 @@ static void pci_std_enable_acs(struct pci_dev *dev)
> > > /* Upstream Forwarding */
> > > ctrl |= (cap & PCI_ACS_UF);
> > >
> > > +   /* Translation Blocking */
> > > +   ctrl |= (cap & PCI_ACS_TB);
> > > +
> > > pci_write_config_word(dev, pos + PCI_ACS_CTRL, ctrl);
> > >  }
> > >
> > > @@ -904,6 +907,60 @@ static void pci_enable_acs(struct pci_dev *dev)
> > > pci_disable_acs_redir(dev);
> > >  }
> > >
> > > +void pci_disable_acs_trans_blocking(struct pci_dev *pdev)
> > > +{
> > > +   u16 cap, ctrl, pos;
> > > +   struct pci_dev *dev;
> > > +
> > > +   if (!pci_acs_enable)
> > > +   return;
> > > +
> > > +   for (dev = pdev; dev; dev = pci_upstream_bridge(pdev)) {
> > > +
> > > +   pos = dev->acs_cap;
> > > +   if (!pos)
> > > +   continue;
> > > +
> > > +   /*
> > > +* Disable translation blocking when first downstream
> > > +* device that needs it (for ATS) wants to enable ATS
> > > +*/
> > > +   if (++dev->ats_dependencies == 1) {
> >
> > I am a little worried about a potential race condition here. I know
> > that 2 PCI devices cannot be enumerating at the same time. Do we know
> > if multiple pci_enable_ats() and pci_disable_ats() function calls can
> > be simultaneously executing (even for different devices)? If so, we
> > may need an atomic_t variable for ats_dependencies.
> >
> > Thanks,
> >
> > Rajat
> >
> >
> > > +   pci_read_config_word(dev, pos + PCI_ACS_CAP, 
> > > );
> > > +   

Re: [PATCH v5] PCI/ACS: Enable PCI_ACS_TB and disable only when needed for ATS

2020-08-01 Thread Rajat Jain
Hi Bjorn,


On Tue, Jul 14, 2020 at 1:24 PM Rajat Jain  wrote:
>
> On Tue, Jul 14, 2020 at 1:15 PM Rajat Jain  wrote:
> >
> > The ACS "Translation Blocking" bit blocks the translated addresses from
> > the devices. We don't expect such traffic from devices unless ATS is
> > enabled on them. A device sending such traffic without ATS enabled,
> > indicates malicious intent, and thus should be blocked.
> >
> > Enable PCI_ACS_TB by default for all devices, and it stays enabled until
> > atleast one of the devices downstream wants to enable ATS. It gets
> > disabled to enable ATS on a device downstream it, and then gets enabled
> > back on once all the downstream devices don't need ATS.
> >
> > Signed-off-by: Rajat Jain 

Just checking to see if you got a chance to look at this V5 patch.

Thanks & Best Regards,

Rajat

> > ---
> > Note that I'm ignoring the devices that require quirks to enable or
> > disable ACS, instead of using the standard way for ACS configuration.
> > The reason is that it would require adding yet another quirk table or
> > quirk function pointer, that I don't know how to implement for those
> > devices, and will neither have the devices to test that code.
> >
> > v5: Enable TB and disable ATS for all devices on boot. Disable TB later
> > only if needed to enable ATS on downstream devices.
> > v4: Add braces to avoid warning from kernel robot
> > print warning for only external-facing devices.
> > v3: print warning if ACS_TB not supported on external-facing/untrusted 
> > ports.
> > Minor code comments fixes.
> > v2: Commit log change
> >
> >  drivers/pci/ats.c   |  5 
> >  drivers/pci/pci.c   | 57 +
> >  drivers/pci/pci.h   |  2 ++
> >  drivers/pci/probe.c |  2 +-
> >  include/linux/pci.h |  2 ++
> >  5 files changed, 67 insertions(+), 1 deletion(-)
> >
> > diff --git a/drivers/pci/ats.c b/drivers/pci/ats.c
> > index b761c1f72f67..e2ea9083f30f 100644
> > --- a/drivers/pci/ats.c
> > +++ b/drivers/pci/ats.c
> > @@ -28,6 +28,9 @@ void pci_ats_init(struct pci_dev *dev)
> > return;
> >
> > dev->ats_cap = pos;
> > +
> > +   dev->ats_enabled = 1; /* To avoid WARN_ON from pci_disable_ats() */
> > +   pci_disable_ats(dev);
> >  }
> >
> >  /**
> > @@ -82,6 +85,7 @@ int pci_enable_ats(struct pci_dev *dev, int ps)
> > }
> > pci_write_config_word(dev, dev->ats_cap + PCI_ATS_CTRL, ctrl);
> >
> > +   pci_disable_acs_trans_blocking(dev);
> > dev->ats_enabled = 1;
> > return 0;
> >  }
> > @@ -102,6 +106,7 @@ void pci_disable_ats(struct pci_dev *dev)
> > ctrl &= ~PCI_ATS_CTRL_ENABLE;
> > pci_write_config_word(dev, dev->ats_cap + PCI_ATS_CTRL, ctrl);
> >
> > +   pci_enable_acs_trans_blocking(dev);
> > dev->ats_enabled = 0;
> >  }
> >  EXPORT_SYMBOL_GPL(pci_disable_ats);
> > diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c
> > index 73a862782214..614e3c1e8c56 100644
> > --- a/drivers/pci/pci.c
> > +++ b/drivers/pci/pci.c
> > @@ -876,6 +876,9 @@ static void pci_std_enable_acs(struct pci_dev *dev)
> > /* Upstream Forwarding */
> > ctrl |= (cap & PCI_ACS_UF);
> >
> > +   /* Translation Blocking */
> > +   ctrl |= (cap & PCI_ACS_TB);
> > +
> > pci_write_config_word(dev, pos + PCI_ACS_CTRL, ctrl);
> >  }
> >
> > @@ -904,6 +907,60 @@ static void pci_enable_acs(struct pci_dev *dev)
> > pci_disable_acs_redir(dev);
> >  }
> >
> > +void pci_disable_acs_trans_blocking(struct pci_dev *pdev)
> > +{
> > +   u16 cap, ctrl, pos;
> > +   struct pci_dev *dev;
> > +
> > +   if (!pci_acs_enable)
> > +   return;
> > +
> > +   for (dev = pdev; dev; dev = pci_upstream_bridge(pdev)) {
> > +
> > +   pos = dev->acs_cap;
> > +   if (!pos)
> > +   continue;
> > +
> > +   /*
> > +* Disable translation blocking when first downstream
> > +* device that needs it (for ATS) wants to enable ATS
> > +*/
> > +   if (++dev->ats_dependencies == 1) {
>
> I am a little worried about a potential race condition here. I know
> that 2 PCI devices cannot be enumerating at the same time. Do we know
> if multiple pci_enable_ats() and pci_disable_ats() function calls can
> be simultaneously executing (even for different devices)? If so, we
> may need an atomic_t variable for ats_dependencies.
>
> Thanks,
>
> Rajat
>
>
> > +   pci_read_config_word(dev, pos + PCI_ACS_CAP, );
> > +   pci_read_config_word(dev, pos + PCI_ACS_CTRL, 
> > );
> > +   ctrl &= ~(cap & PCI_ACS_TB);
> > +   pci_write_config_word(dev, pos + PCI_ACS_CTRL, 
> > ctrl);
> > +   }
> > +   }
> > +}
> > +
> > +void pci_enable_acs_trans_blocking(struct pci_dev *pdev)
> > +{
> > +   u16 cap, ctrl, pos;
> > +   struct pci_dev *dev;
> > +
> > +   if 

Re: [PATCH v5] PCI/ACS: Enable PCI_ACS_TB and disable only when needed for ATS

2020-07-14 Thread Rajat Jain
On Tue, Jul 14, 2020 at 1:15 PM Rajat Jain  wrote:
>
> The ACS "Translation Blocking" bit blocks the translated addresses from
> the devices. We don't expect such traffic from devices unless ATS is
> enabled on them. A device sending such traffic without ATS enabled,
> indicates malicious intent, and thus should be blocked.
>
> Enable PCI_ACS_TB by default for all devices, and it stays enabled until
> atleast one of the devices downstream wants to enable ATS. It gets
> disabled to enable ATS on a device downstream it, and then gets enabled
> back on once all the downstream devices don't need ATS.
>
> Signed-off-by: Rajat Jain 
> ---
> Note that I'm ignoring the devices that require quirks to enable or
> disable ACS, instead of using the standard way for ACS configuration.
> The reason is that it would require adding yet another quirk table or
> quirk function pointer, that I don't know how to implement for those
> devices, and will neither have the devices to test that code.
>
> v5: Enable TB and disable ATS for all devices on boot. Disable TB later
> only if needed to enable ATS on downstream devices.
> v4: Add braces to avoid warning from kernel robot
> print warning for only external-facing devices.
> v3: print warning if ACS_TB not supported on external-facing/untrusted ports.
> Minor code comments fixes.
> v2: Commit log change
>
>  drivers/pci/ats.c   |  5 
>  drivers/pci/pci.c   | 57 +
>  drivers/pci/pci.h   |  2 ++
>  drivers/pci/probe.c |  2 +-
>  include/linux/pci.h |  2 ++
>  5 files changed, 67 insertions(+), 1 deletion(-)
>
> diff --git a/drivers/pci/ats.c b/drivers/pci/ats.c
> index b761c1f72f67..e2ea9083f30f 100644
> --- a/drivers/pci/ats.c
> +++ b/drivers/pci/ats.c
> @@ -28,6 +28,9 @@ void pci_ats_init(struct pci_dev *dev)
> return;
>
> dev->ats_cap = pos;
> +
> +   dev->ats_enabled = 1; /* To avoid WARN_ON from pci_disable_ats() */
> +   pci_disable_ats(dev);
>  }
>
>  /**
> @@ -82,6 +85,7 @@ int pci_enable_ats(struct pci_dev *dev, int ps)
> }
> pci_write_config_word(dev, dev->ats_cap + PCI_ATS_CTRL, ctrl);
>
> +   pci_disable_acs_trans_blocking(dev);
> dev->ats_enabled = 1;
> return 0;
>  }
> @@ -102,6 +106,7 @@ void pci_disable_ats(struct pci_dev *dev)
> ctrl &= ~PCI_ATS_CTRL_ENABLE;
> pci_write_config_word(dev, dev->ats_cap + PCI_ATS_CTRL, ctrl);
>
> +   pci_enable_acs_trans_blocking(dev);
> dev->ats_enabled = 0;
>  }
>  EXPORT_SYMBOL_GPL(pci_disable_ats);
> diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c
> index 73a862782214..614e3c1e8c56 100644
> --- a/drivers/pci/pci.c
> +++ b/drivers/pci/pci.c
> @@ -876,6 +876,9 @@ static void pci_std_enable_acs(struct pci_dev *dev)
> /* Upstream Forwarding */
> ctrl |= (cap & PCI_ACS_UF);
>
> +   /* Translation Blocking */
> +   ctrl |= (cap & PCI_ACS_TB);
> +
> pci_write_config_word(dev, pos + PCI_ACS_CTRL, ctrl);
>  }
>
> @@ -904,6 +907,60 @@ static void pci_enable_acs(struct pci_dev *dev)
> pci_disable_acs_redir(dev);
>  }
>
> +void pci_disable_acs_trans_blocking(struct pci_dev *pdev)
> +{
> +   u16 cap, ctrl, pos;
> +   struct pci_dev *dev;
> +
> +   if (!pci_acs_enable)
> +   return;
> +
> +   for (dev = pdev; dev; dev = pci_upstream_bridge(pdev)) {
> +
> +   pos = dev->acs_cap;
> +   if (!pos)
> +   continue;
> +
> +   /*
> +* Disable translation blocking when first downstream
> +* device that needs it (for ATS) wants to enable ATS
> +*/
> +   if (++dev->ats_dependencies == 1) {

I am a little worried about a potential race condition here. I know
that 2 PCI devices cannot be enumerating at the same time. Do we know
if multiple pci_enable_ats() and pci_disable_ats() function calls can
be simultaneously executing (even for different devices)? If so, we
may need an atomic_t variable for ats_dependencies.

Thanks,

Rajat


> +   pci_read_config_word(dev, pos + PCI_ACS_CAP, );
> +   pci_read_config_word(dev, pos + PCI_ACS_CTRL, );
> +   ctrl &= ~(cap & PCI_ACS_TB);
> +   pci_write_config_word(dev, pos + PCI_ACS_CTRL, ctrl);
> +   }
> +   }
> +}
> +
> +void pci_enable_acs_trans_blocking(struct pci_dev *pdev)
> +{
> +   u16 cap, ctrl, pos;
> +   struct pci_dev *dev;
> +
> +   if (!pci_acs_enable)
> +   return;
> +
> +   for (dev = pdev; dev; dev = pci_upstream_bridge(pdev)) {
> +
> +   pos = dev->acs_cap;
> +   if (!pos)
> +   continue;
> +
> +   /*
> +* Enable translation blocking when last downstream device
> +* that depends on it (for ATS), doesn't need ATS anymore
> +*/
> +

[PATCH v5] PCI/ACS: Enable PCI_ACS_TB and disable only when needed for ATS

2020-07-14 Thread Rajat Jain
The ACS "Translation Blocking" bit blocks the translated addresses from
the devices. We don't expect such traffic from devices unless ATS is
enabled on them. A device sending such traffic without ATS enabled,
indicates malicious intent, and thus should be blocked.

Enable PCI_ACS_TB by default for all devices, and it stays enabled until
atleast one of the devices downstream wants to enable ATS. It gets
disabled to enable ATS on a device downstream it, and then gets enabled
back on once all the downstream devices don't need ATS.

Signed-off-by: Rajat Jain 
---
Note that I'm ignoring the devices that require quirks to enable or
disable ACS, instead of using the standard way for ACS configuration.
The reason is that it would require adding yet another quirk table or
quirk function pointer, that I don't know how to implement for those
devices, and will neither have the devices to test that code.

v5: Enable TB and disable ATS for all devices on boot. Disable TB later
only if needed to enable ATS on downstream devices.
v4: Add braces to avoid warning from kernel robot
print warning for only external-facing devices.
v3: print warning if ACS_TB not supported on external-facing/untrusted ports.
Minor code comments fixes.
v2: Commit log change

 drivers/pci/ats.c   |  5 
 drivers/pci/pci.c   | 57 +
 drivers/pci/pci.h   |  2 ++
 drivers/pci/probe.c |  2 +-
 include/linux/pci.h |  2 ++
 5 files changed, 67 insertions(+), 1 deletion(-)

diff --git a/drivers/pci/ats.c b/drivers/pci/ats.c
index b761c1f72f67..e2ea9083f30f 100644
--- a/drivers/pci/ats.c
+++ b/drivers/pci/ats.c
@@ -28,6 +28,9 @@ void pci_ats_init(struct pci_dev *dev)
return;
 
dev->ats_cap = pos;
+
+   dev->ats_enabled = 1; /* To avoid WARN_ON from pci_disable_ats() */
+   pci_disable_ats(dev);
 }
 
 /**
@@ -82,6 +85,7 @@ int pci_enable_ats(struct pci_dev *dev, int ps)
}
pci_write_config_word(dev, dev->ats_cap + PCI_ATS_CTRL, ctrl);
 
+   pci_disable_acs_trans_blocking(dev);
dev->ats_enabled = 1;
return 0;
 }
@@ -102,6 +106,7 @@ void pci_disable_ats(struct pci_dev *dev)
ctrl &= ~PCI_ATS_CTRL_ENABLE;
pci_write_config_word(dev, dev->ats_cap + PCI_ATS_CTRL, ctrl);
 
+   pci_enable_acs_trans_blocking(dev);
dev->ats_enabled = 0;
 }
 EXPORT_SYMBOL_GPL(pci_disable_ats);
diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c
index 73a862782214..614e3c1e8c56 100644
--- a/drivers/pci/pci.c
+++ b/drivers/pci/pci.c
@@ -876,6 +876,9 @@ static void pci_std_enable_acs(struct pci_dev *dev)
/* Upstream Forwarding */
ctrl |= (cap & PCI_ACS_UF);
 
+   /* Translation Blocking */
+   ctrl |= (cap & PCI_ACS_TB);
+
pci_write_config_word(dev, pos + PCI_ACS_CTRL, ctrl);
 }
 
@@ -904,6 +907,60 @@ static void pci_enable_acs(struct pci_dev *dev)
pci_disable_acs_redir(dev);
 }
 
+void pci_disable_acs_trans_blocking(struct pci_dev *pdev)
+{
+   u16 cap, ctrl, pos;
+   struct pci_dev *dev;
+
+   if (!pci_acs_enable)
+   return;
+
+   for (dev = pdev; dev; dev = pci_upstream_bridge(pdev)) {
+
+   pos = dev->acs_cap;
+   if (!pos)
+   continue;
+
+   /*
+* Disable translation blocking when first downstream
+* device that needs it (for ATS) wants to enable ATS
+*/
+   if (++dev->ats_dependencies == 1) {
+   pci_read_config_word(dev, pos + PCI_ACS_CAP, );
+   pci_read_config_word(dev, pos + PCI_ACS_CTRL, );
+   ctrl &= ~(cap & PCI_ACS_TB);
+   pci_write_config_word(dev, pos + PCI_ACS_CTRL, ctrl);
+   }
+   }
+}
+
+void pci_enable_acs_trans_blocking(struct pci_dev *pdev)
+{
+   u16 cap, ctrl, pos;
+   struct pci_dev *dev;
+
+   if (!pci_acs_enable)
+   return;
+
+   for (dev = pdev; dev; dev = pci_upstream_bridge(pdev)) {
+
+   pos = dev->acs_cap;
+   if (!pos)
+   continue;
+
+   /*
+* Enable translation blocking when last downstream device
+* that depends on it (for ATS), doesn't need ATS anymore
+*/
+   if (--dev->ats_dependencies == 0) {
+   pci_read_config_word(dev, pos + PCI_ACS_CAP, );
+   pci_read_config_word(dev, pos + PCI_ACS_CTRL, );
+   ctrl |= (cap & PCI_ACS_TB);
+   pci_write_config_word(dev, pos + PCI_ACS_CTRL, ctrl);
+   }
+   }
+}
+
 /**
  * pci_restore_bars - restore a device's BAR values (e.g. after wake-up)
  * @dev: PCI device to have its BARs restored
diff --git a/drivers/pci/pci.h b/drivers/pci/pci.h
index 12fb79fbe29d..f5d8ecb6ba96 100644
--- a/drivers/pci/pci.h
+++ b/drivers/pci/pci.h
@@ -552,6 +552,8 @@