[PATCH v1] s390x/pci: Fix endianness for zPCI BAR values.

Jaehoon Kim posted 1 patch 13 hours ago
Patches applied successfully (tree, apply log)
git fetch https://github.com/patchew-project/qemu tags/patchew/20260206164645.1845366-1-jhkim@linux.ibm.com
Maintainers: Matthew Rosato <mjrosato@linux.ibm.com>, Eric Farman <farman@linux.ibm.com>, Thomas Huth <thuth@redhat.com>, Richard Henderson <richard.henderson@linaro.org>, Ilya Leoshkevich <iii@linux.ibm.com>, David Hildenbrand <david@kernel.org>, Halil Pasic <pasic@linux.ibm.com>, Christian Borntraeger <borntraeger@linux.ibm.com>
hw/s390x/s390-pci-inst.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
[PATCH v1] s390x/pci: Fix endianness for zPCI BAR values.
Posted by Jaehoon Kim 13 hours ago
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
Re: [PATCH v1] s390x/pci: Fix endianness for zPCI BAR values.
Posted by Farhan Ali 12 hours ago
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>


Re: [PATCH v1] s390x/pci: Fix endianness for zPCI BAR values.
Posted by JAEHOON KIM 13 hours ago
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,
Re: [PATCH v1] s390x/pci: Fix endianness for zPCI BAR values.
Posted by Matthew Rosato 12 hours ago
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,
> 
>