Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 9d783ba0 authored by Suresh Siddha's avatar Suresh Siddha Committed by H. Peter Anvin
Browse files

x86, x2apic: enable fault handling for intr-remapping



Impact: interface augmentation (not yet used)

Enable fault handling flow for intr-remapping aswell. Fault handling
code now shared by both dma-remapping and intr-remapping.

Signed-off-by: default avatarSuresh Siddha <suresh.b.siddha@intel.com>
Signed-off-by: default avatarH. Peter Anvin <hpa@linux.intel.com>
parent 0ac2491f
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -47,6 +47,7 @@
#define	 MSI_ADDR_DEST_ID_MASK		0x00ffff0
#define  MSI_ADDR_DEST_ID(dest)		(((dest) << MSI_ADDR_DEST_ID_SHIFT) & \
					 MSI_ADDR_DEST_ID_MASK)
#define MSI_ADDR_EXT_DEST_ID(dest)	((dest) & 0xffffff00)

#define MSI_ADDR_IR_EXT_INT		(1 << 4)
#define MSI_ADDR_IR_SHV			(1 << 3)
+7 −2
Original line number Diff line number Diff line
@@ -3294,7 +3294,12 @@ static int msi_compose_msg(struct pci_dev *pdev, unsigned int irq, struct msi_ms
	} else
#endif
	{
		if (x2apic_enabled())
			msg->address_hi = MSI_ADDR_BASE_HI |
					  MSI_ADDR_EXT_DEST_ID(dest);
		else
			msg->address_hi = MSI_ADDR_BASE_HI;

		msg->address_lo =
			MSI_ADDR_BASE_LO |
			((apic->irq_dest_mode == 0) ?
@@ -3528,7 +3533,7 @@ void arch_teardown_msi_irq(unsigned int irq)
	destroy_irq(irq);
}

#ifdef CONFIG_DMAR
#if defined (CONFIG_DMAR) || defined (CONFIG_INTR_REMAP)
#ifdef CONFIG_SMP
static void dmar_msi_set_affinity(unsigned int irq, const struct cpumask *mask)
{
+9 −0
Original line number Diff line number Diff line
@@ -68,6 +68,15 @@ void __init default_setup_apic_routing(void)
			apic = &apic_physflat;
		printk(KERN_INFO "Setting APIC routing to %s\n", apic->name);
	}

#ifdef CONFIG_X86_X2APIC
	/*
	 * Now that apic routing model is selected, configure the
	 * fault handling for intr remapping.
	 */
	if (intr_remapping_enabled)
		enable_drhd_fault_handling();
#endif
}

