| From fc8605e74b51d9e0ab8efd0489eca2e11d807f07 Mon Sep 17 00:00:00 2001 |
| From: Manoj Kumar <manoj.kumar3@arm.com> |
| Date: Tue, 31 Aug 2021 16:15:38 +0000 |
| Subject: [PATCH] n1sdp: pci_quirk: add acs override for PCI devices |
| |
| Patch taken from: |
| https://gitlab.com/Queuecumber/linux-acs-override/raw/master/workspaces/5.4/acso.patch |
| |
| Change-Id: Ib926bf50524ce9990fbaa2f2f8670fe84bd571f9 |
| Signed-off-by: Manoj Kumar <manoj.kumar3@arm.com> |
| |
| Upstream-Status: Inappropriate [will not be submitted as its a workaround to address hardware issue] |
| Signed-off-by: Khasim Syed Mohammed <khasim.mohammed@arm.com> |
| Signed-off-by: Vishnu Banavath <vishnu.banavath@arm.com> |
| Signed-off-by: Adam Johnston <adam.johnston@arm.com> |
| --- |
| .../admin-guide/kernel-parameters.txt | 8 ++ |
| drivers/pci/quirks.c | 102 ++++++++++++++++++ |
| 2 files changed, 110 insertions(+) |
| |
| diff --git a/Documentation/admin-guide/kernel-parameters.txt b/Documentation/admin-guide/kernel-parameters.txt |
| index 963cdaecabcb..8e94af513b9f 100644 |
| --- a/Documentation/admin-guide/kernel-parameters.txt |
| +++ b/Documentation/admin-guide/kernel-parameters.txt |
| @@ -4162,6 +4162,14 @@ |
| nomsi [MSI] If the PCI_MSI kernel config parameter is |
| enabled, this kernel boot option can be used to |
| disable the use of MSI interrupts system-wide. |
| + pcie_acs_override [PCIE] Override missing PCIe ACS support for |
| + downstream |
| + All downstream ports - full ACS capabilities |
| + multfunction |
| + All multifunction devices - multifunction ACS subset |
| + id:nnnn:nnnn |
| + Specfic device - full ACS capabilities |
| + Specified as vid:did (vendor/device ID) in hex |
| noioapicquirk [APIC] Disable all boot interrupt quirks. |
| Safety option to keep boot IRQs enabled. This |
| should never be necessary. |
| diff --git a/drivers/pci/quirks.c b/drivers/pci/quirks.c |
| index 285acc4aaccc..d6ebef1f30db 100644 |
| --- a/drivers/pci/quirks.c |
| +++ b/drivers/pci/quirks.c |
| @@ -3612,6 +3612,107 @@ static void quirk_no_bus_reset(struct pci_dev *dev) |
| dev->dev_flags |= PCI_DEV_FLAGS_NO_BUS_RESET; |
| } |
| |
| +static bool acs_on_downstream; |
| +static bool acs_on_multifunction; |
| + |
| +#define NUM_ACS_IDS 16 |
| +struct acs_on_id { |
| + unsigned short vendor; |
| + unsigned short device; |
| +}; |
| +static struct acs_on_id acs_on_ids[NUM_ACS_IDS]; |
| +static u8 max_acs_id; |
| + |
| +static __init int pcie_acs_override_setup(char *p) |
| +{ |
| + if (!p) |
| + return -EINVAL; |
| + |
| + while (*p) { |
| + if (!strncmp(p, "downstream", 10)) |
| + acs_on_downstream = true; |
| + if (!strncmp(p, "multifunction", 13)) |
| + acs_on_multifunction = true; |
| + if (!strncmp(p, "id:", 3)) { |
| + char opt[5]; |
| + int ret; |
| + long val; |
| + |
| + if (max_acs_id >= NUM_ACS_IDS - 1) { |
| + pr_warn("Out of PCIe ACS override slots (%d)\n", |
| + NUM_ACS_IDS); |
| + goto next; |
| + } |
| + |
| + p += 3; |
| + snprintf(opt, 5, "%s", p); |
| + ret = kstrtol(opt, 16, &val); |
| + if (ret) { |
| + pr_warn("PCIe ACS ID parse error %d\n", ret); |
| + goto next; |
| + } |
| + acs_on_ids[max_acs_id].vendor = val; |
| + |
| + p += strcspn(p, ":"); |
| + if (*p != ':') { |
| + pr_warn("PCIe ACS invalid ID\n"); |
| + goto next; |
| + } |
| + |
| + p++; |
| + snprintf(opt, 5, "%s", p); |
| + ret = kstrtol(opt, 16, &val); |
| + if (ret) { |
| + pr_warn("PCIe ACS ID parse error %d\n", ret); |
| + goto next; |
| + } |
| + acs_on_ids[max_acs_id].device = val; |
| + max_acs_id++; |
| + } |
| +next: |
| + p += strcspn(p, ","); |
| + if (*p == ',') |
| + p++; |
| + } |
| + |
| + if (acs_on_downstream || acs_on_multifunction || max_acs_id) |
| + pr_warn("Warning: PCIe ACS overrides enabled; This may allow non-IOMMU protected peer-to-peer DMA\n"); |
| + |
| + return 0; |
| +} |
| +early_param("pcie_acs_override", pcie_acs_override_setup); |
| + |
| +static int pcie_acs_overrides(struct pci_dev *dev, u16 acs_flags) |
| +{ |
| + int i; |
| + |
| + /* Never override ACS for legacy devices or devices with ACS caps */ |
| + if (!pci_is_pcie(dev) || |
| + pci_find_ext_capability(dev, PCI_EXT_CAP_ID_ACS)) |
| + return -ENOTTY; |
| + |
| + for (i = 0; i < max_acs_id; i++) |
| + if (acs_on_ids[i].vendor == dev->vendor && |
| + acs_on_ids[i].device == dev->device) |
| + return 1; |
| + |
| + switch (pci_pcie_type(dev)) { |
| + case PCI_EXP_TYPE_DOWNSTREAM: |
| + case PCI_EXP_TYPE_ROOT_PORT: |
| + if (acs_on_downstream) |
| + return 1; |
| + break; |
| + case PCI_EXP_TYPE_ENDPOINT: |
| + case PCI_EXP_TYPE_UPSTREAM: |
| + case PCI_EXP_TYPE_LEG_END: |
| + case PCI_EXP_TYPE_RC_END: |
| + if (acs_on_multifunction && dev->multifunction) |
| + return 1; |
| + } |
| + |
| + return -ENOTTY; |
| +} |
| + |
| /* |
| * Some NVIDIA GPU devices do not work with bus reset, SBR needs to be |
| * prevented for those affected devices. |
| @@ -4980,6 +5081,7 @@ static const struct pci_dev_acs_enabled { |
| { PCI_VENDOR_ID_ZHAOXIN, PCI_ANY_ID, pci_quirk_zhaoxin_pcie_ports_acs }, |
| /* Wangxun nics */ |
| { PCI_VENDOR_ID_WANGXUN, PCI_ANY_ID, pci_quirk_wangxun_nic_acs }, |
| + { PCI_ANY_ID, PCI_ANY_ID, pcie_acs_overrides }, |
| { 0 } |
| }; |
| |