Re: [PATCH v5 1/2] PCI: ACPI: Support Microsoft's "DmaProperty"

From: Rajat Jain
Date: Thu Apr 14 2022 - 19:16:36 EST


Hello Bjorn,


On Thu, Apr 7, 2022 at 12:17 PM Bjorn Helgaas <helgaas@xxxxxxxxxx> wrote:
>
> In subject,
>
> PCI/ACPI: ...
>
> would be consistent with previous history (at least things coming
> through the PCI tree :)).

Will do.

>
> On Fri, Mar 25, 2022 at 11:46:08AM -0700, Rajat Jain wrote:
> > The "DmaProperty" is supported and documented by Microsoft here:
> > https://docs.microsoft.com/en-us/windows-hardware/drivers/pci/dsd-for-pcie-root-ports
>
> Here's a more specific link (could probably be referenced below to
> avoid cluttering the text here):
>
> https://docs.microsoft.com/en-us/windows-hardware/drivers/pci/dsd-for-pcie-root-ports#identifying-internal-pcie-ports-accessible-to-users-and-requiring-dma-protection

Will do.

>
> > They use this property for DMA protection:
> > https://docs.microsoft.com/en-us/windows/security/information-protection/kernel-dma-protection-for-thunderbolt
> >
> > Support the "DmaProperty" with the same semantics. This is useful for
> > internal PCI devices that do not hang off a PCIe rootport, but offer
> > an attack surface for DMA attacks (e.g. internal network devices).
>
> Same semantics as what?

Er, I meant the same semantics as the "DmaProperty". Please also see below.

>
> The MS description of "ExternalFacingPort" says:
>
> This ACPI object enables the operating system to identify externally
> exposed PCIe hierarchies, such as Thunderbolt.
>

No, my patch doesn't have to do with this one.

> and "DmaProperty" says:
>
> This ACPI object enables the operating system to identify internal
> PCIe hierarchies that are easily accessible by users (such as,
> Laptop M.2 PCIe slots accessible by way of a latch) and require
> protection by the OS Kernel DMA Protection mechanism.

Yes, this is the property that my patch uses. Microsoft has agreed to
update this documentation (in a sideband thread that I also copied you
on), with the updated semantics that this property can be used to
identify any PCI devices that require Kernel DMA protection. i.e. the
property is not restricted to identify "internal PCIe hierarchies"
(starting at root port), but to "any PCI device".

>
> I don't really understand why they called out "laptop M.2 PCIe slots"
> here. Is the idea that those are more accessible than a standard
> internal PCIe slot? Seems like a pretty small distinction to me.
>
> I can understand your example of internal network devices adding an
> attack surface. But I don't see how "DmaProperty" helps identify
> those. Wouldn't a NIC in a standard internal PCIe slot add the same
> attack surface?

Yes it would. The attack surface is the same. They probably only
thought of devices external to the SoC (starting from a root port)
when designing this property and thus called out internal M.2 PCI
slots. But nowhave realized that this could be opened to any PCI
device.

>
> > Signed-off-by: Rajat Jain <rajatja@xxxxxxxxxx>
> > Reviewed-by: Mika Westerberg <mika.westerberg@xxxxxxxxxxxxxxx>
> > ---
> > v5: * Reorder the patches in the series
> > v4: * Add the GUID.
> > * Update the comment and commitlog.
> > v3: * Use Microsoft's documented property "DmaProperty"
> > * Resctrict to ACPI only
> >
> > drivers/acpi/property.c | 3 +++
> > drivers/pci/pci-acpi.c | 16 ++++++++++++++++
> > 2 files changed, 19 insertions(+)
> >
> > diff --git a/drivers/acpi/property.c b/drivers/acpi/property.c
> > index d0986bda2964..20603cacc28d 100644
> > --- a/drivers/acpi/property.c
> > +++ b/drivers/acpi/property.c
> > @@ -48,6 +48,9 @@ static const guid_t prp_guids[] = {
> > /* Storage device needs D3 GUID: 5025030f-842f-4ab4-a561-99a5189762d0 */
> > GUID_INIT(0x5025030f, 0x842f, 0x4ab4,
> > 0xa5, 0x61, 0x99, 0xa5, 0x18, 0x97, 0x62, 0xd0),
> > + /* DmaProperty for PCI devices GUID: 70d24161-6dd5-4c9e-8070-705531292865 */
> > + GUID_INIT(0x70d24161, 0x6dd5, 0x4c9e,
> > + 0x80, 0x70, 0x70, 0x55, 0x31, 0x29, 0x28, 0x65),
> > };
> >
> > /* ACPI _DSD data subnodes GUID: dbb8e3e6-5886-4ba6-8795-1319f52a966b */
> > diff --git a/drivers/pci/pci-acpi.c b/drivers/pci/pci-acpi.c
> > index 1f15ab7eabf8..378e05096c52 100644
> > --- a/drivers/pci/pci-acpi.c
> > +++ b/drivers/pci/pci-acpi.c
> > @@ -1350,12 +1350,28 @@ static void pci_acpi_set_external_facing(struct pci_dev *dev)
> > dev->external_facing = 1;
> > }
> >
> > +static void pci_acpi_check_for_dma_protection(struct pci_dev *dev)
>
> I try to avoid function names like *_check_*() because they don't give
> any hint about whether there's a side effect or what direction things
> are going. I prefer things that return a value or make sense when
> used as a predicate. Maybe something like this?
>
> int pci_dev_has_dma_property(struct pci_dev *dev)
>
> dev->untrusted |= pci_dev_has_dma_property(pci_dev);
>

