| diff -uNr linux-4.2.5/Documentation/kernel-parameters.txt linux-4.2.5-acs/Documentation/kernel-parameters.txt | |
| --- linux-4.2.5/Documentation/kernel-parameters.txt 2015-10-26 17:53:59.000000000 -0700 | |
| +++ linux-4.2.5-acs/Documentation/kernel-parameters.txt 2015-11-02 18:02:28.643526086 -0800 | |
| @@ -2850,6 +2850,16 @@ | |
| nomsi Do not use MSI for native PCIe PME signaling (this makes | |
| all PCIe root ports use INTx for all services). | |
| + pcie_acs_override = | |
| + [PCIE] Override missing PCIe ACS support for: | |
| + downstream | |
| + All downstream ports - full ACS capabilties | |
| + multifunction | |
| + All multifunction devices - multifunction ACS subset | |
| + id:nnnn:nnnn | |
| + Specfic device - full ACS capabilities | |
| + Specified as vid:did (vendor/device ID) in hex | |
| + | |
| pcmv= [HW,PCMCIA] BadgePAD 4 | |
| pd_ignore_unused | |
| diff -uNr linux-4.2.5/drivers/pci/quirks.c linux-4.2.5-acs/drivers/pci/quirks.c | |
| --- linux-4.2.5/drivers/pci/quirks.c 2015-10-26 17:53:59.000000000 -0700 | |
| +++ linux-4.2.5-acs/drivers/pci/quirks.c 2015-11-02 18:02:28.643526086 -0800 | |
| @@ -3361,6 +3361,107 @@ | |
| fs_initcall_sync(pci_apply_final_quirks); | |
| +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; | |
| +} | |
| + | |
| /* | |
| * Followings are device-specific reset methods which can be used to | |
| * reset a single function if other methods (e.g. FLR, PM D0->D3) are | |
| @@ -3822,6 +3923,7 @@ | |
| return acs_flags ? 0 : 1; | |
| } | |
| + | |
| static const struct pci_dev_acs_enabled { | |
| u16 vendor; | |
| u16 device; | |
| @@ -3890,6 +3992,7 @@ | |
| { PCI_VENDOR_ID_INTEL, 0x10D9, pci_quirk_mf_endpoint_acs }, | |
| /* Intel PCH root ports */ | |
| { PCI_VENDOR_ID_INTEL, PCI_ANY_ID, pci_quirk_intel_pch_acs }, | |
| + { PCI_ANY_ID, PCI_ANY_ID, pcie_acs_overrides }, | |
| { 0x19a2, 0x710, pci_quirk_mf_endpoint_acs }, /* Emulex BE3-R */ | |
| { 0x10df, 0x720, pci_quirk_mf_endpoint_acs }, /* Emulex Skyhawk-R */ | |
| { 0 } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment