PCI/sysfs: Fix read byte order in pci_read_legacy_io()

pci_read_legacy_io() passes the sysfs buffer directly to pci_legacy_read():

  return pci_legacy_read(bus, off, (u32 *)buf, count);

The PowerPC implementation stores the result as a native-endian integer:

  *((u16 *)val) = in_le16(addr);

On big-endian PowerPC this stores the bytes in the wrong order, so
a 2-byte read of a device register returns different bytes than two
1-byte reads at the same addresses.  The same applies to 4-byte
reads.  On little-endian the native byte order already matches PCI
I/O port byte order, so the conversion is a no-op.

Thus, let pci_legacy_read() store into a local u32 variable, then
copy the I/O port value to the sysfs buffer using put_unaligned_le16()
and put_unaligned_le32() for the 2 and 4 byte cases, converting from
the native integer to little-endian byte order matching PCI I/O port
space.

No changes are needed for the Alpha platform.

The legacy_io file is root-only and exists only on Alpha and PowerPC,
the two architectures that define HAVE_PCI_LEGACY.

Cc: stable@vger.kernel.org
Link: https://patch.msgid.link/20260616163131.2763281-2-kwilczynski@kernel.org
Signed-off-by: Krzysztof Wilczyński <kwilczynski@kernel.org>
This commit is contained in:
Krzysztof Wilczyński 2026-06-16 16:31:31 +00:00
parent dc76258d01
commit 5b95212de6

View File

@ -888,12 +888,30 @@ static ssize_t pci_read_legacy_io(struct file *filp, struct kobject *kobj,
char *buf, loff_t off, size_t count)
{
struct pci_bus *bus = to_pci_bus(kobj_to_dev(kobj));
u32 val = 0;
int ret;
/* Only support 1, 2 or 4 byte accesses */
if (count != 1 && count != 2 && count != 4)
return -EINVAL;
return pci_legacy_read(bus, off, (u32 *)buf, count);
ret = pci_legacy_read(bus, off, &val, count);
if (ret < 0)
return ret;
switch (count) {
case 1:
buf[0] = *(u8 *)&val;
break;
case 2:
put_unaligned_le16(*(u16 *)&val, buf);
break;
case 4:
put_unaligned_le32(val, buf);
break;
}
return ret;
}
/**