PCI/VPD: Add pci_read/write_vpd_any()
authorHeiner Kallweit <hkallweit1@gmail.com>
Fri, 10 Sep 2021 06:22:06 +0000 (08:22 +0200)
committerBjorn Helgaas <bhelgaas@google.com>
Mon, 11 Oct 2021 20:10:11 +0000 (15:10 -0500)
In certain cases we need a variant of pci_read_vpd()/pci_write_vpd() that
does not check against dev->vpd.len. Such cases are:

  - Reading VPD if dev->vpd.len isn't set yet (in pci_vpd_size())

  - Devices that map non-VPD information to arbitrary places in VPD address
    space (example: Chelsio T3 EEPROM write-protect flag)

Therefore add pci_read_vpd_any() and pci_write_vpd_any() that check against
PCI_VPD_MAX_SIZE only.

Link: https://lore.kernel.org/r/93ecce28-a158-f02a-d134-8afcaced8efe@gmail.com
Signed-off-by: Heiner Kallweit <hkallweit1@gmail.com>
Signed-off-by: Bjorn Helgaas <bhelgaas@google.com>
Acked-by: Jakub Kicinski <kuba@kernel.org>
drivers/pci/vpd.c
include/linux/pci.h

index 4be2489..0a80471 100644 (file)
@@ -156,9 +156,10 @@ static int pci_vpd_wait(struct pci_dev *dev, bool set)
 }
 
 static ssize_t pci_vpd_read(struct pci_dev *dev, loff_t pos, size_t count,
-                           void *arg)
+                           void *arg, bool check_size)
 {
        struct pci_vpd *vpd = &dev->vpd;
+       unsigned int max_len = check_size ? vpd->len : PCI_VPD_MAX_SIZE;
        int ret = 0;
        loff_t end = pos + count;
        u8 *buf = arg;
@@ -169,11 +170,11 @@ static ssize_t pci_vpd_read(struct pci_dev *dev, loff_t pos, size_t count,
        if (pos < 0)
                return -EINVAL;
 
-       if (pos > vpd->len)
+       if (pos >= max_len)
                return 0;
 
-       if (end > vpd->len) {
-               end = vpd->len;
+       if (end > max_len) {
+               end = max_len;
                count = end - pos;
        }
 
@@ -217,9 +218,10 @@ static ssize_t pci_vpd_read(struct pci_dev *dev, loff_t pos, size_t count,
 }
 
 static ssize_t pci_vpd_write(struct pci_dev *dev, loff_t pos, size_t count,
-                            const void *arg)
+                            const void *arg, bool check_size)
 {
        struct pci_vpd *vpd = &dev->vpd;
+       unsigned int max_len = check_size ? vpd->len : PCI_VPD_MAX_SIZE;
        const u8 *buf = arg;
        loff_t end = pos + count;
        int ret = 0;
@@ -230,7 +232,7 @@ static ssize_t pci_vpd_write(struct pci_dev *dev, loff_t pos, size_t count,
        if (pos < 0 || (pos & 3) || (count & 3))
                return -EINVAL;
 
-       if (end > vpd->len)
+       if (end > max_len)
                return -EINVAL;
 
        if (mutex_lock_killable(&vpd->lock))
@@ -381,6 +383,24 @@ static int pci_vpd_find_info_keyword(const u8 *buf, unsigned int off,
        return -ENOENT;
 }
 
+static ssize_t __pci_read_vpd(struct pci_dev *dev, loff_t pos, size_t count, void *buf,
+                             bool check_size)
+{
+       ssize_t ret;
+
+       if (dev->dev_flags & PCI_DEV_FLAGS_VPD_REF_F0) {
+               dev = pci_get_func0_dev(dev);
+               if (!dev)
+                       return -ENODEV;
+
+               ret = pci_vpd_read(dev, pos, count, buf, check_size);
+               pci_dev_put(dev);
+               return ret;
+       }
+
+       return pci_vpd_read(dev, pos, count, buf, check_size);
+}
+
 /**
  * pci_read_vpd - Read one entry from Vital Product Data
  * @dev:       PCI device struct
@@ -390,6 +410,20 @@ static int pci_vpd_find_info_keyword(const u8 *buf, unsigned int off,
  */
 ssize_t pci_read_vpd(struct pci_dev *dev, loff_t pos, size_t count, void *buf)
 {
+       return __pci_read_vpd(dev, pos, count, buf, true);
+}
+EXPORT_SYMBOL(pci_read_vpd);
+
+/* Same, but allow to access any address */
+ssize_t pci_read_vpd_any(struct pci_dev *dev, loff_t pos, size_t count, void *buf)
+{
+       return __pci_read_vpd(dev, pos, count, buf, false);
+}
+EXPORT_SYMBOL(pci_read_vpd_any);
+
+static ssize_t __pci_write_vpd(struct pci_dev *dev, loff_t pos, size_t count,
+                              const void *buf, bool check_size)
+{
        ssize_t ret;
 
        if (dev->dev_flags & PCI_DEV_FLAGS_VPD_REF_F0) {
@@ -397,14 +431,13 @@ ssize_t pci_read_vpd(struct pci_dev *dev, loff_t pos, size_t count, void *buf)
                if (!dev)
                        return -ENODEV;
 
-               ret = pci_vpd_read(dev, pos, count, buf);
+               ret = pci_vpd_write(dev, pos, count, buf, check_size);
                pci_dev_put(dev);
                return ret;
        }
 
-       return pci_vpd_read(dev, pos, count, buf);
+       return pci_vpd_write(dev, pos, count, buf, check_size);
 }
-EXPORT_SYMBOL(pci_read_vpd);
 
 /**
  * pci_write_vpd - Write entry to Vital Product Data
@@ -415,22 +448,17 @@ EXPORT_SYMBOL(pci_read_vpd);
  */
 ssize_t pci_write_vpd(struct pci_dev *dev, loff_t pos, size_t count, const void *buf)
 {
-       ssize_t ret;
-
-       if (dev->dev_flags & PCI_DEV_FLAGS_VPD_REF_F0) {
-               dev = pci_get_func0_dev(dev);
-               if (!dev)
-                       return -ENODEV;
-
-               ret = pci_vpd_write(dev, pos, count, buf);
-               pci_dev_put(dev);
-               return ret;
-       }
-
-       return pci_vpd_write(dev, pos, count, buf);
+       return __pci_write_vpd(dev, pos, count, buf, true);
 }
 EXPORT_SYMBOL(pci_write_vpd);
 
+/* Same, but allow to access any address */
+ssize_t pci_write_vpd_any(struct pci_dev *dev, loff_t pos, size_t count, const void *buf)
+{
+       return __pci_write_vpd(dev, pos, count, buf, false);
+}
+EXPORT_SYMBOL(pci_write_vpd_any);
+
 int pci_vpd_find_ro_info_keyword(const void *buf, unsigned int len,
                                 const char *kw, unsigned int *size)
 {
index cd8aa6f..9649bd9 100644 (file)
@@ -1350,6 +1350,8 @@ void pci_unlock_rescan_remove(void);
 /* Vital Product Data routines */
 ssize_t pci_read_vpd(struct pci_dev *dev, loff_t pos, size_t count, void *buf);
 ssize_t pci_write_vpd(struct pci_dev *dev, loff_t pos, size_t count, const void *buf);
+ssize_t pci_read_vpd_any(struct pci_dev *dev, loff_t pos, size_t count, void *buf);
+ssize_t pci_write_vpd_any(struct pci_dev *dev, loff_t pos, size_t count, const void *buf);
 
 /* Helper functions for low-level code (drivers/pci/setup-[bus,res].c) */
 resource_size_t pcibios_retrieve_fw_addr(struct pci_dev *dev, int idx);