diff options
Diffstat (limited to 'drivers/pci/pci.c')
-rw-r--r-- | drivers/pci/pci.c | 118 |
1 files changed, 97 insertions, 21 deletions
diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index 1574b2da25e7..a6c38b15ac33 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c | |||
@@ -192,6 +192,89 @@ EXPORT_SYMBOL_GPL(pci_ioremap_wc_bar); | |||
192 | #endif | 192 | #endif |
193 | 193 | ||
194 | /** | 194 | /** |
195 | * pci_dev_str_match_path - test if a path string matches a device | ||
196 | * @dev: the PCI device to test | ||
197 | * @p: string to match the device against | ||
198 | * @endptr: pointer to the string after the match | ||
199 | * | ||
200 | * Test if a string (typically from a kernel parameter) formatted as a | ||
201 | * path of device/function addresses matches a PCI device. The string must | ||
202 | * be of the form: | ||
203 | * | ||
204 | * [<domain>:]<bus>:<device>.<func>[/<device>.<func>]* | ||
205 | * | ||
206 | * A path for a device can be obtained using 'lspci -t'. Using a path | ||
207 | * is more robust against bus renumbering than using only a single bus, | ||
208 | * device and function address. | ||
209 | * | ||
210 | * Returns 1 if the string matches the device, 0 if it does not and | ||
211 | * a negative error code if it fails to parse the string. | ||
212 | */ | ||
213 | static int pci_dev_str_match_path(struct pci_dev *dev, const char *path, | ||
214 | const char **endptr) | ||
215 | { | ||
216 | int ret; | ||
217 | int seg, bus, slot, func; | ||
218 | char *wpath, *p; | ||
219 | char end; | ||
220 | |||
221 | *endptr = strchrnul(path, ';'); | ||
222 | |||
223 | wpath = kmemdup_nul(path, *endptr - path, GFP_KERNEL); | ||
224 | if (!wpath) | ||
225 | return -ENOMEM; | ||
226 | |||
227 | while (1) { | ||
228 | p = strrchr(wpath, '/'); | ||
229 | if (!p) | ||
230 | break; | ||
231 | ret = sscanf(p, "/%x.%x%c", &slot, &func, &end); | ||
232 | if (ret != 2) { | ||
233 | ret = -EINVAL; | ||
234 | goto free_and_exit; | ||
235 | } | ||
236 | |||
237 | if (dev->devfn != PCI_DEVFN(slot, func)) { | ||
238 | ret = 0; | ||
239 | goto free_and_exit; | ||
240 | } | ||
241 | |||
242 | /* | ||
243 | * Note: we don't need to get a reference to the upstream | ||
244 | * bridge because we hold a reference to the top level | ||
245 | * device which should hold a reference to the bridge, | ||
246 | * and so on. | ||
247 | */ | ||
248 | dev = pci_upstream_bridge(dev); | ||
249 | if (!dev) { | ||
250 | ret = 0; | ||
251 | goto free_and_exit; | ||
252 | } | ||
253 | |||
254 | *p = 0; | ||
255 | } | ||
256 | |||
257 | ret = sscanf(wpath, "%x:%x:%x.%x%c", &seg, &bus, &slot, | ||
258 | &func, &end); | ||
259 | if (ret != 4) { | ||
260 | seg = 0; | ||
261 | ret = sscanf(wpath, "%x:%x.%x%c", &bus, &slot, &func, &end); | ||
262 | if (ret != 3) { | ||
263 | ret = -EINVAL; | ||
264 | goto free_and_exit; | ||
265 | } | ||
266 | } | ||
267 | |||
268 | ret = (seg == pci_domain_nr(dev->bus) && | ||
269 | bus == dev->bus->number && | ||
270 | dev->devfn == PCI_DEVFN(slot, func)); | ||
271 | |||
272 | free_and_exit: | ||
273 | kfree(wpath); | ||
274 | return ret; | ||
275 | } | ||
276 | |||
277 | /** | ||
195 | * pci_dev_str_match - test if a string matches a device | 278 | * pci_dev_str_match - test if a string matches a device |
196 | * @dev: the PCI device to test | 279 | * @dev: the PCI device to test |
197 | * @p: string to match the device against | 280 | * @p: string to match the device against |
@@ -200,13 +283,16 @@ EXPORT_SYMBOL_GPL(pci_ioremap_wc_bar); | |||
200 | * Test if a string (typically from a kernel parameter) matches a specified | 283 | * Test if a string (typically from a kernel parameter) matches a specified |
201 | * PCI device. The string may be of one of the following formats: | 284 | * PCI device. The string may be of one of the following formats: |
202 | * | 285 | * |
203 | * [<domain>:]<bus>:<device>.<func> | 286 | * [<domain>:]<bus>:<device>.<func>[/<device>.<func>]* |
204 | * pci:<vendor>:<device>[:<subvendor>:<subdevice>] | 287 | * pci:<vendor>:<device>[:<subvendor>:<subdevice>] |
205 | * | 288 | * |
206 | * The first format specifies a PCI bus/device/function address which | 289 | * The first format specifies a PCI bus/device/function address which |
207 | * may change if new hardware is inserted, if motherboard firmware changes, | 290 | * may change if new hardware is inserted, if motherboard firmware changes, |
208 | * or due to changes caused in kernel parameters. If the domain is | 291 | * or due to changes caused in kernel parameters. If the domain is |
209 | * left unspecified, it is taken to be 0. | 292 | * left unspecified, it is taken to be 0. In order to be robust against |
293 | * bus renumbering issues, a path of PCI device/function numbers may be used | ||
294 | * to address the specific device. The path for a device can be determined | ||
295 | * through the use of 'lspci -t'. | ||
210 | * | 296 | * |
211 | * The second format matches devices using IDs in the configuration | 297 | * The second format matches devices using IDs in the configuration |
212 | * space which may match multiple devices in the system. A value of 0 | 298 | * space which may match multiple devices in the system. A value of 0 |
@@ -222,7 +308,7 @@ static int pci_dev_str_match(struct pci_dev *dev, const char *p, | |||
222 | const char **endptr) | 308 | const char **endptr) |
223 | { | 309 | { |
224 | int ret; | 310 | int ret; |
225 | int seg, bus, slot, func, count; | 311 | int count; |
226 | unsigned short vendor, device, subsystem_vendor, subsystem_device; | 312 | unsigned short vendor, device, subsystem_vendor, subsystem_device; |
227 | 313 | ||
228 | if (strncmp(p, "pci:", 4) == 0) { | 314 | if (strncmp(p, "pci:", 4) == 0) { |
@@ -248,25 +334,15 @@ static int pci_dev_str_match(struct pci_dev *dev, const char *p, | |||
248 | (!subsystem_device || | 334 | (!subsystem_device || |
249 | subsystem_device == dev->subsystem_device)) | 335 | subsystem_device == dev->subsystem_device)) |
250 | goto found; | 336 | goto found; |
251 | |||
252 | } else { | 337 | } else { |
253 | /* PCI Bus, Device, Function IDs are specified */ | 338 | /* |
254 | ret = sscanf(p, "%x:%x:%x.%x%n", &seg, &bus, &slot, | 339 | * PCI Bus, Device, Function IDs are specified |
255 | &func, &count); | 340 | * (optionally, may include a path of devfns following it) |
256 | if (ret != 4) { | 341 | */ |
257 | seg = 0; | 342 | ret = pci_dev_str_match_path(dev, p, &p); |
258 | ret = sscanf(p, "%x:%x.%x%n", &bus, &slot, | 343 | if (ret < 0) |
259 | &func, &count); | 344 | return ret; |
260 | if (ret != 3) | 345 | else if (ret) |
261 | return -EINVAL; | ||
262 | } | ||
263 | |||
264 | p += count; | ||
265 | |||
266 | if (seg == pci_domain_nr(dev->bus) && | ||
267 | bus == dev->bus->number && | ||
268 | slot == PCI_SLOT(dev->devfn) && | ||
269 | func == PCI_FUNC(dev->devfn)) | ||
270 | goto found; | 346 | goto found; |
271 | } | 347 | } |
272 | 348 | ||