Hi Frank, On Wed, 22 Oct 2014 17:11:59 +0200 Frank Blaschka <blasc...@linux.vnet.ibm.com> wrote:
> From: Frank Blaschka <frank.blasc...@de.ibm.com> > > This patch implements the s390 pci instructions in qemu. It allows > to access and drive pci devices attached to the s390 pci bus. > Because of platform constrains devices using IO BARs are not > supported. Also a device has to support MSI/MSI-X to run on s390. > > Signed-off-by: Frank Blaschka <frank.blasc...@de.ibm.com> > --- > target-s390x/Makefile.objs | 2 +- > target-s390x/kvm.c | 52 ++++ > target-s390x/pci_ic.c | 735 > +++++++++++++++++++++++++++++++++++++++++++++ > target-s390x/pci_ic.h | 316 +++++++++++++++++++ > 4 files changed, 1104 insertions(+), 1 deletion(-) > create mode 100644 target-s390x/pci_ic.c > create mode 100644 target-s390x/pci_ic.h ... > diff --git a/target-s390x/pci_ic.c b/target-s390x/pci_ic.c > new file mode 100644 > index 0000000..a496e6b > --- /dev/null > +++ b/target-s390x/pci_ic.c > @@ -0,0 +1,735 @@ > +/* > + * s390 PCI intercepts > + * > + * Copyright 2014 IBM Corp. > + * Author(s): Frank Blaschka <frank.blasc...@de.ibm.com> > + * Hong Bo Li <lih...@cn.ibm.com> > + * Yi Min Zhao <zyi...@cn.ibm.com> > + * > + * This work is licensed under the terms of the GNU GPL, version 2 or (at > + * your option) any later version. See the COPYING file in the top-level > + * directory. > + */ > + > +#include <sys/types.h> > +#include <sys/ioctl.h> > +#include <sys/mman.h> > + > +#include <linux/kvm.h> > +#include <asm/ptrace.h> > +#include <hw/pci/pci.h> > +#include <hw/pci/pci_host.h> > +#include <net/net.h> > + > +#include "qemu-common.h" > +#include "qemu/timer.h" > +#include "migration/qemu-file.h" > +#include "sysemu/sysemu.h" > +#include "sysemu/kvm.h" > +#include "cpu.h" > +#include "sysemu/device_tree.h" > +#include "monitor/monitor.h" > +#include "pci_ic.h" > + > +#include "hw/hw.h" > +#include "hw/pci/pci.h" > +#include "hw/pci/pci_bridge.h" > +#include "hw/pci/pci_bus.h" > +#include "hw/pci/pci_host.h" > +#include "hw/s390x/s390-pci-bus.h" > +#include "exec/exec-all.h" > +#include "exec/memory-internal.h" > + > +/* #define DEBUG_S390PCI_IC */ > +#ifdef DEBUG_S390PCI_IC > +#define DPRINTF(fmt, ...) \ > + do { fprintf(stderr, "s390pci_ic: " fmt, ## __VA_ARGS__); } while (0) > +#else > +#define DPRINTF(fmt, ...) \ > + do { } while (0) > +#endif > + > +static uint64_t resume_token; > + > +static uint8_t barsize(uint64_t size) > +{ > + uint64_t mask = 1; > + int i; > + > + if (!size) { > + return 0; > + } > + > + for (i = 0; i < 64; i++) { > + if (size & mask) { > + break; > + } > + mask = (mask << 1); > + } > + > + return i; > +} > + > +static void s390_set_status_code(CPUS390XState *env, > + uint8_t r, uint64_t status_code) > +{ > + env->regs[r] &= ~0xff000000; > + env->regs[r] |= (status_code & 0xff) << 24; > +} > + > +static int list_pci(ClpReqRspListPci *rrb, uint8_t *cc) > +{ > + S390PCIBusDevice *pbdev; > + uint32_t res_code, initial_l2, g_l2, finish; > + int rc, idx; > + > + rc = 0; > + if (be16_to_cpu(rrb->request.hdr.len) != 32) { > + res_code = CLP_RC_LEN; > + rc = -EINVAL; > + goto out; > + } > + > + if ((be32_to_cpu(rrb->request.fmt) & CLP_MASK_FMT) != 0) { > + res_code = CLP_RC_FMT; > + rc = -EINVAL; > + goto out; > + } > + > + if ((be32_to_cpu(rrb->request.fmt) & ~CLP_MASK_FMT) != 0 || > + rrb->request.reserved1 != 0 || > + rrb->request.reserved2 != 0) { > + res_code = CLP_RC_RESNOT0; > + rc = -EINVAL; > + goto out; > + } > + > + if (be64_to_cpu(rrb->request.resume_token) == 0) { > + resume_token = 0; > + } else if (be64_to_cpu(rrb->request.resume_token) != resume_token) { > + res_code = CLP_RC_LISTPCI_BADRT; > + rc = -EINVAL; > + goto out; > + } > + > + if (be16_to_cpu(rrb->response.hdr.len) < 48) { > + res_code = CLP_RC_8K; > + rc = -EINVAL; > + goto out; > + } > + > + initial_l2 = be16_to_cpu(rrb->response.hdr.len); > + if ((initial_l2 - LIST_PCI_HDR_LEN) % sizeof(ClpFhListEntry) > + != 0) { > + rc = -EINVAL; Don't you need to set res_code to a proper value here, too? > + goto out; > + } > + > + rrb->response.fmt = 0; > + rrb->response.reserved1 = rrb->response.reserved2 = 0; > + rrb->response.mdd = cpu_to_be32(FH_VIRT); > + rrb->response.max_fn = cpu_to_be16(PCI_MAX_FUNCTIONS); > + rrb->response.entry_size = sizeof(ClpFhListEntry); > + finish = 0; > + idx = resume_token; > + g_l2 = LIST_PCI_HDR_LEN; > + do { > + pbdev = s390_pci_find_dev_by_idx(idx); > + if (!pbdev) { > + finish = 1; > + break; > + } > + rrb->response.fh_list[idx - resume_token].device_id = > + pci_get_word(pbdev->pdev->config + PCI_DEVICE_ID); > + rrb->response.fh_list[idx - resume_token].vendor_id = > + pci_get_word(pbdev->pdev->config + PCI_VENDOR_ID); > + rrb->response.fh_list[idx - resume_token].config = > + cpu_to_be32(0x80000000); > + rrb->response.fh_list[idx - resume_token].fid = > cpu_to_be32(pbdev->fid); > + rrb->response.fh_list[idx - resume_token].fh = > cpu_to_be32(pbdev->fh); > + > + g_l2 += sizeof(ClpFhListEntry); > + DPRINTF("g_l2 %d vendor id 0x%x device id 0x%x fid 0x%x fh 0x%x\n", > + g_l2, > + rrb->response.fh_list[idx - resume_token].vendor_id, > + rrb->response.fh_list[idx - resume_token].device_id, > + rrb->response.fh_list[idx - resume_token].fid, > + rrb->response.fh_list[idx - resume_token].fh); > + idx++; > + } while (g_l2 < initial_l2); > + > + if (finish == 1) { > + resume_token = 0; > + } else { > + resume_token = idx; > + } > + rrb->response.resume_token = cpu_to_be64(resume_token); > + rrb->response.hdr.len = cpu_to_be16(g_l2); > + rrb->response.hdr.rsp = cpu_to_be16(CLP_RC_OK); > +out: > + if (rc) { > + DPRINTF("list pci failed rc 0x%x\n", rc); > + rrb->response.hdr.rsp = cpu_to_be16(res_code); > + *cc = 3; Not sure, but I think the condition code is only set to 3 when no response has been generated ... so I think you could simply remove the cc magic from this function? > + } > + return rc; > +} > + > +int kvm_clp_service_call(S390CPU *cpu, struct kvm_run *run) > +{ > + ClpReqHdr *reqh; > + ClpRspHdr *resh; > + S390PCIBusDevice *pbdev; > + uint32_t req_len; > + uint32_t res_len; > + uint8_t *buffer; > + uint8_t cc; > + CPUS390XState *env = &cpu->env; > + uint8_t r2 = (run->s390_sieic.ipb & 0x000f0000) >> 16; > + int rc = 0; > + int i; > + > + buffer = g_malloc0(4096 * 2); > + cpu_synchronize_state(CPU(cpu)); CLP is a priviledged function, too, so you should also check PSW_MASK_PSTATE here. > + cpu_physical_memory_rw(env->regs[r2], buffer, sizeof(reqh), 0); > + reqh = (ClpReqHdr *)buffer; > + req_len = be16_to_cpu(reqh->len); > + > + cpu_physical_memory_rw(env->regs[r2], buffer, req_len + sizeof(resh), 0); > + resh = (ClpRspHdr *)(buffer + req_len); > + res_len = be16_to_cpu(resh->len); > + > + cpu_physical_memory_rw(env->regs[r2], buffer, req_len + res_len, 0); Not sure, but do you really have to read the _response_ buffer here, too? I'd rather expect that the response area is only written to... Anyway, you should do some sanity checks on req_len (and res_len) first, or the guest could use this to create a buffer overflow in the host. > + switch (reqh->cmd) { > + case CLP_LIST_PCI: { > + ClpReqRspListPci *rrb = (ClpReqRspListPci *)buffer; > + rc = list_pci(rrb, &cc); > + break; > + } > + case CLP_SET_PCI_FN: { > + ClpReqSetPci *reqsetpci = (ClpReqSetPci *)reqh; > + ClpRspSetPci *ressetpci = (ClpRspSetPci *)resh; > + > + pbdev = s390_pci_find_dev_by_fh(be32_to_cpu(reqsetpci->fh)); > + if (!pbdev) { > + ressetpci->hdr.rsp = cpu_to_be16(CLP_RC_SETPCIFN_FH); > + goto out; > + } > + > + switch (reqsetpci->oc) { > + case CLP_SET_ENABLE_PCI_FN: > + if (pbdev->is_virt) { > + pbdev->fh = pbdev->fh | 1 << ENABLE_BIT_OFFSET; > + ressetpci->fh = cpu_to_be32(pbdev->fh); > + ressetpci->hdr.rsp = cpu_to_be16(CLP_RC_OK); > + } else { > + pbdev->fh = be32_to_cpu(ressetpci->fh); > + } > + break; > + case CLP_SET_DISABLE_PCI_FN: > + if (pbdev->is_virt) { > + pbdev->fh = pbdev->fh & ~(1 << ENABLE_BIT_OFFSET); > + ressetpci->fh = cpu_to_be32(pbdev->fh); > + ressetpci->hdr.rsp = cpu_to_be16(CLP_RC_OK); > + } else { > + pbdev->fh = be32_to_cpu(ressetpci->fh); > + } > + break; > + default: > + DPRINTF("unknown set pci command\n"); > + ressetpci->hdr.rsp = cpu_to_be16(CLP_RC_SETPCIFN_FHOP); > + break; > + } > + break; > + } > + case CLP_QUERY_PCI_FN: { > + ClpReqQueryPci *reqquery = (ClpReqQueryPci *)reqh; > + ClpRspQueryPci *resquery = (ClpRspQueryPci *)resh; > + > + pbdev = s390_pci_find_dev_by_fh(reqquery->fh); > + if (!pbdev) { > + DPRINTF("query pci no pci dev\n"); > + return -EIO; > + } > + > + for (i = 0; i < PCI_BAR_COUNT; i++) { > + uint64_t data = pci_host_config_read_common(pbdev->pdev, > + 0x10 + (i * 4), pci_config_size(pbdev->pdev), 4); > + > + resquery->bar[i] = bswap32(data); > + resquery->bar_size[i] = barsize(pbdev->pdev->io_regions[i].size); > + DPRINTF("bar %d addr 0x%x size 0x%lx barsize 0x%x\n", i, > + resquery->bar[i], pbdev->pdev->io_regions[i].size, > + resquery->bar_size[i]); > + } > + > + /* may optain from host dev (e.g. via new sysfs attr */ > + resquery->sdma = 0x100000000; > + resquery->edma = 0x1ffffffffffffff; > + resquery->pchid = 0; > + resquery->ug = 1; > + > + resquery->hdr.rsp = CLP_RC_OK; > + break; > + } > + case CLP_QUERY_PCI_FNGRP: { > + ClpRspQueryPciGrp *resgrp = (ClpRspQueryPciGrp *)resh; > + /* find function group for now we go with fixed values */ > + resgrp->fr = 1; > + resgrp->dasm = 0; > + resgrp->msia = ZPCI_MSI_ADDR; > + resgrp->mui = 0; > + resgrp->i = 128; > + resgrp->version = 0; > + > + resgrp->hdr.rsp = CLP_RC_OK; > + break; > + } > + default: > + DPRINTF("unknown clp command\n"); > + resh->rsp = cpu_to_be16(CLP_RC_CMD); > + break; > + } > + > +out: > + cpu_physical_memory_rw(env->regs[r2], buffer, req_len + res_len, 1); > + g_free(buffer); > + setcc(cpu, 0); Here you are ignoring the "cc" variable that gets set by list_pci() anyway ... So either the cc variable can be removed from this function here, too, or you should use it here instead of the 0 ? > + return rc; Also if list_pci() returned an error code and you pass this negative value up to the caller, the handle_instruction() in kvm.c will finally inject an operation program exception. I doubt that's want you want, so this function here should likely always return zero instead? > +} > + > +int kvm_pcilg_service_call(S390CPU *cpu, struct kvm_run *run) > +{ > + CPUS390XState *env = &cpu->env; > + S390PCIBusDevice *pbdev; > + uint8_t r1 = (run->s390_sieic.ipb & 0x00f00000) >> 20; > + uint8_t r2 = (run->s390_sieic.ipb & 0x000f0000) >> 16; > + PciLgStg *rp; > + uint64_t offset; > + uint64_t data; > + > + cpu_synchronize_state(CPU(cpu)); > + > + if (env->psw.mask & PSW_MASK_PSTATE) { > + program_interrupt(env, PGM_PRIVILEGED, 4); > + return 0; > + } > + > + if (r2 & 0x1) { > + program_interrupt(env, PGM_SPECIFICATION, 4); > + return 0; > + } > + > + rp = (PciLgStg *)&env->regs[r2]; > + offset = env->regs[r2 + 1]; > + > + pbdev = s390_pci_find_dev_by_fh(rp->fh); > + if (!pbdev) { > + DPRINTF("pcilg no pci dev\n"); > + setcc(cpu, 3); > + return 0; > + } > + > + if (rp->pcias < 6) { > + if ((8 - (offset & 0x7)) < (rp->len & 0xF)) { > + program_interrupt(env, PGM_OPERAND, 4); > + return 0; > + } > + MemoryRegion *mr = pbdev->pdev->io_regions[rp->pcias].memory; > + io_mem_read(mr, offset, &data, rp->len); > + } else if (rp->pcias == 15) { > + if ((4 - (offset & 0x3)) < (rp->len & 0xF)) { > + program_interrupt(env, PGM_OPERAND, 4); > + return 0; > + } > + data = pci_host_config_read_common( > + pbdev->pdev, offset, pci_config_size(pbdev->pdev), > rp->len); > + > + switch (rp->len) { The rp->len field is only in the lowest 4 bits of the register, but you've only masked it when checking for the PGM_OPERAND condition... May I suggest that you introduce a new local variable a la: len = rp->len & 0x0f; and use that everywhere instead of rp->len ? > + case 1: > + break; > + case 2: > + data = cpu_to_le16(data); > + break; > + case 4: > + data = cpu_to_le32(data); > + break; > + case 8: > + data = cpu_to_le64(data); > + break; > + default: > + program_interrupt(env, PGM_OPERAND, 4); > + return 0; > + } > + } else { > + DPRINTF("invalid space\n"); > + setcc(cpu, 1); > + s390_set_status_code(env, r2, 20ul); Maybe you could introduce a proper #define for that "20" status code here and everywhere else? Also I think you can omit the "ul" suffix. > + return 0; > + } > + > + env->regs[r1] = data; > + setcc(cpu, 0); > + return 0; > +} > + > +static void update_msix_table_msg_data(S390PCIBusDevice *pbdev, uint64_t > offset, > + uint64_t *data, uint8_t len) > +{ > + uint32_t msg_data; > + > + if (offset % PCI_MSIX_ENTRY_SIZE != 8) { > + return; > + } > + > + if (len != 4) { > + DPRINTF("access msix table msg data but len is %d\n", len); > + return; > + } > + > + msg_data = (pbdev->fid << ZPCI_MSI_VEC_BITS) | le32_to_cpu(*data); > + *data = cpu_to_le32(msg_data); > + DPRINTF("update msix msg_data to 0x%x\n", msg_data); > +} > + > +static int trap_msix(S390PCIBusDevice *pbdev, uint64_t offset, uint8_t pcias) > +{ > + if (pbdev->msix.available && pbdev->msix.table_bar == pcias && > + offset >= pbdev->msix.table_offset && > + offset <= pbdev->msix.table_offset + > + (pbdev->msix.entries - 1) * PCI_MSIX_ENTRY_SIZE) { > + return 1; > + } else { > + return 0; > + } > +} > + > +int kvm_pcistg_service_call(S390CPU *cpu, struct kvm_run *run) > +{ > + CPUS390XState *env = &cpu->env; > + uint8_t r1 = (run->s390_sieic.ipb & 0x00f00000) >> 20; > + uint8_t r2 = (run->s390_sieic.ipb & 0x000f0000) >> 16; > + PciLgStg *rp; > + uint64_t offset, data; > + S390PCIBusDevice *pbdev; > + > + cpu_synchronize_state(CPU(cpu)); > + > + if (env->psw.mask & PSW_MASK_PSTATE) { > + program_interrupt(env, PGM_PRIVILEGED, 4); > + return 0; > + } > + > + if (r2 & 0x1) { > + program_interrupt(env, PGM_SPECIFICATION, 4); > + return 0; > + } > + > + rp = (PciLgStg *)&env->regs[r2]; > + offset = env->regs[r2 + 1]; > + > + pbdev = s390_pci_find_dev_by_fh(rp->fh); > + if (!pbdev) { > + DPRINTF("pcistg no pci dev\n"); > + setcc(cpu, 3); > + return 0; > + } > + > + data = env->regs[r1]; > + > + if (rp->pcias < 6) { > + if ((8 - (offset & 0x7)) < (rp->len & 0xF)) { > + program_interrupt(env, PGM_OPERAND, 4); > + return 0; > + } > + MemoryRegion *mr; > + if (trap_msix(pbdev, offset, rp->pcias)) { > + offset = offset - pbdev->msix.table_offset; > + mr = &pbdev->pdev->msix_table_mmio; > + update_msix_table_msg_data(pbdev, offset, &data, rp->len); > + } else { > + mr = pbdev->pdev->io_regions[rp->pcias].memory; > + } > + > + io_mem_write(mr, offset, data, rp->len); > + } else if (rp->pcias == 15) { > + if ((4 - (offset & 0x3)) < (rp->len & 0xF)) { > + program_interrupt(env, PGM_OPERAND, 4); > + return 0; > + } > + switch (rp->len) { The comment from pcilg also applies here -- rp->len is 4-bits only again. > + case 1: > + break; > + case 2: > + data = le16_to_cpu(data); > + break; > + case 4: > + data = le32_to_cpu(data); > + break; > + case 8: > + data = le64_to_cpu(data); > + break; > + default: > + program_interrupt(env, PGM_OPERAND, 4); > + return 0; > + } > + > + pci_host_config_write_common(pbdev->pdev, offset, > + pci_config_size(pbdev->pdev), > + data, rp->len); > + } else { > + DPRINTF("pcistg invalid space\n"); > + setcc(cpu, 1); > + s390_set_status_code(env, r2, 20ul); > + return 0; > + } > + > + setcc(cpu, 0); > + return 0; > +} > + > +int kvm_rpcit_service_call(S390CPU *cpu, struct kvm_run *run) > +{ > + CPUS390XState *env = &cpu->env; > + uint8_t r1 = (run->s390_sieic.ipb & 0x00f00000) >> 20; > + uint8_t r2 = (run->s390_sieic.ipb & 0x000f0000) >> 16; > + uint32_t fh; > + uint64_t pte; > + S390PCIBusDevice *pbdev; > + ram_addr_t size; > + int flags; > + IOMMUTLBEntry entry; > + > + cpu_synchronize_state(CPU(cpu)); > + > + if (env->psw.mask & PSW_MASK_PSTATE) { > + program_interrupt(env, PGM_PRIVILEGED, 4); > + return 0; > + } > + > + if (r2 & 0x1) { > + program_interrupt(env, PGM_SPECIFICATION, 4); > + return 0; > + } > + > + fh = env->regs[r1] >> 32; > + size = env->regs[r2 + 1]; > + > + pbdev = s390_pci_find_dev_by_fh(fh); > + > + if (!pbdev) { > + DPRINTF("rpcit no pci dev\n"); > + setcc(cpu, 3); > + return 0; > + } > + > + pte = s390_guest_io_table_walk(pbdev->g_iota, env->regs[r2]); > + flags = pte & ZPCI_PTE_FLAG_MASK; > + entry.target_as = &address_space_memory; > + entry.iova = env->regs[r2]; > + entry.translated_addr = pte & ZPCI_PTE_ADDR_MASK; > + entry.addr_mask = size - 1; > + > + if (flags & ZPCI_PTE_INVALID) { > + entry.perm = IOMMU_NONE; > + } else { > + entry.perm = IOMMU_RW; > + } > + > + memory_region_notify_iommu(pci_device_iommu_address_space( > + pbdev->pdev)->root, entry); > + > + setcc(cpu, 0); > + return 0; > +} > + > +int kvm_sic_service_call(S390CPU *cpu, struct kvm_run *run) > +{ > + DPRINTF("sic\n"); Is it ok to simply ignore this call? Or would it be better to inject a program check instead, so that this does not go unnoticed by the guest? > + return 0; > +} > + > +int kvm_pcistb_service_call(S390CPU *cpu, struct kvm_run *run) > +{ > + CPUS390XState *env = &cpu->env; > + uint8_t r1 = (run->s390_sieic.ipa & 0x00f0) >> 4; > + uint8_t r3 = run->s390_sieic.ipa & 0x000f; > + PciStb *rp; > + uint64_t gaddr; > + uint64_t *uaddr, *pu; > + hwaddr len; > + S390PCIBusDevice *pbdev; > + MemoryRegion *mr; > + int i; > + > + cpu_synchronize_state(CPU(cpu)); > + > + if (env->psw.mask & PSW_MASK_PSTATE) { > + program_interrupt(env, PGM_PRIVILEGED, 6); > + return 0; > + } > + > + rp = (PciStb *)&env->regs[r1]; > + if (rp->pcias > 5) { > + DPRINTF("pcistb invalid space\n"); > + s390_set_status_code(env, r1, 20ul); > + return 0; > + } > + > + switch (rp->len) { > + case 16: > + case 32: > + case 64: > + case 128: > + break; > + default: > + program_interrupt(env, PGM_SPECIFICATION, 6); > + return 0; > + } > + > + gaddr = get_base_disp_rsy(cpu, run); > + len = rp->len; > + > + pbdev = s390_pci_find_dev_by_fh(rp->fh); > + if (!pbdev) { > + DPRINTF("pcistb no pci dev fh 0x%x\n", rp->fh); > + setcc(cpu, 3); > + return 0; > + } > + > + uaddr = cpu_physical_memory_map(gaddr, &len, 0); > + mr = pbdev->pdev->io_regions[rp->pcias].memory; > + if (!memory_region_access_valid(mr, env->regs[r3], rp->len, true)) { > + cpu_physical_memory_unmap(uaddr, len, 0, len); > + program_interrupt(env, PGM_OPERAND, 6); PGM_ADDRESSING might be the better choice in this case. > + return 0; > + } > + > + pu = uaddr; > + for (i = 0; i < rp->len / 8; i++) { > + io_mem_write(mr, env->regs[r3] + i * 8, *pu, 8); > + pu++; > + } > + > + cpu_physical_memory_unmap(uaddr, len, 0, len); > + setcc(cpu, 0); > + return 0; > +} ... > +int kvm_mpcifc_service_call(S390CPU *cpu, struct kvm_run *run) > +{ > + CPUS390XState *env = &cpu->env; > + uint8_t r1 = (run->s390_sieic.ipa & 0x00f0) >> 4; > + uint8_t oc; > + uint32_t fh; > + uint64_t fiba; > + ZpciFib fib; > + S390PCIBusDevice *pbdev; > + > + cpu_synchronize_state(CPU(cpu)); > + > + if (env->psw.mask & PSW_MASK_PSTATE) { > + program_interrupt(env, PGM_PRIVILEGED, 6); > + return 0; > + } > + > + oc = env->regs[r1] & 0xff; > + fh = env->regs[r1] >> 32; > + fiba = get_base_disp_rxy(cpu, run); > + > + if (fiba & 0x7) { > + program_interrupt(env, PGM_SPECIFICATION, 6); > + return 0; > + } > + > + pbdev = s390_pci_find_dev_by_fh(fh); > + if (!pbdev) { > + DPRINTF("mpcifc no pci dev fh 0x%x\n", fh); > + setcc(cpu, 3); > + return 0; > + } > + > + cpu_physical_memory_rw(fiba, (uint8_t *)&fib, sizeof(fib), 0); > + > + switch (oc) { > + case ZPCI_MOD_FC_REG_INT: { > + pbdev->isc = FIB_DATA_ISC(fib.data); > + reg_irqs(env, pbdev, fib); > + break; > + } > + case ZPCI_MOD_FC_DEREG_INT: > + dereg_irqs(pbdev); > + break; > + case ZPCI_MOD_FC_REG_IOAT: > + if (fib.pba > fib.pal) { > + program_interrupt(&cpu->env, PGM_OPERAND, 6); > + return 0; > + } > + pbdev->g_iota = fib.iota & ~ZPCI_IOTA_RTTO_FLAG; > + break; > + case ZPCI_MOD_FC_DEREG_IOAT: > + break; > + case ZPCI_MOD_FC_REREG_IOAT: > + break; > + case ZPCI_MOD_FC_RESET_ERROR: > + break; > + case ZPCI_MOD_FC_RESET_BLOCK: > + break; > + case ZPCI_MOD_FC_SET_MEASURE: > + break; > + default: > + program_interrupt(&cpu->env, PGM_OPERAND, 6); > + return 0; > + } > + > + setcc(cpu, 0); > + return 0; > +} > + > +int kvm_stpcifc_service_call(S390CPU *cpu, struct kvm_run *run) > +{ > + DPRINTF("stpcifc\n"); As with SIC, I wonder whether it might be better to inject an exception by default? > + return 0; > +} Thomas