pcie_tph_get_st_table_loc() incorrectly uses FIELD_GET(), which shifts the field value to bit 0. But the function is designed to return raw PCI_TPH_LOC_* values as defined in the function comment. This causes incorrect ST table location detection. Fix it by using bitwise AND with PCI_TPH_CAP_LOC_MASK to return the unshifted field value matching the function specification. This doesn't make a difference to mlx5_st_create(), the lone external caller, because it only checks for PCI_TPH_LOC_NONE (0), but will be needed for callers that check for PCI_TPH_LOC_CAP or PCI_TPH_LOC_MSIX. Also add tph_cap validation for pcie_tph_get_st_table_loc() to prevent invalid PCI configuration space access when TPH is not supported. Add stub functions for pcie_tph_get_st_table_size() and pcie_tph_get_st_table_loc() when !CONFIG_PCIE_TPH. Fixes: d2e8a34876ce ("PCI/TPH: Add Steering Tag support") Cc: stable@vger.kernel.org Signed-off-by: Chengwen Feng Reviewed-by: Alex Williamson Reviewed-by: Bjorn Helgaas --- drivers/pci/tph.c | 12 +++++------- include/linux/pci-tph.h | 4 ++++ 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/drivers/pci/tph.c b/drivers/pci/tph.c index 91145e8d9d95..bef3a55539c4 100644 --- a/drivers/pci/tph.c +++ b/drivers/pci/tph.c @@ -166,11 +166,14 @@ static u8 get_st_modes(struct pci_dev *pdev) */ u32 pcie_tph_get_st_table_loc(struct pci_dev *pdev) { - u32 reg; + u32 reg = 0; + + if (!pdev->tph_cap) + return PCI_TPH_LOC_NONE; pci_read_config_dword(pdev, pdev->tph_cap + PCI_TPH_CAP, ®); - return FIELD_GET(PCI_TPH_CAP_LOC_MASK, reg); + return reg & PCI_TPH_CAP_LOC_MASK; } EXPORT_SYMBOL(pcie_tph_get_st_table_loc); @@ -185,9 +188,6 @@ u16 pcie_tph_get_st_table_size(struct pci_dev *pdev) /* Check ST table location first */ loc = pcie_tph_get_st_table_loc(pdev); - - /* Convert loc to match with PCI_TPH_LOC_* defined in pci_regs.h */ - loc = FIELD_PREP(PCI_TPH_CAP_LOC_MASK, loc); if (loc != PCI_TPH_LOC_CAP) return 0; @@ -316,8 +316,6 @@ int pcie_tph_set_st_entry(struct pci_dev *pdev, unsigned int index, u16 tag) set_ctrl_reg_req_en(pdev, PCI_TPH_REQ_DISABLE); loc = pcie_tph_get_st_table_loc(pdev); - /* Convert loc to match with PCI_TPH_LOC_* */ - loc = FIELD_PREP(PCI_TPH_CAP_LOC_MASK, loc); switch (loc) { case PCI_TPH_LOC_MSIX: diff --git a/include/linux/pci-tph.h b/include/linux/pci-tph.h index be68cd17f2f8..7502fef81833 100644 --- a/include/linux/pci-tph.h +++ b/include/linux/pci-tph.h @@ -41,6 +41,10 @@ static inline int pcie_tph_get_cpu_st(struct pci_dev *dev, static inline void pcie_disable_tph(struct pci_dev *pdev) { } static inline int pcie_enable_tph(struct pci_dev *pdev, int mode) { return -EINVAL; } +static inline u16 pcie_tph_get_st_table_size(struct pci_dev *pdev) +{ return 0; } +static inline u32 pcie_tph_get_st_table_loc(struct pci_dev *pdev) +{ return 0; } #endif #endif /* LINUX_PCI_TPH_H */ -- 2.17.1