Commit 94e6108803469a37ee1e3c92dafdd1d59298602f

Authored by Ben Hutchings
Committed by Greg Kroah-Hartman
1 parent 5e0d2a6fc0

PCI: Expose PCI VPD through sysfs

Vital Product Data (VPD) may be exposed by PCI devices in several
ways.  It is generally unsafe to read this information through the
existing interfaces to user-land because of stateful interfaces.

This adds:
- abstract operations for VPD access (struct pci_vpd_ops)
- VPD state information in struct pci_dev (struct pci_vpd)
- an implementation of the VPD access method specified in PCI 2.2
  (in access.c)
- a 'vpd' binary file in sysfs directories for PCI devices with VPD
  operations defined

It adds a probe for PCI 2.2 VPD in pci_scan_device() and release of
VPD state in pci_release_dev().

Signed-off-by: Ben Hutchings <bhutchings@solarflare.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>

Showing 6 changed files with 297 additions and 14 deletions Side-by-side Diff

Documentation/ABI/testing/sysfs-bus-pci
  1 +What: /sys/bus/pci/devices/.../vpd
  2 +Date: February 2008
  3 +Contact: Ben Hutchings <bhutchings@solarflare.com>
  4 +Description:
  5 + A file named vpd in a device directory will be a
  6 + binary file containing the Vital Product Data for the
  7 + device. It should follow the VPD format defined in
  8 + PCI Specification 2.1 or 2.2, but users should consider
  9 + that some devices may have malformatted data. If the
  10 + underlying VPD has a writable section then the
  11 + corresponding section of this file will be writable.
drivers/pci/access.c
  1 +#include <linux/delay.h>
