Sophie

Sophie

distrib > Scientific%20Linux > 5x > x86_64 > by-pkgid > fc11cd6e1c513a17304da94a5390f3cd > files > 4076

kernel-2.6.18-194.11.1.el5.src.rpm

From: Bhavna Sarathy <bnagendr@redhat.com>
Date: Wed, 28 Oct 2009 19:59:00 -0400
Subject: [x86_64] amd: iommu system management erratum 63 fix
Message-id: <4AE8A284.8050900@redhat.com>
Patchwork-id: 21247
O-Subject: Re: [RHEL5 PATCH] Add AMD IOMMU erratum fix
Bugzilla: 531469
RH-Acked-by: Prarit Bhargava <prarit@redhat.com>
RH-Acked-by: Don Dutile <ddutile@redhat.com>

Resolves BZ 531469

This patch fixes an AMD erratum that results in an IO PAGE FAULT on
Magny-cours while  booting up RHEL5.x.   A recent BIOS enabled access
to the system management bits. Linux marks all enries in device table
as valid at driver attach time, but leaves
the SysMgt bits at zero, which is defines as target abort all system
management requests.   Those sysmgt bits are filled according to IVHD
only at the time when device driver makes memory bind request.
The problem is that SMBus that generates those system management
accesses never asks to bind memory (i.e. it never does regular DMA).
Therefore those bits are never set. This results in IO PAGE FAULT
messages at boot time on the M-C system.

AMD Erratum 63:
    IOMMU Blocks Writes in the HT System Management Address Range when
    SysMgt=0x1

    Write requests to the HT system management address range may be
    blocked by the IOMMU when the associated device table entry has the
    SysMgt field set to 01b (pass through writes) with V=1, TV=1 and IW=0.

    Potential Effect on System: Unpredictable system behaviour.

    This issue has only been observed in the simulation environment.
    Suggested Workaround Only set DTE.IW=1 for the south bridge ACPI
    controller as this is the only device in the system that should
    issue system management requests.

The function iommu_feature_disable is required on system shutdown to
disable the IOMMU but it is marked as __init.  This may result in a
panic if the memory is reused.  I have added this one line fix
to the erratum fix for the AMD IOMMU driver.

Testing:
Successfully tested on Dinar system, and confirmed that with patch
the issue is resolves.

Upstream: The patch will be submitted upstream for inclusion in
2.6.32.   The patch was literally developed yesterday.

diff --git a/arch/x86_64/kernel/amd_iommu.c b/arch/x86_64/kernel/amd_iommu.c
index 945fbd3..6e19734 100644
--- a/arch/x86_64/kernel/amd_iommu.c
+++ b/arch/x86_64/kernel/amd_iommu.c
@@ -969,6 +969,8 @@ static void __detach_device(struct protection_domain *domain, u16 devid)
 	amd_iommu_dev_table[devid].data[1] = 0;
 	amd_iommu_dev_table[devid].data[2] = 0;
 
+	amd_iommu_apply_erratum_63(devid);
+
 	/* decrease reference counter */
 	domain->dev_cnt -= 1;
 
diff --git a/arch/x86_64/kernel/amd_iommu_init.c b/arch/x86_64/kernel/amd_iommu_init.c
index 34dc114..043efd1 100644
--- a/arch/x86_64/kernel/amd_iommu_init.c
+++ b/arch/x86_64/kernel/amd_iommu_init.c
@@ -232,7 +232,7 @@ static void __init iommu_feature_enable(struct amd_iommu *iommu, u8 bit)
 	writel(ctrl, iommu->mmio_base + MMIO_CONTROL_OFFSET);
 }
 
-static void __init iommu_feature_disable(struct amd_iommu *iommu, u8 bit)
+static void iommu_feature_disable(struct amd_iommu *iommu, u8 bit)
 {
 	u32 ctrl;
 
@@ -476,6 +476,25 @@ static void set_dev_entry_bit(u16 devid, u8 bit)
 	amd_iommu_dev_table[devid].data[i] |= (1 << _bit);
 }
 
+static int get_dev_entry_bit(u16 devid, u8 bit)
+{
+	int i = (bit >> 5) & 0x07;
+	int _bit = bit & 0x1f;
+
+	return (amd_iommu_dev_table[devid].data[i] & (1 << _bit)) >> _bit;
+}
+
+void amd_iommu_apply_erratum_63(u16 devid)
+{
+	int sysmgt;
+
+	sysmgt = get_dev_entry_bit(devid, DEV_ENTRY_SYSMGT1) |
+		 (get_dev_entry_bit(devid, DEV_ENTRY_SYSMGT2) << 1);
+
+	if (sysmgt == 0x01)
+		set_dev_entry_bit(devid, DEV_ENTRY_IW);
+}
+
 /* Writes the specific IOMMU for a device into the rlookup table */
 static void __init set_iommu_for_device(struct amd_iommu *iommu, u16 devid)
 {
@@ -504,6 +523,8 @@ static void __init set_dev_entry_from_acpi(struct amd_iommu *iommu,
 	if (flags & ACPI_DEVFLAG_LINT1)
 		set_dev_entry_bit(devid, DEV_ENTRY_LINT1_PASS);
 
+	amd_iommu_apply_erratum_63(devid);
+
 	set_iommu_for_device(iommu, devid);
 }
 
diff --git a/include/asm-x86_64/amd_iommu.h b/include/asm-x86_64/amd_iommu.h
index 1e689c1..cbf6829 100644
--- a/include/asm-x86_64/amd_iommu.h
+++ b/include/asm-x86_64/amd_iommu.h
@@ -28,6 +28,7 @@ extern int amd_iommu_init_dma_ops(void);
 extern void amd_iommu_detect(void);
 extern irqreturn_t amd_iommu_int_handler(int irq, void *data,
 					 struct pt_regs *regs);
+extern void amd_iommu_apply_erratum_63(u16 devid);
 #else
 static inline int amd_iommu_init(void) { return -ENODEV; }
 static inline void amd_iommu_detect(void) { }