Commit bf2928c7 authored by Heiner Kallweit's avatar Heiner Kallweit Committed by Bjorn Helgaas
Browse files

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: default avatarHeiner Kallweit <hkallweit1@gmail.com>
Signed-off-by: default avatarBjorn Helgaas <bhelgaas@google.com>
Acked-by: default avatarJakub Kicinski <kuba@kernel.org>
parent e4e737bb
Loading
Loading
Loading
Loading
+50 −22
Original line number Diff line number Diff line
@@ -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
@@ -389,6 +409,20 @@ static int pci_vpd_find_info_keyword(const u8 *buf, unsigned int off,
 * @buf:	pointer to where to store result
 */
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;

@@ -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,21 +448,16 @@ 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_write_vpd(dev, pos, count, buf, true);
}
EXPORT_SYMBOL(pci_write_vpd);

	return pci_vpd_write(dev, pos, count, buf);
/* 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);
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)
+2 −0
Original line number Diff line number Diff line
@@ -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);