[PATCH 2/2] PCI/sysfs: Fix read byte order in pci_read_legacy_io()

From: Krzysztof Wilczyński

Date: Tue Jun 16 2026 - 12:38:05 EST


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@xxxxxxxxxxxxxxx
Signed-off-by: Krzysztof Wilczyński <kwilczynski@xxxxxxxxxx>
---
drivers/pci/pci-sysfs.c | 20 +++++++++++++++++++-
1 file changed, 19 insertions(+), 1 deletion(-)

diff --git a/drivers/pci/pci-sysfs.c b/drivers/pci/pci-sysfs.c
index b56000ba3a33..2354d09fd3fa 100644
--- a/drivers/pci/pci-sysfs.c
+++ b/drivers/pci/pci-sysfs.c
@@ -908,12 +908,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;
}

/**
--
2.54.0