summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorHeiner Kallweit <hkallweit1@gmail.com>2021-09-10 08:22:06 +0200
committerBjorn Helgaas <bhelgaas@google.com>2021-10-11 15:10:11 -0500
commitbf2928c7a284e31f9f91a004b5dca09c522157c0 (patch)
tree249089b5b3d093cfcb4451949ddde23598223783
parente4e737bb5c170df6135a127739a9e6148ee3da82 (diff)
downloadlinux-bf2928c7a284e31f9f91a004b5dca09c522157c0.tar.gz
linux-bf2928c7a284e31f9f91a004b5dca09c522157c0.tar.bz2
linux-bf2928c7a284e31f9f91a004b5dca09c522157c0.zip
PCI/VPD: Add pci_read/write_vpd_any()
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>
-rw-r--r--drivers/pci/vpd.c72
-rw-r--r--include/linux/pci.h2
2 files changed, 52 insertions, 22 deletions
diff --git a/drivers/pci/vpd.c b/drivers/pci/vpd.c
index 4be24890132e..0a804715d98e 100644
--- a/drivers/pci/vpd.c
+++ b/drivers/pci/vpd.c
@@ -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)
{
diff --git a/include/linux/pci.h b/include/linux/pci.h
index cd8aa6fce204..9649bd9e468d 100644
--- a/include/linux/pci.h
+++ b/include/linux/pci.h
@@ -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);