hw/s390x/s390-pci-inst.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
During zPCI scan, BAR configuration data retrieved via CLP Query was
misinterpreted due to an endianness mismatch between QEMU and the guest
kernel.
The guest kernel's clp_store_query_pci_fn() expects BAR values in
little-endian format and converts them with le32_to_cpu(). However, QEMU
was incorrectly sending them in big-endian format, not following the
architecture specification. This caused incorrect bit-swapping in the
kernel, leading zpci_setup_bus_resources() to perform registration checks
against invalid flags, making the process ineffective.
Observation values for zPCI device (NVMe passthrough):
LPAR from real CLP:
[ 0.865595] Resource: PCI Bus 0000:00 -> zdev->bar[0].val: 0x4
[ 0.865597] start: 0x4000000000000000
[ 0.865598] end: 0x4000000000003fff
[ 0.865600] flags: 0x100200
QEMU before fix (wrong):
[ 0.601083] Resource: PCI Bus 0001:00 -> zdev->bar[0].val: 0x4000000
[ 0.601085] start: 0x4003000000000000
[ 0.601086] end: 0x4003000000003fff
[ 0.601087] flags: 0x200
QEMU after fix (correct):
[ 0.601116] Resource: PCI Bus 0001:00 -> zdev->bar[0].val: 0x4
[ 0.601117] start: 0x4003000000000000
[ 0.601118] end: 0x4003000000003fff
[ 0.601119] flags: 0x100200
Signed-off-by: Jaehoon Kim <jhkim@linux.ibm.com>
---
hw/s390x/s390-pci-inst.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/hw/s390x/s390-pci-inst.c b/hw/s390x/s390-pci-inst.c
index 6b67c3c109..10066ca618 100644
--- a/hw/s390x/s390-pci-inst.c
+++ b/hw/s390x/s390-pci-inst.c
@@ -307,7 +307,7 @@ int clp_service_call(S390CPU *cpu, uint8_t r2, uintptr_t ra)
uint32_t data = pci_get_long(pbdev->pdev->config +
PCI_BASE_ADDRESS_0 + (i * 4));
- stl_be_p(&resquery->bar[i], data);
+ stl_le_p(&resquery->bar[i], data);
resquery->bar_size[i] = pbdev->pdev->io_regions[i].size ?
ctz64(pbdev->pdev->io_regions[i].size) : 0;
trace_s390_pci_bar(i,
--
2.50.1
On 2/6/2026 8:46 AM, Jaehoon Kim wrote: > During zPCI scan, BAR configuration data retrieved via CLP Query was > misinterpreted due to an endianness mismatch between QEMU and the guest > kernel. > > The guest kernel's clp_store_query_pci_fn() expects BAR values in > little-endian format and converts them with le32_to_cpu(). However, QEMU > was incorrectly sending them in big-endian format, not following the > architecture specification. This caused incorrect bit-swapping in the > kernel, leading zpci_setup_bus_resources() to perform registration checks > against invalid flags, making the process ineffective. > > Observation values for zPCI device (NVMe passthrough): > LPAR from real CLP: > [ 0.865595] Resource: PCI Bus 0000:00 -> zdev->bar[0].val: 0x4 > [ 0.865597] start: 0x4000000000000000 > [ 0.865598] end: 0x4000000000003fff > [ 0.865600] flags: 0x100200 > > QEMU before fix (wrong): > [ 0.601083] Resource: PCI Bus 0001:00 -> zdev->bar[0].val: 0x4000000 > [ 0.601085] start: 0x4003000000000000 > [ 0.601086] end: 0x4003000000003fff > [ 0.601087] flags: 0x200 > > QEMU after fix (correct): > [ 0.601116] Resource: PCI Bus 0001:00 -> zdev->bar[0].val: 0x4 > [ 0.601117] start: 0x4003000000000000 > [ 0.601118] end: 0x4003000000003fff > [ 0.601119] flags: 0x100200 > > Signed-off-by: Jaehoon Kim <jhkim@linux.ibm.com> > --- > hw/s390x/s390-pci-inst.c | 2 +- > 1 file changed, 1 insertion(+), 1 deletion(-) > > diff --git a/hw/s390x/s390-pci-inst.c b/hw/s390x/s390-pci-inst.c > index 6b67c3c109..10066ca618 100644 > --- a/hw/s390x/s390-pci-inst.c > +++ b/hw/s390x/s390-pci-inst.c > @@ -307,7 +307,7 @@ int clp_service_call(S390CPU *cpu, uint8_t r2, uintptr_t ra) > uint32_t data = pci_get_long(pbdev->pdev->config + > PCI_BASE_ADDRESS_0 + (i * 4)); > > - stl_be_p(&resquery->bar[i], data); > + stl_le_p(&resquery->bar[i], data); > resquery->bar_size[i] = pbdev->pdev->io_regions[i].size ? > ctz64(pbdev->pdev->io_regions[i].size) : 0; > trace_s390_pci_bar(i, This is indeed correct architecturally, thanks for fixing this. Reviewed-by: Farhan Ali <alifm@linux.ibm.com> <mailto:alifm@linux.ibm.com>
On 2/6/2026 10:46 AM, Jaehoon Kim wrote: The previous mail didn't reach one reviewer; resending to the correct address. > During zPCI scan, BAR configuration data retrieved via CLP Query was > misinterpreted due to an endianness mismatch between QEMU and the guest > kernel. > > The guest kernel's clp_store_query_pci_fn() expects BAR values in > little-endian format and converts them with le32_to_cpu(). However, QEMU > was incorrectly sending them in big-endian format, not following the > architecture specification. This caused incorrect bit-swapping in the > kernel, leading zpci_setup_bus_resources() to perform registration checks > against invalid flags, making the process ineffective. > > Observation values for zPCI device (NVMe passthrough): > LPAR from real CLP: > [ 0.865595] Resource: PCI Bus 0000:00 -> zdev->bar[0].val: 0x4 > [ 0.865597] start: 0x4000000000000000 > [ 0.865598] end: 0x4000000000003fff > [ 0.865600] flags: 0x100200 > > QEMU before fix (wrong): > [ 0.601083] Resource: PCI Bus 0001:00 -> zdev->bar[0].val: 0x4000000 > [ 0.601085] start: 0x4003000000000000 > [ 0.601086] end: 0x4003000000003fff > [ 0.601087] flags: 0x200 > > QEMU after fix (correct): > [ 0.601116] Resource: PCI Bus 0001:00 -> zdev->bar[0].val: 0x4 > [ 0.601117] start: 0x4003000000000000 > [ 0.601118] end: 0x4003000000003fff > [ 0.601119] flags: 0x100200 > > Signed-off-by: Jaehoon Kim <jhkim@linux.ibm.com> > --- > hw/s390x/s390-pci-inst.c | 2 +- > 1 file changed, 1 insertion(+), 1 deletion(-) > > diff --git a/hw/s390x/s390-pci-inst.c b/hw/s390x/s390-pci-inst.c > index 6b67c3c109..10066ca618 100644 > --- a/hw/s390x/s390-pci-inst.c > +++ b/hw/s390x/s390-pci-inst.c > @@ -307,7 +307,7 @@ int clp_service_call(S390CPU *cpu, uint8_t r2, uintptr_t ra) > uint32_t data = pci_get_long(pbdev->pdev->config + > PCI_BASE_ADDRESS_0 + (i * 4)); > > - stl_be_p(&resquery->bar[i], data); > + stl_le_p(&resquery->bar[i], data); > resquery->bar_size[i] = pbdev->pdev->io_regions[i].size ? > ctz64(pbdev->pdev->io_regions[i].size) : 0; > trace_s390_pci_bar(i,
On 2/6/26 12:08 PM, JAEHOON KIM wrote:
> On 2/6/2026 10:46 AM, Jaehoon Kim wrote:
>
> The previous mail didn't reach one reviewer; resending to the correct address.
>
>> During zPCI scan, BAR configuration data retrieved via CLP Query was
>> misinterpreted due to an endianness mismatch between QEMU and the guest
>> kernel.
>>
>> The guest kernel's clp_store_query_pci_fn() expects BAR values in
>> little-endian format and converts them with le32_to_cpu(). However, QEMU
And indeed that's what the platform documentation says specifically for the BAR values - they should be presented in the CLP response in LE, exactly as in config sapce.
AFAICT this was subtly wrong since the initial QEMU implementation. A simple git blame would point at c76c86fba5 ("hw/s390x: Use explicit big-endian LD/ST API"), but prior to this commit stl_p was used instead which still would depend on the target CPU type (BE).
Meanwhile, common code will always use PCI config space accesses (which are unaffected by this) rather than looking at the CLP response -- I think this is ultimately why it went quietly unnoticed.
I don't see it mentioned here, but I also asked Jaehoon to regression test with both virtio-pci and vfio-pci passthrough on s390. I also asked him to test it with tcg on a little-endian host and verify we see the same fix works as expected there too.
Thanks for finding this Jaehoon!
Reviewed-by: Matthew Rosato <mjrosato@linux.ibm.com>
>> was incorrectly sending them in big-endian format, not following the
>> architecture specification. This caused incorrect bit-swapping in the
>> kernel, leading zpci_setup_bus_resources() to perform registration checks
>> against invalid flags, making the process ineffective.
>>
>> Observation values for zPCI device (NVMe passthrough):
>> LPAR from real CLP:
>> [ 0.865595] Resource: PCI Bus 0000:00 -> zdev->bar[0].val: 0x4
>> [ 0.865597] start: 0x4000000000000000
>> [ 0.865598] end: 0x4000000000003fff
>> [ 0.865600] flags: 0x100200
>>
>> QEMU before fix (wrong):
>> [ 0.601083] Resource: PCI Bus 0001:00 -> zdev->bar[0].val: 0x4000000
>> [ 0.601085] start: 0x4003000000000000
>> [ 0.601086] end: 0x4003000000003fff
>> [ 0.601087] flags: 0x200
>>
>> QEMU after fix (correct):
>> [ 0.601116] Resource: PCI Bus 0001:00 -> zdev->bar[0].val: 0x4
>> [ 0.601117] start: 0x4003000000000000
>> [ 0.601118] end: 0x4003000000003fff
>> [ 0.601119] flags: 0x100200
>>
>> Signed-off-by: Jaehoon Kim <jhkim@linux.ibm.com>
>> ---
>> hw/s390x/s390-pci-inst.c | 2 +-
>> 1 file changed, 1 insertion(+), 1 deletion(-)
>>
>> diff --git a/hw/s390x/s390-pci-inst.c b/hw/s390x/s390-pci-inst.c
>> index 6b67c3c109..10066ca618 100644
>> --- a/hw/s390x/s390-pci-inst.c
>> +++ b/hw/s390x/s390-pci-inst.c
>> @@ -307,7 +307,7 @@ int clp_service_call(S390CPU *cpu, uint8_t r2, uintptr_t ra)
>> uint32_t data = pci_get_long(pbdev->pdev->config +
>> PCI_BASE_ADDRESS_0 + (i * 4));
>> - stl_be_p(&resquery->bar[i], data);
>> + stl_le_p(&resquery->bar[i], data);
>> resquery->bar_size[i] = pbdev->pdev->io_regions[i].size ?
>> ctz64(pbdev->pdev->io_regions[i].size) : 0;
>> trace_s390_pci_bar(i,
>
>
© 2016 - 2026 Red Hat, Inc.