Linux PCI subsystem development
 help / color / mirror / Atom feed
* PCI endpoint: pci-epf-test is broken on big-endian
@ 2024-10-21  7:49 Niklas Cassel
  2024-10-21  9:49 ` Damien Le Moal
                   ` (2 more replies)
  0 siblings, 3 replies; 6+ messages in thread
From: Niklas Cassel @ 2024-10-21  7:49 UTC (permalink / raw)
  To: Manivannan Sadhasivam, Krzysztof Wilczyński
  Cc: Kishon Vijay Abraham I, linux-pci, Damien Le Moal

Hello PCI endpoint maintainers,


While looking at the pci-epf-test.c driver, I noticed that
pci-epf-test is completely broken with regards to endianness.

As you probably know, PCI devices are inherently little-endian,
and the data stored in the PCI BARs should be in little-endian.

However, pci-epf-test does no conversion before storing the data
to backing memory, and no conversion after reading the data from
backing memory.

For the data backing test_reg BAR (usually BAR0), which has the
format as defined by struct pci_epf_test_reg, is simply stored
to memory using e.g.:
reg->status = STATUS_WRITE_SUCCESS;

Surely, this should be:
reg->status = cpu_to_le32(STATUS_WRITE_SUCCESS);


Likewise the src and dst address is accessed simply by
reg->dst_addr and reg->src_addr.

Surely, this should be accessed using:
dst_addr = le64_to_cpu(reg->dst_addr);
src_addr = le64_to_cpu(reg->src_addr);

So bottom line, pci-epf-test will currently not behave correctly
on big-endian.



Looking at pci-endpoint-test however, it does all its accesses using
readl() and writel(), and if you look at the implementations of
readl()/writel():
https://github.com/torvalds/linux/blob/v6.12-rc4/include/asm-generic/io.h#L181-L184

They convert to CPU native after reading, and convert to little-endian
before writing, so pci-endpoint-test (RC side driver) is okay, it is
just pci-epf-test (EP side driver) that is broken.

I'm not planning on spending time on this, but I thought that I ought to at
least report it, such that maintainers/developers/users are aware of it.


Kind regards,
Niklas

^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: PCI endpoint: pci-epf-test is broken on big-endian
  2024-10-21  7:49 PCI endpoint: pci-epf-test is broken on big-endian Niklas Cassel
@ 2024-10-21  9:49 ` Damien Le Moal
  2024-10-22  3:26   ` Manivannan Sadhasivam
  2024-10-21 20:03 ` Frank Li
  2024-10-22  3:21 ` Manivannan Sadhasivam
  2 siblings, 1 reply; 6+ messages in thread
From: Damien Le Moal @ 2024-10-21  9:49 UTC (permalink / raw)
  To: Niklas Cassel, Manivannan Sadhasivam, Krzysztof Wilczyński
  Cc: Kishon Vijay Abraham I, linux-pci

On 10/21/24 16:49, Niklas Cassel wrote:
> Hello PCI endpoint maintainers,
> 
> 
> While looking at the pci-epf-test.c driver, I noticed that
> pci-epf-test is completely broken with regards to endianness.
> 
> As you probably know, PCI devices are inherently little-endian,
> and the data stored in the PCI BARs should be in little-endian.
> 
> However, pci-epf-test does no conversion before storing the data
> to backing memory, and no conversion after reading the data from
> backing memory.
> 
> For the data backing test_reg BAR (usually BAR0), which has the
> format as defined by struct pci_epf_test_reg, is simply stored
> to memory using e.g.:
> reg->status = STATUS_WRITE_SUCCESS;
> 
> Surely, this should be:
> reg->status = cpu_to_le32(STATUS_WRITE_SUCCESS);
> 
> 
> Likewise the src and dst address is accessed simply by
> reg->dst_addr and reg->src_addr.
> 
> Surely, this should be accessed using:
> dst_addr = le64_to_cpu(reg->dst_addr);
> src_addr = le64_to_cpu(reg->src_addr);
> 
> So bottom line, pci-epf-test will currently not behave correctly
> on big-endian.
> 
> 
> 
> Looking at pci-endpoint-test however, it does all its accesses using
> readl() and writel(), and if you look at the implementations of
> readl()/writel():
> https://github.com/torvalds/linux/blob/v6.12-rc4/include/asm-generic/io.h#L181-L184
> 
> They convert to CPU native after reading, and convert to little-endian
> before writing, so pci-endpoint-test (RC side driver) is okay, it is
> just pci-epf-test (EP side driver) that is broken.

That in itself is another problem. The use of readl/writel for things in the EPF
BAR memory is also *wrong*, because that memory is NOT a real mmio memory. We
should be using READ_ONCE()/WRITE_ONCE() to treat the BAR as volatile memory but
not use readl/writel.

> 
> I'm not planning on spending time on this, but I thought that I ought to at
> least report it, such that maintainers/developers/users are aware of it.
> 
> 
> Kind regards,
> Niklas


-- 
Damien Le Moal
Western Digital Research

^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: PCI endpoint: pci-epf-test is broken on big-endian
  2024-10-21  7:49 PCI endpoint: pci-epf-test is broken on big-endian Niklas Cassel
  2024-10-21  9:49 ` Damien Le Moal
@ 2024-10-21 20:03 ` Frank Li
  2024-10-22  3:21 ` Manivannan Sadhasivam
  2 siblings, 0 replies; 6+ messages in thread
From: Frank Li @ 2024-10-21 20:03 UTC (permalink / raw)
  To: Niklas Cassel
  Cc: Manivannan Sadhasivam, Krzysztof Wilczyński,
	Kishon Vijay Abraham I, linux-pci, Damien Le Moal

On Mon, Oct 21, 2024 at 09:49:54AM +0200, Niklas Cassel wrote:
> Hello PCI endpoint maintainers,
>
>
> While looking at the pci-epf-test.c driver, I noticed that
> pci-epf-test is completely broken with regards to endianness.
>
> As you probably know, PCI devices are inherently little-endian,
> and the data stored in the PCI BARs should be in little-endian.
>
> However, pci-epf-test does no conversion before storing the data
> to backing memory, and no conversion after reading the data from
> backing memory.
>
> For the data backing test_reg BAR (usually BAR0), which has the
> format as defined by struct pci_epf_test_reg, is simply stored
> to memory using e.g.:
> reg->status = STATUS_WRITE_SUCCESS;
>
> Surely, this should be:
> reg->status = cpu_to_le32(STATUS_WRITE_SUCCESS);

struct pci_epf_test_reg {
        u32     magic;
 	^^ should be le32.
	...
} __packed;

So sparse can detect all endian problem.

Frank

>
>
> Likewise the src and dst address is accessed simply by
> reg->dst_addr and reg->src_addr.
>
> Surely, this should be accessed using:
> dst_addr = le64_to_cpu(reg->dst_addr);
> src_addr = le64_to_cpu(reg->src_addr);
>
> So bottom line, pci-epf-test will currently not behave correctly
> on big-endian.
>
>
>
> Looking at pci-endpoint-test however, it does all its accesses using
> readl() and writel(), and if you look at the implementations of
> readl()/writel():
> https://github.com/torvalds/linux/blob/v6.12-rc4/include/asm-generic/io.h#L181-L184
>
> They convert to CPU native after reading, and convert to little-endian
> before writing, so pci-endpoint-test (RC side driver) is okay, it is
> just pci-epf-test (EP side driver) that is broken.
>
> I'm not planning on spending time on this, but I thought that I ought to at
> least report it, such that maintainers/developers/users are aware of it.
>
>
> Kind regards,
> Niklas

^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: PCI endpoint: pci-epf-test is broken on big-endian
  2024-10-21  7:49 PCI endpoint: pci-epf-test is broken on big-endian Niklas Cassel
  2024-10-21  9:49 ` Damien Le Moal
  2024-10-21 20:03 ` Frank Li
@ 2024-10-22  3:21 ` Manivannan Sadhasivam
  2 siblings, 0 replies; 6+ messages in thread
From: Manivannan Sadhasivam @ 2024-10-22  3:21 UTC (permalink / raw)
  To: Niklas Cassel
  Cc: Krzysztof Wilczyński, Kishon Vijay Abraham I, linux-pci,
	Damien Le Moal

On Mon, Oct 21, 2024 at 09:49:54AM +0200, Niklas Cassel wrote:
> Hello PCI endpoint maintainers,
> 
> 
> While looking at the pci-epf-test.c driver, I noticed that
> pci-epf-test is completely broken with regards to endianness.
> 
> As you probably know, PCI devices are inherently little-endian,
> and the data stored in the PCI BARs should be in little-endian.
> 
> However, pci-epf-test does no conversion before storing the data
> to backing memory, and no conversion after reading the data from
> backing memory.
> 
> For the data backing test_reg BAR (usually BAR0), which has the
> format as defined by struct pci_epf_test_reg, is simply stored
> to memory using e.g.:
> reg->status = STATUS_WRITE_SUCCESS;
> 
> Surely, this should be:
> reg->status = cpu_to_le32(STATUS_WRITE_SUCCESS);
> 
> 
> Likewise the src and dst address is accessed simply by
> reg->dst_addr and reg->src_addr.
> 
> Surely, this should be accessed using:
> dst_addr = le64_to_cpu(reg->dst_addr);
> src_addr = le64_to_cpu(reg->src_addr);
> 
> So bottom line, pci-epf-test will currently not behave correctly
> on big-endian.
> 
> 
> 
> Looking at pci-endpoint-test however, it does all its accesses using
> readl() and writel(), and if you look at the implementations of
> readl()/writel():
> https://github.com/torvalds/linux/blob/v6.12-rc4/include/asm-generic/io.h#L181-L184
> 
> They convert to CPU native after reading, and convert to little-endian
> before writing, so pci-endpoint-test (RC side driver) is okay, it is
> just pci-epf-test (EP side driver) that is broken.
> 
> I'm not planning on spending time on this, but I thought that I ought to at
> least report it, such that maintainers/developers/users are aware of it.
> 

Hi Niklas,

Thanks a lot for reporting. Yes, I acknowledge the issue on the ep side. Will
try to spare some cycle to fix it asap.

- Mani

-- 
மணிவண்ணன் சதாசிவம்

^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: PCI endpoint: pci-epf-test is broken on big-endian
  2024-10-21  9:49 ` Damien Le Moal
@ 2024-10-22  3:26   ` Manivannan Sadhasivam
  2024-10-22  4:15     ` Damien Le Moal
  0 siblings, 1 reply; 6+ messages in thread
From: Manivannan Sadhasivam @ 2024-10-22  3:26 UTC (permalink / raw)
  To: Damien Le Moal
  Cc: Niklas Cassel, Krzysztof Wilczyński, Kishon Vijay Abraham I,
	linux-pci

On Mon, Oct 21, 2024 at 06:49:11PM +0900, Damien Le Moal wrote:
> On 10/21/24 16:49, Niklas Cassel wrote:
> > Hello PCI endpoint maintainers,
> > 
> > 
> > While looking at the pci-epf-test.c driver, I noticed that
> > pci-epf-test is completely broken with regards to endianness.
> > 
> > As you probably know, PCI devices are inherently little-endian,
> > and the data stored in the PCI BARs should be in little-endian.
> > 
> > However, pci-epf-test does no conversion before storing the data
> > to backing memory, and no conversion after reading the data from
> > backing memory.
> > 
> > For the data backing test_reg BAR (usually BAR0), which has the
> > format as defined by struct pci_epf_test_reg, is simply stored
> > to memory using e.g.:
> > reg->status = STATUS_WRITE_SUCCESS;
> > 
> > Surely, this should be:
> > reg->status = cpu_to_le32(STATUS_WRITE_SUCCESS);
> > 
> > 
> > Likewise the src and dst address is accessed simply by
> > reg->dst_addr and reg->src_addr.
> > 
> > Surely, this should be accessed using:
> > dst_addr = le64_to_cpu(reg->dst_addr);
> > src_addr = le64_to_cpu(reg->src_addr);
> > 
> > So bottom line, pci-epf-test will currently not behave correctly
> > on big-endian.
> > 
> > 
> > 
> > Looking at pci-endpoint-test however, it does all its accesses using
> > readl() and writel(), and if you look at the implementations of
> > readl()/writel():
> > https://github.com/torvalds/linux/blob/v6.12-rc4/include/asm-generic/io.h#L181-L184
> > 
> > They convert to CPU native after reading, and convert to little-endian
> > before writing, so pci-endpoint-test (RC side driver) is okay, it is
> > just pci-epf-test (EP side driver) that is broken.
> 
> That in itself is another problem. The use of readl/writel for things in the EPF
> BAR memory is also *wrong*, because that memory is NOT a real mmio memory. We
> should be using READ_ONCE()/WRITE_ONCE() to treat the BAR as volatile memory but
> not use readl/writel.
> 

Not at all. The memory returned by pci_ioremap_bar() is annotated with __iomem,
which means it should *only* be accessed with the relevant accessors like
readl(), ioread32() etc... The memory is still treated as MMIO, so all the
restrictions (alignment) applies to it also.

- Mani

-- 
மணிவண்ணன் சதாசிவம்

^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: PCI endpoint: pci-epf-test is broken on big-endian
  2024-10-22  3:26   ` Manivannan Sadhasivam
@ 2024-10-22  4:15     ` Damien Le Moal
  0 siblings, 0 replies; 6+ messages in thread
From: Damien Le Moal @ 2024-10-22  4:15 UTC (permalink / raw)
  To: Manivannan Sadhasivam
  Cc: Niklas Cassel, Krzysztof Wilczyński, Kishon Vijay Abraham I,
	linux-pci

On 10/22/24 12:26, Manivannan Sadhasivam wrote:
>>> Looking at pci-endpoint-test however, it does all its accesses using
>>> readl() and writel(), and if you look at the implementations of
>>> readl()/writel():
>>> https://github.com/torvalds/linux/blob/v6.12-rc4/include/asm-generic/io.h#L181-L184
>>>
>>> They convert to CPU native after reading, and convert to little-endian
>>> before writing, so pci-endpoint-test (RC side driver) is okay, it is
>>> just pci-epf-test (EP side driver) that is broken.
>>
>> That in itself is another problem. The use of readl/writel for things in the EPF
>> BAR memory is also *wrong*, because that memory is NOT a real mmio memory. We
>> should be using READ_ONCE()/WRITE_ONCE() to treat the BAR as volatile memory but
>> not use readl/writel.
>>
> 
> Not at all. The memory returned by pci_ioremap_bar() is annotated with __iomem,
> which means it should *only* be accessed with the relevant accessors like
> readl(), ioread32() etc... The memory is still treated as MMIO, so all the
> restrictions (alignment) applies to it also.

You are talking about the host (RC side) ? I was talking about the EP side...

pci_epf_alloc_space() returns a "void *" pointer, and the same is true for the
dma_alloc_coherent() call that actually allocates the BAR space. So on the EP, a
BAR memory should be treated as regular memory, but "volatile" since it can
change under the driver (due to the host writing to the BAR). Hence
READ_ONCE()/WRITE_ONCE() is the correct way to access a BAR on the endpoint.

And yes, readl() and friends are for the PCI RC (host) side, that I know.

Sorry about the confusing comment. I was thinking about the EP while reading
Niklas's comment :)

-- 
Damien Le Moal
Western Digital Research

^ permalink raw reply	[flat|nested] 6+ messages in thread

end of thread, other threads:[~2024-10-22  4:15 UTC | newest]

Thread overview: 6+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2024-10-21  7:49 PCI endpoint: pci-epf-test is broken on big-endian Niklas Cassel
2024-10-21  9:49 ` Damien Le Moal
2024-10-22  3:26   ` Manivannan Sadhasivam
2024-10-22  4:15     ` Damien Le Moal
2024-10-21 20:03 ` Frank Li
2024-10-22  3:21 ` Manivannan Sadhasivam

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox