From e2cdd86b561719da9ac928635f2a55b370dbb5b1 Mon Sep 17 00:00:00 2001 From: Bjorn Helgaas Date: Thu, 15 Jul 2021 16:59:55 -0500 Subject: PCI/VPD: Correct diagnostic for VPD read failure Previously, when a VPD read failed, we warned about an "invalid large VPD tag". Warn about the VPD read failure instead. Signed-off-by: Bjorn Helgaas Reviewed-by: Hannes Reinecke --- drivers/pci/vpd.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/pci') diff --git a/drivers/pci/vpd.c b/drivers/pci/vpd.c index 26bf7c877de5..8c4fad7e3b6a 100644 --- a/drivers/pci/vpd.c +++ b/drivers/pci/vpd.c @@ -92,8 +92,8 @@ static size_t pci_vpd_size(struct pci_dev *dev, size_t old_size) (tag == PCI_VPD_LTIN_RW_DATA)) { if (pci_read_vpd(dev, off+1, 2, &header[1]) != 2) { - pci_warn(dev, "invalid large VPD tag %02x size at offset %zu", - tag, off + 1); + pci_warn(dev, "failed VPD read at offset %zu\n", + off + 1); return 0; } off += PCI_VPD_LRDT_TAG_SIZE + -- cgit v1.2.3 From 70730db0f611a721fe652cfe92c7f94ccf687454 Mon Sep 17 00:00:00 2001 From: Bjorn Helgaas Date: Thu, 15 Jul 2021 16:59:56 -0500 Subject: PCI/VPD: Check Resource Item Names against those valid for type Previously, we checked for PCI_VPD_STIN_END, PCI_VPD_LTIN_ID_STRING, etc., outside the Large and Small Resource cases, so we checked Large Resource Item Names against a Small Resource name and vice versa. Move these tests into the Large and Small Resource cases, so we only check PCI_VPD_STIN_END for Small Resources and PCI_VPD_LTIN_* for Large Resources. Signed-off-by: Bjorn Helgaas Reviewed-by: Hannes Reinecke --- drivers/pci/vpd.c | 18 ++++++------------ 1 file changed, 6 insertions(+), 12 deletions(-) (limited to 'drivers/pci') diff --git a/drivers/pci/vpd.c b/drivers/pci/vpd.c index 8c4fad7e3b6a..28052d4d1990 100644 --- a/drivers/pci/vpd.c +++ b/drivers/pci/vpd.c @@ -98,24 +98,18 @@ static size_t pci_vpd_size(struct pci_dev *dev, size_t old_size) } off += PCI_VPD_LRDT_TAG_SIZE + pci_vpd_lrdt_size(header); + } else { + pci_warn(dev, "invalid large VPD tag %02x at offset %zu\n", + tag, off); + return 0; } } else { /* Short Resource Data Type Tag */ off += PCI_VPD_SRDT_TAG_SIZE + pci_vpd_srdt_size(header); tag = pci_vpd_srdt_tag(header); - } - - if (tag == PCI_VPD_STIN_END) /* End tag descriptor */ - return off; - - if ((tag != PCI_VPD_LTIN_ID_STRING) && - (tag != PCI_VPD_LTIN_RO_DATA) && - (tag != PCI_VPD_LTIN_RW_DATA)) { - pci_warn(dev, "invalid %s VPD tag %02x at offset %zu", - (header[0] & PCI_VPD_LRDT) ? "large" : "short", - tag, off); - return 0; + if (tag == PCI_VPD_STIN_END) /* End tag descriptor */ + return off; } } return 0; -- cgit v1.2.3 From 4e0d77f8e831fcbe86a02dd0ead12d9c8c057700 Mon Sep 17 00:00:00 2001 From: Heiner Kallweit Date: Thu, 29 Jul 2021 12:22:25 -0500 Subject: PCI/VPD: Treat initial 0xff as missing EEPROM Previously we assumed that the first tag being 0x00 meant an EEPROM was missing. The first tag being 0xff means the same thing; check for that also. [bhelgaas: rework error mesage] Signed-off-by: Heiner Kallweit Signed-off-by: Bjorn Helgaas Reviewed-by: Hannes Reinecke --- drivers/pci/vpd.c | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) (limited to 'drivers/pci') diff --git a/drivers/pci/vpd.c b/drivers/pci/vpd.c index 28052d4d1990..05e4df0a84d3 100644 --- a/drivers/pci/vpd.c +++ b/drivers/pci/vpd.c @@ -78,10 +78,8 @@ static size_t pci_vpd_size(struct pci_dev *dev, size_t old_size) while (off < old_size && pci_read_vpd(dev, off, 1, header) == 1) { unsigned char tag; - if (!header[0] && !off) { - pci_info(dev, "Invalid VPD tag 00, assume missing optional VPD EPROM\n"); - return 0; - } + if (off == 0 && (header[0] == 0x00 || header[0] == 0xff)) + goto error; if (header[0] & PCI_VPD_LRDT) { /* Large Resource Data Type Tag */ @@ -113,6 +111,12 @@ static size_t pci_vpd_size(struct pci_dev *dev, size_t old_size) } } return 0; + +error: + pci_info(dev, "invalid VPD tag %#04x at offset %zu%s\n", + header[0], off, off == 0 ? + "; assume missing optional EEPROM" : ""); + return 0; } /* -- cgit v1.2.3 From 6303049d16f0e69d0449c3c80d0e3695d4f02f94 Mon Sep 17 00:00:00 2001 From: Bjorn Helgaas Date: Thu, 15 Jul 2021 16:59:57 -0500 Subject: PCI/VPD: Reject resource tags with invalid size VPD is limited in size by the 15-bit VPD Address field in the VPD Capability. Each resource tag includes a length that determines the overall size of the resource. Reject any resources that would extend past the maximum VPD size. Signed-off-by: Bjorn Helgaas Reviewed-by: Hannes Reinecke --- drivers/pci/vpd.c | 24 +++++++++++++++--------- 1 file changed, 15 insertions(+), 9 deletions(-) (limited to 'drivers/pci') diff --git a/drivers/pci/vpd.c b/drivers/pci/vpd.c index 05e4df0a84d3..850deff0dd43 100644 --- a/drivers/pci/vpd.c +++ b/drivers/pci/vpd.c @@ -72,11 +72,11 @@ EXPORT_SYMBOL(pci_write_vpd); */ static size_t pci_vpd_size(struct pci_dev *dev, size_t old_size) { - size_t off = 0; - unsigned char header[1+2]; /* 1 byte tag, 2 bytes length */ + size_t off = 0, size; + unsigned char tag, header[1+2]; /* 1 byte tag, 2 bytes length */ while (off < old_size && pci_read_vpd(dev, off, 1, header) == 1) { - unsigned char tag; + size = 0; if (off == 0 && (header[0] == 0x00 || header[0] == 0xff)) goto error; @@ -94,8 +94,11 @@ static size_t pci_vpd_size(struct pci_dev *dev, size_t old_size) off + 1); return 0; } - off += PCI_VPD_LRDT_TAG_SIZE + - pci_vpd_lrdt_size(header); + size = pci_vpd_lrdt_size(header); + if (off + size > PCI_VPD_MAX_SIZE) + goto error; + + off += PCI_VPD_LRDT_TAG_SIZE + size; } else { pci_warn(dev, "invalid large VPD tag %02x at offset %zu\n", tag, off); @@ -103,9 +106,12 @@ static size_t pci_vpd_size(struct pci_dev *dev, size_t old_size) } } else { /* Short Resource Data Type Tag */ - off += PCI_VPD_SRDT_TAG_SIZE + - pci_vpd_srdt_size(header); tag = pci_vpd_srdt_tag(header); + size = pci_vpd_srdt_size(header); + if (off + size > PCI_VPD_MAX_SIZE) + goto error; + + off += PCI_VPD_SRDT_TAG_SIZE + size; if (tag == PCI_VPD_STIN_END) /* End tag descriptor */ return off; } @@ -113,8 +119,8 @@ static size_t pci_vpd_size(struct pci_dev *dev, size_t old_size) return 0; error: - pci_info(dev, "invalid VPD tag %#04x at offset %zu%s\n", - header[0], off, off == 0 ? + pci_info(dev, "invalid VPD tag %#04x (size %zu) at offset %zu%s\n", + header[0], size, off, off == 0 ? "; assume missing optional EEPROM" : ""); return 0; } -- cgit v1.2.3 From 7fa75dd8c64590850a54991a8bb914667c512b4c Mon Sep 17 00:00:00 2001 From: Bjorn Helgaas Date: Thu, 15 Jul 2021 16:59:58 -0500 Subject: PCI/VPD: Don't check Large Resource Item Names for validity VPD consists of a series of Small and Large Resources. Computing the size of VPD requires only the length of each, which is specified in the generic tag of each resource. We only expect to see ID_STRING, RO_DATA, and RW_DATA in VPD, but it's not a problem if it contains other resource types because all we care about is the size. Drop the validity checking of Large Resource items. Signed-off-by: Bjorn Helgaas Reviewed-by: Hannes Reinecke --- drivers/pci/vpd.c | 27 ++++++++------------------- 1 file changed, 8 insertions(+), 19 deletions(-) (limited to 'drivers/pci') diff --git a/drivers/pci/vpd.c b/drivers/pci/vpd.c index 850deff0dd43..602d30220ec4 100644 --- a/drivers/pci/vpd.c +++ b/drivers/pci/vpd.c @@ -83,27 +83,16 @@ static size_t pci_vpd_size(struct pci_dev *dev, size_t old_size) if (header[0] & PCI_VPD_LRDT) { /* Large Resource Data Type Tag */ - tag = pci_vpd_lrdt_tag(header); - /* Only read length from known tag items */ - if ((tag == PCI_VPD_LTIN_ID_STRING) || - (tag == PCI_VPD_LTIN_RO_DATA) || - (tag == PCI_VPD_LTIN_RW_DATA)) { - if (pci_read_vpd(dev, off+1, 2, - &header[1]) != 2) { - pci_warn(dev, "failed VPD read at offset %zu\n", - off + 1); - return 0; - } - size = pci_vpd_lrdt_size(header); - if (off + size > PCI_VPD_MAX_SIZE) - goto error; - - off += PCI_VPD_LRDT_TAG_SIZE + size; - } else { - pci_warn(dev, "invalid large VPD tag %02x at offset %zu\n", - tag, off); + if (pci_read_vpd(dev, off + 1, 2, &header[1]) != 2) { + pci_warn(dev, "failed VPD read at offset %zu\n", + off + 1); return 0; } + size = pci_vpd_lrdt_size(header); + if (off + size > PCI_VPD_MAX_SIZE) + goto error; + + off += PCI_VPD_LRDT_TAG_SIZE + size; } else { /* Short Resource Data Type Tag */ tag = pci_vpd_srdt_tag(header); -- cgit v1.2.3 From 5fe204eab174fd474227f23fd47faee4e7a6c000 Mon Sep 17 00:00:00 2001 From: Bjorn Helgaas Date: Thu, 15 Jul 2021 16:59:59 -0500 Subject: PCI/VPD: Allow access to valid parts of VPD if some is invalid Previously, if we found any error in the VPD, we returned size 0, which prevents access to all of VPD. But there may be valid resources in VPD before the error, and there's no reason to prevent access to those. "off" covers only VPD resources known to have valid header tags. In case of error, return "off" (which may be zero if we haven't found any valid header tags at all). Signed-off-by: Bjorn Helgaas Reviewed-by: Hannes Reinecke --- drivers/pci/vpd.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'drivers/pci') diff --git a/drivers/pci/vpd.c b/drivers/pci/vpd.c index 602d30220ec4..a85c8b51703a 100644 --- a/drivers/pci/vpd.c +++ b/drivers/pci/vpd.c @@ -86,7 +86,7 @@ static size_t pci_vpd_size(struct pci_dev *dev, size_t old_size) if (pci_read_vpd(dev, off + 1, 2, &header[1]) != 2) { pci_warn(dev, "failed VPD read at offset %zu\n", off + 1); - return 0; + return off; } size = pci_vpd_lrdt_size(header); if (off + size > PCI_VPD_MAX_SIZE) @@ -105,13 +105,13 @@ static size_t pci_vpd_size(struct pci_dev *dev, size_t old_size) return off; } } - return 0; + return off; error: pci_info(dev, "invalid VPD tag %#04x (size %zu) at offset %zu%s\n", header[0], size, off, off == 0 ? "; assume missing optional EEPROM" : ""); - return 0; + return off; } /* -- cgit v1.2.3 From 1285762c07121b449cd60166b813c0084b792736 Mon Sep 17 00:00:00 2001 From: Heiner Kallweit Date: Thu, 13 May 2021 22:56:09 +0200 Subject: PCI/VPD: Remove pci_vpd_size() old_size argument vpd->len is initialized to PCI_VPD_MAX_SIZE, and if a quirk is used to set a specific VPD size, then pci_vpd_set_size() sets vpd->valid, resulting in pci_vpd_size() not being called. Therefore we can remove the old_size argument. Note that we don't have to check off < PCI_VPD_MAX_SIZE because that's implicitly done by pci_read_vpd(). Link: https://lore.kernel.org/r/ede36c16-5335-6867-43a1-293641348430@gmail.com Signed-off-by: Heiner Kallweit Signed-off-by: Bjorn Helgaas --- drivers/pci/vpd.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'drivers/pci') diff --git a/drivers/pci/vpd.c b/drivers/pci/vpd.c index a85c8b51703a..4f262f8530ea 100644 --- a/drivers/pci/vpd.c +++ b/drivers/pci/vpd.c @@ -68,14 +68,13 @@ EXPORT_SYMBOL(pci_write_vpd); /** * pci_vpd_size - determine actual size of Vital Product Data * @dev: pci device struct - * @old_size: current assumed size, also maximum allowed size */ -static size_t pci_vpd_size(struct pci_dev *dev, size_t old_size) +static size_t pci_vpd_size(struct pci_dev *dev) { size_t off = 0, size; unsigned char tag, header[1+2]; /* 1 byte tag, 2 bytes length */ - while (off < old_size && pci_read_vpd(dev, off, 1, header) == 1) { + while (pci_read_vpd(dev, off, 1, header) == 1) { size = 0; if (off == 0 && (header[0] == 0x00 || header[0] == 0xff)) @@ -172,7 +171,7 @@ static ssize_t pci_vpd_read(struct pci_dev *dev, loff_t pos, size_t count, if (!vpd->valid) { vpd->valid = 1; - vpd->len = pci_vpd_size(dev, vpd->len); + vpd->len = pci_vpd_size(dev); } if (vpd->len == 0) @@ -239,7 +238,7 @@ static ssize_t pci_vpd_write(struct pci_dev *dev, loff_t pos, size_t count, if (!vpd->valid) { vpd->valid = 1; - vpd->len = pci_vpd_size(dev, vpd->len); + vpd->len = pci_vpd_size(dev); } if (vpd->len == 0) @@ -463,6 +462,7 @@ static void quirk_blacklist_vpd(struct pci_dev *dev) { if (dev->vpd) { dev->vpd->len = 0; + dev->vpd->valid = 1; pci_warn(dev, FW_BUG "disabling VPD access (can't determine size of non-standard VPD format)\n"); } } -- cgit v1.2.3 From 91ab5d9d02a97264368eb1d72efdba2ec18cc0d4 Mon Sep 17 00:00:00 2001 From: Heiner Kallweit Date: Thu, 13 May 2021 22:56:41 +0200 Subject: PCI/VPD: Make pci_vpd_wait() uninterruptible Reading/writing 4 bytes should be fast enough even on a slow bus, therefore pci_vpd_wait() doesn't have to be interruptible. Making it uninterruptible allows to simplify the code. In addition make VPD writes uninterruptible in general. It's about vital data, and allowing writes to be interruptible may leave the VPD in an inconsistent state. Link: https://lore.kernel.org/r/258bf994-bc2a-2907-9181-2c7a562986d5@gmail.com Signed-off-by: Heiner Kallweit Signed-off-by: Bjorn Helgaas --- drivers/pci/vpd.c | 33 +++++++++------------------------ 1 file changed, 9 insertions(+), 24 deletions(-) (limited to 'drivers/pci') diff --git a/drivers/pci/vpd.c b/drivers/pci/vpd.c index 4f262f8530ea..3d9223f03a22 100644 --- a/drivers/pci/vpd.c +++ b/drivers/pci/vpd.c @@ -24,7 +24,6 @@ struct pci_vpd { unsigned int len; u16 flag; u8 cap; - unsigned int busy:1; unsigned int valid:1; }; @@ -129,22 +128,14 @@ static int pci_vpd_wait(struct pci_dev *dev) u16 status; int ret; - if (!vpd->busy) - return 0; - do { ret = pci_user_read_config_word(dev, vpd->cap + PCI_VPD_ADDR, &status); if (ret < 0) return ret; - if ((status & PCI_VPD_ADDR_F) == vpd->flag) { - vpd->busy = 0; + if ((status & PCI_VPD_ADDR_F) == vpd->flag) return 0; - } - - if (fatal_signal_pending(current)) - return -EINTR; if (time_after(jiffies, timeout)) break; @@ -162,7 +153,7 @@ static ssize_t pci_vpd_read(struct pci_dev *dev, loff_t pos, size_t count, void *arg) { struct pci_vpd *vpd = dev->vpd; - int ret; + int ret = 0; loff_t end = pos + count; u8 *buf = arg; @@ -188,19 +179,19 @@ static ssize_t pci_vpd_read(struct pci_dev *dev, loff_t pos, size_t count, if (mutex_lock_killable(&vpd->lock)) return -EINTR; - ret = pci_vpd_wait(dev); - if (ret < 0) - goto out; - while (pos < end) { u32 val; unsigned int i, skip; + if (fatal_signal_pending(current)) { + ret = -EINTR; + break; + } + ret = pci_user_write_config_word(dev, vpd->cap + PCI_VPD_ADDR, pos & ~3); if (ret < 0) break; - vpd->busy = 1; vpd->flag = PCI_VPD_ADDR_F; ret = pci_vpd_wait(dev); if (ret < 0) @@ -220,7 +211,7 @@ static ssize_t pci_vpd_read(struct pci_dev *dev, loff_t pos, size_t count, val >>= 8; } } -out: + mutex_unlock(&vpd->lock); return ret ? ret : count; } @@ -250,10 +241,6 @@ static ssize_t pci_vpd_write(struct pci_dev *dev, loff_t pos, size_t count, if (mutex_lock_killable(&vpd->lock)) return -EINTR; - ret = pci_vpd_wait(dev); - if (ret < 0) - goto out; - while (pos < end) { u32 val; @@ -270,7 +257,6 @@ static ssize_t pci_vpd_write(struct pci_dev *dev, loff_t pos, size_t count, if (ret < 0) break; - vpd->busy = 1; vpd->flag = 0; ret = pci_vpd_wait(dev); if (ret < 0) @@ -278,7 +264,7 @@ static ssize_t pci_vpd_write(struct pci_dev *dev, loff_t pos, size_t count, pos += sizeof(u32); } -out: + mutex_unlock(&vpd->lock); return ret ? ret : count; } @@ -341,7 +327,6 @@ void pci_vpd_init(struct pci_dev *dev) vpd->ops = &pci_vpd_ops; mutex_init(&vpd->lock); vpd->cap = cap; - vpd->busy = 0; vpd->valid = 0; dev->vpd = vpd; } -- cgit v1.2.3 From fe943bd8ab75552f2773ee27c7c5ae6b48941582 Mon Sep 17 00:00:00 2001 From: Heiner Kallweit Date: Thu, 13 May 2021 23:02:01 +0200 Subject: PCI/VPD: Remove struct pci_vpd.flag The struct pci_vpd.flag member was used only to communicate between pci_vpd_wait() and its callers. Remove the flag member and pass the value directly to pci_vpd_wait() to simplify the code. [bhelgaas: commit log] Link: https://lore.kernel.org/r/e4ef6845-6b23-1646-28a0-d5c5a28347b6@gmail.com Signed-off-by: Heiner Kallweit Signed-off-by: Bjorn Helgaas --- drivers/pci/vpd.c | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) (limited to 'drivers/pci') diff --git a/drivers/pci/vpd.c b/drivers/pci/vpd.c index 3d9223f03a22..d7f705ba6664 100644 --- a/drivers/pci/vpd.c +++ b/drivers/pci/vpd.c @@ -22,7 +22,6 @@ struct pci_vpd { const struct pci_vpd_ops *ops; struct mutex lock; unsigned int len; - u16 flag; u8 cap; unsigned int valid:1; }; @@ -117,10 +116,11 @@ error: * This code has to spin since there is no other notification from the PCI * hardware. Since the VPD is often implemented by serial attachment to an * EEPROM, it may take many milliseconds to complete. + * @set: if true wait for flag to be set, else wait for it to be cleared * * Returns 0 on success, negative values indicate error. */ -static int pci_vpd_wait(struct pci_dev *dev) +static int pci_vpd_wait(struct pci_dev *dev, bool set) { struct pci_vpd *vpd = dev->vpd; unsigned long timeout = jiffies + msecs_to_jiffies(125); @@ -134,7 +134,7 @@ static int pci_vpd_wait(struct pci_dev *dev) if (ret < 0) return ret; - if ((status & PCI_VPD_ADDR_F) == vpd->flag) + if (!!(status & PCI_VPD_ADDR_F) == set) return 0; if (time_after(jiffies, timeout)) @@ -192,8 +192,7 @@ static ssize_t pci_vpd_read(struct pci_dev *dev, loff_t pos, size_t count, pos & ~3); if (ret < 0) break; - vpd->flag = PCI_VPD_ADDR_F; - ret = pci_vpd_wait(dev); + ret = pci_vpd_wait(dev, true); if (ret < 0) break; @@ -257,8 +256,7 @@ static ssize_t pci_vpd_write(struct pci_dev *dev, loff_t pos, size_t count, if (ret < 0) break; - vpd->flag = 0; - ret = pci_vpd_wait(dev); + ret = pci_vpd_wait(dev, false); if (ret < 0) break; -- cgit v1.2.3 From d27f7344ba89897d0ce6ebe0c9eecbe214f9bb8f Mon Sep 17 00:00:00 2001 From: Heiner Kallweit Date: Sun, 8 Aug 2021 19:19:10 +0200 Subject: PCI/VPD: Reorder pci_read_vpd(), pci_write_vpd() Reorder pci_read_vpd() and pci_write_vpd() to prepare for future patches. No functional change intended. Link: https://lore.kernel.org/r/89f0f5a2-293b-a017-fc67-a36473a792bf@gmail.com Signed-off-by: Heiner Kallweit Signed-off-by: Bjorn Helgaas --- drivers/pci/vpd.c | 60 +++++++++++++++++++++++++++---------------------------- 1 file changed, 30 insertions(+), 30 deletions(-) (limited to 'drivers/pci') diff --git a/drivers/pci/vpd.c b/drivers/pci/vpd.c index d7f705ba6664..118dbd072fbe 100644 --- a/drivers/pci/vpd.c +++ b/drivers/pci/vpd.c @@ -31,36 +31,6 @@ static struct pci_dev *pci_get_func0_dev(struct pci_dev *dev) return pci_get_slot(dev->bus, PCI_DEVFN(PCI_SLOT(dev->devfn), 0)); } -/** - * pci_read_vpd - Read one entry from Vital Product Data - * @dev: pci device struct - * @pos: offset in vpd space - * @count: number of bytes to read - * @buf: pointer to where to store result - */ -ssize_t pci_read_vpd(struct pci_dev *dev, loff_t pos, size_t count, void *buf) -{ - if (!dev->vpd || !dev->vpd->ops) - return -ENODEV; - return dev->vpd->ops->read(dev, pos, count, buf); -} -EXPORT_SYMBOL(pci_read_vpd); - -/** - * pci_write_vpd - Write entry to Vital Product Data - * @dev: pci device struct - * @pos: offset in vpd space - * @count: number of bytes to write - * @buf: buffer containing write data - */ -ssize_t pci_write_vpd(struct pci_dev *dev, loff_t pos, size_t count, const void *buf) -{ - if (!dev->vpd || !dev->vpd->ops) - return -ENODEV; - return dev->vpd->ops->write(dev, pos, count, buf); -} -EXPORT_SYMBOL(pci_write_vpd); - #define PCI_VPD_MAX_SIZE (PCI_VPD_ADDR_MASK + 1) /** @@ -408,6 +378,36 @@ int pci_vpd_find_info_keyword(const u8 *buf, unsigned int off, } EXPORT_SYMBOL_GPL(pci_vpd_find_info_keyword); +/** + * pci_read_vpd - Read one entry from Vital Product Data + * @dev: PCI device struct + * @pos: offset in VPD space + * @count: number of bytes to read + * @buf: pointer to where to store result + */ +ssize_t pci_read_vpd(struct pci_dev *dev, loff_t pos, size_t count, void *buf) +{ + if (!dev->vpd || !dev->vpd->ops) + return -ENODEV; + return dev->vpd->ops->read(dev, pos, count, buf); +} +EXPORT_SYMBOL(pci_read_vpd); + +/** + * pci_write_vpd - Write entry to Vital Product Data + * @dev: PCI device struct + * @pos: offset in VPD space + * @count: number of bytes to write + * @buf: buffer containing write data + */ +ssize_t pci_write_vpd(struct pci_dev *dev, loff_t pos, size_t count, const void *buf) +{ + if (!dev->vpd || !dev->vpd->ops) + return -ENODEV; + return dev->vpd->ops->write(dev, pos, count, buf); +} +EXPORT_SYMBOL(pci_write_vpd); + #ifdef CONFIG_PCI_QUIRKS /* * Quirk non-zero PCI functions to route VPD access through function 0 for -- cgit v1.2.3 From a38fccdb6289d9e4796e746040b491bb0f31b151 Mon Sep 17 00:00:00 2001 From: Heiner Kallweit Date: Sun, 8 Aug 2021 19:20:05 +0200 Subject: PCI/VPD: Remove struct pci_vpd_ops Some multi-function devices share VPD hardware across functions and don't work correctly for concurrent VPD accesses to different functions. Struct pci_vpd_ops was added by 932c435caba8 ("PCI: Add dev_flags bit to access VPD through function 0") so that on these devices, VPD accesses to any function would always go to function 0. It's easier to just check for the PCI_DEV_FLAGS_VPD_REF_F0 quirk bit in the two places we need it than to deal with the struct pci_vpd_ops. Simplify the code by removing struct pci_vpd_ops and removing the indirect calls. [bhelgaas: check for !func0_dev earlier, commit log] Link: https://lore.kernel.org/r/b2532a41-df8b-860f-461f-d5c066c819d0@gmail.com Signed-off-by: Heiner Kallweit Signed-off-by: Bjorn Helgaas --- drivers/pci/vpd.c | 86 +++++++++++++++++++++---------------------------------- 1 file changed, 32 insertions(+), 54 deletions(-) (limited to 'drivers/pci') diff --git a/drivers/pci/vpd.c b/drivers/pci/vpd.c index 118dbd072fbe..e2439547a691 100644 --- a/drivers/pci/vpd.c +++ b/drivers/pci/vpd.c @@ -13,13 +13,7 @@ /* VPD access through PCI 2.2+ VPD capability */ -struct pci_vpd_ops { - ssize_t (*read)(struct pci_dev *dev, loff_t pos, size_t count, void *buf); - ssize_t (*write)(struct pci_dev *dev, loff_t pos, size_t count, const void *buf); -}; - struct pci_vpd { - const struct pci_vpd_ops *ops; struct mutex lock; unsigned int len; u8 cap; @@ -127,6 +121,9 @@ static ssize_t pci_vpd_read(struct pci_dev *dev, loff_t pos, size_t count, loff_t end = pos + count; u8 *buf = arg; + if (!vpd) + return -ENODEV; + if (pos < 0) return -EINVAL; @@ -193,6 +190,9 @@ static ssize_t pci_vpd_write(struct pci_dev *dev, loff_t pos, size_t count, loff_t end = pos + count; int ret = 0; + if (!vpd) + return -ENODEV; + if (pos < 0 || (pos & 3) || (count & 3)) return -EINVAL; @@ -237,44 +237,6 @@ static ssize_t pci_vpd_write(struct pci_dev *dev, loff_t pos, size_t count, return ret ? ret : count; } -static const struct pci_vpd_ops pci_vpd_ops = { - .read = pci_vpd_read, - .write = pci_vpd_write, -}; - -static ssize_t pci_vpd_f0_read(struct pci_dev *dev, loff_t pos, size_t count, - void *arg) -{ - struct pci_dev *tdev = pci_get_func0_dev(dev); - ssize_t ret; - - if (!tdev) - return -ENODEV; - - ret = pci_read_vpd(tdev, pos, count, arg); - pci_dev_put(tdev); - return ret; -} - -static ssize_t pci_vpd_f0_write(struct pci_dev *dev, loff_t pos, size_t count, - const void *arg) -{ - struct pci_dev *tdev = pci_get_func0_dev(dev); - ssize_t ret; - - if (!tdev) - return -ENODEV; - - ret = pci_write_vpd(tdev, pos, count, arg); - pci_dev_put(tdev); - return ret; -} - -static const struct pci_vpd_ops pci_vpd_f0_ops = { - .read = pci_vpd_f0_read, - .write = pci_vpd_f0_write, -}; - void pci_vpd_init(struct pci_dev *dev) { struct pci_vpd *vpd; @@ -289,10 +251,6 @@ void pci_vpd_init(struct pci_dev *dev) return; vpd->len = PCI_VPD_MAX_SIZE; - if (dev->dev_flags & PCI_DEV_FLAGS_VPD_REF_F0) - vpd->ops = &pci_vpd_f0_ops; - else - vpd->ops = &pci_vpd_ops; mutex_init(&vpd->lock); vpd->cap = cap; vpd->valid = 0; @@ -387,9 +345,19 @@ EXPORT_SYMBOL_GPL(pci_vpd_find_info_keyword); */ ssize_t pci_read_vpd(struct pci_dev *dev, loff_t pos, size_t count, void *buf) { - if (!dev->vpd || !dev->vpd->ops) - return -ENODEV; - return dev->vpd->ops->read(dev, pos, count, 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_read(dev, pos, count, buf); + pci_dev_put(dev); + return ret; + } + + return pci_vpd_read(dev, pos, count, buf); } EXPORT_SYMBOL(pci_read_vpd); @@ -402,9 +370,19 @@ EXPORT_SYMBOL(pci_read_vpd); */ ssize_t pci_write_vpd(struct pci_dev *dev, loff_t pos, size_t count, const void *buf) { - if (!dev->vpd || !dev->vpd->ops) - return -ENODEV; - return dev->vpd->ops->write(dev, pos, count, 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); } EXPORT_SYMBOL(pci_write_vpd); -- cgit v1.2.3 From 22ff2bcec704a7a8c43a998251e0757cd2de66e1 Mon Sep 17 00:00:00 2001 From: Heiner Kallweit Date: Sun, 8 Aug 2021 19:21:02 +0200 Subject: PCI/VPD: Remove struct pci_vpd.valid member Instead of having a separate flag, use vp->len != 0 as indicator that VPD validity has been checked. Now vpd->len == PCI_VPD_SZ_INVALID indicates that VPD is invalid. Link: https://lore.kernel.org/r/9f777bc7-5316-e1b8-e5d4-f9f609bdb5dd@gmail.com Signed-off-by: Heiner Kallweit Signed-off-by: Bjorn Helgaas --- drivers/pci/vpd.c | 29 ++++++++++++----------------- 1 file changed, 12 insertions(+), 17 deletions(-) (limited to 'drivers/pci') diff --git a/drivers/pci/vpd.c b/drivers/pci/vpd.c index e2439547a691..9d9cff5f89e2 100644 --- a/drivers/pci/vpd.c +++ b/drivers/pci/vpd.c @@ -17,7 +17,6 @@ struct pci_vpd { struct mutex lock; unsigned int len; u8 cap; - unsigned int valid:1; }; static struct pci_dev *pci_get_func0_dev(struct pci_dev *dev) @@ -25,7 +24,8 @@ static struct pci_dev *pci_get_func0_dev(struct pci_dev *dev) return pci_get_slot(dev->bus, PCI_DEVFN(PCI_SLOT(dev->devfn), 0)); } -#define PCI_VPD_MAX_SIZE (PCI_VPD_ADDR_MASK + 1) +#define PCI_VPD_MAX_SIZE (PCI_VPD_ADDR_MASK + 1) +#define PCI_VPD_SZ_INVALID UINT_MAX /** * pci_vpd_size - determine actual size of Vital Product Data @@ -36,6 +36,9 @@ static size_t pci_vpd_size(struct pci_dev *dev) size_t off = 0, size; unsigned char tag, header[1+2]; /* 1 byte tag, 2 bytes length */ + /* Otherwise the following reads would fail. */ + dev->vpd->len = PCI_VPD_MAX_SIZE; + while (pci_read_vpd(dev, off, 1, header) == 1) { size = 0; @@ -47,7 +50,7 @@ static size_t pci_vpd_size(struct pci_dev *dev) if (pci_read_vpd(dev, off + 1, 2, &header[1]) != 2) { pci_warn(dev, "failed VPD read at offset %zu\n", off + 1); - return off; + return off ?: PCI_VPD_SZ_INVALID; } size = pci_vpd_lrdt_size(header); if (off + size > PCI_VPD_MAX_SIZE) @@ -72,7 +75,7 @@ error: pci_info(dev, "invalid VPD tag %#04x (size %zu) at offset %zu%s\n", header[0], size, off, off == 0 ? "; assume missing optional EEPROM" : ""); - return off; + return off ?: PCI_VPD_SZ_INVALID; } /* @@ -127,12 +130,10 @@ static ssize_t pci_vpd_read(struct pci_dev *dev, loff_t pos, size_t count, if (pos < 0) return -EINVAL; - if (!vpd->valid) { - vpd->valid = 1; + if (!vpd->len) vpd->len = pci_vpd_size(dev); - } - if (vpd->len == 0) + if (vpd->len == PCI_VPD_SZ_INVALID) return -EIO; if (pos > vpd->len) @@ -196,12 +197,10 @@ 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 (!vpd->valid) { - vpd->valid = 1; + if (!vpd->len) vpd->len = pci_vpd_size(dev); - } - if (vpd->len == 0) + if (vpd->len == PCI_VPD_SZ_INVALID) return -EIO; if (end > vpd->len) @@ -250,10 +249,8 @@ void pci_vpd_init(struct pci_dev *dev) if (!vpd) return; - vpd->len = PCI_VPD_MAX_SIZE; mutex_init(&vpd->lock); vpd->cap = cap; - vpd->valid = 0; dev->vpd = vpd; } @@ -422,8 +419,7 @@ DECLARE_PCI_FIXUP_CLASS_EARLY(PCI_VENDOR_ID_INTEL, PCI_ANY_ID, static void quirk_blacklist_vpd(struct pci_dev *dev) { if (dev->vpd) { - dev->vpd->len = 0; - dev->vpd->valid = 1; + dev->vpd->len = PCI_VPD_SZ_INVALID; pci_warn(dev, FW_BUG "disabling VPD access (can't determine size of non-standard VPD format)\n"); } } @@ -454,7 +450,6 @@ static void pci_vpd_set_size(struct pci_dev *dev, size_t len) if (!vpd || len == 0 || len > PCI_VPD_MAX_SIZE) return; - vpd->valid = 1; vpd->len = len; } -- cgit v1.2.3 From fd00faa375fbb9d46ae0730d0faf4a3006301005 Mon Sep 17 00:00:00 2001 From: Heiner Kallweit Date: Sun, 8 Aug 2021 19:21:56 +0200 Subject: PCI/VPD: Embed struct pci_vpd in struct pci_dev Now that struct pci_vpd is really small, simplify the code by embedding struct pci_vpd directly in struct pci_dev instead of dynamically allocating it. Link: https://lore.kernel.org/r/d898489e-22ba-71f1-2f31-f1a78dc15849@gmail.com Signed-off-by: Heiner Kallweit Signed-off-by: Bjorn Helgaas --- drivers/pci/probe.c | 1 - drivers/pci/vpd.c | 63 ++++++++++++----------------------------------------- include/linux/pci.h | 9 ++++++-- 3 files changed, 21 insertions(+), 52 deletions(-) (limited to 'drivers/pci') diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c index 79177ac37880..0ec5c792c27d 100644 --- a/drivers/pci/probe.c +++ b/drivers/pci/probe.c @@ -2225,7 +2225,6 @@ static void pci_release_capabilities(struct pci_dev *dev) { pci_aer_exit(dev); pci_rcec_exit(dev); - pci_vpd_release(dev); pci_iov_release(dev); pci_free_cap_save_buffers(dev); } diff --git a/drivers/pci/vpd.c b/drivers/pci/vpd.c index 9d9cff5f89e2..ee48e167145f 100644 --- a/drivers/pci/vpd.c +++ b/drivers/pci/vpd.c @@ -13,12 +13,6 @@ /* VPD access through PCI 2.2+ VPD capability */ -struct pci_vpd { - struct mutex lock; - unsigned int len; - u8 cap; -}; - static struct pci_dev *pci_get_func0_dev(struct pci_dev *dev) { return pci_get_slot(dev->bus, PCI_DEVFN(PCI_SLOT(dev->devfn), 0)); @@ -37,7 +31,7 @@ static size_t pci_vpd_size(struct pci_dev *dev) unsigned char tag, header[1+2]; /* 1 byte tag, 2 bytes length */ /* Otherwise the following reads would fail. */ - dev->vpd->len = PCI_VPD_MAX_SIZE; + dev->vpd.len = PCI_VPD_MAX_SIZE; while (pci_read_vpd(dev, off, 1, header) == 1) { size = 0; @@ -89,7 +83,7 @@ error: */ static int pci_vpd_wait(struct pci_dev *dev, bool set) { - struct pci_vpd *vpd = dev->vpd; + struct pci_vpd *vpd = &dev->vpd; unsigned long timeout = jiffies + msecs_to_jiffies(125); unsigned long max_sleep = 16; u16 status; @@ -119,12 +113,12 @@ 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) { - struct pci_vpd *vpd = dev->vpd; + struct pci_vpd *vpd = &dev->vpd; int ret = 0; loff_t end = pos + count; u8 *buf = arg; - if (!vpd) + if (!vpd->cap) return -ENODEV; if (pos < 0) @@ -186,12 +180,12 @@ 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) { - struct pci_vpd *vpd = dev->vpd; + struct pci_vpd *vpd = &dev->vpd; const u8 *buf = arg; loff_t end = pos + count; int ret = 0; - if (!vpd) + if (!vpd->cap) return -ENODEV; if (pos < 0 || (pos & 3) || (count & 3)) @@ -238,25 +232,8 @@ static ssize_t pci_vpd_write(struct pci_dev *dev, loff_t pos, size_t count, void pci_vpd_init(struct pci_dev *dev) { - struct pci_vpd *vpd; - u8 cap; - - cap = pci_find_capability(dev, PCI_CAP_ID_VPD); - if (!cap) - return; - - vpd = kzalloc(sizeof(*vpd), GFP_ATOMIC); - if (!vpd) - return; - - mutex_init(&vpd->lock); - vpd->cap = cap; - dev->vpd = vpd; -} - -void pci_vpd_release(struct pci_dev *dev) -{ - kfree(dev->vpd); + dev->vpd.cap = pci_find_capability(dev, PCI_CAP_ID_VPD); + mutex_init(&dev->vpd.lock); } static ssize_t vpd_read(struct file *filp, struct kobject *kobj, @@ -288,7 +265,7 @@ static umode_t vpd_attr_is_visible(struct kobject *kobj, { struct pci_dev *pdev = to_pci_dev(kobj_to_dev(kobj)); - if (!pdev->vpd) + if (!pdev->vpd.cap) return 0; return a->attr.mode; @@ -400,7 +377,7 @@ static void quirk_f0_vpd_link(struct pci_dev *dev) if (!f0) return; - if (f0->vpd && dev->class == f0->class && + if (f0->vpd.cap && dev->class == f0->class && dev->vendor == f0->vendor && dev->device == f0->device) dev->dev_flags |= PCI_DEV_FLAGS_VPD_REF_F0; @@ -418,10 +395,8 @@ DECLARE_PCI_FIXUP_CLASS_EARLY(PCI_VENDOR_ID_INTEL, PCI_ANY_ID, */ static void quirk_blacklist_vpd(struct pci_dev *dev) { - if (dev->vpd) { - dev->vpd->len = PCI_VPD_SZ_INVALID; - pci_warn(dev, FW_BUG "disabling VPD access (can't determine size of non-standard VPD format)\n"); - } + dev->vpd.len = PCI_VPD_SZ_INVALID; + pci_warn(dev, FW_BUG "disabling VPD access (can't determine size of non-standard VPD format)\n"); } DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_LSI_LOGIC, 0x0060, quirk_blacklist_vpd); DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_LSI_LOGIC, 0x007c, quirk_blacklist_vpd); @@ -443,16 +418,6 @@ DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_ATTANSIC, PCI_ANY_ID, DECLARE_PCI_FIXUP_CLASS_FINAL(PCI_VENDOR_ID_AMAZON_ANNAPURNA_LABS, 0x0031, PCI_CLASS_BRIDGE_PCI, 8, quirk_blacklist_vpd); -static void pci_vpd_set_size(struct pci_dev *dev, size_t len) -{ - struct pci_vpd *vpd = dev->vpd; - - if (!vpd || len == 0 || len > PCI_VPD_MAX_SIZE) - return; - - vpd->len = len; -} - static void quirk_chelsio_extend_vpd(struct pci_dev *dev) { int chip = (dev->device & 0xf000) >> 12; @@ -471,9 +436,9 @@ static void quirk_chelsio_extend_vpd(struct pci_dev *dev) * limits. */ if (chip == 0x0 && prod >= 0x20) - pci_vpd_set_size(dev, 8192); + dev->vpd.len = 8192; else if (chip >= 0x4 && func < 0x8) - pci_vpd_set_size(dev, 2048); + dev->vpd.len = 2048; } DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_CHELSIO, PCI_ANY_ID, diff --git a/include/linux/pci.h b/include/linux/pci.h index 540b377ca8f6..e752cc39a1fe 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -300,9 +300,14 @@ struct pci_cap_saved_state { struct pci_cap_saved_data cap; }; +struct pci_vpd { + struct mutex lock; + unsigned int len; + u8 cap; +}; + struct irq_affinity; struct pcie_link_state; -struct pci_vpd; struct pci_sriov; struct pci_p2pdma; struct rcec_ea; @@ -473,7 +478,7 @@ struct pci_dev { #ifdef CONFIG_PCI_MSI const struct attribute_group **msi_irq_groups; #endif - struct pci_vpd *vpd; + struct pci_vpd vpd; #ifdef CONFIG_PCIE_DPC u16 dpc_cap; unsigned int dpc_rp_extensions:1; -- cgit v1.2.3 From 7bac54497c3e3b2ca37b7043f1fa78586540f10e Mon Sep 17 00:00:00 2001 From: Heiner Kallweit Date: Sun, 8 Aug 2021 19:22:52 +0200 Subject: PCI/VPD: Determine VPD size in pci_vpd_init() Determine VPD size in pci_vpd_init(). Quirks set dev->vpd.len to a non-zero value, so they cause us to skip the dynamic size calculation. Prerequisite is that we move the quirks from FINAL to HEADER so they are run before pci_vpd_init(). Link: https://lore.kernel.org/r/cc4a6538-557a-294d-4f94-e6d1d3c91589@gmail.com Signed-off-by: Heiner Kallweit Signed-off-by: Bjorn Helgaas --- drivers/pci/vpd.c | 42 +++++++++++++++++++----------------------- 1 file changed, 19 insertions(+), 23 deletions(-) (limited to 'drivers/pci') diff --git a/drivers/pci/vpd.c b/drivers/pci/vpd.c index ee48e167145f..a62251a3c7a1 100644 --- a/drivers/pci/vpd.c +++ b/drivers/pci/vpd.c @@ -124,9 +124,6 @@ static ssize_t pci_vpd_read(struct pci_dev *dev, loff_t pos, size_t count, if (pos < 0) return -EINVAL; - if (!vpd->len) - vpd->len = pci_vpd_size(dev); - if (vpd->len == PCI_VPD_SZ_INVALID) return -EIO; @@ -191,9 +188,6 @@ 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 (!vpd->len) - vpd->len = pci_vpd_size(dev); - if (vpd->len == PCI_VPD_SZ_INVALID) return -EIO; @@ -234,6 +228,9 @@ void pci_vpd_init(struct pci_dev *dev) { dev->vpd.cap = pci_find_capability(dev, PCI_CAP_ID_VPD); mutex_init(&dev->vpd.lock); + + if (!dev->vpd.len) + dev->vpd.len = pci_vpd_size(dev); } static ssize_t vpd_read(struct file *filp, struct kobject *kobj, @@ -398,25 +395,24 @@ static void quirk_blacklist_vpd(struct pci_dev *dev) dev->vpd.len = PCI_VPD_SZ_INVALID; pci_warn(dev, FW_BUG "disabling VPD access (can't determine size of non-standard VPD format)\n"); } -DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_LSI_LOGIC, 0x0060, quirk_blacklist_vpd); -DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_LSI_LOGIC, 0x007c, quirk_blacklist_vpd); -DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_LSI_LOGIC, 0x0413, quirk_blacklist_vpd); -DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_LSI_LOGIC, 0x0078, quirk_blacklist_vpd); -DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_LSI_LOGIC, 0x0079, quirk_blacklist_vpd); -DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_LSI_LOGIC, 0x0073, quirk_blacklist_vpd); -DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_LSI_LOGIC, 0x0071, quirk_blacklist_vpd); -DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_LSI_LOGIC, 0x005b, quirk_blacklist_vpd); -DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_LSI_LOGIC, 0x002f, quirk_blacklist_vpd); -DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_LSI_LOGIC, 0x005d, quirk_blacklist_vpd); -DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_LSI_LOGIC, 0x005f, quirk_blacklist_vpd); -DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_ATTANSIC, PCI_ANY_ID, - quirk_blacklist_vpd); +DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_LSI_LOGIC, 0x0060, quirk_blacklist_vpd); +DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_LSI_LOGIC, 0x007c, quirk_blacklist_vpd); +DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_LSI_LOGIC, 0x0413, quirk_blacklist_vpd); +DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_LSI_LOGIC, 0x0078, quirk_blacklist_vpd); +DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_LSI_LOGIC, 0x0079, quirk_blacklist_vpd); +DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_LSI_LOGIC, 0x0073, quirk_blacklist_vpd); +DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_LSI_LOGIC, 0x0071, quirk_blacklist_vpd); +DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_LSI_LOGIC, 0x005b, quirk_blacklist_vpd); +DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_LSI_LOGIC, 0x002f, quirk_blacklist_vpd); +DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_LSI_LOGIC, 0x005d, quirk_blacklist_vpd); +DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_LSI_LOGIC, 0x005f, quirk_blacklist_vpd); +DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_ATTANSIC, PCI_ANY_ID, quirk_blacklist_vpd); /* * The Amazon Annapurna Labs 0x0031 device id is reused for other non Root Port * device types, so the quirk is registered for the PCI_CLASS_BRIDGE_PCI class. */ -DECLARE_PCI_FIXUP_CLASS_FINAL(PCI_VENDOR_ID_AMAZON_ANNAPURNA_LABS, 0x0031, - PCI_CLASS_BRIDGE_PCI, 8, quirk_blacklist_vpd); +DECLARE_PCI_FIXUP_CLASS_HEADER(PCI_VENDOR_ID_AMAZON_ANNAPURNA_LABS, 0x0031, + PCI_CLASS_BRIDGE_PCI, 8, quirk_blacklist_vpd); static void quirk_chelsio_extend_vpd(struct pci_dev *dev) { @@ -441,7 +437,7 @@ static void quirk_chelsio_extend_vpd(struct pci_dev *dev) dev->vpd.len = 2048; } -DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_CHELSIO, PCI_ANY_ID, - quirk_chelsio_extend_vpd); +DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_CHELSIO, PCI_ANY_ID, + quirk_chelsio_extend_vpd); #endif -- cgit v1.2.3 From fe7568cf2f2dc3a0783f6ffdb3802c1ce2085466 Mon Sep 17 00:00:00 2001 From: Heiner Kallweit Date: Sun, 8 Aug 2021 19:23:57 +0200 Subject: PCI/VPD: Treat invalid VPD like missing VPD capability Exporting sysfs files that can't be accessed doesn't make much sense. Therefore, if either a quirk or the dynamic size calculation result in VPD being marked as invalid, treat this as though the device has no VPD capability. One consequence is that the "vpd" sysfs file is not visible. Link: https://lore.kernel.org/r/6a02b204-4ed2-4553-c3b2-eacf9554fa8d@gmail.com Signed-off-by: Heiner Kallweit Signed-off-by: Bjorn Helgaas --- drivers/pci/vpd.c | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) (limited to 'drivers/pci') diff --git a/drivers/pci/vpd.c b/drivers/pci/vpd.c index a62251a3c7a1..3b0425fb49f5 100644 --- a/drivers/pci/vpd.c +++ b/drivers/pci/vpd.c @@ -124,9 +124,6 @@ static ssize_t pci_vpd_read(struct pci_dev *dev, loff_t pos, size_t count, if (pos < 0) return -EINVAL; - if (vpd->len == PCI_VPD_SZ_INVALID) - return -EIO; - if (pos > vpd->len) return 0; @@ -188,9 +185,6 @@ 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 (vpd->len == PCI_VPD_SZ_INVALID) - return -EIO; - if (end > vpd->len) return -EINVAL; @@ -231,6 +225,9 @@ void pci_vpd_init(struct pci_dev *dev) if (!dev->vpd.len) dev->vpd.len = pci_vpd_size(dev); + + if (dev->vpd.len == PCI_VPD_SZ_INVALID) + dev->vpd.cap = 0; } static ssize_t vpd_read(struct file *filp, struct kobject *kobj, -- cgit v1.2.3 From 76f3c032adad86aad26f8ad3eebc993b4ba32138 Mon Sep 17 00:00:00 2001 From: Heiner Kallweit Date: Wed, 18 Aug 2021 20:59:31 +0200 Subject: PCI/VPD: Add pci_vpd_alloc() Several users of the VPD API use a fixed-size buffer and read the VPD into it for further usage. This requires special handling for the case that the buffer isn't big enough to hold the full VPD data. Also the buffer is often allocated on the stack, which isn't too nice. Add pci_vpd_alloc() to dynamically allocate buffer of the correct size and read VPD into it. Link: https://lore.kernel.org/r/955ff598-0021-8446-f856-0c2c077635d7@gmail.com Signed-off-by: Heiner Kallweit Signed-off-by: Bjorn Helgaas --- drivers/pci/vpd.c | 26 ++++++++++++++++++++++++++ include/linux/pci.h | 9 +++++++++ 2 files changed, 35 insertions(+) (limited to 'drivers/pci') diff --git a/drivers/pci/vpd.c b/drivers/pci/vpd.c index 3b0425fb49f5..7c3a097379bb 100644 --- a/drivers/pci/vpd.c +++ b/drivers/pci/vpd.c @@ -270,6 +270,32 @@ const struct attribute_group pci_dev_vpd_attr_group = { .is_bin_visible = vpd_attr_is_visible, }; +void *pci_vpd_alloc(struct pci_dev *dev, unsigned int *size) +{ + unsigned int len = dev->vpd.len; + void *buf; + int cnt; + + if (!dev->vpd.cap) + return ERR_PTR(-ENODEV); + + buf = kmalloc(len, GFP_KERNEL); + if (!buf) + return ERR_PTR(-ENOMEM); + + cnt = pci_read_vpd(dev, 0, len, buf); + if (cnt != len) { + kfree(buf); + return ERR_PTR(-EIO); + } + + if (size) + *size = len; + + return buf; +} +EXPORT_SYMBOL_GPL(pci_vpd_alloc); + int pci_vpd_find_tag(const u8 *buf, unsigned int len, u8 rdt) { int i = 0; diff --git a/include/linux/pci.h b/include/linux/pci.h index e752cc39a1fe..8c681e24be8b 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -2330,6 +2330,15 @@ static inline u8 pci_vpd_info_field_size(const u8 *info_field) return info_field[2]; } +/** + * pci_vpd_alloc - Allocate buffer and read VPD into it + * @dev: PCI device + * @size: pointer to field where VPD length is returned + * + * Returns pointer to allocated buffer or an ERR_PTR in case of failure + */ +void *pci_vpd_alloc(struct pci_dev *dev, unsigned int *size); + /** * pci_vpd_find_tag - Locates the Resource Data Type tag provided * @buf: Pointer to buffered vpd data -- cgit v1.2.3 From 9e515c9f6c0b6f0ace6f5cf2202b527d745b494d Mon Sep 17 00:00:00 2001 From: Heiner Kallweit Date: Wed, 18 Aug 2021 21:00:57 +0200 Subject: PCI/VPD: Add pci_vpd_find_ro_info_keyword() All users of pci_vpd_find_info_keyword() are interested in the VPD RO section only. In addition all calls are followed by the same activities to calculate start of tag data area and size of the data area. Add pci_vpd_find_ro_info_keyword() that combines these functionalities. pci_vpd_find_info_keyword() can be phased out once all users are converted. [bhelgaas: split pci_vpd_check_csum() to separate patch] Link: https://lore.kernel.org/r/1643bd7a-088e-1028-c9b0-9d112cf48d63@gmail.com Signed-off-by: Heiner Kallweit Signed-off-by: Bjorn Helgaas --- drivers/pci/vpd.c | 33 +++++++++++++++++++++++++++++++++ include/linux/pci.h | 13 +++++++++++++ 2 files changed, 46 insertions(+) (limited to 'drivers/pci') diff --git a/drivers/pci/vpd.c b/drivers/pci/vpd.c index 7c3a097379bb..b1d012900f1e 100644 --- a/drivers/pci/vpd.c +++ b/drivers/pci/vpd.c @@ -380,6 +380,39 @@ ssize_t pci_write_vpd(struct pci_dev *dev, loff_t pos, size_t count, const void } EXPORT_SYMBOL(pci_write_vpd); +int pci_vpd_find_ro_info_keyword(const void *buf, unsigned int len, + const char *kw, unsigned int *size) +{ + int ro_start, infokw_start; + unsigned int ro_len, infokw_size; + + ro_start = pci_vpd_find_tag(buf, len, PCI_VPD_LRDT_RO_DATA); + if (ro_start < 0) + return ro_start; + + ro_len = pci_vpd_lrdt_size(buf + ro_start); + ro_start += PCI_VPD_LRDT_TAG_SIZE; + + if (ro_start + ro_len > len) + ro_len = len - ro_start; + + infokw_start = pci_vpd_find_info_keyword(buf, ro_start, ro_len, kw); + if (infokw_start < 0) + return infokw_start; + + infokw_size = pci_vpd_info_field_size(buf + infokw_start); + infokw_start += PCI_VPD_INFO_FLD_HDR_SIZE; + + if (infokw_start + infokw_size > len) + return -EINVAL; + + if (size) + *size = infokw_size; + + return infokw_start; +} +EXPORT_SYMBOL_GPL(pci_vpd_find_ro_info_keyword); + #ifdef CONFIG_PCI_QUIRKS /* * Quirk non-zero PCI functions to route VPD access through function 0 for diff --git a/include/linux/pci.h b/include/linux/pci.h index 8c681e24be8b..9e3b60963a52 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -2363,6 +2363,19 @@ int pci_vpd_find_tag(const u8 *buf, unsigned int len, u8 rdt); int pci_vpd_find_info_keyword(const u8 *buf, unsigned int off, unsigned int len, const char *kw); +/** + * pci_vpd_find_ro_info_keyword - Locate info field keyword in VPD RO section + * @buf: Pointer to buffered VPD data + * @len: The length of the buffer area in which to search + * @kw: The keyword to search for + * @size: Pointer to field where length of found keyword data is returned + * + * Returns the index of the information field keyword data or -ENOENT if + * not found. + */ +int pci_vpd_find_ro_info_keyword(const void *buf, unsigned int len, + const char *kw, unsigned int *size); + /* PCI <-> OF binding helpers */ #ifdef CONFIG_OF struct device_node; -- cgit v1.2.3 From 6107e5cb907cffc5576cc1297847f9fc69a8d5d9 Mon Sep 17 00:00:00 2001 From: Heiner Kallweit Date: Fri, 20 Aug 2021 15:32:42 -0500 Subject: PCI/VPD: Add pci_vpd_check_csum() VPD checksum information and checksum calculation are specified by PCIe r5.0, sec 6.28.2.2. Therefore checksum handling can and should be moved into the PCI VPD core. Add pci_vpd_check_csum() to validate the VPD checksum. [bhelgaas: split to separate patch] Link: https://lore.kernel.org/r/1643bd7a-088e-1028-c9b0-9d112cf48d63@gmail.com Signed-off-by: Heiner Kallweit Signed-off-by: Bjorn Helgaas --- drivers/pci/vpd.c | 23 +++++++++++++++++++++++ include/linux/pci.h | 9 +++++++++ 2 files changed, 32 insertions(+) (limited to 'drivers/pci') diff --git a/drivers/pci/vpd.c b/drivers/pci/vpd.c index b1d012900f1e..01e57594781e 100644 --- a/drivers/pci/vpd.c +++ b/drivers/pci/vpd.c @@ -413,6 +413,29 @@ int pci_vpd_find_ro_info_keyword(const void *buf, unsigned int len, } EXPORT_SYMBOL_GPL(pci_vpd_find_ro_info_keyword); +int pci_vpd_check_csum(const void *buf, unsigned int len) +{ + const u8 *vpd = buf; + unsigned int size; + u8 csum = 0; + int rv_start; + + rv_start = pci_vpd_find_ro_info_keyword(buf, len, PCI_VPD_RO_KEYWORD_CHKSUM, &size); + if (rv_start == -ENOENT) /* no checksum in VPD */ + return 1; + else if (rv_start < 0) + return rv_start; + + if (!size) + return -EINVAL; + + while (rv_start >= 0) + csum += vpd[rv_start--]; + + return csum ? -EILSEQ : 0; +} +EXPORT_SYMBOL_GPL(pci_vpd_check_csum); + #ifdef CONFIG_PCI_QUIRKS /* * Quirk non-zero PCI functions to route VPD access through function 0 for diff --git a/include/linux/pci.h b/include/linux/pci.h index 9e3b60963a52..827b7eefd550 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -2376,6 +2376,15 @@ int pci_vpd_find_info_keyword(const u8 *buf, unsigned int off, int pci_vpd_find_ro_info_keyword(const void *buf, unsigned int len, const char *kw, unsigned int *size); +/** + * pci_vpd_check_csum - Check VPD checksum + * @buf: Pointer to buffered VPD data + * @len: VPD size + * + * Returns 1 if VPD has no checksum, otherwise 0 or an errno + */ +int pci_vpd_check_csum(const void *buf, unsigned int len); + /* PCI <-> OF binding helpers */ #ifdef CONFIG_OF struct device_node; -- cgit v1.2.3 From a61590892ef097c180144fa469abe2256b9ae715 Mon Sep 17 00:00:00 2001 From: Heiner Kallweit Date: Thu, 26 Aug 2021 20:53:42 +0200 Subject: PCI/VPD: Stop exporting pci_vpd_find_tag() Now that the last users have been migrated to pci_vpd_find_ro_keyword() we can stop exporting this function. It's still used in VPD core code. Link: https://lore.kernel.org/r/71131eca-0502-7878-365f-30b6614161cf@gmail.com Signed-off-by: Heiner Kallweit Signed-off-by: Bjorn Helgaas --- drivers/pci/vpd.c | 3 +-- include/linux/pci.h | 11 ----------- 2 files changed, 1 insertion(+), 13 deletions(-) (limited to 'drivers/pci') diff --git a/drivers/pci/vpd.c b/drivers/pci/vpd.c index 01e57594781e..5726fbb7a03f 100644 --- a/drivers/pci/vpd.c +++ b/drivers/pci/vpd.c @@ -296,7 +296,7 @@ void *pci_vpd_alloc(struct pci_dev *dev, unsigned int *size) } EXPORT_SYMBOL_GPL(pci_vpd_alloc); -int pci_vpd_find_tag(const u8 *buf, unsigned int len, u8 rdt) +static int pci_vpd_find_tag(const u8 *buf, unsigned int len, u8 rdt) { int i = 0; @@ -310,7 +310,6 @@ int pci_vpd_find_tag(const u8 *buf, unsigned int len, u8 rdt) return -ENOENT; } -EXPORT_SYMBOL_GPL(pci_vpd_find_tag); int pci_vpd_find_info_keyword(const u8 *buf, unsigned int off, unsigned int len, const char *kw) diff --git a/include/linux/pci.h b/include/linux/pci.h index 827b7eefd550..4fb233e374c5 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -2339,17 +2339,6 @@ static inline u8 pci_vpd_info_field_size(const u8 *info_field) */ void *pci_vpd_alloc(struct pci_dev *dev, unsigned int *size); -/** - * pci_vpd_find_tag - Locates the Resource Data Type tag provided - * @buf: Pointer to buffered vpd data - * @len: The length of the vpd buffer - * @rdt: The Resource Data Type to search for - * - * Returns the index where the Resource Data Type was found or - * -ENOENT otherwise. - */ -int pci_vpd_find_tag(const u8 *buf, unsigned int len, u8 rdt); - /** * pci_vpd_find_info_keyword - Locates an information field keyword in the VPD * @buf: Pointer to buffered vpd data -- cgit v1.2.3 From 59b83b29bb5532bbff54a271e0b4f321e28b954f Mon Sep 17 00:00:00 2001 From: Heiner Kallweit Date: Thu, 26 Aug 2021 20:54:23 +0200 Subject: PCI/VPD: Stop exporting pci_vpd_find_info_keyword() Now that the last users have been migrated to pci_vpd_find_ro_keyword() we can stop exporting this function. It's still used in VPD core code. Link: https://lore.kernel.org/r/96ca2a56-383e-9b61-9cba-4f1e5611dc15@gmail.com Signed-off-by: Heiner Kallweit Signed-off-by: Bjorn Helgaas --- drivers/pci/vpd.c | 3 +-- include/linux/pci.h | 13 ------------- 2 files changed, 1 insertion(+), 15 deletions(-) (limited to 'drivers/pci') diff --git a/drivers/pci/vpd.c b/drivers/pci/vpd.c index 5726fbb7a03f..0e7a5e8a8f17 100644 --- a/drivers/pci/vpd.c +++ b/drivers/pci/vpd.c @@ -311,7 +311,7 @@ static int pci_vpd_find_tag(const u8 *buf, unsigned int len, u8 rdt) return -ENOENT; } -int pci_vpd_find_info_keyword(const u8 *buf, unsigned int off, +static int pci_vpd_find_info_keyword(const u8 *buf, unsigned int off, unsigned int len, const char *kw) { int i; @@ -327,7 +327,6 @@ int pci_vpd_find_info_keyword(const u8 *buf, unsigned int off, return -ENOENT; } -EXPORT_SYMBOL_GPL(pci_vpd_find_info_keyword); /** * pci_read_vpd - Read one entry from Vital Product Data diff --git a/include/linux/pci.h b/include/linux/pci.h index 4fb233e374c5..196cbf4c76a1 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -2339,19 +2339,6 @@ static inline u8 pci_vpd_info_field_size(const u8 *info_field) */ void *pci_vpd_alloc(struct pci_dev *dev, unsigned int *size); -/** - * pci_vpd_find_info_keyword - Locates an information field keyword in the VPD - * @buf: Pointer to buffered vpd data - * @off: The offset into the buffer at which to begin the search - * @len: The length of the buffer area, relative to off, in which to search - * @kw: The keyword to search for - * - * Returns the index where the information field keyword was found or - * -ENOENT otherwise. - */ -int pci_vpd_find_info_keyword(const u8 *buf, unsigned int off, - unsigned int len, const char *kw); - /** * pci_vpd_find_ro_info_keyword - Locate info field keyword in VPD RO section * @buf: Pointer to buffered VPD data -- cgit v1.2.3 From 46a347835cc50b04da0996a070939ed8927d69bd Mon Sep 17 00:00:00 2001 From: Heiner Kallweit Date: Thu, 26 Aug 2021 20:55:07 +0200 Subject: PCI/VPD: Include post-processing in pci_vpd_find_tag() Move pci_vpd_find_tag() post-processing from pci_vpd_find_ro_info_keyword() to pci_vpd_find_tag(). This simplifies function pci_vpd_find_id_string() that will be added in a subsequent patch. Link: https://lore.kernel.org/r/fb15393f-d3b2-e140-2643-570d3abd7382@gmail.com Signed-off-by: Heiner Kallweit Signed-off-by: Bjorn Helgaas --- drivers/pci/vpd.c | 23 +++++++++++++---------- 1 file changed, 13 insertions(+), 10 deletions(-) (limited to 'drivers/pci') diff --git a/drivers/pci/vpd.c b/drivers/pci/vpd.c index 0e7a5e8a8f17..b7bf014ccc5f 100644 --- a/drivers/pci/vpd.c +++ b/drivers/pci/vpd.c @@ -296,16 +296,25 @@ void *pci_vpd_alloc(struct pci_dev *dev, unsigned int *size) } EXPORT_SYMBOL_GPL(pci_vpd_alloc); -static int pci_vpd_find_tag(const u8 *buf, unsigned int len, u8 rdt) +static int pci_vpd_find_tag(const u8 *buf, unsigned int len, u8 rdt, unsigned int *size) { int i = 0; /* look for LRDT tags only, end tag is the only SRDT tag */ while (i + PCI_VPD_LRDT_TAG_SIZE <= len && buf[i] & PCI_VPD_LRDT) { - if (buf[i] == rdt) + unsigned int lrdt_len = pci_vpd_lrdt_size(buf + i); + u8 tag = buf[i]; + + i += PCI_VPD_LRDT_TAG_SIZE; + if (tag == rdt) { + if (i + lrdt_len > len) + lrdt_len = len - i; + if (size) + *size = lrdt_len; return i; + } - i += PCI_VPD_LRDT_TAG_SIZE + pci_vpd_lrdt_size(buf + i); + i += lrdt_len; } return -ENOENT; @@ -384,16 +393,10 @@ int pci_vpd_find_ro_info_keyword(const void *buf, unsigned int len, int ro_start, infokw_start; unsigned int ro_len, infokw_size; - ro_start = pci_vpd_find_tag(buf, len, PCI_VPD_LRDT_RO_DATA); + ro_start = pci_vpd_find_tag(buf, len, PCI_VPD_LRDT_RO_DATA, &ro_len); if (ro_start < 0) return ro_start; - ro_len = pci_vpd_lrdt_size(buf + ro_start); - ro_start += PCI_VPD_LRDT_TAG_SIZE; - - if (ro_start + ro_len > len) - ro_len = len - ro_start; - infokw_start = pci_vpd_find_info_keyword(buf, ro_start, ro_len, kw); if (infokw_start < 0) return infokw_start; -- cgit v1.2.3 From acfbb1b8a494d7bfd316dfb363a820e6df637e8d Mon Sep 17 00:00:00 2001 From: Heiner Kallweit Date: Thu, 26 Aug 2021 20:55:43 +0200 Subject: PCI/VPD: Add pci_vpd_find_id_string() Add a pci_vpd_find_id_string() API function to retrieve the ID string from VPD. This way callers don't need pci_vpd_lrdt_size() any longer, and it can be made private to the VPD core. Link: https://lore.kernel.org/r/c5225bf6-8d29-970d-e271-0d7b52252630@gmail.com Signed-off-by: Heiner Kallweit Signed-off-by: Bjorn Helgaas --- drivers/pci/vpd.c | 6 ++++++ include/linux/pci.h | 10 ++++++++++ 2 files changed, 16 insertions(+) (limited to 'drivers/pci') diff --git a/drivers/pci/vpd.c b/drivers/pci/vpd.c index b7bf014ccc5f..79712b3d17b6 100644 --- a/drivers/pci/vpd.c +++ b/drivers/pci/vpd.c @@ -320,6 +320,12 @@ static int pci_vpd_find_tag(const u8 *buf, unsigned int len, u8 rdt, unsigned in return -ENOENT; } +int pci_vpd_find_id_string(const u8 *buf, unsigned int len, unsigned int *size) +{ + return pci_vpd_find_tag(buf, len, PCI_VPD_LRDT_ID_STRING, size); +} +EXPORT_SYMBOL_GPL(pci_vpd_find_id_string); + static int pci_vpd_find_info_keyword(const u8 *buf, unsigned int off, unsigned int len, const char *kw) { diff --git a/include/linux/pci.h b/include/linux/pci.h index 196cbf4c76a1..ea330ca0501a 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -2339,6 +2339,16 @@ static inline u8 pci_vpd_info_field_size(const u8 *info_field) */ void *pci_vpd_alloc(struct pci_dev *dev, unsigned int *size); +/** + * pci_vpd_find_id_string - Locate id string in VPD + * @buf: Pointer to buffered VPD data + * @len: The length of the buffer area in which to search + * @size: Pointer to field where length of id string is returned + * + * Returns the index of the id string or -ENOENT if not found. + */ +int pci_vpd_find_id_string(const u8 *buf, unsigned int len, unsigned int *size); + /** * pci_vpd_find_ro_info_keyword - Locate info field keyword in VPD RO section * @buf: Pointer to buffered VPD data -- cgit v1.2.3 From 06e1913d457121a98ee276179734c34dab30f388 Mon Sep 17 00:00:00 2001 From: Heiner Kallweit Date: Thu, 26 Aug 2021 20:57:01 +0200 Subject: PCI/VPD: Clean up public VPD defines and inline functions After recent introduction of new VPD API functions and user migration these defines and inline functions aren't used outside VPD core any longer. Link: https://lore.kernel.org/r/d33e06bf-bc5e-ece7-bf35-7245ae224d1b@gmail.com Signed-off-by: Heiner Kallweit Signed-off-by: Bjorn Helgaas --- drivers/pci/vpd.c | 26 ++++++++++++++++++++ include/linux/pci.h | 69 ----------------------------------------------------- 2 files changed, 26 insertions(+), 69 deletions(-) (limited to 'drivers/pci') diff --git a/drivers/pci/vpd.c b/drivers/pci/vpd.c index 79712b3d17b6..ff600dff4557 100644 --- a/drivers/pci/vpd.c +++ b/drivers/pci/vpd.c @@ -11,6 +11,32 @@ #include #include "pci.h" +#define PCI_VPD_LRDT_TAG_SIZE 3 +#define PCI_VPD_SRDT_LEN_MASK 0x07 +#define PCI_VPD_SRDT_TAG_SIZE 1 +#define PCI_VPD_STIN_END 0x0f +#define PCI_VPD_INFO_FLD_HDR_SIZE 3 + +static u16 pci_vpd_lrdt_size(const u8 *lrdt) +{ + return (u16)lrdt[1] + ((u16)lrdt[2] << 8); +} + +static u8 pci_vpd_srdt_tag(const u8 *srdt) +{ + return *srdt >> 3; +} + +static u8 pci_vpd_srdt_size(const u8 *srdt) +{ + return *srdt & PCI_VPD_SRDT_LEN_MASK; +} + +static u8 pci_vpd_info_field_size(const u8 *info_field) +{ + return info_field[2]; +} + /* VPD access through PCI 2.2+ VPD capability */ static struct pci_dev *pci_get_func0_dev(struct pci_dev *dev) diff --git a/include/linux/pci.h b/include/linux/pci.h index ea330ca0501a..303034d03c33 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -2255,81 +2255,12 @@ int pci_enable_atomic_ops_to_root(struct pci_dev *dev, u32 cap_mask); #define PCI_VPD_LRDT_RO_DATA PCI_VPD_LRDT_ID(PCI_VPD_LTIN_RO_DATA) #define PCI_VPD_LRDT_RW_DATA PCI_VPD_LRDT_ID(PCI_VPD_LTIN_RW_DATA) -/* Small Resource Data Type Tag Item Names */ -#define PCI_VPD_STIN_END 0x0f /* End */ - -#define PCI_VPD_SRDT_END (PCI_VPD_STIN_END << 3) - -#define PCI_VPD_SRDT_TIN_MASK 0x78 -#define PCI_VPD_SRDT_LEN_MASK 0x07 -#define PCI_VPD_LRDT_TIN_MASK 0x7f - -#define PCI_VPD_LRDT_TAG_SIZE 3 -#define PCI_VPD_SRDT_TAG_SIZE 1 - -#define PCI_VPD_INFO_FLD_HDR_SIZE 3 - #define PCI_VPD_RO_KEYWORD_PARTNO "PN" #define PCI_VPD_RO_KEYWORD_SERIALNO "SN" #define PCI_VPD_RO_KEYWORD_MFR_ID "MN" #define PCI_VPD_RO_KEYWORD_VENDOR0 "V0" #define PCI_VPD_RO_KEYWORD_CHKSUM "RV" -/** - * pci_vpd_lrdt_size - Extracts the Large Resource Data Type length - * @lrdt: Pointer to the beginning of the Large Resource Data Type tag - * - * Returns the extracted Large Resource Data Type length. - */ -static inline u16 pci_vpd_lrdt_size(const u8 *lrdt) -{ - return (u16)lrdt[1] + ((u16)lrdt[2] << 8); -} - -/** - * pci_vpd_lrdt_tag - Extracts the Large Resource Data Type Tag Item - * @lrdt: Pointer to the beginning of the Large Resource Data Type tag - * - * Returns the extracted Large Resource Data Type Tag item. - */ -static inline u16 pci_vpd_lrdt_tag(const u8 *lrdt) -{ - return (u16)(lrdt[0] & PCI_VPD_LRDT_TIN_MASK); -} - -/** - * pci_vpd_srdt_size - Extracts the Small Resource Data Type length - * @srdt: Pointer to the beginning of the Small Resource Data Type tag - * - * Returns the extracted Small Resource Data Type length. - */ -static inline u8 pci_vpd_srdt_size(const u8 *srdt) -{ - return (*srdt) & PCI_VPD_SRDT_LEN_MASK; -} - -/** - * pci_vpd_srdt_tag - Extracts the Small Resource Data Type Tag Item - * @srdt: Pointer to the beginning of the Small Resource Data Type tag - * - * Returns the extracted Small Resource Data Type Tag Item. - */ -static inline u8 pci_vpd_srdt_tag(const u8 *srdt) -{ - return ((*srdt) & PCI_VPD_SRDT_TIN_MASK) >> 3; -} - -/** - * pci_vpd_info_field_size - Extracts the information field length - * @info_field: Pointer to the beginning of an information field header - * - * Returns the extracted information field length. - */ -static inline u8 pci_vpd_info_field_size(const u8 *info_field) -{ - return info_field[2]; -} - /** * pci_vpd_alloc - Allocate buffer and read VPD into it * @dev: PCI device -- cgit v1.2.3 From 2c208abd4f9efac02622d8f3c9989f4b7b1ad973 Mon Sep 17 00:00:00 2001 From: Heiner Kallweit Date: Thu, 26 Aug 2021 20:58:07 +0200 Subject: PCI/VPD: Use unaligned access helpers Use unaligned access helpers to simplify the code. Link: https://lore.kernel.org/r/0f1c7e21-5330-72ab-139d-f5ce3c65f04a@gmail.com Signed-off-by: Heiner Kallweit Signed-off-by: Bjorn Helgaas --- drivers/pci/vpd.c | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) (limited to 'drivers/pci') diff --git a/drivers/pci/vpd.c b/drivers/pci/vpd.c index ff600dff4557..25557b272a4f 100644 --- a/drivers/pci/vpd.c +++ b/drivers/pci/vpd.c @@ -9,6 +9,7 @@ #include #include #include +#include #include "pci.h" #define PCI_VPD_LRDT_TAG_SIZE 3 @@ -19,7 +20,7 @@ static u16 pci_vpd_lrdt_size(const u8 *lrdt) { - return (u16)lrdt[1] + ((u16)lrdt[2] << 8); + return get_unaligned_le16(lrdt + 1); } static u8 pci_vpd_srdt_tag(const u8 *srdt) @@ -218,14 +219,8 @@ static ssize_t pci_vpd_write(struct pci_dev *dev, loff_t pos, size_t count, return -EINTR; while (pos < end) { - u32 val; - - val = *buf++; - val |= *buf++ << 8; - val |= *buf++ << 16; - val |= *buf++ << 24; - - ret = pci_user_write_config_dword(dev, vpd->cap + PCI_VPD_DATA, val); + ret = pci_user_write_config_dword(dev, vpd->cap + PCI_VPD_DATA, + get_unaligned_le32(buf)); if (ret < 0) break; ret = pci_user_write_config_word(dev, vpd->cap + PCI_VPD_ADDR, @@ -237,6 +232,7 @@ static ssize_t pci_vpd_write(struct pci_dev *dev, loff_t pos, size_t count, if (ret < 0) break; + buf += sizeof(u32); pos += sizeof(u32); } -- cgit v1.2.3