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 --- include/linux/pci.h | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) (limited to 'include') 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 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 'include') 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 'include') 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 'include') 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 'include') 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 'include') 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 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 'include') 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 'include') 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