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. Therefore add an API function that combines these functionalities. pci_vpd_find_info_keyword() can be phased out once all users are converted. VPD checksum information and checksum calculation are part of the PCI standard. Therefore checksum handling can and should be moved into PCI VPD core. Add an API function pci_vpd_check_csum() for that. Signed-off-by: Heiner Kallweit <hkallweit1@xxxxxxxxx> --- drivers/pci/vpd.c | 56 +++++++++++++++++++++++++++++++++++++++++++++ include/linux/pci.h | 21 +++++++++++++++++ 2 files changed, 77 insertions(+) diff --git a/drivers/pci/vpd.c b/drivers/pci/vpd.c index 7c3a09737..01e575947 100644 --- a/drivers/pci/vpd.c +++ b/drivers/pci/vpd.c @@ -380,6 +380,62 @@ 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); + +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 3061cc943..a82f5910f 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -2363,6 +2363,27 @@ 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_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_vpd_find_ro_info_keyword - Locates an 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; -- 2.32.0