aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/pci/pci.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/pci/pci.c')
-rw-r--r--drivers/pci/pci.c118
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 */
213static 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
272free_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