OK, will do.


> > +{
> > + u8 val;
> > +
> > + /*
> > + * Property also used by Microsoft Windows for same purpose,
> > + * (to implement DMA protection from a device, using the IOMMU).
> > + */
> > + if (device_property_read_u8(&dev->dev, "DmaProperty", &val))
>
> The MS web page says a _DSD with this property must be implemented in
> the Root Port device scope, but we don't enforce that here. We *do*
> enforce it in pci_acpi_set_untrusted(). Shouldn't we do the same
> here?

No, the whole point of doing this (please refer to the discussion on
the previous versions of this patch) was that we want to have a
property that is NOT limited to the root ports only. And we have
reached an agreement with Microsoft about that.

>
> We currently look at three properties from the same _DSD:
>
> DmaProperty
> ExternalFacingPort
> HotPlugSupportInD3
>
> For "HotPlugSupportInD3", we check that "value == 1". For
> "ExternalFacingPort", we check that it's non-zero. The MS doc isn't
> explicit about the values, but shows "1" in the sample ASL. I think
> we should handle all three cases the same.

I think we can handle all three cases with "value==1". I will send a
separate patch for the ExternalFacingPort, and will fixup this patch
too).

>
> The first two use device_property_read_u8(); the last uses
> acpi_dev_get_property(). Again, I think they should all be the same.
>
> acpi_dev_get_property() is easier for me to read because there are
> slightly fewer layers of abstraction between _DSD and
> acpi_dev_get_property().

I'd prefer that too, and can change it to acpi_dev_get_property.

>
> But IIUC, device_property_read_u8() works for either ACPI or DT
> properties, and maybe there is interest in using this for DT systems.
> None of these appear in any in-tree DTs, but maybe it is important to
> handle these in DTs?
>
> If that's the case, this code would no longer be specific to ACPI and
> should be moved to somewhere that's compiled even when CONFIG_ACPI
> isn't set.

I think unifying ACPI and GPIO systems to use the same code / function
to read the properties might be more work/investigation, because
reading the properties for ACPI system happens much later than DT
systems (For acpi systems, it happens in pci_acpi_setup() which is
called much later). Given that no one wants to use this for DT
systems, I'd prefer for this to be ACPI specific for now, and then we
can solve it for DT once someone needs it.

WDYT?

Thanks & Best regards,

Rajat

>
> > + return;
> > +
> > + if (val)
> > + dev->untrusted = 1;
> > +}
> > +
> > void pci_acpi_setup(struct device *dev, struct acpi_device *adev)
> > {
> > struct pci_dev *pci_dev = to_pci_dev(dev);
> >
> > pci_acpi_optimize_delay(pci_dev, adev->handle);
> > pci_acpi_set_external_facing(pci_dev);
> > + pci_acpi_check_for_dma_protection(pci_dev);
> > pci_acpi_add_edr_notifier(pci_dev);
> >
> > pci_acpi_add_pm_notifier(adev, pci_dev);
> > --
> > 2.35.1.1021.g381101b075-goog
> >