/* Same for both flat and physical. */
+84 −18
Original line number Diff line number Diff line
@@ -511,6 +511,7 @@ int alloc_iommu(struct dmar_drhd_unit *drhd)
		return -ENOMEM;

	iommu->seq_id = iommu_allocated++;
	sprintf (iommu->name, "dmar%d", iommu->seq_id);

	iommu->reg = ioremap(drhd->reg_base_addr, VTD_PAGE_SIZE);
	if (!iommu->reg) {
@@ -817,7 +818,13 @@ int dmar_enable_qi(struct intel_iommu *iommu)

/* iommu interrupt handling. Most stuff are MSI-like. */

static const char *fault_reason_strings[] =
enum faulttype {
	DMA_REMAP,
	INTR_REMAP,
	UNKNOWN,
};

static const char *dma_remap_fault_reasons[] =
{
	"Software",
	"Present bit in root entry is clear",
@@ -833,14 +840,33 @@ static const char *fault_reason_strings[] =
	"non-zero reserved fields in CTP",
	"non-zero reserved fields in PTE",
};

static const char *intr_remap_fault_reasons[] =
{
	"Detected reserved fields in the decoded interrupt-remapped request",
	"Interrupt index exceeded the interrupt-remapping table size",
	"Present field in the IRTE entry is clear",
	"Error accessing interrupt-remapping table pointed by IRTA_REG",
	"Detected reserved fields in the IRTE entry",
	"Blocked a compatibility format interrupt request",
	"Blocked an interrupt request due to source-id verification failure",
};

#define MAX_FAULT_REASON_IDX 	(ARRAY_SIZE(fault_reason_strings) - 1)

const char *dmar_get_fault_reason(u8 fault_reason)
const char *dmar_get_fault_reason(u8 fault_reason, int *fault_type)
{
	if (fault_reason > MAX_FAULT_REASON_IDX)
	if (fault_reason >= 0x20 && (fault_reason <= 0x20 +
				     ARRAY_SIZE(intr_remap_fault_reasons))) {
		*fault_type = INTR_REMAP;
		return intr_remap_fault_reasons[fault_reason - 0x20];
	} else if (fault_reason < ARRAY_SIZE(dma_remap_fault_reasons)) {
		*fault_type = DMA_REMAP;
		return dma_remap_fault_reasons[fault_reason];
	} else {
		*fault_type = UNKNOWN;
		return "Unknown";
	else
		return fault_reason_strings[fault_reason];
	}
}

void dmar_msi_unmask(unsigned int irq)
@@ -897,9 +923,18 @@ static int dmar_fault_do_one(struct intel_iommu *iommu, int type,
		u8 fault_reason, u16 source_id, unsigned long long addr)
{
	const char *reason;
	int fault_type;

	reason = dmar_get_fault_reason(fault_reason);
	reason = dmar_get_fault_reason(fault_reason, &fault_type);

	if (fault_type == INTR_REMAP)
		printk(KERN_ERR "INTR-REMAP: Request device [[%02x:%02x.%d] "
		       "fault index %llx\n"
			"INTR-REMAP:[fault reason %02d] %s\n",
			(source_id >> 8), PCI_SLOT(source_id & 0xFF),
			PCI_FUNC(source_id & 0xFF), addr >> 48,
			fault_reason, reason);
	else
		printk(KERN_ERR
		       "DMAR:[%s] Request device [%02x:%02x.%d] "
		       "fault addr %llx \n"
@@ -920,10 +955,13 @@ static irqreturn_t dmar_fault(int irq, void *dev_id)

	spin_lock_irqsave(&iommu->register_lock, flag);
	fault_status = readl(iommu->reg + DMAR_FSTS_REG);
	if (fault_status)
		printk(KERN_ERR "DRHD: handling fault status reg %x\n",
		       fault_status);

	/* TBD: ignore advanced fault log currently */
	if (!(fault_status & DMA_FSTS_PPF))
		goto clear_overflow;
		goto clear_rest;

	fault_index = dma_fsts_fault_record_index(fault_status);
	reg = cap_fault_reg_offset(iommu->cap);
@@ -964,11 +1002,10 @@ static irqreturn_t dmar_fault(int irq, void *dev_id)
			fault_index = 0;
		spin_lock_irqsave(&iommu->register_lock, flag);
	}
clear_overflow:
	/* clear primary fault overflow */
clear_rest:
	/* clear all the other faults */
	fault_status = readl(iommu->reg + DMAR_FSTS_REG);
	if (fault_status & DMA_FSTS_PFO)
		writel(DMA_FSTS_PFO, iommu->reg + DMAR_FSTS_REG);
	writel(fault_status, iommu->reg + DMAR_FSTS_REG);

	spin_unlock_irqrestore(&iommu->register_lock, flag);
	return IRQ_HANDLED;
@@ -978,6 +1015,12 @@ int dmar_set_interrupt(struct intel_iommu *iommu)
{
	int irq, ret;

	/*
	 * Check if the fault interrupt is already initialized.
	 */
	if (iommu->irq)
		return 0;

	irq = create_irq();
	if (!irq) {
		printk(KERN_ERR "IOMMU: no free vectors\n");
@@ -1003,3 +1046,26 @@ int dmar_set_interrupt(struct intel_iommu *iommu)
		printk(KERN_ERR "IOMMU: can't request irq\n");
	return ret;
}

int __init enable_drhd_fault_handling(void)
{
	struct dmar_drhd_unit *drhd;

	/*
	 * Enable fault control interrupt.
	 */
	for_each_drhd_unit(drhd) {
		int ret;
		struct intel_iommu *iommu = drhd->iommu;
		ret = dmar_set_interrupt(iommu);

		if (ret) {
			printk(KERN_ERR "DRHD %Lx: failed to enable fault, "
			       " interrupt, ret %d\n",
			       (unsigned long long)drhd->reg_base_addr, ret);
			return -1;
		}
	}

	return 0;
}
+1 −2
Original line number Diff line number Diff line
@@ -1799,7 +1799,7 @@ static int __init init_dmars(void)
	struct dmar_rmrr_unit *rmrr;
	struct pci_dev *pdev;
	struct intel_iommu *iommu;
	int i, ret, unit = 0;
	int i, ret;

	/*
	 * for each drhd
@@ -1921,7 +1921,6 @@ static int __init init_dmars(void)
		if (drhd->ignored)
			continue;
		iommu = drhd->iommu;
		sprintf (iommu->name, "dmar%d", unit++);

		iommu_flush_write_buffer(iommu);

Loading