1 2 #include <linux/pci.h>
2 3 #include <linux/module.h>
3 4 #include <linux/sched.h>
... ... @@ -125,6 +126,171 @@
125 126 PCI_USER_WRITE_CONFIG(byte, u8)
126 127 PCI_USER_WRITE_CONFIG(word, u16)
127 128 PCI_USER_WRITE_CONFIG(dword, u32)
  129 +
  130 +/* VPD access through PCI 2.2+ VPD capability */
  131 +
  132 +#define PCI_VPD_PCI22_SIZE (PCI_VPD_ADDR_MASK + 1)
  133 +
  134 +struct pci_vpd_pci22 {
  135 + struct pci_vpd base;
  136 + spinlock_t lock; /* controls access to hardware and the flags */
  137 + u8 cap;
  138 + bool busy;
  139 + bool flag; /* value of F bit to wait for */
  140 +};
  141 +
  142 +/* Wait for last operation to complete */
  143 +static int pci_vpd_pci22_wait(struct pci_dev *dev)
  144 +{
  145 + struct pci_vpd_pci22 *vpd =
  146 + container_of(dev->vpd, struct pci_vpd_pci22, base);
  147 + u16 flag, status;
  148 + int wait;
  149 + int ret;
  150 +
  151 + if (!vpd->busy)
  152 + return 0;
  153 +
  154 + flag = vpd->flag ? PCI_VPD_ADDR_F : 0;
  155 + wait = vpd->flag ? 10 : 1000; /* read: 100 us; write: 10 ms */
  156 + for (;;) {
  157 + ret = pci_user_read_config_word(dev,
  158 + vpd->cap + PCI_VPD_ADDR,
  159 + &status);
  160 + if (ret < 0)
  161 + return ret;
  162 + if ((status & PCI_VPD_ADDR_F) == flag) {
  163 + vpd->busy = false;
  164 + return 0;
  165 + }
  166 + if (wait-- == 0)
  167 + return -ETIMEDOUT;
  168 + udelay(10);
  169 + }
  170 +}
  171 +
  172 +static int pci_vpd_pci22_read(struct pci_dev *dev, int pos, int size,
  173 + char *buf)
  174 +{
  175 + struct pci_vpd_pci22 *vpd =
  176 + container_of(dev->vpd, struct pci_vpd_pci22, base);
  177 + u32 val;
  178 + int ret;
  179 + int begin, end, i;
  180 +
  181 + if (pos < 0 || pos > PCI_VPD_PCI22_SIZE ||
  182 + size > PCI_VPD_PCI22_SIZE - pos)
  183 + return -EINVAL;
  184 + if (size == 0)
  185 + return 0;
  186 +
  187 + spin_lock_irq(&vpd->lock);
  188 + ret = pci_vpd_pci22_wait(dev);
  189 + if (ret < 0)
  190 + goto out;
  191 + ret = pci_user_write_config_word(dev, vpd->cap + PCI_VPD_ADDR,
  192 + pos & ~3);
  193 + if (ret < 0)
  194 + goto out;
  195 + vpd->busy = true;
  196 + vpd->flag = 1;
  197 + ret = pci_vpd_pci22_wait(dev);
  198 + if (ret < 0)
  199 + goto out;
  200 + ret = pci_user_read_config_dword(dev, vpd->cap + PCI_VPD_DATA,
  201 + &val);
  202 +out:
  203 + spin_unlock_irq(&vpd->lock);
  204 + if (ret < 0)
  205 + return ret;
  206 +
  207 + /* Convert to bytes */
  208 + begin = pos & 3;
  209 + end = min(4, begin + size);
  210 + for (i = 0; i < end; ++i) {
  211 + if (i >= begin)
  212 + *buf++ = val;
  213 + val >>= 8;
  214 + }
  215 + return end - begin;
  216 +}
  217 +
  218 +static int pci_vpd_pci22_write(struct pci_dev *dev, int pos, int size,
  219 + const char *buf)
  220 +{
  221 + struct pci_vpd_pci22 *vpd =
  222 + container_of(dev->vpd, struct pci_vpd_pci22, base);
  223 + u32 val;
  224 + int ret;
  225 +
  226 + if (pos < 0 || pos > PCI_VPD_PCI22_SIZE || pos & 3 ||
  227 + size > PCI_VPD_PCI22_SIZE - pos || size < 4)
  228 + return -EINVAL;
  229 +
  230 + val = (u8) *buf++;
  231 + val |= ((u8) *buf++) << 8;
  232 + val |= ((u8) *buf++) << 16;
  233 + val |= ((u32)(u8) *buf++) << 24;
  234 +
  235 + spin_lock_irq(&vpd->lock);
  236 + ret = pci_vpd_pci22_wait(dev);
  237 + if (ret < 0)
  238 + goto out;
  239 + ret = pci_user_write_config_dword(dev, vpd->cap + PCI_VPD_DATA,
  240 + val);
  241 + if (ret < 0)
  242 + goto out;
  243 + ret = pci_user_write_config_word(dev, vpd->cap + PCI_VPD_ADDR,
  244 + pos | PCI_VPD_ADDR_F);
  245 + if (ret < 0)
  246 + goto out;
  247 + vpd->busy = true;
  248 + vpd->flag = 0;
  249 + ret = pci_vpd_pci22_wait(dev);
  250 +out:
  251 + spin_unlock_irq(&vpd->lock);
  252 + if (ret < 0)
  253 + return ret;
  254 +
  255 + return 4;
  256 +}
  257 +
  258 +static int pci_vpd_pci22_get_size(struct pci_dev *dev)
  259 +{
  260 + return PCI_VPD_PCI22_SIZE;
  261 +}
  262 +
  263 +static void pci_vpd_pci22_release(struct pci_dev *dev)
  264 +{
  265 + kfree(container_of(dev->vpd, struct pci_vpd_pci22, base));
  266 +}
  267 +
  268 +static struct pci_vpd_ops pci_vpd_pci22_ops = {
  269 + .read = pci_vpd_pci22_read,
  270 + .write = pci_vpd_pci22_write,
  271 + .get_size = pci_vpd_pci22_get_size,
  272 + .release = pci_vpd_pci22_release,
  273 +};
  274 +
  275 +int pci_vpd_pci22_init(struct pci_dev *dev)
  276 +{
  277 + struct pci_vpd_pci22 *vpd;
  278 + u8 cap;
  279 +
  280 + cap = pci_find_capability(dev, PCI_CAP_ID_VPD);
  281 + if (!cap)
  282 + return -ENODEV;
  283 + vpd = kzalloc(sizeof(*vpd), GFP_ATOMIC);
  284 + if (!vpd)
  285 + return -ENOMEM;
  286 +
  287 + vpd->base.ops = &pci_vpd_pci22_ops;
  288 + spin_lock_init(&vpd->lock);
  289 + vpd->cap = cap;
  290 + vpd->busy = false;
  291 + dev->vpd = &vpd->base;
  292 + return 0;
  293 +}
