aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/pci
diff options
context:
space:
mode:
authorBrian King <brking@us.ibm.com>2005-09-27 04:21:55 -0400
committerGreg Kroah-Hartman <gregkh@suse.de>2005-10-28 18:36:58 -0400
commite04b0ea2e0f9c1bb0d874db4493fc7f7a623116b (patch)
tree75937e50de883f69e906a4c8bc9f119d86c14411 /drivers/pci
parent2cea752f683af1be58ee8f25717c0a8118e0ac5b (diff)
[PATCH] PCI: Block config access during BIST
Some PCI adapters (eg. ipr scsi adapters) have an exposure today in that they issue BIST to the adapter to reset the card. If, during the time it takes to complete BIST, userspace attempts to access PCI config space, the host bus bridge will master abort the access since the ipr adapter does not respond on the PCI bus for a brief period of time when running BIST. On PPC64 hardware, this master abort results in the host PCI bridge isolating that PCI device from the rest of the system, making the device unusable until Linux is rebooted. This patch is an attempt to close that exposure by introducing some blocking code in the PCI code. When blocked, writes will be humored and reads will return the cached value. Ben Herrenschmidt has also mentioned that he plans to use this in PPC power management. Signed-off-by: Brian King <brking@us.ibm.com> Cc: Benjamin Herrenschmidt <benh@kernel.crashing.org> Signed-off-by: Andrew Morton <akpm@osdl.org> Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> drivers/pci/access.c | 89 ++++++++++++++++++++++++++++++++++++++++++++++++ drivers/pci/pci-sysfs.c | 20 +++++----- drivers/pci/pci.h | 7 +++ drivers/pci/proc.c | 28 +++++++-------- drivers/pci/syscall.c | 14 +++---- include/linux/pci.h | 7 +++ 6 files changed, 134 insertions(+), 31 deletions(-)
Diffstat (limited to 'drivers/pci')
-rw-r--r--drivers/pci/access.c89
-rw-r--r--drivers/pci/pci-sysfs.c20
-rw-r--r--drivers/pci/pci.h7
-rw-r--r--drivers/pci/proc.c28
-rw-r--r--drivers/pci/syscall.c14
5 files changed, 127 insertions, 31 deletions
diff --git a/drivers/pci/access.c b/drivers/pci/access.c
index 24a76de49f41..2a42add7f563 100644
--- a/drivers/pci/access.c
+++ b/drivers/pci/access.c
@@ -60,3 +60,92 @@ EXPORT_SYMBOL(pci_bus_read_config_dword);
60EXPORT_SYMBOL(pci_bus_write_config_byte); 60EXPORT_SYMBOL(pci_bus_write_config_byte);
61EXPORT_SYMBOL(pci_bus_write_config_word); 61EXPORT_SYMBOL(pci_bus_write_config_word);
62EXPORT_SYMBOL(pci_bus_write_config_dword); 62EXPORT_SYMBOL(pci_bus_write_config_dword);
63
64static u32 pci_user_cached_config(struct pci_dev *dev, int pos)
65{
66 u32 data;
67
68 data = dev->saved_config_space[pos/sizeof(dev->saved_config_space[0])];
69 data >>= (pos % sizeof(dev->saved_config_space[0])) * 8;
70 return data;
71}
72
73#define PCI_USER_READ_CONFIG(size,type) \
74int pci_user_read_config_##size \
75 (struct pci_dev *dev, int pos, type *val) \
76{ \
77 unsigned long flags; \
78 int ret = 0; \
79 u32 data = -1; \
80 if (PCI_##size##_BAD) return PCIBIOS_BAD_REGISTER_NUMBER; \
81 spin_lock_irqsave(&pci_lock, flags); \
82 if (likely(!dev->block_ucfg_access)) \
83 ret = dev->bus->ops->read(dev->bus, dev->devfn, \
84 pos, sizeof(type), &data); \
85 else if (pos < sizeof(dev->saved_config_space)) \
86 data = pci_user_cached_config(dev, pos); \
87 spin_unlock_irqrestore(&pci_lock, flags); \
88 *val = (type)data; \
89 return ret; \
90}
91
92#define PCI_USER_WRITE_CONFIG(size,type) \
93int pci_user_write_config_##size \
94 (struct pci_dev *dev, int pos, type val) \
95{ \
96 unsigned long flags; \
97 int ret = -EIO; \
98 if (PCI_##size##_BAD) return PCIBIOS_BAD_REGISTER_NUMBER; \
99 spin_lock_irqsave(&pci_lock, flags); \
100 if (likely(!dev->block_ucfg_access)) \
101 ret = dev->bus->ops->write(dev->bus, dev->devfn, \
102 pos, sizeof(type), val); \
103 spin_unlock_irqrestore(&pci_lock, flags); \
104 return ret; \
105}
106
107PCI_USER_READ_CONFIG(byte, u8)
108PCI_USER_READ_CONFIG(word, u16)
109PCI_USER_READ_CONFIG(dword, u32)
110PCI_USER_WRITE_CONFIG(byte, u8)
111PCI_USER_WRITE_CONFIG(word, u16)
112PCI_USER_WRITE_CONFIG(dword, u32)
113
114/**
115 * pci_block_user_cfg_access - Block userspace PCI config reads/writes
116 * @dev: pci device struct
117 *
118 * This function blocks any userspace PCI config accesses from occurring.
119 * When blocked, any writes will be bit bucketed and reads will return the
120 * data saved using pci_save_state for the first 64 bytes of config
121 * space and return 0xff for all other config reads.
122 **/
123void pci_block_user_cfg_access(struct pci_dev *dev)
124{
125 unsigned long flags;
126
127 pci_save_state(dev);
128
129 /* spinlock to synchronize with anyone reading config space now */
130 spin_lock_irqsave(&pci_lock, flags);
131 dev->block_ucfg_access = 1;
132 spin_unlock_irqrestore(&pci_lock, flags);
133}
134EXPORT_SYMBOL_GPL(pci_block_user_cfg_access);
135
136/**
137 * pci_unblock_user_cfg_access - Unblock userspace PCI config reads/writes
138 * @dev: pci device struct
139 *
140 * This function allows userspace PCI config accesses to resume.
141 **/
142void pci_unblock_user_cfg_access(struct pci_dev *dev)
143{
144 unsigned long flags;
145
146 /* spinlock to synchronize with anyone reading saved config space */
147 spin_lock_irqsave(&pci_lock, flags);
148 dev->block_ucfg_access = 0;
149 spin_unlock_irqrestore(&pci_lock, flags);
150}
151EXPORT_SYMBOL_GPL(pci_unblock_user_cfg_access);
diff --git a/drivers/pci/pci-sysfs.c b/drivers/pci/pci-sysfs.c
index 2898830c496f..965a5934623a 100644
--- a/drivers/pci/pci-sysfs.c
+++ b/drivers/pci/pci-sysfs.c
@@ -130,7 +130,7 @@ pci_read_config(struct kobject *kobj, char *buf, loff_t off, size_t count)
130 130
131 if ((off & 1) && size) { 131 if ((off & 1) && size) {
132 u8 val; 132 u8 val;
133 pci_read_config_byte(dev, off, &val); 133 pci_user_read_config_byte(dev, off, &val);
134 data[off - init_off] = val; 134 data[off - init_off] = val;
135 off++; 135 off++;
136 size--; 136 size--;
@@ -138,7 +138,7 @@ pci_read_config(struct kobject *kobj, char *buf, loff_t off, size_t count)
138 138
139 if ((off & 3) && size > 2) { 139 if ((off & 3) && size > 2) {
140 u16 val; 140 u16 val;
141 pci_read_config_word(dev, off, &val); 141 pci_user_read_config_word(dev, off, &val);
142 data[off - init_off] = val & 0xff; 142 data[off - init_off] = val & 0xff;
143 data[off - init_off + 1] = (val >> 8) & 0xff; 143 data[off - init_off + 1] = (val >> 8) & 0xff;
144 off += 2; 144 off += 2;
@@ -147,7 +147,7 @@ pci_read_config(struct kobject *kobj, char *buf, loff_t off, size_t count)
147 147
148 while (size > 3) { 148 while (size > 3) {
149 u32 val; 149 u32 val;
150 pci_read_config_dword(dev, off, &val); 150 pci_user_read_config_dword(dev, off, &val);
151 data[off - init_off] = val & 0xff; 151 data[off - init_off] = val & 0xff;
152 data[off - init_off + 1] = (val >> 8) & 0xff; 152 data[off - init_off + 1] = (val >> 8) & 0xff;
153 data[off - init_off + 2] = (val >> 16) & 0xff; 153 data[off - init_off + 2] = (val >> 16) & 0xff;
@@ -158,7 +158,7 @@ pci_read_config(struct kobject *kobj, char *buf, loff_t off, size_t count)
158 158
159 if (size >= 2) { 159 if (size >= 2) {
160 u16 val; 160 u16 val;
161 pci_read_config_word(dev, off, &val); 161 pci_user_read_config_word(dev, off, &val);
162 data[off - init_off] = val & 0xff; 162 data[off - init_off] = val & 0xff;
163 data[off - init_off + 1] = (val >> 8) & 0xff; 163 data[off - init_off + 1] = (val >> 8) & 0xff;
164 off += 2; 164 off += 2;
@@ -167,7 +167,7 @@ pci_read_config(struct kobject *kobj, char *buf, loff_t off, size_t count)
167 167
168 if (size > 0) { 168 if (size > 0) {
169 u8 val; 169 u8 val;
170 pci_read_config_byte(dev, off, &val); 170 pci_user_read_config_byte(dev, off, &val);
171 data[off - init_off] = val; 171 data[off - init_off] = val;
172 off++; 172 off++;
173 --size; 173 --size;
@@ -192,7 +192,7 @@ pci_write_config(struct kobject *kobj, char *buf, loff_t off, size_t count)
192 } 192 }
193 193
194 if ((off & 1) && size) { 194 if ((off & 1) && size) {
195 pci_write_config_byte(dev, off, data[off - init_off]); 195 pci_user_write_config_byte(dev, off, data[off - init_off]);
196 off++; 196 off++;
197 size--; 197 size--;
198 } 198 }
@@ -200,7 +200,7 @@ pci_write_config(struct kobject *kobj, char *buf, loff_t off, size_t count)
200 if ((off & 3) && size > 2) { 200 if ((off & 3) && size > 2) {
201 u16 val = data[off - init_off]; 201 u16 val = data[off - init_off];
202 val |= (u16) data[off - init_off + 1] << 8; 202 val |= (u16) data[off - init_off + 1] << 8;
203 pci_write_config_word(dev, off, val); 203 pci_user_write_config_word(dev, off, val);
204 off += 2; 204 off += 2;
205 size -= 2; 205 size -= 2;
206 } 206 }
@@ -210,7 +210,7 @@ pci_write_config(struct kobject *kobj, char *buf, loff_t off, size_t count)
210 val |= (u32) data[off - init_off + 1] << 8; 210 val |= (u32) data[off - init_off + 1] << 8;
211 val |= (u32) data[off - init_off + 2] << 16; 211 val |= (u32) data[off - init_off + 2] << 16;
212 val |= (u32) data[off - init_off + 3] << 24; 212 val |= (u32) data[off - init_off + 3] << 24;
213 pci_write_config_dword(dev, off, val); 213 pci_user_write_config_dword(dev, off, val);
214 off += 4; 214 off += 4;
215 size -= 4; 215 size -= 4;
216 } 216 }
@@ -218,13 +218,13 @@ pci_write_config(struct kobject *kobj, char *buf, loff_t off, size_t count)
218 if (size >= 2) { 218 if (size >= 2) {
219 u16 val = data[off - init_off]; 219 u16 val = data[off - init_off];
220 val |= (u16) data[off - init_off + 1] << 8; 220 val |= (u16) data[off - init_off + 1] << 8;
221 pci_write_config_word(dev, off, val); 221 pci_user_write_config_word(dev, off, val);
222 off += 2; 222 off += 2;
223 size -= 2; 223 size -= 2;
224 } 224 }
225 225
226 if (size) { 226 if (size) {
227 pci_write_config_byte(dev, off, data[off - init_off]); 227 pci_user_write_config_byte(dev, off, data[off - init_off]);
228 off++; 228 off++;
229 --size; 229 --size;
230 } 230 }
diff --git a/drivers/pci/pci.h b/drivers/pci/pci.h
index d3f3dd42240d..6527b36c9a61 100644
--- a/drivers/pci/pci.h
+++ b/drivers/pci/pci.h
@@ -15,6 +15,13 @@ extern int pci_bus_alloc_resource(struct pci_bus *bus, struct resource *res,
15extern int (*platform_pci_choose_state)(struct pci_dev *dev, pm_message_t state); 15extern int (*platform_pci_choose_state)(struct pci_dev *dev, pm_message_t state);
16extern int (*platform_pci_set_power_state)(struct pci_dev *dev, pci_power_t state); 16extern int (*platform_pci_set_power_state)(struct pci_dev *dev, pci_power_t state);
17 17
18extern int pci_user_read_config_byte(struct pci_dev *dev, int where, u8 *val);
19extern int pci_user_read_config_word(struct pci_dev *dev, int where, u16 *val);
20extern int pci_user_read_config_dword(struct pci_dev *dev, int where, u32 *val);
21extern int pci_user_write_config_byte(struct pci_dev *dev, int where, u8 val);
22extern int pci_user_write_config_word(struct pci_dev *dev, int where, u16 val);
23extern int pci_user_write_config_dword(struct pci_dev *dev, int where, u32 val);
24
18/* PCI /proc functions */ 25/* PCI /proc functions */
19#ifdef CONFIG_PROC_FS 26#ifdef CONFIG_PROC_FS
20extern int pci_proc_attach_device(struct pci_dev *dev); 27extern int pci_proc_attach_device(struct pci_dev *dev);
diff --git a/drivers/pci/proc.c b/drivers/pci/proc.c
index 9613f666c110..9eb465727fce 100644
--- a/drivers/pci/proc.c
+++ b/drivers/pci/proc.c
@@ -80,7 +80,7 @@ proc_bus_pci_read(struct file *file, char __user *buf, size_t nbytes, loff_t *pp
80 80
81 if ((pos & 1) && cnt) { 81 if ((pos & 1) && cnt) {
82 unsigned char val; 82 unsigned char val;
83 pci_read_config_byte(dev, pos, &val); 83 pci_user_read_config_byte(dev, pos, &val);
84 __put_user(val, buf); 84 __put_user(val, buf);
85 buf++; 85 buf++;
86 pos++; 86 pos++;
@@ -89,7 +89,7 @@ proc_bus_pci_read(struct file *file, char __user *buf, size_t nbytes, loff_t *pp
89 89
90 if ((pos & 3) && cnt > 2) { 90 if ((pos & 3) && cnt > 2) {
91 unsigned short val; 91 unsigned short val;
92 pci_read_config_word(dev, pos, &val); 92 pci_user_read_config_word(dev, pos, &val);
93 __put_user(cpu_to_le16(val), (unsigned short __user *) buf); 93 __put_user(cpu_to_le16(val), (unsigned short __user *) buf);
94 buf += 2; 94 buf += 2;
95 pos += 2; 95 pos += 2;
@@ -98,7 +98,7 @@ proc_bus_pci_read(struct file *file, char __user *buf, size_t nbytes, loff_t *pp
98 98
99 while (cnt >= 4) { 99 while (cnt >= 4) {
100 unsigned int val; 100 unsigned int val;
101 pci_read_config_dword(dev, pos, &val); 101 pci_user_read_config_dword(dev, pos, &val);
102 __put_user(cpu_to_le32(val), (unsigned int __user *) buf); 102 __put_user(cpu_to_le32(val), (unsigned int __user *) buf);
103 buf += 4; 103 buf += 4;
104 pos += 4; 104 pos += 4;
@@ -107,7 +107,7 @@ proc_bus_pci_read(struct file *file, char __user *buf, size_t nbytes, loff_t *pp
107 107
108 if (cnt >= 2) { 108 if (cnt >= 2) {
109 unsigned short val; 109 unsigned short val;
110 pci_read_config_word(dev, pos, &val); 110 pci_user_read_config_word(dev, pos, &val);
111 __put_user(cpu_to_le16(val), (unsigned short __user *) buf); 111 __put_user(cpu_to_le16(val), (unsigned short __user *) buf);
112 buf += 2; 112 buf += 2;
113 pos += 2; 113 pos += 2;
@@ -116,7 +116,7 @@ proc_bus_pci_read(struct file *file, char __user *buf, size_t nbytes, loff_t *pp
116 116
117 if (cnt) { 117 if (cnt) {
118 unsigned char val; 118 unsigned char val;
119 pci_read_config_byte(dev, pos, &val); 119 pci_user_read_config_byte(dev, pos, &val);
120 __put_user(val, buf); 120 __put_user(val, buf);
121 buf++; 121 buf++;
122 pos++; 122 pos++;
@@ -151,7 +151,7 @@ proc_bus_pci_write(struct file *file, const char __user *buf, size_t nbytes, lof
151 if ((pos & 1) && cnt) { 151 if ((pos & 1) && cnt) {
152 unsigned char val; 152 unsigned char val;
153 __get_user(val, buf); 153 __get_user(val, buf);
154 pci_write_config_byte(dev, pos, val); 154 pci_user_write_config_byte(dev, pos, val);
155 buf++; 155 buf++;
156 pos++; 156 pos++;
157 cnt--; 157 cnt--;
@@ -160,7 +160,7 @@ proc_bus_pci_write(struct file *file, const char __user *buf, size_t nbytes, lof
160 if ((pos & 3) && cnt > 2) { 160 if ((pos & 3) && cnt > 2) {
161 unsigned short val; 161 unsigned short val;
162 __get_user(val, (unsigned short __user *) buf); 162 __get_user(val, (unsigned short __user *) buf);
163 pci_write_config_word(dev, pos, le16_to_cpu(val)); 163 pci_user_write_config_word(dev, pos, le16_to_cpu(val));
164 buf += 2; 164 buf += 2;
165 pos += 2; 165 pos += 2;
166 cnt -= 2; 166 cnt -= 2;
@@ -169,7 +169,7 @@ proc_bus_pci_write(struct file *file, const char __user *buf, size_t nbytes, lof
169 while (cnt >= 4) { 169 while (cnt >= 4) {
170 unsigned int val; 170 unsigned int val;
171 __get_user(val, (unsigned int __user *) buf); 171 __get_user(val, (unsigned int __user *) buf);
172 pci_write_config_dword(dev, pos, le32_to_cpu(val)); 172 pci_user_write_config_dword(dev, pos, le32_to_cpu(val));
173 buf += 4; 173 buf += 4;
174 pos += 4; 174 pos += 4;
175 cnt -= 4; 175 cnt -= 4;
@@ -178,7 +178,7 @@ proc_bus_pci_write(struct file *file, const char __user *buf, size_t nbytes, lof
178 if (cnt >= 2) { 178 if (cnt >= 2) {
179 unsigned short val; 179 unsigned short val;
180 __get_user(val, (unsigned short __user *) buf); 180 __get_user(val, (unsigned short __user *) buf);
181 pci_write_config_word(dev, pos, le16_to_cpu(val)); 181 pci_user_write_config_word(dev, pos, le16_to_cpu(val));
182 buf += 2; 182 buf += 2;
183 pos += 2; 183 pos += 2;
184 cnt -= 2; 184 cnt -= 2;
@@ -187,7 +187,7 @@ proc_bus_pci_write(struct file *file, const char __user *buf, size_t nbytes, lof
187 if (cnt) { 187 if (cnt) {
188 unsigned char val; 188 unsigned char val;
189 __get_user(val, buf); 189 __get_user(val, buf);
190 pci_write_config_byte(dev, pos, val); 190 pci_user_write_config_byte(dev, pos, val);
191 buf++; 191 buf++;
192 pos++; 192 pos++;
193 cnt--; 193 cnt--;
@@ -484,10 +484,10 @@ static int show_dev_config(struct seq_file *m, void *v)
484 484
485 drv = pci_dev_driver(dev); 485 drv = pci_dev_driver(dev);
486 486
487 pci_read_config_dword(dev, PCI_CLASS_REVISION, &class_rev); 487 pci_user_read_config_dword(dev, PCI_CLASS_REVISION, &class_rev);
488 pci_read_config_byte (dev, PCI_LATENCY_TIMER, &latency); 488 pci_user_read_config_byte (dev, PCI_LATENCY_TIMER, &latency);
489 pci_read_config_byte (dev, PCI_MIN_GNT, &min_gnt); 489 pci_user_read_config_byte (dev, PCI_MIN_GNT, &min_gnt);
490 pci_read_config_byte (dev, PCI_MAX_LAT, &max_lat); 490 pci_user_read_config_byte (dev, PCI_MAX_LAT, &max_lat);
491 seq_printf(m, " Bus %2d, device %3d, function %2d:\n", 491 seq_printf(m, " Bus %2d, device %3d, function %2d:\n",
492 dev->bus->number, PCI_SLOT(dev->devfn), PCI_FUNC(dev->devfn)); 492 dev->bus->number, PCI_SLOT(dev->devfn), PCI_FUNC(dev->devfn));
493 seq_printf(m, " Class %04x", class_rev >> 16); 493 seq_printf(m, " Class %04x", class_rev >> 16);
diff --git a/drivers/pci/syscall.c b/drivers/pci/syscall.c
index c071790cc983..87fafc08cb9d 100644
--- a/drivers/pci/syscall.c
+++ b/drivers/pci/syscall.c
@@ -13,7 +13,7 @@
13#include <linux/smp_lock.h> 13#include <linux/smp_lock.h>
14#include <linux/syscalls.h> 14#include <linux/syscalls.h>
15#include <asm/uaccess.h> 15#include <asm/uaccess.h>
16 16#include "pci.h"
17 17
18asmlinkage long 18asmlinkage long
19sys_pciconfig_read(unsigned long bus, unsigned long dfn, 19sys_pciconfig_read(unsigned long bus, unsigned long dfn,
@@ -38,13 +38,13 @@ sys_pciconfig_read(unsigned long bus, unsigned long dfn,
38 lock_kernel(); 38 lock_kernel();
39 switch (len) { 39 switch (len) {
40 case 1: 40 case 1:
41 cfg_ret = pci_read_config_byte(dev, off, &byte); 41 cfg_ret = pci_user_read_config_byte(dev, off, &byte);
42 break; 42 break;
43 case 2: 43 case 2:
44 cfg_ret = pci_read_config_word(dev, off, &word); 44 cfg_ret = pci_user_read_config_word(dev, off, &word);
45 break; 45 break;
46 case 4: 46 case 4:
47 cfg_ret = pci_read_config_dword(dev, off, &dword); 47 cfg_ret = pci_user_read_config_dword(dev, off, &dword);
48 break; 48 break;
49 default: 49 default:
50 err = -EINVAL; 50 err = -EINVAL;
@@ -112,7 +112,7 @@ sys_pciconfig_write(unsigned long bus, unsigned long dfn,
112 err = get_user(byte, (u8 __user *)buf); 112 err = get_user(byte, (u8 __user *)buf);
113 if (err) 113 if (err)
114 break; 114 break;
115 err = pci_write_config_byte(dev, off, byte); 115 err = pci_user_write_config_byte(dev, off, byte);
116 if (err != PCIBIOS_SUCCESSFUL) 116 if (err != PCIBIOS_SUCCESSFUL)
117 err = -EIO; 117 err = -EIO;
118 break; 118 break;
@@ -121,7 +121,7 @@ sys_pciconfig_write(unsigned long bus, unsigned long dfn,
121 err = get_user(word, (u16 __user *)buf); 121 err = get_user(word, (u16 __user *)buf);
122 if (err) 122 if (err)
123 break; 123 break;
124 err = pci_write_config_word(dev, off, word); 124 err = pci_user_write_config_word(dev, off, word);
125 if (err != PCIBIOS_SUCCESSFUL) 125 if (err != PCIBIOS_SUCCESSFUL)
126 err = -EIO; 126 err = -EIO;
127 break; 127 break;
@@ -130,7 +130,7 @@ sys_pciconfig_write(unsigned long bus, unsigned long dfn,
130 err = get_user(dword, (u32 __user *)buf); 130 err = get_user(dword, (u32 __user *)buf);
131 if (err) 131 if (err)
132 break; 132 break;
133 err = pci_write_config_dword(dev, off, dword); 133 err = pci_user_write_config_dword(dev, off, dword);
134 if (err != PCIBIOS_SUCCESSFUL) 134 if (err != PCIBIOS_SUCCESSFUL)
135 err = -EIO; 135 err = -EIO;
136 break; 136 break;