diff options
| author | Weidong Han <weidong.han@intel.com> | 2009-05-22 12:41:15 -0400 |
|---|---|---|
| committer | David Woodhouse <David.Woodhouse@intel.com> | 2009-06-23 17:09:17 -0400 |
| commit | f007e99c8e2e322b8331aba72414715119a2920d (patch) | |
| tree | 616bfcdda74341dc8b5d9ea1013bb7506407a961 | |
| parent | c4658b4e777bebf69884f4884a9bfb2f84dd71d9 (diff) | |
Intel-IOMMU, intr-remap: source-id checking
To support domain-isolation usages, the platform hardware must be
capable of uniquely identifying the requestor (source-id) for each
interrupt message. Without source-id checking for interrupt remapping
, a rouge guest/VM with assigned devices can launch interrupt attacks
to bring down anothe guest/VM or the VMM itself.
This patch adds source-id checking for interrupt remapping, and then
really isolates interrupts for guests/VMs with assigned devices.
Because PCI subsystem is not initialized yet when set up IOAPIC
entries, use read_pci_config_byte to access PCI config space directly.
Signed-off-by: Weidong Han <weidong.han@intel.com>
Signed-off-by: David Woodhouse <David.Woodhouse@intel.com>
| -rw-r--r-- | arch/x86/kernel/apic/io_apic.c | 6 | ||||
| -rw-r--r-- | drivers/pci/intr_remapping.c | 120 | ||||
| -rw-r--r-- | drivers/pci/intr_remapping.h | 2 | ||||
| -rw-r--r-- | include/linux/dmar.h | 11 |
4 files changed, 136 insertions, 3 deletions
diff --git a/arch/x86/kernel/apic/io_apic.c b/arch/x86/kernel/apic/io_apic.c index b7a79207295e..4d0216fcb36c 100644 --- a/arch/x86/kernel/apic/io_apic.c +++ b/arch/x86/kernel/apic/io_apic.c | |||
| @@ -1414,6 +1414,9 @@ int setup_ioapic_entry(int apic_id, int irq, | |||
| 1414 | irte.vector = vector; | 1414 | irte.vector = vector; |
| 1415 | irte.dest_id = IRTE_DEST(destination); | 1415 | irte.dest_id = IRTE_DEST(destination); |
| 1416 | 1416 | ||
| 1417 | /* Set source-id of interrupt request */ | ||
| 1418 | set_ioapic_sid(&irte, apic_id); | ||
| 1419 | |||
| 1417 | modify_irte(irq, &irte); | 1420 | modify_irte(irq, &irte); |
| 1418 | 1421 | ||
| 1419 | ir_entry->index2 = (index >> 15) & 0x1; | 1422 | ir_entry->index2 = (index >> 15) & 0x1; |
| @@ -3290,6 +3293,9 @@ static int msi_compose_msg(struct pci_dev *pdev, unsigned int irq, struct msi_ms | |||
| 3290 | irte.vector = cfg->vector; | 3293 | irte.vector = cfg->vector; |
| 3291 | irte.dest_id = IRTE_DEST(dest); | 3294 | irte.dest_id = IRTE_DEST(dest); |
| 3292 | 3295 | ||
| 3296 | /* Set source-id of interrupt request */ | ||
| 3297 | set_msi_sid(&irte, pdev); | ||
| 3298 | |||
| 3293 | modify_irte(irq, &irte); | 3299 | modify_irte(irq, &irte); |
| 3294 | 3300 | ||
| 3295 | msg->address_hi = MSI_ADDR_BASE_HI; | 3301 | msg->address_hi = MSI_ADDR_BASE_HI; |
diff --git a/drivers/pci/intr_remapping.c b/drivers/pci/intr_remapping.c index 44025a0c2bb6..4f5b8712931f 100644 --- a/drivers/pci/intr_remapping.c +++ b/drivers/pci/intr_remapping.c | |||
| @@ -10,6 +10,8 @@ | |||
| 10 | #include <linux/intel-iommu.h> | 10 | #include <linux/intel-iommu.h> |
| 11 | #include "intr_remapping.h" | 11 | #include "intr_remapping.h" |
| 12 | #include <acpi/acpi.h> | 12 | #include <acpi/acpi.h> |
| 13 | #include <asm/pci-direct.h> | ||
| 14 | #include "pci.h" | ||
| 13 | 15 | ||
| 14 | static struct ioapic_scope ir_ioapic[MAX_IO_APICS]; | 16 | static struct ioapic_scope ir_ioapic[MAX_IO_APICS]; |
| 15 | static int ir_ioapic_num; | 17 | static int ir_ioapic_num; |
| @@ -418,6 +420,91 @@ int free_irte(int irq) | |||
| 418 | return rc; | 420 | return rc; |
| 419 | } | 421 | } |
| 420 | 422 | ||
| 423 | /* | ||
| 424 | * source validation type | ||
| 425 | */ | ||
| 426 | #define SVT_NO_VERIFY 0x0 /* no verification is required */ | ||
| 427 | #define SVT_VERIFY_SID_SQ 0x1 /* verify using SID and SQ fiels */ | ||
| 428 | #define SVT_VERIFY_BUS 0x2 /* verify bus of request-id */ | ||
| 429 | |||
| 430 | /* | ||
| 431 | * source-id qualifier | ||
| 432 | */ | ||
| 433 | #define SQ_ALL_16 0x0 /* verify all 16 bits of request-id */ | ||
| 434 | #define SQ_13_IGNORE_1 0x1 /* verify most significant 13 bits, ignore | ||
| 435 | * the third least significant bit | ||
| 436 | */ | ||
| 437 | #define SQ_13_IGNORE_2 0x2 /* verify most significant 13 bits, ignore | ||
| 438 | * the second and third least significant bits | ||
| 439 | */ | ||
| 440 | #define SQ_13_IGNORE_3 0x3 /* verify most significant 13 bits, ignore | ||
| 441 | * the least three significant bits | ||
| 442 | */ | ||
| 443 | |||
| 444 | /* | ||
| 445 | * set SVT, SQ and SID fields of irte to verify | ||
| 446 | * source ids of interrupt requests | ||
| 447 | */ | ||
| 448 | static void set_irte_sid(struct irte *irte, unsigned int svt, | ||
| 449 | unsigned int sq, unsigned int sid) | ||
| 450 | { | ||
| 451 | irte->svt = svt; | ||
| 452 | irte->sq = sq; | ||
| 453 | irte->sid = sid; | ||
| 454 | } | ||
| 455 | |||
| 456 | int set_ioapic_sid(struct irte *irte, int apic) | ||
| 457 | { | ||
| 458 | int i; | ||
| 459 | u16 sid = 0; | ||
| 460 | |||
| 461 | if (!irte) | ||
| 462 | return -1; | ||
| 463 | |||
| 464 | for (i = 0; i < MAX_IO_APICS; i++) { | ||
| 465 | if (ir_ioapic[i].id == apic) { | ||
| 466 | sid = (ir_ioapic[i].bus << 8) | ir_ioapic[i].devfn; | ||
| 467 | break; | ||
| 468 | } | ||
| 469 | } | ||
| 470 | |||
| 471 | if (sid == 0) { | ||
| 472 | pr_warning("Failed to set source-id of IOAPIC (%d)\n", apic); | ||
| 473 | return -1; | ||
| 474 | } | ||
| 475 | |||
| 476 | set_irte_sid(irte, 1, 0, sid); | ||
| 477 | |||
| 478 | return 0; | ||
| 479 | } | ||
| 480 | |||
| 481 | int set_msi_sid(struct irte *irte, struct pci_dev *dev) | ||
| 482 | { | ||
| 483 | struct pci_dev *bridge; | ||
| 484 | |||
| 485 | if (!irte || !dev) | ||
| 486 | return -1; | ||
| 487 | |||
| 488 | /* PCIe device or Root Complex integrated PCI device */ | ||
| 489 | if (dev->is_pcie || !dev->bus->parent) { | ||
| 490 | set_irte_sid(irte, SVT_VERIFY_SID_SQ, SQ_ALL_16, | ||
| 491 | (dev->bus->number << 8) | dev->devfn); | ||
| 492 | return 0; | ||
| 493 | } | ||
| 494 | |||
| 495 | bridge = pci_find_upstream_pcie_bridge(dev); | ||
| 496 | if (bridge) { | ||
| 497 | if (bridge->is_pcie) /* this is a PCIE-to-PCI/PCIX bridge */ | ||
| 498 | set_irte_sid(irte, SVT_VERIFY_BUS, SQ_ALL_16, | ||
| 499 | (bridge->bus->number << 8) | dev->bus->number); | ||
| 500 | else /* this is a legacy PCI bridge */ | ||
| 501 | set_irte_sid(irte, SVT_VERIFY_SID_SQ, SQ_ALL_16, | ||
| 502 | (bridge->bus->number << 8) | bridge->devfn); | ||
| 503 | } | ||
| 504 | |||
| 505 | return 0; | ||
| 506 | } | ||
| 507 | |||
| 421 | static void iommu_set_intr_remapping(struct intel_iommu *iommu, int mode) | 508 | static void iommu_set_intr_remapping(struct intel_iommu *iommu, int mode) |
| 422 | { | 509 | { |
| 423 | u64 addr; | 510 | u64 addr; |
| @@ -624,6 +711,35 @@ error: | |||
| 624 | return -1; | 711 | return -1; |
| 625 | } | 712 | } |
| 626 | 713 | ||
| 714 | static void ir_parse_one_ioapic_scope(struct acpi_dmar_device_scope *scope, | ||
| 715 | struct intel_iommu *iommu) | ||
| 716 | { | ||
| 717 | struct acpi_dmar_pci_path *path; | ||
| 718 | u8 bus; | ||
| 719 | int count; | ||
| 720 | |||
| 721 | bus = scope->bus; | ||
| 722 | path = (struct acpi_dmar_pci_path *)(scope + 1); | ||
| 723 | count = (scope->length - sizeof(struct acpi_dmar_device_scope)) | ||
| 724 | / sizeof(struct acpi_dmar_pci_path); | ||
| 725 | |||
| 726 | while (--count > 0) { | ||
| 727 | /* | ||
| 728 | * Access PCI directly due to the PCI | ||
| 729 | * subsystem isn't initialized yet. | ||
| 730 | */ | ||
| 731 | bus = read_pci_config_byte(bus, path->dev, path->fn, | ||
| 732 | PCI_SECONDARY_BUS); | ||
| 733 | path++; | ||
| 734 | } | ||
| 735 | |||
| 736 | ir_ioapic[ir_ioapic_num].bus = bus; | ||
| 737 | ir_ioapic[ir_ioapic_num].devfn = PCI_DEVFN(path->dev, path->fn); | ||
| 738 | ir_ioapic[ir_ioapic_num].iommu = iommu; | ||
| 739 | ir_ioapic[ir_ioapic_num].id = scope->enumeration_id; | ||
| 740 | ir_ioapic_num++; | ||
| 741 | } | ||
| 742 | |||
| 627 | static int ir_parse_ioapic_scope(struct acpi_dmar_header *header, | 743 | static int ir_parse_ioapic_scope(struct acpi_dmar_header *header, |
| 628 | struct intel_iommu *iommu) | 744 | struct intel_iommu *iommu) |
| 629 | { | 745 | { |
| @@ -648,9 +764,7 @@ static int ir_parse_ioapic_scope(struct acpi_dmar_header *header, | |||
| 648 | " 0x%Lx\n", scope->enumeration_id, | 764 | " 0x%Lx\n", scope->enumeration_id, |
| 649 | drhd->address); | 765 | drhd->address); |
| 650 | 766 | ||
| 651 | ir_ioapic[ir_ioapic_num].iommu = iommu; | 767 | ir_parse_one_ioapic_scope(scope, iommu); |
| 652 | ir_ioapic[ir_ioapic_num].id = scope->enumeration_id; | ||
| 653 | ir_ioapic_num++; | ||
| 654 | } | 768 | } |
| 655 | start += scope->length; | 769 | start += scope->length; |
| 656 | } | 770 | } |
diff --git a/drivers/pci/intr_remapping.h b/drivers/pci/intr_remapping.h index ca48f0df8ac9..63a263c18415 100644 --- a/drivers/pci/intr_remapping.h +++ b/drivers/pci/intr_remapping.h | |||
| @@ -3,6 +3,8 @@ | |||
| 3 | struct ioapic_scope { | 3 | struct ioapic_scope { |
| 4 | struct intel_iommu *iommu; | 4 | struct intel_iommu *iommu; |
| 5 | unsigned int id; | 5 | unsigned int id; |
| 6 | unsigned int bus; /* PCI bus number */ | ||
| 7 | unsigned int devfn; /* PCI devfn number */ | ||
| 6 | }; | 8 | }; |
| 7 | 9 | ||
| 8 | #define IR_X2APIC_MODE(mode) (mode ? (1 << 11) : 0) | 10 | #define IR_X2APIC_MODE(mode) (mode ? (1 << 11) : 0) |
diff --git a/include/linux/dmar.h b/include/linux/dmar.h index 1731fb5fd775..4a2b162c256a 100644 --- a/include/linux/dmar.h +++ b/include/linux/dmar.h | |||
| @@ -126,6 +126,8 @@ extern int free_irte(int irq); | |||
| 126 | extern int irq_remapped(int irq); | 126 | extern int irq_remapped(int irq); |
| 127 | extern struct intel_iommu *map_dev_to_ir(struct pci_dev *dev); | 127 | extern struct intel_iommu *map_dev_to_ir(struct pci_dev *dev); |
| 128 | extern struct intel_iommu *map_ioapic_to_ir(int apic); | 128 | extern struct intel_iommu *map_ioapic_to_ir(int apic); |
| 129 | extern int set_ioapic_sid(struct irte *irte, int apic); | ||
| 130 | extern int set_msi_sid(struct irte *irte, struct pci_dev *dev); | ||
| 129 | #else | 131 | #else |
| 130 | static inline int alloc_irte(struct intel_iommu *iommu, int irq, u16 count) | 132 | static inline int alloc_irte(struct intel_iommu *iommu, int irq, u16 count) |
| 131 | { | 133 | { |
| @@ -156,6 +158,15 @@ static inline struct intel_iommu *map_ioapic_to_ir(int apic) | |||
| 156 | { | 158 | { |
| 157 | return NULL; | 159 | return NULL; |
| 158 | } | 160 | } |
| 161 | static inline int set_ioapic_sid(struct irte *irte, int apic) | ||
| 162 | { | ||
| 163 | return 0; | ||
| 164 | } | ||
| 165 | static inline int set_msi_sid(struct irte *irte, struct pci_dev *dev) | ||
| 166 | { | ||
| 167 | return 0; | ||
| 168 | } | ||
| 169 | |||
| 159 | #define irq_remapped(irq) (0) | 170 | #define irq_remapped(irq) (0) |
| 160 | #define enable_intr_remapping(mode) (-1) | 171 | #define enable_intr_remapping(mode) (-1) |
| 161 | #define disable_intr_remapping() (0) | 172 | #define disable_intr_remapping() (0) |
