[bug report] PCI: rockchip: Fix window mapping and address translation for endpoint

Rick Wertenbroek rick.wertenbroek at gmail.com
Tue Jun 27 02:39:16 PDT 2023


On Tue, Jun 27, 2023 at 9:19 AM Dan Carpenter <dan.carpenter at linaro.org> wrote:
>
> Hello Rick Wertenbroek,
>
> The patch dc73ed0f1b8b: "PCI: rockchip: Fix window mapping and
> address translation for endpoint" from Apr 18, 2023, leads to the
> following Smatch static checker warning:
>
>         drivers/pci/controller/pcie-rockchip-ep.c:405 rockchip_pcie_ep_send_msi_irq()
>         warn: was expecting a 64 bit value instead of '~4294967040'
>
> drivers/pci/controller/pcie-rockchip-ep.c
>     351 static int rockchip_pcie_ep_send_msi_irq(struct rockchip_pcie_ep *ep, u8 fn,
>     352                                          u8 interrupt_num)
>     353 {
>     354         struct rockchip_pcie *rockchip = &ep->rockchip;
>     355         u32 flags, mme, data, data_mask;
>     356         u8 msi_count;
>     357         u64 pci_addr;
>                 ^^^^^^^^^^^^^
>
>     358         u32 r;
>     359
>     360         /* Check MSI enable bit */
>     361         flags = rockchip_pcie_read(&ep->rockchip,
>     362                                    ROCKCHIP_PCIE_EP_FUNC_BASE(fn) +
>     363                                    ROCKCHIP_PCIE_EP_MSI_CTRL_REG);
>     364         if (!(flags & ROCKCHIP_PCIE_EP_MSI_CTRL_ME))
>     365                 return -EINVAL;
>     366
>     367         /* Get MSI numbers from MME */
>     368         mme = ((flags & ROCKCHIP_PCIE_EP_MSI_CTRL_MME_MASK) >>
>     369                         ROCKCHIP_PCIE_EP_MSI_CTRL_MME_OFFSET);
>     370         msi_count = 1 << mme;
>     371         if (!interrupt_num || interrupt_num > msi_count)
>     372                 return -EINVAL;
>     373
>     374         /* Set MSI private data */
>     375         data_mask = msi_count - 1;
>     376         data = rockchip_pcie_read(rockchip,
>     377                                   ROCKCHIP_PCIE_EP_FUNC_BASE(fn) +
>     378                                   ROCKCHIP_PCIE_EP_MSI_CTRL_REG +
>     379                                   PCI_MSI_DATA_64);
>     380         data = (data & ~data_mask) | ((interrupt_num - 1) & data_mask);
>     381
>     382         /* Get MSI PCI address */
>     383         pci_addr = rockchip_pcie_read(rockchip,
>     384                                       ROCKCHIP_PCIE_EP_FUNC_BASE(fn) +
>     385                                       ROCKCHIP_PCIE_EP_MSI_CTRL_REG +
>     386                                       PCI_MSI_ADDRESS_HI);
>     387         pci_addr <<= 32;
>
> The high 32 bits are definitely set.
>
>     388         pci_addr |= rockchip_pcie_read(rockchip,
>     389                                        ROCKCHIP_PCIE_EP_FUNC_BASE(fn) +
>     390                                        ROCKCHIP_PCIE_EP_MSI_CTRL_REG +
>     391                                        PCI_MSI_ADDRESS_LO);
>     392
>     393         /* Set the outbound region if needed. */
>     394         if (unlikely(ep->irq_pci_addr != (pci_addr & PCIE_ADDR_MASK) ||
>     395                      ep->irq_pci_fn != fn)) {
>     396                 r = rockchip_ob_region(ep->irq_phys_addr);
>     397                 rockchip_pcie_prog_ep_ob_atu(rockchip, fn, r,
>     398                                              ep->irq_phys_addr,
>     399                                              pci_addr & PCIE_ADDR_MASK,
>     400                                              ~PCIE_ADDR_MASK + 1);
>     401                 ep->irq_pci_addr = (pci_addr & PCIE_ADDR_MASK);
>     402                 ep->irq_pci_fn = fn;
>     403         }
>     404
> --> 405         writew(data, ep->irq_cpu_addr + (pci_addr & ~PCIE_ADDR_MASK));
>
> PCIE_ADDR_MASK is 0xffffff00 (which is unsigned int).  What Smatch is
> saying here is that pci_addr is a u64 it looks like the intention was to
> zero out the last byte, but instead we are zeroing the high 32 bits as
> well as the last 8 bits.

Hello,
You are right there is a problem.

The warning at line 405, however, seems to be a false positive. Because the
mask is 0xffffff00 (which is unsigned int) and the ~ is applied before promotion
this results in 0xff, which is then promoted to 0x00000000000000ff when applied
to pci_addr so this is fine.

What you describe about zeroing the upper 32 bits and not only the lower 8
refers to line 399 where we apply the PCI_ADDR_MASK 0xffffff00 with the
& operator to pci_addr, this is the real issue, as you said this
zeroes the upper
32-bits, which is not the intended behavior. Thank you for pointing this out.

The original function had the mask constant as a u64 variable (with inverted
logic) :
https://elixir.bootlin.com/linux/latest/source/drivers/pci/controller/pcie-rockchip-ep.c#L415

I replaced this with a constant and thought it would be promoted, but since
it is promoted to unsigned it isn't sign-extended, therefore the upper 32-bits
are zeroed which is not the intended behavior

The simplest way to fix this would be to revert to the initial logic with the
variable as linked above. Or to replace set the constant to 0xffffffffffffff00
and revert the two changes in drivers/pci/controller/pcie-rockchip.h :

-#define   PCIE_CORE_OB_REGION_ADDR0_LO_ADDR    0xffffff00
+#define   PCIE_CORE_OB_REGION_ADDR0_LO_ADDR    PCIE_ADDR_MASK

-#define   PCIE_CORE_IB_REGION_ADDR0_LO_ADDR    0xffffff00
+#define   PCIE_CORE_IB_REGION_ADDR0_LO_ADDR    PCIE_ADDR_MASK

>
>     406         return 0;
>     407 }
>
> regards,
> dan carpenter

Thank you very much for spotting this.

I will prepare the patch for this in a few days (I am currently out of office),
I'll add the "reported-by" tag and fix this, unless you want to fix and submit
it right away.

Thanks.
Best regards,
Rick Wertenbroek



More information about the Linux-rockchip mailing list