Re: pci: Arch hook to determine config space size

From: Brian King
Date: Tue Feb 01 2005 - 15:19:35 EST


Matthew Wilcox wrote:
On Mon, Jan 31, 2005 at 10:52:29PM -0600, Brian King wrote:

@@ -62,8 +72,11 @@ static int rtas_read_config(struct devic
return PCIBIOS_DEVICE_NOT_FOUND;
if (where & (size - 1))
return PCIBIOS_BAD_REGISTER_NUMBER;


You should probably delete this redundant test at the same time ...

Done. The new patch below also adds some address checking to iSeries config accessor functions. Additionally, this patch should address Arnd's concern, as it now looks for the "ibm,pci-config-space-type" property on the device itself rather than on the bridge.


--
Brian King
eServer Storage I/O
IBM Linux Technology Center

When working with a PCI-X Mode 2 adapter on a PCI-X Mode 1 PPC64
system, the current code used to determine the config space size
of a device results in a PCI Master abort and an EEH error, resulting
in the device being taken offline. This patch checks OF to see if
the PCI bridge supports PCI-X Mode 2 and fails config accesses beyond
256 bytes if it does not.

Signed-off-by: Brian King <brking@xxxxxxxxxx>
---

linux-2.6.11-rc2-bk9-bjking1/arch/ppc64/kernel/iSeries_pci.c | 6 +++
linux-2.6.11-rc2-bk9-bjking1/arch/ppc64/kernel/pSeries_pci.c | 20 ++++++++---
linux-2.6.11-rc2-bk9-bjking1/arch/ppc64/kernel/pci_dn.c | 6 +++
linux-2.6.11-rc2-bk9-bjking1/include/asm-ppc64/prom.h | 1
4 files changed, 29 insertions(+), 4 deletions(-)

diff -puN arch/ppc64/kernel/pSeries_pci.c~ppc64_pcix_mode2_cfg arch/ppc64/kernel/pSeries_pci.c
--- linux-2.6.11-rc2-bk9/arch/ppc64/kernel/pSeries_pci.c~ppc64_pcix_mode2_cfg 2005-02-01 13:31:46.000000000 -0600
+++ linux-2.6.11-rc2-bk9-bjking1/arch/ppc64/kernel/pSeries_pci.c 2005-02-01 13:39:32.000000000 -0600
@@ -52,6 +52,16 @@ static int s7a_workaround;

extern struct mpic *pSeries_mpic;

+static int config_access_valid(struct device_node *dn, int where)
+{
+ if (where < 256)
+ return 1;
+ if (where < 4096 && dn->pci_ext_config_space)
+ return 1;
+
+ return 0;
+}
+
static int rtas_read_config(struct device_node *dn, int where, int size, u32 *val)
{
int returnval = -1;
@@ -60,10 +70,11 @@ static int rtas_read_config(struct devic

if (!dn)
return PCIBIOS_DEVICE_NOT_FOUND;
- if (where & (size - 1))
+ if (!config_access_valid(dn, where))
return PCIBIOS_BAD_REGISTER_NUMBER;

- addr = (dn->busno << 16) | (dn->devfn << 8) | where;
+ addr = ((where & 0xf00) << 20) | (dn->busno << 16) |
+ (dn->devfn << 8) | (where & 0xff);
buid = dn->phb->buid;
if (buid) {
ret = rtas_call(ibm_read_pci_config, 4, 2, &returnval,
@@ -108,10 +119,11 @@ static int rtas_write_config(struct devi

if (!dn)
return PCIBIOS_DEVICE_NOT_FOUND;
- if (where & (size - 1))
+ if (!config_access_valid(dn, where))
return PCIBIOS_BAD_REGISTER_NUMBER;

- addr = (dn->busno << 16) | (dn->devfn << 8) | where;
+ addr = ((where & 0xf00) << 20) | (dn->busno << 16) |
+ (dn->devfn << 8) | (where & 0xff);
buid = dn->phb->buid;
if (buid) {
ret = rtas_call(ibm_write_pci_config, 5, 1, NULL, addr, buid >> 32, buid & 0xffffffff, size, (ulong) val);
diff -puN include/asm-ppc64/prom.h~ppc64_pcix_mode2_cfg include/asm-ppc64/prom.h
--- linux-2.6.11-rc2-bk9/include/asm-ppc64/prom.h~ppc64_pcix_mode2_cfg 2005-02-01 13:31:46.000000000 -0600
+++ linux-2.6.11-rc2-bk9-bjking1/include/asm-ppc64/prom.h 2005-02-01 13:31:46.000000000 -0600
@@ -137,6 +137,7 @@ struct device_node {
int devfn; /* for pci devices */
int eeh_mode; /* See eeh.h for possible EEH_MODEs */
int eeh_config_addr;
+ int pci_ext_config_space; /* for pci devices */
struct pci_controller *phb; /* for pci devices */
struct iommu_table *iommu_table; /* for phb's or bridges */

diff -puN arch/ppc64/kernel/pci_dn.c~ppc64_pcix_mode2_cfg arch/ppc64/kernel/pci_dn.c
--- linux-2.6.11-rc2-bk9/arch/ppc64/kernel/pci_dn.c~ppc64_pcix_mode2_cfg 2005-02-01 13:31:46.000000000 -0600
+++ linux-2.6.11-rc2-bk9-bjking1/arch/ppc64/kernel/pci_dn.c 2005-02-01 13:31:46.000000000 -0600
@@ -37,6 +37,7 @@
static void * __devinit update_dn_pci_info(struct device_node *dn, void *data)
{
struct pci_controller *phb = data;
+ int *type = (int *)get_property(dn, "ibm,pci-config-space-type", NULL);
u32 *regs;

dn->phb = phb;
@@ -46,6 +47,11 @@ static void * __devinit update_dn_pci_in
dn->busno = (regs[0] >> 16) & 0xff;
dn->devfn = (regs[0] >> 8) & 0xff;
}
+
+ if (type && *type == 1)
+ dn->pci_ext_config_space = 1;
+ else
+ dn->pci_ext_config_space = 0;
return NULL;
}

diff -puN arch/ppc64/kernel/iSeries_pci.c~ppc64_pcix_mode2_cfg arch/ppc64/kernel/iSeries_pci.c
--- linux-2.6.11-rc2-bk9/arch/ppc64/kernel/iSeries_pci.c~ppc64_pcix_mode2_cfg 2005-02-01 13:43:04.000000000 -0600
+++ linux-2.6.11-rc2-bk9-bjking1/arch/ppc64/kernel/iSeries_pci.c 2005-02-01 13:44:49.000000000 -0600
@@ -610,6 +610,10 @@ static int iSeries_pci_read_config(struc

if (node == NULL)
return PCIBIOS_DEVICE_NOT_FOUND;
+ if (offset > 255) {
+ *val = ~0;
+ return PCIBIOS_BAD_REGISTER_NUMBER;
+ }

fn = hv_cfg_read_func[(size - 1) & 3];
HvCall3Ret16(fn, &ret, node->DsaAddr.DsaAddr, offset, 0);
@@ -636,6 +640,8 @@ static int iSeries_pci_write_config(stru

if (node == NULL)
return PCIBIOS_DEVICE_NOT_FOUND;
+ if (offset > 255)
+ return PCIBIOS_BAD_REGISTER_NUMBER;

fn = hv_cfg_write_func[(size - 1) & 3];
ret = HvCall4(fn, node->DsaAddr.DsaAddr, offset, val, 0);
_