128 294  
129 295 /**
130 296 * pci_block_user_cfg_access - Block userspace PCI config reads/writes
drivers/pci/pci-sysfs.c
... ... @@ -343,6 +343,58 @@
343 343 return count;
344 344 }
345 345  
  346 +static ssize_t
  347 +pci_read_vpd(struct kobject *kobj, struct bin_attribute *bin_attr,
  348 + char *buf, loff_t off, size_t count)
  349 +{
  350 + struct pci_dev *dev =
  351 + to_pci_dev(container_of(kobj, struct device, kobj));
  352 + int end;
  353 + int ret;
  354 +
  355 + if (off > bin_attr->size)
  356 + count = 0;
  357 + else if (count > bin_attr->size - off)
  358 + count = bin_attr->size - off;
  359 + end = off + count;
  360 +
  361 + while (off < end) {
  362 + ret = dev->vpd->ops->read(dev, off, end - off, buf);
  363 + if (ret < 0)
  364 + return ret;
  365 + buf += ret;
  366 + off += ret;
  367 + }
  368 +
  369 + return count;
  370 +}
  371 +
  372 +static ssize_t
  373 +pci_write_vpd(struct kobject *kobj, struct bin_attribute *bin_attr,
  374 + char *buf, loff_t off, size_t count)
  375 +{
  376 + struct pci_dev *dev =
  377 + to_pci_dev(container_of(kobj, struct device, kobj));
  378 + int end;
  379 + int ret;
  380 +
  381 + if (off > bin_attr->size)
  382 + count = 0;
  383 + else if (count > bin_attr->size - off)
  384 + count = bin_attr->size - off;
  385 + end = off + count;
  386 +
  387 + while (off < end) {
  388 + ret = dev->vpd->ops->write(dev, off, end - off, buf);
  389 + if (ret < 0)
  390 + return ret;
  391 + buf += ret;
  392 + off += ret;
  393 + }
  394 +
  395 + return count;
  396 +}
  397 +
346 398 #ifdef HAVE_PCI_LEGACY
347 399 /**
348 400 * pci_read_legacy_io - read byte(s) from legacy I/O port space
... ... @@ -611,7 +663,7 @@
611 663  
612 664 int __must_check pci_create_sysfs_dev_files (struct pci_dev *pdev)
613 665 {
614   - struct bin_attribute *rom_attr = NULL;
  666 + struct bin_attribute *attr = NULL;
615 667 int retval;
616 668  
617 669 if (!sysfs_initialized)
618 670  
619 671  
... ... @@ -624,22 +676,41 @@
624 676 if (retval)
625 677 goto err;
626 678  
  679 + /* If the device has VPD, try to expose it in sysfs. */
  680 + if (pdev->vpd) {
  681 + attr = kzalloc(sizeof(*attr), GFP_ATOMIC);
  682 + if (attr) {
  683 + pdev->vpd->attr = attr;
  684 + attr->size = pdev->vpd->ops->get_size(pdev);
  685 + attr->attr.name = "vpd";
  686 + attr->attr.mode = S_IRUGO | S_IWUSR;
  687 + attr->read = pci_read_vpd;
  688 + attr->write = pci_write_vpd;
  689 + retval = sysfs_create_bin_file(&pdev->dev.kobj, attr);
  690 + if (retval)
  691 + goto err_vpd;
  692 + } else {
  693 + retval = -ENOMEM;
  694 + goto err_config_file;
  695 + }
  696 + }
  697 +
627 698 retval = pci_create_resource_files(pdev);
628 699 if (retval)
629   - goto err_bin_file;
  700 + goto err_vpd_file;
