116c0b05fSPeter Xu /* 216c0b05fSPeter Xu * Intel IOMMU APIs 316c0b05fSPeter Xu * 416c0b05fSPeter Xu * Copyright (C) 2016 Red Hat, Inc. 516c0b05fSPeter Xu * 616c0b05fSPeter Xu * Authors: 716c0b05fSPeter Xu * Peter Xu <peterx@redhat.com>, 816c0b05fSPeter Xu * 916c0b05fSPeter Xu * This work is licensed under the terms of the GNU LGPL, version 2 or 1016c0b05fSPeter Xu * later. 1116c0b05fSPeter Xu */ 1216c0b05fSPeter Xu 1316c0b05fSPeter Xu #include "intel-iommu.h" 1492d2c192SPeter Xu #include "libcflat.h" 157f2477c2SPeter Xu #include "pci.h" 16b4377753SPeter Xu #include "atomic.h" 1792d2c192SPeter Xu 1892d2c192SPeter Xu /* 1992d2c192SPeter Xu * VT-d in QEMU currently only support 39 bits address width, which is 2092d2c192SPeter Xu * 3-level translation. 2192d2c192SPeter Xu */ 2292d2c192SPeter Xu #define VTD_PAGE_LEVEL 3 2392d2c192SPeter Xu #define VTD_CE_AW_39BIT 0x1 2492d2c192SPeter Xu 2592d2c192SPeter Xu typedef uint64_t vtd_pte_t; 2692d2c192SPeter Xu 2792d2c192SPeter Xu struct vtd_root_entry { 2892d2c192SPeter Xu /* Quad 1 */ 2992d2c192SPeter Xu uint64_t present:1; 3092d2c192SPeter Xu uint64_t __reserved:11; 3192d2c192SPeter Xu uint64_t context_table_p:52; 3292d2c192SPeter Xu /* Quad 2 */ 3392d2c192SPeter Xu uint64_t __reserved_2; 3492d2c192SPeter Xu } __attribute__ ((packed)); 3592d2c192SPeter Xu typedef struct vtd_root_entry vtd_re_t; 3692d2c192SPeter Xu 3792d2c192SPeter Xu struct vtd_context_entry { 3892d2c192SPeter Xu /* Quad 1 */ 3992d2c192SPeter Xu uint64_t present:1; 4092d2c192SPeter Xu uint64_t disable_fault_report:1; 4192d2c192SPeter Xu uint64_t trans_type:2; 4292d2c192SPeter Xu uint64_t __reserved:8; 4392d2c192SPeter Xu uint64_t slptptr:52; 4492d2c192SPeter Xu /* Quad 2 */ 4592d2c192SPeter Xu uint64_t addr_width:3; 4692d2c192SPeter Xu uint64_t __ignore:4; 4792d2c192SPeter Xu uint64_t __reserved_2:1; 4892d2c192SPeter Xu uint64_t domain_id:16; 4992d2c192SPeter Xu uint64_t __reserved_3:40; 5092d2c192SPeter Xu } __attribute__ ((packed)); 5192d2c192SPeter Xu typedef struct vtd_context_entry vtd_ce_t; 5216c0b05fSPeter Xu 537f2477c2SPeter Xu struct vtd_irte { 547f2477c2SPeter Xu uint32_t present:1; 557f2477c2SPeter Xu uint32_t fault_disable:1; /* Fault Processing Disable */ 567f2477c2SPeter Xu uint32_t dest_mode:1; /* Destination Mode */ 577f2477c2SPeter Xu uint32_t redir_hint:1; /* Redirection Hint */ 587f2477c2SPeter Xu uint32_t trigger_mode:1; /* Trigger Mode */ 597f2477c2SPeter Xu uint32_t delivery_mode:3; /* Delivery Mode */ 607f2477c2SPeter Xu uint32_t __avail:4; /* Available spaces for software */ 617f2477c2SPeter Xu uint32_t __reserved_0:3; /* Reserved 0 */ 627f2477c2SPeter Xu uint32_t irte_mode:1; /* IRTE Mode */ 637f2477c2SPeter Xu uint32_t vector:8; /* Interrupt Vector */ 647f2477c2SPeter Xu uint32_t __reserved_1:8; /* Reserved 1 */ 657f2477c2SPeter Xu uint32_t dest_id; /* Destination ID */ 667f2477c2SPeter Xu uint16_t source_id:16; /* Source-ID */ 677f2477c2SPeter Xu uint64_t sid_q:2; /* Source-ID Qualifier */ 687f2477c2SPeter Xu uint64_t sid_vtype:2; /* Source-ID Validation Type */ 697f2477c2SPeter Xu uint64_t __reserved_2:44; /* Reserved 2 */ 707f2477c2SPeter Xu } __attribute__ ((packed)); 717f2477c2SPeter Xu typedef struct vtd_irte vtd_irte_t; 727f2477c2SPeter Xu 7316c0b05fSPeter Xu #define VTD_RTA_MASK (PAGE_MASK) 7416c0b05fSPeter Xu #define VTD_IRTA_MASK (PAGE_MASK) 7516c0b05fSPeter Xu 7616c0b05fSPeter Xu void *vtd_reg_base; 7716c0b05fSPeter Xu 7816c0b05fSPeter Xu static uint64_t vtd_root_table(void) 7916c0b05fSPeter Xu { 8016c0b05fSPeter Xu /* No extend root table support yet */ 8116c0b05fSPeter Xu return vtd_readq(DMAR_RTADDR_REG) & VTD_RTA_MASK; 8216c0b05fSPeter Xu } 8316c0b05fSPeter Xu 8416c0b05fSPeter Xu static uint64_t vtd_ir_table(void) 8516c0b05fSPeter Xu { 8616c0b05fSPeter Xu return vtd_readq(DMAR_IRTA_REG) & VTD_IRTA_MASK; 8716c0b05fSPeter Xu } 8816c0b05fSPeter Xu 8916c0b05fSPeter Xu static void vtd_gcmd_or(uint32_t cmd) 9016c0b05fSPeter Xu { 9116c0b05fSPeter Xu uint32_t status; 9216c0b05fSPeter Xu 9316c0b05fSPeter Xu /* We only allow set one bit for each time */ 9416c0b05fSPeter Xu assert(is_power_of_2(cmd)); 9516c0b05fSPeter Xu 9616c0b05fSPeter Xu status = vtd_readl(DMAR_GSTS_REG); 9716c0b05fSPeter Xu vtd_writel(DMAR_GCMD_REG, status | cmd); 9816c0b05fSPeter Xu 9916c0b05fSPeter Xu if (cmd & VTD_GCMD_ONE_SHOT_BITS) { 10016c0b05fSPeter Xu /* One-shot bits are taking effect immediately */ 10116c0b05fSPeter Xu return; 10216c0b05fSPeter Xu } 10316c0b05fSPeter Xu 10416c0b05fSPeter Xu /* Make sure IOMMU handled our command request */ 10516c0b05fSPeter Xu while (!(vtd_readl(DMAR_GSTS_REG) & cmd)) 10616c0b05fSPeter Xu cpu_relax(); 10716c0b05fSPeter Xu } 10816c0b05fSPeter Xu 10916c0b05fSPeter Xu static void vtd_dump_init_info(void) 11016c0b05fSPeter Xu { 11116c0b05fSPeter Xu uint32_t version; 11216c0b05fSPeter Xu 11316c0b05fSPeter Xu version = vtd_readl(DMAR_VER_REG); 11416c0b05fSPeter Xu 11516c0b05fSPeter Xu /* Major version >= 1 */ 11616c0b05fSPeter Xu assert(((version >> 3) & 0xf) >= 1); 11716c0b05fSPeter Xu 11816c0b05fSPeter Xu printf("VT-d version: 0x%x\n", version); 11916c0b05fSPeter Xu printf(" cap: 0x%016lx\n", vtd_readq(DMAR_CAP_REG)); 12016c0b05fSPeter Xu printf(" ecap: 0x%016lx\n", vtd_readq(DMAR_ECAP_REG)); 12116c0b05fSPeter Xu } 12216c0b05fSPeter Xu 12316c0b05fSPeter Xu static void vtd_setup_root_table(void) 12416c0b05fSPeter Xu { 12516c0b05fSPeter Xu void *root = alloc_page(); 12616c0b05fSPeter Xu 12716c0b05fSPeter Xu memset(root, 0, PAGE_SIZE); 12816c0b05fSPeter Xu vtd_writeq(DMAR_RTADDR_REG, virt_to_phys(root)); 12916c0b05fSPeter Xu vtd_gcmd_or(VTD_GCMD_ROOT); 13016c0b05fSPeter Xu printf("DMAR table address: 0x%016lx\n", vtd_root_table()); 13116c0b05fSPeter Xu } 13216c0b05fSPeter Xu 13316c0b05fSPeter Xu static void vtd_setup_ir_table(void) 13416c0b05fSPeter Xu { 13516c0b05fSPeter Xu void *root = alloc_page(); 13616c0b05fSPeter Xu 13716c0b05fSPeter Xu memset(root, 0, PAGE_SIZE); 13816c0b05fSPeter Xu /* 0xf stands for table size (2^(0xf+1) == 65536) */ 13916c0b05fSPeter Xu vtd_writeq(DMAR_IRTA_REG, virt_to_phys(root) | 0xf); 14016c0b05fSPeter Xu vtd_gcmd_or(VTD_GCMD_IR_TABLE); 14116c0b05fSPeter Xu printf("IR table address: 0x%016lx\n", vtd_ir_table()); 14216c0b05fSPeter Xu } 14316c0b05fSPeter Xu 14492d2c192SPeter Xu static void vtd_install_pte(vtd_pte_t *root, iova_t iova, 14592d2c192SPeter Xu phys_addr_t pa, int level_target) 14692d2c192SPeter Xu { 14792d2c192SPeter Xu int level; 14892d2c192SPeter Xu unsigned int offset; 14992d2c192SPeter Xu void *page; 15092d2c192SPeter Xu 15192d2c192SPeter Xu for (level = VTD_PAGE_LEVEL; level > level_target; level--) { 15292d2c192SPeter Xu offset = PGDIR_OFFSET(iova, level); 15392d2c192SPeter Xu if (!(root[offset] & VTD_PTE_RW)) { 15492d2c192SPeter Xu page = alloc_page(); 15592d2c192SPeter Xu memset(page, 0, PAGE_SIZE); 15692d2c192SPeter Xu root[offset] = virt_to_phys(page) | VTD_PTE_RW; 15792d2c192SPeter Xu } 15892d2c192SPeter Xu root = (uint64_t *)(phys_to_virt(root[offset] & 15992d2c192SPeter Xu VTD_PTE_ADDR)); 16092d2c192SPeter Xu } 16192d2c192SPeter Xu 16292d2c192SPeter Xu offset = PGDIR_OFFSET(iova, level); 16392d2c192SPeter Xu root[offset] = pa | VTD_PTE_RW; 16492d2c192SPeter Xu if (level != 1) { 16592d2c192SPeter Xu /* This is huge page */ 16692d2c192SPeter Xu root[offset] |= VTD_PTE_HUGE; 16792d2c192SPeter Xu } 16892d2c192SPeter Xu } 16992d2c192SPeter Xu 17092d2c192SPeter Xu #define VTD_PHYS_TO_VIRT(x) \ 17192d2c192SPeter Xu ((void *)(((uint64_t)phys_to_virt(x)) >> VTD_PAGE_SHIFT)) 17292d2c192SPeter Xu 17392d2c192SPeter Xu /** 17492d2c192SPeter Xu * vtd_map_range: setup IO address mapping for specific memory range 17592d2c192SPeter Xu * 17692d2c192SPeter Xu * @sid: source ID of the device to setup 17792d2c192SPeter Xu * @iova: start IO virtual address 17892d2c192SPeter Xu * @pa: start physical address 17992d2c192SPeter Xu * @size: size of the mapping area 18092d2c192SPeter Xu */ 18192d2c192SPeter Xu void vtd_map_range(uint16_t sid, iova_t iova, phys_addr_t pa, size_t size) 18292d2c192SPeter Xu { 18392d2c192SPeter Xu uint8_t bus_n, devfn; 18492d2c192SPeter Xu void *slptptr; 18592d2c192SPeter Xu vtd_ce_t *ce; 18692d2c192SPeter Xu vtd_re_t *re = phys_to_virt(vtd_root_table()); 18792d2c192SPeter Xu 18892d2c192SPeter Xu assert(IS_ALIGNED(iova, SZ_4K)); 18992d2c192SPeter Xu assert(IS_ALIGNED(pa, SZ_4K)); 19092d2c192SPeter Xu assert(IS_ALIGNED(size, SZ_4K)); 19192d2c192SPeter Xu 19292d2c192SPeter Xu bus_n = PCI_BDF_GET_BUS(sid); 19392d2c192SPeter Xu devfn = PCI_BDF_GET_DEVFN(sid); 19492d2c192SPeter Xu 19592d2c192SPeter Xu /* Point to the correct root entry */ 19692d2c192SPeter Xu re += bus_n; 19792d2c192SPeter Xu 19892d2c192SPeter Xu if (!re->present) { 19992d2c192SPeter Xu ce = alloc_page(); 20092d2c192SPeter Xu memset(ce, 0, PAGE_SIZE); 20192d2c192SPeter Xu memset(re, 0, sizeof(*re)); 20292d2c192SPeter Xu re->context_table_p = virt_to_phys(ce) >> VTD_PAGE_SHIFT; 20392d2c192SPeter Xu re->present = 1; 20492d2c192SPeter Xu printf("allocated vt-d root entry for PCI bus %d\n", 20592d2c192SPeter Xu bus_n); 20692d2c192SPeter Xu } else 20792d2c192SPeter Xu ce = VTD_PHYS_TO_VIRT(re->context_table_p); 20892d2c192SPeter Xu 20992d2c192SPeter Xu /* Point to the correct context entry */ 21092d2c192SPeter Xu ce += devfn; 21192d2c192SPeter Xu 21292d2c192SPeter Xu if (!ce->present) { 21392d2c192SPeter Xu slptptr = alloc_page(); 21492d2c192SPeter Xu memset(slptptr, 0, PAGE_SIZE); 21592d2c192SPeter Xu memset(ce, 0, sizeof(*ce)); 21692d2c192SPeter Xu /* To make it simple, domain ID is the same as SID */ 21792d2c192SPeter Xu ce->domain_id = sid; 21892d2c192SPeter Xu /* We only test 39 bits width case (3-level paging) */ 21992d2c192SPeter Xu ce->addr_width = VTD_CE_AW_39BIT; 22092d2c192SPeter Xu ce->slptptr = virt_to_phys(slptptr) >> VTD_PAGE_SHIFT; 22192d2c192SPeter Xu ce->trans_type = VTD_CONTEXT_TT_MULTI_LEVEL; 22292d2c192SPeter Xu ce->present = 1; 22392d2c192SPeter Xu /* No error reporting yet */ 22492d2c192SPeter Xu ce->disable_fault_report = 1; 22592d2c192SPeter Xu printf("allocated vt-d context entry for devfn 0x%x\n", 22692d2c192SPeter Xu devfn); 22792d2c192SPeter Xu } else 22892d2c192SPeter Xu slptptr = VTD_PHYS_TO_VIRT(ce->slptptr); 22992d2c192SPeter Xu 23092d2c192SPeter Xu while (size) { 23192d2c192SPeter Xu /* TODO: currently we only map 4K pages (level = 1) */ 23292d2c192SPeter Xu printf("map 4K page IOVA 0x%lx to 0x%lx (sid=0x%04x)\n", 23392d2c192SPeter Xu iova, pa, sid); 23492d2c192SPeter Xu vtd_install_pte(slptptr, iova, pa, 1); 23592d2c192SPeter Xu size -= VTD_PAGE_SIZE; 23692d2c192SPeter Xu iova += VTD_PAGE_SIZE; 23792d2c192SPeter Xu pa += VTD_PAGE_SIZE; 23892d2c192SPeter Xu } 23992d2c192SPeter Xu } 24092d2c192SPeter Xu 2417f2477c2SPeter Xu static uint16_t vtd_intr_index_alloc(void) 2427f2477c2SPeter Xu { 243b4377753SPeter Xu static volatile int index_ctr = 0; 244b4377753SPeter Xu int ctr; 245b4377753SPeter Xu 2467f2477c2SPeter Xu assert(index_ctr < 65535); 247b4377753SPeter Xu ctr = atomic_inc_fetch(&index_ctr); 248b4377753SPeter Xu printf("INTR: alloc IRTE index %d\n", ctr); 249b4377753SPeter Xu return ctr; 2507f2477c2SPeter Xu } 2517f2477c2SPeter Xu 2527f2477c2SPeter Xu static void vtd_setup_irte(struct pci_dev *dev, vtd_irte_t *irte, 253560586d9SPeter Xu int vector, int dest_id, trigger_mode_t trigger) 2547f2477c2SPeter Xu { 2557f2477c2SPeter Xu assert(sizeof(vtd_irte_t) == 16); 2567f2477c2SPeter Xu memset(irte, 0, sizeof(*irte)); 2577f2477c2SPeter Xu irte->fault_disable = 1; 2587f2477c2SPeter Xu irte->dest_mode = 0; /* physical */ 259560586d9SPeter Xu irte->trigger_mode = trigger; 2607f2477c2SPeter Xu irte->delivery_mode = 0; /* fixed */ 2617f2477c2SPeter Xu irte->irte_mode = 0; /* remapped */ 2627f2477c2SPeter Xu irte->vector = vector; 2637f2477c2SPeter Xu irte->dest_id = dest_id; 2647f2477c2SPeter Xu irte->source_id = dev->bdf; 2657f2477c2SPeter Xu irte->sid_q = 0; 2667f2477c2SPeter Xu irte->sid_vtype = 1; /* full-sid verify */ 2677f2477c2SPeter Xu irte->present = 1; 2687f2477c2SPeter Xu } 2697f2477c2SPeter Xu 2707f2477c2SPeter Xu struct vtd_msi_addr { 2717f2477c2SPeter Xu uint32_t __dont_care:2; 2727f2477c2SPeter Xu uint32_t handle_15:1; /* handle[15] */ 2737f2477c2SPeter Xu uint32_t shv:1; 2747f2477c2SPeter Xu uint32_t interrupt_format:1; 2757f2477c2SPeter Xu uint32_t handle_0_14:15; /* handle[0:14] */ 2767f2477c2SPeter Xu uint32_t head:12; /* 0xfee */ 2777f2477c2SPeter Xu uint32_t addr_hi; /* not used except with x2apic */ 2787f2477c2SPeter Xu } __attribute__ ((packed)); 2797f2477c2SPeter Xu typedef struct vtd_msi_addr vtd_msi_addr_t; 2807f2477c2SPeter Xu 2817f2477c2SPeter Xu struct vtd_msi_data { 2827f2477c2SPeter Xu uint16_t __reserved; 2837f2477c2SPeter Xu uint16_t subhandle; 2847f2477c2SPeter Xu } __attribute__ ((packed)); 2857f2477c2SPeter Xu typedef struct vtd_msi_data vtd_msi_data_t; 2867f2477c2SPeter Xu 287*3223ade2SPeter Xu struct vtd_ioapic_entry { 288*3223ade2SPeter Xu uint64_t vector:8; 289*3223ade2SPeter Xu uint64_t __zeros:3; 290*3223ade2SPeter Xu uint64_t index_15:1; 291*3223ade2SPeter Xu uint64_t delivery_status:1; 292*3223ade2SPeter Xu uint64_t polarity:1; 293*3223ade2SPeter Xu uint64_t remote_irr:1; 294*3223ade2SPeter Xu uint64_t trigger_mode:1; 295*3223ade2SPeter Xu uint64_t mask:1; 296*3223ade2SPeter Xu uint64_t __zeros_2:31; 297*3223ade2SPeter Xu uint64_t interrupt_format:1; 298*3223ade2SPeter Xu uint64_t index_0_14:15; 299*3223ade2SPeter Xu } __attribute__ ((packed)); 300*3223ade2SPeter Xu typedef struct vtd_ioapic_entry vtd_ioapic_entry_t; 301*3223ade2SPeter Xu 3027f2477c2SPeter Xu /** 3037f2477c2SPeter Xu * vtd_setup_msi - setup MSI message for a device 3047f2477c2SPeter Xu * 3057f2477c2SPeter Xu * @dev: PCI device to setup MSI 3067f2477c2SPeter Xu * @vector: interrupt vector 3077f2477c2SPeter Xu * @dest_id: destination processor 3087f2477c2SPeter Xu */ 3097f2477c2SPeter Xu bool vtd_setup_msi(struct pci_dev *dev, int vector, int dest_id) 3107f2477c2SPeter Xu { 3117f2477c2SPeter Xu vtd_msi_data_t msi_data = {}; 3127f2477c2SPeter Xu vtd_msi_addr_t msi_addr = {}; 3137f2477c2SPeter Xu vtd_irte_t *irte = phys_to_virt(vtd_ir_table()); 3147f2477c2SPeter Xu uint16_t index = vtd_intr_index_alloc(); 3157f2477c2SPeter Xu 3167f2477c2SPeter Xu assert(sizeof(vtd_msi_addr_t) == 8); 3177f2477c2SPeter Xu assert(sizeof(vtd_msi_data_t) == 4); 3187f2477c2SPeter Xu 319560586d9SPeter Xu /* Use edge irq as default */ 320560586d9SPeter Xu vtd_setup_irte(dev, irte + index, vector, 321560586d9SPeter Xu dest_id, TRIGGER_EDGE); 3227f2477c2SPeter Xu 3237f2477c2SPeter Xu msi_addr.handle_15 = index >> 15 & 1; 3247f2477c2SPeter Xu msi_addr.shv = 0; 3257f2477c2SPeter Xu msi_addr.interrupt_format = 1; 3267f2477c2SPeter Xu msi_addr.handle_0_14 = index & 0x7fff; 3277f2477c2SPeter Xu msi_addr.head = 0xfee; 3287f2477c2SPeter Xu msi_data.subhandle = 0; 3297f2477c2SPeter Xu 3307f2477c2SPeter Xu return pci_setup_msi(dev, *(uint64_t *)&msi_addr, 3317f2477c2SPeter Xu *(uint32_t *)&msi_data); 3327f2477c2SPeter Xu } 3337f2477c2SPeter Xu 334*3223ade2SPeter Xu void vtd_setup_ioapic_irq(struct pci_dev *dev, int vector, 335*3223ade2SPeter Xu int dest_id, trigger_mode_t trigger) 336*3223ade2SPeter Xu { 337*3223ade2SPeter Xu vtd_ioapic_entry_t entry = {}; 338*3223ade2SPeter Xu vtd_irte_t *irte = phys_to_virt(vtd_ir_table()); 339*3223ade2SPeter Xu ioapic_redir_entry_t *entry_2 = (ioapic_redir_entry_t *)&entry; 340*3223ade2SPeter Xu uint16_t index = vtd_intr_index_alloc(); 341*3223ade2SPeter Xu uint8_t line; 342*3223ade2SPeter Xu 343*3223ade2SPeter Xu assert(dev); 344*3223ade2SPeter Xu assert(sizeof(vtd_ioapic_entry_t) == 8); 345*3223ade2SPeter Xu 346*3223ade2SPeter Xu vtd_setup_irte(dev, irte + index, vector, 347*3223ade2SPeter Xu dest_id, trigger); 348*3223ade2SPeter Xu 349*3223ade2SPeter Xu entry.vector = vector; 350*3223ade2SPeter Xu entry.trigger_mode = trigger; 351*3223ade2SPeter Xu entry.index_15 = (index >> 15) & 1; 352*3223ade2SPeter Xu entry.interrupt_format = 1; 353*3223ade2SPeter Xu entry.index_0_14 = index & 0x7fff; 354*3223ade2SPeter Xu 355*3223ade2SPeter Xu line = pci_intx_line(dev); 356*3223ade2SPeter Xu ioapic_write_redir(line, *entry_2); 357*3223ade2SPeter Xu } 358*3223ade2SPeter Xu 35916c0b05fSPeter Xu void vtd_init(void) 36016c0b05fSPeter Xu { 36116c0b05fSPeter Xu setup_vm(); 36216c0b05fSPeter Xu smp_init(); 36316c0b05fSPeter Xu 36416c0b05fSPeter Xu vtd_reg_base = ioremap(Q35_HOST_BRIDGE_IOMMU_ADDR, PAGE_SIZE); 36516c0b05fSPeter Xu 36616c0b05fSPeter Xu vtd_dump_init_info(); 36716c0b05fSPeter Xu vtd_gcmd_or(VTD_GCMD_QI); /* Enable QI */ 36816c0b05fSPeter Xu vtd_setup_root_table(); 36916c0b05fSPeter Xu vtd_setup_ir_table(); 37016c0b05fSPeter Xu vtd_gcmd_or(VTD_GCMD_DMAR); /* Enable DMAR */ 37116c0b05fSPeter Xu vtd_gcmd_or(VTD_GCMD_IR); /* Enable IR */ 37216c0b05fSPeter Xu } 373