* [PATCH] x86: Reenable the AMD IOMMU if it's mysteriously vanished over suspend
@ 2010-10-01 14:01 Matthew Garrett
2010-10-04 13:43 ` Roedel, Joerg
0 siblings, 1 reply; 6+ messages in thread
From: Matthew Garrett @ 2010-10-01 14:01 UTC (permalink / raw)
To: linux-kernel
Cc: linux-pci, joerg.roedel, x86, jbarnes, Matthew Garrett, stable
AMD's reference BIOS code had a bug that could result in the firmware
failing to reenable the iommu on resume. It transpires that this causes
certain less than desirable behaviour when it comes to PCI accesses, to
whit them ending up somewhere near Bristol when the more desirable outcome
was Edinburgh. Sadness ensues, perhaps along with filesystem corruption.
Let's make sure that it gets turned back on, and that we restore its
configuration so decisions it makes bear some resemblance to those made by
reasonable people rather than crack-addled lemurs who spent all your DMA
on Thunderbird.
Signed-off-by: Matthew Garrett <mjg@redhat.com>
Cc: stable@kernel.org
---
Turns out that PCI resume is too late for this, so incorporate it all into
the sysdev resume code in the driver
arch/x86/include/asm/amd_iommu_types.h | 21 ++++--
arch/x86/kernel/amd_iommu_init.c | 134 +++++++++++++++++++++++++++++---
2 files changed, 137 insertions(+), 18 deletions(-)
diff --git a/arch/x86/include/asm/amd_iommu_types.h b/arch/x86/include/asm/amd_iommu_types.h
index 0861618..bdd20c8 100644
--- a/arch/x86/include/asm/amd_iommu_types.h
+++ b/arch/x86/include/asm/amd_iommu_types.h
@@ -416,13 +416,22 @@ struct amd_iommu {
struct dma_ops_domain *default_dom;
/*
- * This array is required to work around a potential BIOS bug.
- * The BIOS may miss to restore parts of the PCI configuration
- * space when the system resumes from S3. The result is that the
- * IOMMU does not execute commands anymore which leads to system
- * failure.
+ * We can't rely on the BIOS to restore all values on reinit, so we
+ * need to stash them
*/
- u32 cache_cfg[4];
+
+ /* The iommu BAR */
+ u32 stored_addr_lo;
+ u32 stored_addr_hi;
+
+ /*
+ * Each iommu has 6 l1s, each of which is documented as having 0x12
+ * registers
+ */
+ u32 stored_l1[6][0x12];
+
+ /* The l2 indirect registers */
+ u32 stored_l2[0x83];
};
/*
diff --git a/arch/x86/kernel/amd_iommu_init.c b/arch/x86/kernel/amd_iommu_init.c
index 5a170cb..7ce2936 100644
--- a/arch/x86/kernel/amd_iommu_init.c
+++ b/arch/x86/kernel/amd_iommu_init.c
@@ -194,6 +194,39 @@ static inline unsigned long tbl_size(int entry_size)
return 1UL << shift;
}
+/* Access to l1 and l2 indexed register spaces */
+
+static u32 iommu_read_l1(struct amd_iommu *iommu, u16 l1, u8 address)
+{
+ u32 val;
+
+ pci_write_config_dword(iommu->dev, 0xf8, (address | l1 << 16));
+ pci_read_config_dword(iommu->dev, 0xfc, &val);
+ return val;
+}
+
+static void iommu_write_l1(struct amd_iommu *iommu, u16 l1, u8 address, u32 val)
+{
+ pci_write_config_dword(iommu->dev, 0xf8, (address | l1 << 16 | 1 << 31));
+ pci_write_config_dword(iommu->dev, 0xfc, val);
+ pci_write_config_dword(iommu->dev, 0xf8, (address | l1 << 16));
+}
+
+static u32 iommu_read_l2(struct amd_iommu *iommu, u8 address)
+{
+ u32 val;
+
+ pci_write_config_dword(iommu->dev, 0xf0, address);
+ pci_read_config_dword(iommu->dev, 0xf4, &val);
+ return val;
+}
+
+static void iommu_write_l2(struct amd_iommu *iommu, u8 address, u32 val)
+{
+ pci_write_config_dword(iommu->dev, 0xf0, (address | 1 << 8));
+ pci_write_config_dword(iommu->dev, 0xf4, val);
+}
+
/****************************************************************************
*
* AMD IOMMU MMIO register space handling functions
@@ -619,6 +652,7 @@ static void __init init_iommu_from_pci(struct amd_iommu *iommu)
{
int cap_ptr = iommu->cap_ptr;
u32 range, misc;
+ int i, j;
pci_read_config_dword(iommu->dev, cap_ptr + MMIO_CAP_HDR_OFFSET,
&iommu->cap);
@@ -633,12 +667,27 @@ static void __init init_iommu_from_pci(struct amd_iommu *iommu)
MMIO_GET_LD(range));
iommu->evt_msi_num = MMIO_MSI_NUM(misc);
- if (is_rd890_iommu(iommu->dev)) {
- pci_read_config_dword(iommu->dev, 0xf0, &iommu->cache_cfg[0]);
- pci_read_config_dword(iommu->dev, 0xf4, &iommu->cache_cfg[1]);
- pci_read_config_dword(iommu->dev, 0xf8, &iommu->cache_cfg[2]);
- pci_read_config_dword(iommu->dev, 0xfc, &iommu->cache_cfg[3]);
- }
+ if (!is_rd890_iommu(iommu->dev))
+ return;
+
+ /*
+ * Some rd890 systems may not be fully reconfigured by the BIOS, so
+ * it's necessary for us to store this information so it can be
+ * reprogrammed on resume
+ */
+
+ pci_read_config_dword(iommu->dev, 0x44, &iommu->stored_addr_lo);
+ pci_read_config_dword(iommu->dev, 0x48, &iommu->stored_addr_hi);
+
+ /* Low bit locks writes to configuration space */
+ iommu->stored_addr_lo &= ~1;
+
+ for (i = 0; i < 6; i++)
+ for (j = 0; j < 0x12; j++)
+ iommu->stored_l1[i][j] = iommu_read_l1(iommu, i, j);
+
+ for (i = 0; i < 0x83; i++)
+ iommu->stored_l2[i] = iommu_read_l2(iommu, i);
}
/*
@@ -1129,12 +1178,69 @@ static void iommu_init_flags(struct amd_iommu *iommu)
static void iommu_apply_quirks(struct amd_iommu *iommu)
{
- if (is_rd890_iommu(iommu->dev)) {
- pci_write_config_dword(iommu->dev, 0xf0, iommu->cache_cfg[0]);
- pci_write_config_dword(iommu->dev, 0xf4, iommu->cache_cfg[1]);
- pci_write_config_dword(iommu->dev, 0xf8, iommu->cache_cfg[2]);
- pci_write_config_dword(iommu->dev, 0xfc, iommu->cache_cfg[3]);
+ int i, j;
+ u32 ioc_feature_control;
+ struct pci_dev *pdev = NULL;
+ int device_id;
+
+ /* RD890 BIOSes may not have completely reconfigured the iommu */
+ if (!is_rd890_iommu(iommu->dev))
+ return;
+
+ /*
+ * First, we need to ensure that the iommu is enabled. This is
+ * controlled by a register in the northbridge
+ */
+ device_id = 0x5a10;
+ pdev = pci_get_device(PCI_VENDOR_ID_ATI, device_id, NULL);
+
+ if (!pdev) {
+ device_id = 0x5a12;
+ pdev = pci_get_device(PCI_VENDOR_ID_ATI, device_id, NULL);
}
+
+ if (!pdev) {
+ device_id = 0x5a13;
+ pdev = pci_get_device(PCI_VENDOR_ID_ATI, device_id, NULL);
+ }
+
+ if (!pdev)
+ return;
+
+ /* There may be one iommu per bus, so find the appropriate bridge */
+ while (pdev && (pdev->bus->number != iommu->dev->bus->number)) {
+ pci_dev_put(pdev);
+ pdev = pci_get_device(PCI_VENDOR_ID_ATI, device_id, pdev);
+ }
+
+ if (!pdev)
+ return;
+
+ /* Select Northbridge indirect register 0x75 and enable writing */
+ pci_write_config_dword(pdev, 0x60, 0x75 | (1 << 7));
+ pci_read_config_dword(pdev, 0x64, &ioc_feature_control);
+
+ /* Enable the iommu */
+ if (!(ioc_feature_control & 0x1))
+ pci_write_config_dword(pdev, 0x64, ioc_feature_control | 1);
+
+ pci_dev_put(pdev);
+
+ /* Restore the iommu BAR */
+ pci_write_config_dword(iommu->dev, 0x44, iommu->stored_addr_lo);
+ pci_write_config_dword(iommu->dev, 0x48, iommu->stored_addr_hi);
+
+ /* Restore the l1 indirect regs for each of the 6 l1s */
+ for (i = 0; i < 6; i++)
+ for (j = 0; j < 0x12; j++)
+ iommu_write_l1(iommu, i, j, iommu->stored_l1[i][j]);
+
+ /* Restore the l2 indirect regs */
+ for (i = 0; i < 0x83; i++)
+ iommu_write_l2(iommu, i, iommu->stored_l2[i]);
+
+ /* Lock PCI setup registers */
+ pci_write_config_dword(iommu->dev, 0x44, iommu->stored_addr_lo | 1);
}
/*
@@ -1147,7 +1253,6 @@ static void enable_iommus(void)
for_each_iommu(iommu) {
iommu_disable(iommu);
- iommu_apply_quirks(iommu);
iommu_init_flags(iommu);
iommu_set_device_table(iommu);
iommu_enable_command_buffer(iommu);
@@ -1173,6 +1278,11 @@ static void disable_iommus(void)
static int amd_iommu_resume(struct sys_device *dev)
{
+ struct amd_iommu *iommu;
+
+ for_each_iommu(iommu)
+ iommu_apply_quirks(iommu);
+
/* re-load the hardware */
enable_iommus();
--
1.7.3
^ permalink raw reply related [flat|nested] 6+ messages in thread
* Re: [PATCH] x86: Reenable the AMD IOMMU if it's mysteriously vanished over suspend
2010-10-01 14:01 [PATCH] x86: Reenable the AMD IOMMU if it's mysteriously vanished over suspend Matthew Garrett
@ 2010-10-04 13:43 ` Roedel, Joerg
2010-10-04 13:55 ` Matthew Garrett
2010-10-04 18:59 ` [PATCH v2] " Matthew Garrett
0 siblings, 2 replies; 6+ messages in thread
From: Roedel, Joerg @ 2010-10-04 13:43 UTC (permalink / raw)
To: Matthew Garrett
Cc: linux-kernel@vger.kernel.org, linux-pci@vger.kernel.org,
x86@kernel.org, jbarnes@virtuousgeek.org, stable@kernel.org
Hi Matthew,
first, thanks a lot for your work on this. I had the plan to do this
myself but are busy with other work too. I am very happy that I can
remove this from my todo list :-)
I have tested the patch on one of my test machines with the BIOS bug. It
worked and the box came out of S3 again. The patch needs certainly more
testing on more platforms. I also have a few comments, find them inline
in the patch.
Joerg
On Fri, Oct 01, 2010 at 10:01:23AM -0400, Matthew Garrett wrote:
> @@ -619,6 +652,7 @@ static void __init init_iommu_from_pci(struct amd_iommu *iommu)
> {
> int cap_ptr = iommu->cap_ptr;
> u32 range, misc;
> + int i, j;
>
> pci_read_config_dword(iommu->dev, cap_ptr + MMIO_CAP_HDR_OFFSET,
> &iommu->cap);
> @@ -633,12 +667,27 @@ static void __init init_iommu_from_pci(struct amd_iommu *iommu)
> MMIO_GET_LD(range));
> iommu->evt_msi_num = MMIO_MSI_NUM(misc);
>
> - if (is_rd890_iommu(iommu->dev)) {
> - pci_read_config_dword(iommu->dev, 0xf0, &iommu->cache_cfg[0]);
> - pci_read_config_dword(iommu->dev, 0xf4, &iommu->cache_cfg[1]);
> - pci_read_config_dword(iommu->dev, 0xf8, &iommu->cache_cfg[2]);
> - pci_read_config_dword(iommu->dev, 0xfc, &iommu->cache_cfg[3]);
> - }
> + if (!is_rd890_iommu(iommu->dev))
> + return;
> +
> + /*
> + * Some rd890 systems may not be fully reconfigured by the BIOS, so
> + * it's necessary for us to store this information so it can be
> + * reprogrammed on resume
> + */
> +
> + pci_read_config_dword(iommu->dev, 0x44, &iommu->stored_addr_lo);
> + pci_read_config_dword(iommu->dev, 0x48, &iommu->stored_addr_hi);
Can you use iommu->cap_ptr here? This variant should be safe too but the
magic numbers are non-descriptive.
> static void iommu_apply_quirks(struct amd_iommu *iommu)
> {
> - if (is_rd890_iommu(iommu->dev)) {
> - pci_write_config_dword(iommu->dev, 0xf0, iommu->cache_cfg[0]);
> - pci_write_config_dword(iommu->dev, 0xf4, iommu->cache_cfg[1]);
> - pci_write_config_dword(iommu->dev, 0xf8, iommu->cache_cfg[2]);
> - pci_write_config_dword(iommu->dev, 0xfc, iommu->cache_cfg[3]);
> + int i, j;
> + u32 ioc_feature_control;
> + struct pci_dev *pdev = NULL;
> + int device_id;
> +
> + /* RD890 BIOSes may not have completely reconfigured the iommu */
> + if (!is_rd890_iommu(iommu->dev))
> + return;
> +
> + /*
> + * First, we need to ensure that the iommu is enabled. This is
> + * controlled by a register in the northbridge
> + */
> + device_id = 0x5a10;
> + pdev = pci_get_device(PCI_VENDOR_ID_ATI, device_id, NULL);
> +
> + if (!pdev) {
> + device_id = 0x5a12;
> + pdev = pci_get_device(PCI_VENDOR_ID_ATI, device_id, NULL);
> }
> +
> + if (!pdev) {
> + device_id = 0x5a13;
> + pdev = pci_get_device(PCI_VENDOR_ID_ATI, device_id, NULL);
> + }
> +
> + if (!pdev)
> + return;
> +
> + /* There may be one iommu per bus, so find the appropriate bridge */
> + while (pdev && (pdev->bus->number != iommu->dev->bus->number)) {
> + pci_dev_put(pdev);
> + pdev = pci_get_device(PCI_VENDOR_ID_ATI, device_id, pdev);
> + }
This does not work reliably with more than one IOMMU in the system. I
suggest to get the NB device by using the bus/dev/fn of the IOMMU
device. The IOMMU in RD890 is always function 2 of the NB device. So
just take the bus/dev/fn of the IOMMU device, set fn to zero and get the
NB device for re-enabling function two.
> + pci_write_config_dword(iommu->dev, 0x44, iommu->stored_addr_lo);
> + pci_write_config_dword(iommu->dev, 0x48, iommu->stored_addr_hi);
iommu->cap_ptr
> @@ -1147,7 +1253,6 @@ static void enable_iommus(void)
>
> for_each_iommu(iommu) {
> iommu_disable(iommu);
> - iommu_apply_quirks(iommu);
> iommu_init_flags(iommu);
> iommu_set_device_table(iommu);
> iommu_enable_command_buffer(iommu);
> @@ -1173,6 +1278,11 @@ static void disable_iommus(void)
>
> static int amd_iommu_resume(struct sys_device *dev)
> {
> + struct amd_iommu *iommu;
> +
> + for_each_iommu(iommu)
> + iommu_apply_quirks(iommu);
> +
> /* re-load the hardware */
> enable_iommus();
Why have you moved this out of the enable_iommus() loop?
Joerg
--
AMD Operating System Research Center
Advanced Micro Devices GmbH Einsteinring 24 85609 Dornach
General Managers: Alberto Bozzo, Andrew Bowd
Registration: Dornach, Landkr. Muenchen; Registerger. Muenchen, HRB Nr. 43632
^ permalink raw reply [flat|nested] 6+ messages in thread
* Re: [PATCH] x86: Reenable the AMD IOMMU if it's mysteriously vanished over suspend
2010-10-04 13:43 ` Roedel, Joerg
@ 2010-10-04 13:55 ` Matthew Garrett
2010-10-04 14:13 ` Roedel, Joerg
2010-10-04 18:59 ` [PATCH v2] " Matthew Garrett
1 sibling, 1 reply; 6+ messages in thread
From: Matthew Garrett @ 2010-10-04 13:55 UTC (permalink / raw)
To: Roedel, Joerg
Cc: linux-kernel@vger.kernel.org, linux-pci@vger.kernel.org,
x86@kernel.org, jbarnes@virtuousgeek.org, stable@kernel.org
On Mon, Oct 04, 2010 at 03:43:39PM +0200, Roedel, Joerg wrote:
> > + pci_read_config_dword(iommu->dev, 0x44, &iommu->stored_addr_lo);
> > + pci_read_config_dword(iommu->dev, 0x48, &iommu->stored_addr_hi);
>
> Can you use iommu->cap_ptr here? This variant should be safe too but the
> magic numbers are non-descriptive.
Sure.
> > + /* There may be one iommu per bus, so find the appropriate bridge */
> > + while (pdev && (pdev->bus->number != iommu->dev->bus->number)) {
> > + pci_dev_put(pdev);
> > + pdev = pci_get_device(PCI_VENDOR_ID_ATI, device_id, pdev);
> > + }
>
> This does not work reliably with more than one IOMMU in the system. I
> suggest to get the NB device by using the bus/dev/fn of the IOMMU
> device. The IOMMU in RD890 is always function 2 of the NB device. So
> just take the bus/dev/fn of the IOMMU device, set fn to zero and get the
> NB device for re-enabling function two.
The docs imply that the northbridge will always be device 0 on a given
bus, so this ought to work? On the other hand, your approach is simpler.
> > for_each_iommu(iommu) {
> > iommu_disable(iommu);
> > - iommu_apply_quirks(iommu);
> > iommu_init_flags(iommu);
> > iommu_set_device_table(iommu);
> > iommu_enable_command_buffer(iommu);
> > @@ -1173,6 +1278,11 @@ static void disable_iommus(void)
> >
> > static int amd_iommu_resume(struct sys_device *dev)
> > {
> > + struct amd_iommu *iommu;
> > +
> > + for_each_iommu(iommu)
> > + iommu_apply_quirks(iommu);
> > +
> > /* re-load the hardware */
> > enable_iommus();
>
> Why have you moved this out of the enable_iommus() loop?
enable_iommus() is called on init, whereas this should only be performed
on resume.
I'll send out an updated patch later today.
--
Matthew Garrett | mjg59@srcf.ucam.org
^ permalink raw reply [flat|nested] 6+ messages in thread
* Re: [PATCH] x86: Reenable the AMD IOMMU if it's mysteriously vanished over suspend
2010-10-04 13:55 ` Matthew Garrett
@ 2010-10-04 14:13 ` Roedel, Joerg
0 siblings, 0 replies; 6+ messages in thread
From: Roedel, Joerg @ 2010-10-04 14:13 UTC (permalink / raw)
To: Matthew Garrett
Cc: linux-kernel@vger.kernel.org, linux-pci@vger.kernel.org,
x86@kernel.org, jbarnes@virtuousgeek.org, stable@kernel.org
On Mon, Oct 04, 2010 at 09:55:52AM -0400, Matthew Garrett wrote:
> On Mon, Oct 04, 2010 at 03:43:39PM +0200, Roedel, Joerg wrote:
> >
> > Why have you moved this out of the enable_iommus() loop?
>
> enable_iommus() is called on init, whereas this should only be performed
> on resume.
Ok, it shouldn't be problematic to do this always, but only on resume is
safer. Can you rename iommu_apply_quirks to iommu_apply_resume_quirks
then?
> I'll send out an updated patch later today.
Great. Looking forward to it.
Joerg
--
AMD Operating System Research Center
Advanced Micro Devices GmbH Einsteinring 24 85609 Dornach
General Managers: Alberto Bozzo, Andrew Bowd
Registration: Dornach, Landkr. Muenchen; Registerger. Muenchen, HRB Nr. 43632
^ permalink raw reply [flat|nested] 6+ messages in thread
* [PATCH v2] x86: Reenable the AMD IOMMU if it's mysteriously vanished over suspend
2010-10-04 13:43 ` Roedel, Joerg
2010-10-04 13:55 ` Matthew Garrett
@ 2010-10-04 18:59 ` Matthew Garrett
2010-10-06 10:07 ` Roedel, Joerg
1 sibling, 1 reply; 6+ messages in thread
From: Matthew Garrett @ 2010-10-04 18:59 UTC (permalink / raw)
To: Joerg.Roedel
Cc: linux-kernel, linux-pci, x86, jbarnes, stable, Matthew Garrett
AMD's reference BIOS code had a bug that could result in the firmware
failing to reenable the iommu on resume. It transpires that this causes
certain less than desirable behaviour when it comes to PCI accesses, to
whit them ending up somewhere near Bristol when the more desirable outcome
was Edinburgh. Sadness ensues, perhaps along with filesystem corruption.
Let's make sure that it gets turned back on, and that we restore its
configuration so decisions it makes bear some resemblance to those made by
reasonable people rather than crack-addled lemurs who spent all your DMA
on Thunderbird.
Signed-off-by: Matthew Garrett <mjg@redhat.com>
Cc: stable@kernel.org
---
Cleaned up slightly to match Joerg's comments
arch/x86/include/asm/amd_iommu_types.h | 21 ++++--
arch/x86/kernel/amd_iommu_init.c | 122 ++++++++++++++++++++++++++++----
2 files changed, 123 insertions(+), 20 deletions(-)
diff --git a/arch/x86/include/asm/amd_iommu_types.h b/arch/x86/include/asm/amd_iommu_types.h
index 0861618..bdd20c8 100644
--- a/arch/x86/include/asm/amd_iommu_types.h
+++ b/arch/x86/include/asm/amd_iommu_types.h
@@ -416,13 +416,22 @@ struct amd_iommu {
struct dma_ops_domain *default_dom;
/*
- * This array is required to work around a potential BIOS bug.
- * The BIOS may miss to restore parts of the PCI configuration
- * space when the system resumes from S3. The result is that the
- * IOMMU does not execute commands anymore which leads to system
- * failure.
+ * We can't rely on the BIOS to restore all values on reinit, so we
+ * need to stash them
*/
- u32 cache_cfg[4];
+
+ /* The iommu BAR */
+ u32 stored_addr_lo;
+ u32 stored_addr_hi;
+
+ /*
+ * Each iommu has 6 l1s, each of which is documented as having 0x12
+ * registers
+ */
+ u32 stored_l1[6][0x12];
+
+ /* The l2 indirect registers */
+ u32 stored_l2[0x83];
};
/*
diff --git a/arch/x86/kernel/amd_iommu_init.c b/arch/x86/kernel/amd_iommu_init.c
index 5a170cb..44710d8 100644
--- a/arch/x86/kernel/amd_iommu_init.c
+++ b/arch/x86/kernel/amd_iommu_init.c
@@ -194,6 +194,39 @@ static inline unsigned long tbl_size(int entry_size)
return 1UL << shift;
}
+/* Access to l1 and l2 indexed register spaces */
+
+static u32 iommu_read_l1(struct amd_iommu *iommu, u16 l1, u8 address)
+{
+ u32 val;
+
+ pci_write_config_dword(iommu->dev, 0xf8, (address | l1 << 16));
+ pci_read_config_dword(iommu->dev, 0xfc, &val);
+ return val;
+}
+
+static void iommu_write_l1(struct amd_iommu *iommu, u16 l1, u8 address, u32 val)
+{
+ pci_write_config_dword(iommu->dev, 0xf8, (address | l1 << 16 | 1 << 31));
+ pci_write_config_dword(iommu->dev, 0xfc, val);
+ pci_write_config_dword(iommu->dev, 0xf8, (address | l1 << 16));
+}
+
+static u32 iommu_read_l2(struct amd_iommu *iommu, u8 address)
+{
+ u32 val;
+
+ pci_write_config_dword(iommu->dev, 0xf0, address);
+ pci_read_config_dword(iommu->dev, 0xf4, &val);
+ return val;
+}
+
+static void iommu_write_l2(struct amd_iommu *iommu, u8 address, u32 val)
+{
+ pci_write_config_dword(iommu->dev, 0xf0, (address | 1 << 8));
+ pci_write_config_dword(iommu->dev, 0xf4, val);
+}
+
/****************************************************************************
*
* AMD IOMMU MMIO register space handling functions
@@ -619,6 +652,7 @@ static void __init init_iommu_from_pci(struct amd_iommu *iommu)
{
int cap_ptr = iommu->cap_ptr;
u32 range, misc;
+ int i, j;
pci_read_config_dword(iommu->dev, cap_ptr + MMIO_CAP_HDR_OFFSET,
&iommu->cap);
@@ -633,12 +667,29 @@ static void __init init_iommu_from_pci(struct amd_iommu *iommu)
MMIO_GET_LD(range));
iommu->evt_msi_num = MMIO_MSI_NUM(misc);
- if (is_rd890_iommu(iommu->dev)) {
- pci_read_config_dword(iommu->dev, 0xf0, &iommu->cache_cfg[0]);
- pci_read_config_dword(iommu->dev, 0xf4, &iommu->cache_cfg[1]);
- pci_read_config_dword(iommu->dev, 0xf8, &iommu->cache_cfg[2]);
- pci_read_config_dword(iommu->dev, 0xfc, &iommu->cache_cfg[3]);
- }
+ if (!is_rd890_iommu(iommu->dev))
+ return;
+
+ /*
+ * Some rd890 systems may not be fully reconfigured by the BIOS, so
+ * it's necessary for us to store this information so it can be
+ * reprogrammed on resume
+ */
+
+ pci_read_config_dword(iommu->dev, iommu->cap_ptr + 4,
+ &iommu->stored_addr_lo);
+ pci_read_config_dword(iommu->dev, iommu->cap_ptr + 8,
+ &iommu->stored_addr_hi);
+
+ /* Low bit locks writes to configuration space */
+ iommu->stored_addr_lo &= ~1;
+
+ for (i = 0; i < 6; i++)
+ for (j = 0; j < 0x12; j++)
+ iommu->stored_l1[i][j] = iommu_read_l1(iommu, i, j);
+
+ for (i = 0; i < 0x83; i++)
+ iommu->stored_l2[i] = iommu_read_l2(iommu, i);
}
/*
@@ -1127,14 +1178,53 @@ static void iommu_init_flags(struct amd_iommu *iommu)
iommu_feature_enable(iommu, CONTROL_COHERENT_EN);
}
-static void iommu_apply_quirks(struct amd_iommu *iommu)
+static void iommu_apply_resume_quirks(struct amd_iommu *iommu)
{
- if (is_rd890_iommu(iommu->dev)) {
- pci_write_config_dword(iommu->dev, 0xf0, iommu->cache_cfg[0]);
- pci_write_config_dword(iommu->dev, 0xf4, iommu->cache_cfg[1]);
- pci_write_config_dword(iommu->dev, 0xf8, iommu->cache_cfg[2]);
- pci_write_config_dword(iommu->dev, 0xfc, iommu->cache_cfg[3]);
- }
+ int i, j;
+ u32 ioc_feature_control;
+ struct pci_dev *pdev = NULL;
+
+ /* RD890 BIOSes may not have completely reconfigured the iommu */
+ if (!is_rd890_iommu(iommu->dev))
+ return;
+
+ /*
+ * First, we need to ensure that the iommu is enabled. This is
+ * controlled by a register in the northbridge
+ */
+ pdev = pci_get_bus_and_slot(iommu->dev->bus->number, PCI_DEVFN(0, 0));
+
+ if (!pdev)
+ return;
+
+ /* Select Northbridge indirect register 0x75 and enable writing */
+ pci_write_config_dword(pdev, 0x60, 0x75 | (1 << 7));
+ pci_read_config_dword(pdev, 0x64, &ioc_feature_control);
+
+ /* Enable the iommu */
+ if (!(ioc_feature_control & 0x1))
+ pci_write_config_dword(pdev, 0x64, ioc_feature_control | 1);
+
+ pci_dev_put(pdev);
+
+ /* Restore the iommu BAR */
+ pci_write_config_dword(iommu->dev, iommu->cap_ptr + 4,
+ iommu->stored_addr_lo);
+ pci_write_config_dword(iommu->dev, iommu->cap_ptr + 8,
+ iommu->stored_addr_hi);
+
+ /* Restore the l1 indirect regs for each of the 6 l1s */
+ for (i = 0; i < 6; i++)
+ for (j = 0; j < 0x12; j++)
+ iommu_write_l1(iommu, i, j, iommu->stored_l1[i][j]);
+
+ /* Restore the l2 indirect regs */
+ for (i = 0; i < 0x83; i++)
+ iommu_write_l2(iommu, i, iommu->stored_l2[i]);
+
+ /* Lock PCI setup registers */
+ pci_write_config_dword(iommu->dev, iommu->cap_ptr + 4,
+ iommu->stored_addr_lo | 1);
}
/*
@@ -1147,7 +1237,6 @@ static void enable_iommus(void)
for_each_iommu(iommu) {
iommu_disable(iommu);
- iommu_apply_quirks(iommu);
iommu_init_flags(iommu);
iommu_set_device_table(iommu);
iommu_enable_command_buffer(iommu);
@@ -1173,6 +1262,11 @@ static void disable_iommus(void)
static int amd_iommu_resume(struct sys_device *dev)
{
+ struct amd_iommu *iommu;
+
+ for_each_iommu(iommu)
+ iommu_apply_resume_quirks(iommu);
+
/* re-load the hardware */
enable_iommus();
--
1.7.3.1
^ permalink raw reply related [flat|nested] 6+ messages in thread
* Re: [PATCH v2] x86: Reenable the AMD IOMMU if it's mysteriously vanished over suspend
2010-10-04 18:59 ` [PATCH v2] " Matthew Garrett
@ 2010-10-06 10:07 ` Roedel, Joerg
0 siblings, 0 replies; 6+ messages in thread
From: Roedel, Joerg @ 2010-10-06 10:07 UTC (permalink / raw)
To: Matthew Garrett
Cc: linux-kernel@vger.kernel.org, linux-pci@vger.kernel.org,
x86@kernel.org, jbarnes@virtuousgeek.org, stable@kernel.org
On Mon, Oct 04, 2010 at 02:59:31PM -0400, Matthew Garrett wrote:
> AMD's reference BIOS code had a bug that could result in the firmware
> failing to reenable the iommu on resume. It transpires that this causes
> certain less than desirable behaviour when it comes to PCI accesses, to
> whit them ending up somewhere near Bristol when the more desirable outcome
> was Edinburgh. Sadness ensues, perhaps along with filesystem corruption.
> Let's make sure that it gets turned back on, and that we restore its
> configuration so decisions it makes bear some resemblance to those made by
> reasonable people rather than crack-addled lemurs who spent all your DMA
> on Thunderbird.
>
> Signed-off-by: Matthew Garrett <mjg@redhat.com>
> Cc: stable@kernel.org
Applied, thanks a lot.
--
AMD Operating System Research Center
Advanced Micro Devices GmbH Einsteinring 24 85609 Dornach
General Managers: Alberto Bozzo, Andrew Bowd
Registration: Dornach, Landkr. Muenchen; Registerger. Muenchen, HRB Nr. 43632
^ permalink raw reply [flat|nested] 6+ messages in thread
end of thread, other threads:[~2010-10-06 10:07 UTC | newest]
Thread overview: 6+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2010-10-01 14:01 [PATCH] x86: Reenable the AMD IOMMU if it's mysteriously vanished over suspend Matthew Garrett
2010-10-04 13:43 ` Roedel, Joerg
2010-10-04 13:55 ` Matthew Garrett
2010-10-04 14:13 ` Roedel, Joerg
2010-10-04 18:59 ` [PATCH v2] " Matthew Garrett
2010-10-06 10:07 ` Roedel, Joerg
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox