- Support device ID 0x28C1 and assume that BIOS has already enumerated the hierarchy below VMD and stored bus range info for OS to use (Nirmal Patel) - Add support for VMCONFIG BUS_RESTRICT_CFG=3, which makes it possible to enumerate downstream devices on Intel Arrow Lake-HX systems and probably others (Ali Alaei) - Observe _OSC negotiation for VMD hierarchy only when running on bare metal, not when running in a VM (Nirmal Patel) - Add Nova Lake (NVL) and Dunlow (DNL) Device IDs (Szymon Durawa) * pci/controller/vmd: PCI: vmd: Add Nova Lake (NVL) and Dunlow (DNL) Device IDs PCI: vmd: Only copy root bridge _OSC control flags in bare metal OS PCI: vmd: Handle BUS_RESTRICT_CFG value 3 for Arrow Lake-HX PCI: vmd: Add feature to scan BIOS-enumerated devices
Unified · Split
| @@ -37,6 +37,12 @@ · 36 unmodified lines | ||
| 37 | 37 | #define MB2_SHADOW_OFFSET 0x2000 |
| 38 | 38 | #define MB2_SHADOW_SIZE 16 |
| 39 | 39 | |
| 40 | +/* DMR BAR4 register offsets */ | |
| 41 | +#define SHADOW_MEMBAR1_28C1 0x2818 /* MEMBAR1 physical address */ | |
| 42 | +#define SHADOW_MEMBAR2_28C1 0x2820 /* MEMBAR2 physical address */ | |
| 43 | +#define BASE_ID_REG_28C1 0x2840 | |
| 44 | +#define MEMBAR2_OFFSET_28C1 0x30d0 | |
| 45 | + | |
| 40 | 46 | enum vmd_features { |
| 41 | 47 | /* |
| 42 | 48 | * Device may contain registers which hint the physical location of the |
| @@ -77,6 +83,15 @@ enum vmd_features { · 34 unmodified lines | ||
| 77 | 83 | * proper power management of the SoC. |
| 78 | 84 | */ |
| 79 | 85 | VMD_FEAT_BIOS_PM_QUIRK = (1 << 5), |
| 86 | + | |
| 87 | + /* | |
| 88 | + * Newer VMD with device ID 0x28C1 has unique settings compared to its | |
| 89 | + * predecessor where BIOS enumerates the entire VMD device tree and | |
| 90 | + * stores respective configurations including bus start range and | |
| 91 | + * shadow registers in VMD MMIO space in VMD BAR4/BAR5, otherwise | |
| 92 | + * referred to as MEMBAR2 or MSI-X BAR. | |
| 93 | + */ | |
| 94 | + VMD_FEAT_USE_BIOS_INFO = (1 << 6), | |
| 80 | 95 | }; |
| 81 | 96 | |
| 82 | 97 | #define VMD_BIOS_PM_QUIRK_LTR 0x1003 /* 3145728 ns */ |
| @@ -142,6 +157,7 @@ struct vmd_dev { · 59 unmodified lines | ||
| 142 | 157 | u8 first_vec; |
| 143 | 158 | char *name; |
| 144 | 159 | int instance; |
| 160 | + unsigned long features; | |
| 145 | 161 | }; |
| 146 | 162 | |
| 147 | 163 | static inline struct vmd_dev *vmd_from_bus(struct pci_bus *bus) |
| @@ -366,6 +382,9 @@ static void vmd_set_msi_remapping(struct vmd_dev *vmd, bool enable) · 218 unmodified lines | ||
| 366 | 382 | { |
| 367 | 383 | u16 reg; |
| 368 | 384 | |
| 385 | + if (!!(vmd->features & VMD_FEAT_USE_BIOS_INFO)) | |
| 386 | + return; | |
| 387 | + | |
| 369 | 388 | pci_read_config_word(vmd->dev, PCI_REG_VMCONFIG, ®); |
| 370 | 389 | reg = enable ? (reg & ~VMCONFIG_MSI_REMAP) : |
| 371 | 390 | (reg | VMCONFIG_MSI_REMAP); |
| @@ -389,11 +408,22 @@ static void vmd_remove_irq_domain(struct vmd_dev *vmd) · 17 unmodified lines | ||
| 389 | 408 | } |
| 390 | 409 | } |
| 391 | 410 | |
| 411 | +static unsigned int vmd_bus_to_ecam(struct vmd_dev *vmd, unsigned int busnr) | |
| 412 | +{ | |
| 413 | + if (!!(vmd->features & VMD_FEAT_USE_BIOS_INFO)) | |
| 414 | + return busnr; | |
| 415 | + | |
| 416 | + return busnr - vmd->busn_start; | |
| 417 | +} | |
| 418 | + | |
| 392 | 419 | static void __iomem *vmd_cfg_addr(struct vmd_dev *vmd, struct pci_bus *bus, |
| 393 | 420 | unsigned int devfn, int reg, int len) |
| 394 | 421 | { |
| 395 | − unsigned int busnr_ecam = bus->number - vmd->busn_start; | |
| 396 | − u32 offset = PCIE_ECAM_OFFSET(busnr_ecam, devfn, reg); | |
| 422 | + unsigned int busnr_ecam; | |
| 423 | + u32 offset; | |
| 424 | + | |
| 425 | + busnr_ecam = vmd_bus_to_ecam(vmd, bus->number); | |
| 426 | + offset = PCIE_ECAM_OFFSET(busnr_ecam, devfn, reg); | |
| 397 | 427 | |
| 398 | 428 | if (offset + len >= resource_size(&vmd->dev->resource[VMD_CFGBAR])) |
| 399 | 429 | return NULL; |
| @@ -518,22 +548,37 @@ static inline void vmd_acpi_begin(void) { } · 118 unmodified lines | ||
| 518 | 548 | static inline void vmd_acpi_end(void) { } |
| 519 | 549 | #endif /* CONFIG_ACPI */ |
| 520 | 550 | |
| 551 | +static resource_size_t vmd_cfgbar_ecam_space(struct vmd_dev *vmd) | |
| 552 | +{ | |
| 553 | + resource_size_t cfgbar_buses; | |
| 554 | + unsigned int ecam_start; | |
| 555 | + | |
| 556 | + cfgbar_buses = resource_size(&vmd->dev->resource[VMD_CFGBAR]) >> 20; | |
| 557 | + ecam_start = vmd_bus_to_ecam(vmd, vmd->resources[0].start); | |
| 558 | + if (ecam_start >= cfgbar_buses) | |
| 559 | + return 0; | |
| 560 | + | |
| 561 | + return cfgbar_buses - ecam_start; | |
| 562 | +} | |
| 521 | 563 | static void vmd_domain_reset(struct vmd_dev *vmd) |
| 522 | 564 | { |
| 523 | 565 | u16 bus, max_buses = resource_size(&vmd->resources[0]); |
| 524 | 566 | u8 dev, functions, fn, hdr_type; |
| 567 | + unsigned int ecam_bus; | |
| 525 | 568 | char __iomem *base; |
| 526 | 569 | |
| 570 | + max_buses = min_t(u16, max_buses, vmd_cfgbar_ecam_space(vmd)); | |
| 527 | 571 | for (bus = 0; bus < max_buses; bus++) { |
| 572 | + ecam_bus = vmd_bus_to_ecam(vmd, vmd->resources[0].start + bus); | |
| 528 | 573 | for (dev = 0; dev < 32; dev++) { |
| 529 | − base = vmd->cfgbar + PCIE_ECAM_OFFSET(bus, | |
| 574 | + base = vmd->cfgbar + PCIE_ECAM_OFFSET(ecam_bus, | |
| 530 | 575 | PCI_DEVFN(dev, 0), 0); |
| 531 | 576 | |
| 532 | 577 | hdr_type = readb(base + PCI_HEADER_TYPE); |
| 533 | 578 | |
| 534 | 579 | functions = (hdr_type & PCI_HEADER_TYPE_MFD) ? 8 : 1; |
| 535 | 580 | for (fn = 0; fn < functions; fn++) { |
| 536 | − base = vmd->cfgbar + PCIE_ECAM_OFFSET(bus, | |
| 581 | + base = vmd->cfgbar + PCIE_ECAM_OFFSET(ecam_bus, | |
| 537 | 582 | PCI_DEVFN(dev, fn), 0); |
| 538 | 583 | |
| 539 | 584 | hdr_type = readb(base + PCI_HEADER_TYPE) & |
| @@ -640,6 +685,8 @@ static int vmd_get_bus_number_start(struct vmd_dev *vmd) · 100 unmodified lines | ||
| 640 | 685 | pci_read_config_word(dev, PCI_REG_VMCAP, ®); |
| 641 | 686 | if (BUS_RESTRICT_CAP(reg)) { |
| 642 | 687 | pci_read_config_word(dev, PCI_REG_VMCONFIG, ®); |
| 688 | + if (PCI_POSSIBLE_ERROR(reg)) | |
| 689 | + return -ENODEV; | |
| 643 | 690 | |
| 644 | 691 | switch (BUS_RESTRICT_CFG(reg)) { |
| 645 | 692 | case 0: |
| @@ -648,6 +695,7 @@ static int vmd_get_bus_number_start(struct vmd_dev *vmd) · 2 unmodified lines | ||
| 648 | 695 | case 1: |
| 649 | 696 | vmd->busn_start = 128; |
| 650 | 697 | break; |
| 698 | + case 3: | |
| 651 | 699 | case 2: |
| 652 | 700 | vmd->busn_start = 224; |
| 653 | 701 | break; |
| @@ -661,6 +709,46 @@ static int vmd_get_bus_number_start(struct vmd_dev *vmd) · 7 unmodified lines | ||
| 661 | 709 | return 0; |
| 662 | 710 | } |
| 663 | 711 | |
| 712 | +static int vmd_get_bus_info_from_bar4(struct vmd_dev *vmd, | |
| 713 | + resource_size_t *offset1, | |
| 714 | + resource_size_t *offset2) | |
| 715 | +{ | |
| 716 | + u64 phys1, phys2, bar4_2840; | |
| 717 | + void __iomem *bar4; | |
| 718 | + u32 base_id; | |
| 719 | + u8 base_bus; | |
| 720 | + | |
| 721 | + bar4 = pci_ioremap_bar(vmd->dev, 4); | |
| 722 | + if (!bar4) | |
| 723 | + return -ENOMEM; | |
| 724 | + | |
| 725 | + /* Read shadow registers for MEMBAR1 and MEMBAR2 physical addresses */ | |
| 726 | + phys1 = readq(bar4 + SHADOW_MEMBAR1_28C1); | |
| 727 | + phys2 = readq(bar4 + SHADOW_MEMBAR2_28C1); | |
| 728 | + | |
| 729 | + /* | |
| 730 | + * Read and set bus start number from Base ID register. 24-bit Base ID | |
| 731 | + * register is part of 64-bit shadowed reqid hide range register and | |
| 732 | + * holds segment, bus, device and function. | |
| 733 | + */ | |
| 734 | + bar4_2840 = readq(bar4 + BASE_ID_REG_28C1); | |
| 735 | + base_id = bar4_2840 & 0xFFFFFF; | |
| 736 | + base_bus = base_id >> 8; | |
| 737 | + vmd->busn_start = base_bus; | |
| 738 | + | |
| 739 | + /* Calculate offsets like vmd_get_phys_offsets() does */ | |
| 740 | + if (phys1) | |
| 741 | + *offset1 = vmd->dev->resource[VMD_MEMBAR1].start - | |
| 742 | + (phys1 & PCI_BASE_ADDRESS_MEM_MASK); | |
| 743 | + if (phys2) | |
| 744 | + *offset2 = vmd->dev->resource[VMD_MEMBAR2].start - | |
| 745 | + (phys2 & PCI_BASE_ADDRESS_MEM_MASK); | |
| 746 | + | |
| 747 | + pci_iounmap(vmd->dev, bar4); | |
| 748 | + | |
| 749 | + return 0; | |
| 750 | +} | |
| 751 | + | |
| 664 | 752 | static irqreturn_t vmd_irq(int irq, void *data) |
| 665 | 753 | { |
| 666 | 754 | struct vmd_irq_list *irqs = data; |
| @@ -711,6 +799,52 @@ static int vmd_alloc_irqs(struct vmd_dev *vmd) · 44 unmodified lines | ||
| 711 | 799 | return 0; |
| 712 | 800 | } |
| 713 | 801 | |
| 802 | +static int vmd_prepare_offsets_and_bus(struct vmd_dev *vmd, | |
| 803 | + unsigned long features, | |
| 804 | + resource_size_t *membar2_offset, | |
| 805 | + resource_size_t *offset1, | |
| 806 | + resource_size_t *offset2) | |
| 807 | +{ | |
| 808 | + int ret; | |
| 809 | + | |
| 810 | + /* | |
| 811 | + * Shadow registers may exist in certain VMD device IDs which allow | |
| 812 | + * guests to correctly assign host physical addresses to the root ports | |
| 813 | + * and child devices. These registers will either return the host value | |
| 814 | + * or 0, depending on an enable bit in the VMD device. | |
| 815 | + * | |
| 816 | + * For certain VMD devices (i.e. 0x28C1), BIOS places device info | |
| 817 | + * in BAR4 shadow registers to determine the base bus number and memory | |
| 818 | + * offsets. | |
| 819 | + */ | |
| 820 | + if (features & VMD_FEAT_USE_BIOS_INFO) { | |
| 821 | + *membar2_offset = MEMBAR2_OFFSET_28C1; | |
| 822 | + ret = vmd_get_bus_info_from_bar4(vmd, offset1, offset2); | |
| 823 | + if (ret) | |
| 824 | + return ret; | |
| 825 | + } else if (features & VMD_FEAT_HAS_MEMBAR_SHADOW) { | |
| 826 | + *membar2_offset = MB2_SHADOW_OFFSET + MB2_SHADOW_SIZE; | |
| 827 | + ret = vmd_get_phys_offsets(vmd, true, offset1, offset2); | |
| 828 | + if (ret) | |
| 829 | + return ret; | |
| 830 | + } else if (features & VMD_FEAT_HAS_MEMBAR_SHADOW_VSCAP) { | |
| 831 | + ret = vmd_get_phys_offsets(vmd, false, offset1, offset2); | |
| 832 | + if (ret) | |
| 833 | + return ret; | |
| 834 | + } | |
| 835 | + | |
| 836 | + /* | |
| 837 | + * Certain VMD devices may have a root port configuration option which | |
| 838 | + * limits the bus range to between 0-127, 128-255, or 224-255. | |
| 839 | + */ | |
| 840 | + if (features & VMD_FEAT_HAS_BUS_RESTRICTIONS) { | |
| 841 | + ret = vmd_get_bus_number_start(vmd); | |
| 842 | + if (ret) | |
| 843 | + return ret; | |
| 844 | + } | |
| 845 | + return 0; | |
| 846 | +} | |
| 847 | + | |
| 714 | 848 | /* |
| 715 | 849 | * Since VMD is an aperture to regular PCIe root ports, only allow it to |
| 716 | 850 | * control features that the OS is allowed to control on the physical PCI bus. |
| @@ -780,42 +914,25 @@ static int vmd_enable_domain(struct vmd_dev *vmd, unsigned long features) · 63 unmodified lines | ||
| 780 | 914 | LIST_HEAD(resources); |
| 781 | 915 | resource_size_t offset[2] = {0}; |
| 782 | 916 | resource_size_t membar2_offset = 0x2000; |
| 917 | + resource_size_t busn_end; | |
| 783 | 918 | struct pci_bus *child; |
| 784 | 919 | struct pci_dev *dev; |
| 920 | + bool vmd_in_guest; | |
| 785 | 921 | int ret; |
| 786 | 922 | |
| 787 | − /* | |
| 788 | − * Shadow registers may exist in certain VMD device ids which allow | |
| 789 | − * guests to correctly assign host physical addresses to the root ports | |
| 790 | − * and child devices. These registers will either return the host value | |
| 791 | − * or 0, depending on an enable bit in the VMD device. | |
| 792 | − */ | |
| 793 | − if (features & VMD_FEAT_HAS_MEMBAR_SHADOW) { | |
| 794 | − membar2_offset = MB2_SHADOW_OFFSET + MB2_SHADOW_SIZE; | |
| 795 | − ret = vmd_get_phys_offsets(vmd, true, &offset[0], &offset[1]); | |
| 796 | − if (ret) | |
| 797 | − return ret; | |
| 798 | − } else if (features & VMD_FEAT_HAS_MEMBAR_SHADOW_VSCAP) { | |
| 799 | − ret = vmd_get_phys_offsets(vmd, false, &offset[0], &offset[1]); | |
| 800 | − if (ret) | |
| 801 | − return ret; | |
| 802 | − } | |
| 803 | − | |
| 804 | − /* | |
| 805 | − * Certain VMD devices may have a root port configuration option which | |
| 806 | − * limits the bus range to between 0-127, 128-255, or 224-255 | |
| 807 | − */ | |
| 808 | − if (features & VMD_FEAT_HAS_BUS_RESTRICTIONS) { | |
| 809 | − ret = vmd_get_bus_number_start(vmd); | |
| 810 | − if (ret) | |
| 811 | − return ret; | |
| 812 | − } | |
| 923 | + ret = vmd_prepare_offsets_and_bus(vmd, features, &membar2_offset, | |
| 924 | + &offset[0], &offset[1]); | |
| 925 | + if (ret) | |
| 926 | + return ret; | |
| 813 | 927 | |
| 928 | + /* Do not let resource[0] end go out of bounds */ | |
| 814 | 929 | res = &vmd->dev->resource[VMD_CFGBAR]; |
| 930 | + busn_end = vmd->busn_start + (resource_size(res) >> 20) - 1; | |
| 931 | + busn_end = min_t(resource_size_t, busn_end, 0xff); | |
| 815 | 932 | vmd->resources[0] = (struct resource) { |
| 816 | 933 | .name = "VMD CFGBAR", |
| 817 | 934 | .start = vmd->busn_start, |
| 818 | − .end = vmd->busn_start + (resource_size(res) >> 20) - 1, | |
| 935 | + .end = busn_end, | |
| 819 | 936 | .flags = IORESOURCE_BUS | IORESOURCE_PCI_FIXED, |
| 820 | 937 | }; |
| 821 | 938 | |
| @@ -862,14 +979,16 @@ static int vmd_enable_domain(struct vmd_dev *vmd, unsigned long features) · 40 unmodified lines | ||
| 862 | 979 | .parent = res, |
| 863 | 980 | }; |
| 864 | 981 | |
| 982 | + /* Non-zero offset means guest/direct assign view. */ | |
| 983 | + vmd_in_guest = offset[0] || offset[1]; | |
| 984 | + | |
| 865 | 985 | /* |
| 866 | 986 | * Currently MSI remapping must be enabled in guest passthrough mode |
| 867 | 987 | * due to some missing interrupt remapping plumbing. This is probably |
| 868 | 988 | * acceptable because the guest is usually CPU-limited and MSI |
| 869 | 989 | * remapping doesn't become a performance bottleneck. |
| 870 | 990 | */ |
| 871 | − if (!(features & VMD_FEAT_CAN_BYPASS_MSI_REMAP) || | |
| 872 | − offset[0] || offset[1]) { | |
| 991 | + if (!(features & VMD_FEAT_CAN_BYPASS_MSI_REMAP) || vmd_in_guest) { | |
| 873 | 992 | ret = vmd_alloc_irqs(vmd); |
| 874 | 993 | if (ret) |
| 875 | 994 | return ret; |
| @@ -910,8 +1029,13 @@ static int vmd_enable_domain(struct vmd_dev *vmd, unsigned long features) · 34 unmodified lines | ||
| 910 | 1029 | return -ENODEV; |
| 911 | 1030 | } |
| 912 | 1031 | |
| 913 | − vmd_copy_host_bridge_flags(pci_find_host_bridge(vmd->dev->bus), | |
| 914 | − to_pci_host_bridge(vmd->bus->bridge)); | |
| 1032 | + /* | |
| 1033 | + * Don't copy _OSC control flags from root bridge if running in a VM, as | |
| 1034 | + * they don't reflect the physical root bridge capabilities. | |
| 1035 | + */ | |
| 1036 | + if (!vmd_in_guest) | |
| 1037 | + vmd_copy_host_bridge_flags(pci_find_host_bridge(vmd->dev->bus), | |
| 1038 | + to_pci_host_bridge(vmd->bus->bridge)); | |
| 915 | 1039 | |
| 916 | 1040 | vmd_attach_resources(vmd); |
| 917 | 1041 | if (vmd->irq_domain) |
| @@ -998,6 +1122,7 @@ static int vmd_probe(struct pci_dev *dev, const struct pci_device_id *id) · 80 unmodified lines | ||
| 998 | 1122 | |
| 999 | 1123 | vmd->dev = dev; |
| 1000 | 1124 | vmd->sysdata.domain = PCI_DOMAIN_NR_NOT_SET; |
| 1125 | + vmd->features = features; | |
| 1001 | 1126 | vmd->instance = ida_alloc(&vmd_instance_ida, GFP_KERNEL); |
| 1002 | 1127 | if (vmd->instance < 0) |
| 1003 | 1128 | return vmd->instance; |
| @@ -1114,6 +1239,10 @@ static const struct pci_device_id vmd_ids[] = { · 110 unmodified lines | ||
| 1114 | 1239 | .driver_data = VMD_FEAT_HAS_MEMBAR_SHADOW | |
| 1115 | 1240 | VMD_FEAT_HAS_BUS_RESTRICTIONS | |
| 1116 | 1241 | VMD_FEAT_CAN_BYPASS_MSI_REMAP,}, |
| 1242 | + {PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_VMD_28C1), | |
| 1243 | + .driver_data = VMD_FEAT_HAS_MEMBAR_SHADOW | | |
| 1244 | + VMD_FEAT_CAN_BYPASS_MSI_REMAP | | |
| 1245 | + VMD_FEAT_USE_BIOS_INFO,}, | |
| 1117 | 1246 | {PCI_VDEVICE(INTEL, 0x467f), |
| 1118 | 1247 | .driver_data = VMD_FEATS_CLIENT,}, |
| 1119 | 1248 | {PCI_VDEVICE(INTEL, 0x4c3d), |
| @@ -1132,6 +1261,10 @@ static const struct pci_device_id vmd_ids[] = { · 12 unmodified lines | ||
| 1132 | 1261 | .driver_data = VMD_FEATS_CLIENT,}, |
| 1133 | 1262 | {PCI_VDEVICE(INTEL, 0xb07f), |
| 1134 | 1263 | .driver_data = VMD_FEATS_CLIENT,}, |
| 1264 | + {PCI_VDEVICE(INTEL, 0xd70b), | |
| 1265 | + .driver_data = VMD_FEATS_CLIENT,}, | |
| 1266 | + {PCI_VDEVICE(INTEL, 0xd73b), | |
| 1267 | + .driver_data = VMD_FEATS_CLIENT,}, | |
| 1135 | 1268 | {0,} |
| 1136 | 1269 | }; |
| 1137 | 1270 | MODULE_DEVICE_TABLE(pci, vmd_ids); |
| @@ -2892,6 +2892,7 @@ · 2891 unmodified lines | ||
| 2892 | 2892 | #define PCI_DEVICE_ID_INTEL_HDA_ICH8 0x284b |
| 2893 | 2893 | #define PCI_DEVICE_ID_INTEL_ICH8_6 0x2850 |
| 2894 | 2894 | #define PCI_DEVICE_ID_INTEL_VMD_28C0 0x28c0 |
| 2895 | +#define PCI_DEVICE_ID_INTEL_VMD_28C1 0x28c1 | |
| 2895 | 2896 | #define PCI_DEVICE_ID_INTEL_ICH9_0 0x2910 |
| 2896 | 2897 | #define PCI_DEVICE_ID_INTEL_ICH9_2 0x2912 |
| 2897 | 2898 | #define PCI_DEVICE_ID_INTEL_ICH9_3 0x2913 |