630 701  
631 702 /* If the device has a ROM, try to expose it in sysfs. */
632 703 if (pci_resource_len(pdev, PCI_ROM_RESOURCE) ||
633 704 (pdev->resource[PCI_ROM_RESOURCE].flags & IORESOURCE_ROM_SHADOW)) {
634   - rom_attr = kzalloc(sizeof(*rom_attr), GFP_ATOMIC);
635   - if (rom_attr) {
636   - pdev->rom_attr = rom_attr;
637   - rom_attr->size = pci_resource_len(pdev, PCI_ROM_RESOURCE);
638   - rom_attr->attr.name = "rom";
639   - rom_attr->attr.mode = S_IRUSR;
640   - rom_attr->read = pci_read_rom;
641   - rom_attr->write = pci_write_rom;
642   - retval = sysfs_create_bin_file(&pdev->dev.kobj, rom_attr);
  705 + attr = kzalloc(sizeof(*attr), GFP_ATOMIC);
  706 + if (attr) {
  707 + pdev->rom_attr = attr;
  708 + attr->size = pci_resource_len(pdev, PCI_ROM_RESOURCE);
  709 + attr->attr.name = "rom";
  710 + attr->attr.mode = S_IRUSR;
  711 + attr->read = pci_read_rom;
  712 + attr->write = pci_write_rom;
  713 + retval = sysfs_create_bin_file(&pdev->dev.kobj, attr);
643 714 if (retval)
644 715 goto err_rom;
645 716 } else {
646 717  
647 718  
... ... @@ -657,12 +728,18 @@
657 728  
658 729 err_rom_file:
659 730 if (pci_resource_len(pdev, PCI_ROM_RESOURCE))
660   - sysfs_remove_bin_file(&pdev->dev.kobj, rom_attr);
  731 + sysfs_remove_bin_file(&pdev->dev.kobj, pdev->rom_attr);
661 732 err_rom:
662   - kfree(rom_attr);
  733 + kfree(pdev->rom_attr);
663 734 err_resource_files:
664 735 pci_remove_resource_files(pdev);
665   -err_bin_file:
  736 +err_vpd_file:
  737 + if (pdev->vpd) {
  738 + sysfs_remove_bin_file(&pdev->dev.kobj, pdev->vpd->attr);
  739 +err_vpd:
  740 + kfree(pdev->vpd->attr);
  741 + }
  742 +err_config_file:
666 743 if (pdev->cfg_size < 4096)
667 744 sysfs_remove_bin_file(&pdev->dev.kobj, &pci_config_attr);
668 745 else
... ... @@ -684,6 +761,10 @@
684 761  
685 762 pcie_aspm_remove_sysfs_dev_files(pdev);
686 763  
  764 + if (pdev->vpd) {
  765 + sysfs_remove_bin_file(&pdev->dev.kobj, pdev->vpd->attr);
  766 + kfree(pdev->vpd->attr);
  767 + }
687 768 if (pdev->cfg_size < 4096)
688 769 sysfs_remove_bin_file(&pdev->dev.kobj, &pci_config_attr);
689 770 else
... ... @@ -18,6 +18,25 @@
18 18 extern int pci_user_write_config_word(struct pci_dev *dev, int where, u16 val);
19 19 extern int pci_user_write_config_dword(struct pci_dev *dev, int where, u32 val);
20 20  
  21 +struct pci_vpd_ops {
  22 + int (*read)(struct pci_dev *dev, int pos, int size, char *buf);
  23 + int (*write)(struct pci_dev *dev, int pos, int size, const char *buf);
  24 + int (*get_size)(struct pci_dev *dev);
  25 + void (*release)(struct pci_dev *dev);
  26 +};
  27 +
  28 +struct pci_vpd {
  29 + struct pci_vpd_ops *ops;
  30 + struct bin_attribute *attr; /* descriptor for sysfs VPD entry */
  31 +};
  32 +
  33 +extern int pci_vpd_pci22_init(struct pci_dev *dev);
  34 +static inline void pci_vpd_release(struct pci_dev *dev)
  35 +{
  36 + if (dev->vpd)
  37 + dev->vpd->ops->release(dev);
  38 +}
  39 +
21 40 /* PCI /proc functions */
22 41 #ifdef CONFIG_PROC_FS
23 42 extern int pci_proc_attach_device(struct pci_dev *dev);
... ... @@ -794,6 +794,7 @@
794 794 struct pci_dev *pci_dev;
795 795  
796 796 pci_dev = to_pci_dev(dev);
  797 + pci_vpd_release(pci_dev);
797 798 kfree(pci_dev);
798 799 }
799 800  
... ... @@ -932,6 +933,8 @@
932 933 kfree(dev);
933 934 return NULL;
934 935 }
  936 +
  937 + pci_vpd_pci22_init(dev);
935 938  
936 939 return dev;
937 940 }
... ... @@ -20,6 +20,8 @@
20 20 /* Include the pci register defines */
21 21 #include <linux/pci_regs.h>
22 22  
  23 +struct pci_vpd;
  24 +
23 25 /*
24 26 * The PCI interface treats multi-function devices as independent
25 27 * devices. The slot/function address of each device is encoded
... ... @@ -206,6 +208,7 @@
206 208 #ifdef CONFIG_PCI_MSI
207 209 struct list_head msi_list;
208 210 #endif
  211 + struct pci_vpd *vpd;
209 212 };
210 213  
211 214 extern struct pci_dev *alloc_pci_dev(void);