| /* | 
 |  * s390x PCI MMIO definitions | 
 |  * | 
 |  * Copyright 2025 IBM Corp. | 
 |  * Author(s): Farhan Ali <alifm@linux.ibm.com> | 
 |  * | 
 |  * SPDX-License-Identifier: GPL-2.0-or-later | 
 |  */ | 
 |  | 
 | #include "qemu/osdep.h" | 
 | #include <sys/syscall.h> | 
 | #include "qemu/s390x_pci_mmio.h" | 
 | #include "elf.h" | 
 |  | 
 | union register_pair { | 
 |     unsigned __int128 pair; | 
 |     struct { | 
 |         uint64_t even; | 
 |         uint64_t odd; | 
 |     }; | 
 | }; | 
 |  | 
 | static bool is_mio_supported; | 
 |  | 
 | static __attribute__((constructor)) void check_is_mio_supported(void) | 
 | { | 
 |     is_mio_supported = !!(qemu_getauxval(AT_HWCAP) & HWCAP_S390_PCI_MIO); | 
 | } | 
 |  | 
 | static uint64_t s390x_pcilgi(const void *ioaddr, size_t len) | 
 | { | 
 |     union register_pair ioaddr_len = { .even = (uint64_t)ioaddr, | 
 |                                        .odd = len }; | 
 |     uint64_t val; | 
 |     int cc; | 
 |  | 
 |     asm volatile( | 
 |         /* pcilgi */ | 
 |         ".insn   rre,0xb9d60000,%[val],%[ioaddr_len]\n" | 
 |         "ipm     %[cc]\n" | 
 |         "srl     %[cc],28\n" | 
 |         : [cc] "=d"(cc), [val] "=d"(val), | 
 |         [ioaddr_len] "+d"(ioaddr_len.pair) :: "cc"); | 
 |  | 
 |     if (cc) { | 
 |         val = -1ULL; | 
 |     } | 
 |  | 
 |     return val; | 
 | } | 
 |  | 
 | static void s390x_pcistgi(void *ioaddr, uint64_t val, size_t len) | 
 | { | 
 |     union register_pair ioaddr_len = {.even = (uint64_t)ioaddr, .odd = len}; | 
 |  | 
 |     asm volatile ( | 
 |         /* pcistgi */ | 
 |         ".insn   rre,0xb9d40000,%[val],%[ioaddr_len]\n" | 
 |         : [ioaddr_len] "+d" (ioaddr_len.pair) | 
 |         : [val] "d" (val) | 
 |         : "cc", "memory"); | 
 | } | 
 |  | 
 | uint8_t s390x_pci_mmio_read_8(const void *ioaddr) | 
 | { | 
 |     uint8_t val = 0; | 
 |  | 
 |     if (is_mio_supported) { | 
 |         val = s390x_pcilgi(ioaddr, sizeof(val)); | 
 |     } else { | 
 |         syscall(__NR_s390_pci_mmio_read, ioaddr, &val, sizeof(val)); | 
 |     } | 
 |     return val; | 
 | } | 
 |  | 
 | uint16_t s390x_pci_mmio_read_16(const void *ioaddr) | 
 | { | 
 |     uint16_t val = 0; | 
 |  | 
 |     if (is_mio_supported) { | 
 |         val = s390x_pcilgi(ioaddr, sizeof(val)); | 
 |     } else { | 
 |         syscall(__NR_s390_pci_mmio_read, ioaddr, &val, sizeof(val)); | 
 |     } | 
 |     return val; | 
 | } | 
 |  | 
 | uint32_t s390x_pci_mmio_read_32(const void *ioaddr) | 
 | { | 
 |     uint32_t val = 0; | 
 |  | 
 |     if (is_mio_supported) { | 
 |         val = s390x_pcilgi(ioaddr, sizeof(val)); | 
 |     } else { | 
 |         syscall(__NR_s390_pci_mmio_read, ioaddr, &val, sizeof(val)); | 
 |     } | 
 |     return val; | 
 | } | 
 |  | 
 | uint64_t s390x_pci_mmio_read_64(const void *ioaddr) | 
 | { | 
 |     uint64_t val = 0; | 
 |  | 
 |     if (is_mio_supported) { | 
 |         val = s390x_pcilgi(ioaddr, sizeof(val)); | 
 |     } else { | 
 |         syscall(__NR_s390_pci_mmio_read, ioaddr, &val, sizeof(val)); | 
 |     } | 
 |     return val; | 
 | } | 
 |  | 
 | void s390x_pci_mmio_write_8(void *ioaddr, uint8_t val) | 
 | { | 
 |     if (is_mio_supported) { | 
 |         s390x_pcistgi(ioaddr, val, sizeof(val)); | 
 |     } else { | 
 |         syscall(__NR_s390_pci_mmio_write, ioaddr, &val, sizeof(val)); | 
 |     } | 
 | } | 
 |  | 
 | void s390x_pci_mmio_write_16(void *ioaddr, uint16_t val) | 
 | { | 
 |     if (is_mio_supported) { | 
 |         s390x_pcistgi(ioaddr, val, sizeof(val)); | 
 |     } else { | 
 |         syscall(__NR_s390_pci_mmio_write, ioaddr, &val, sizeof(val)); | 
 |     } | 
 | } | 
 |  | 
 | void s390x_pci_mmio_write_32(void *ioaddr, uint32_t val) | 
 | { | 
 |     if (is_mio_supported) { | 
 |         s390x_pcistgi(ioaddr, val, sizeof(val)); | 
 |     } else { | 
 |         syscall(__NR_s390_pci_mmio_write, ioaddr, &val, sizeof(val)); | 
 |     } | 
 | } | 
 |  | 
 | void s390x_pci_mmio_write_64(void *ioaddr, uint64_t val) | 
 | { | 
 |     if (is_mio_supported) { | 
 |         s390x_pcistgi(ioaddr, val, sizeof(val)); | 
 |     } else { | 
 |         syscall(__NR_s390_pci_mmio_write, ioaddr, &val, sizeof(val)); | 
 |     } | 
 | } |