qemu-devel
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: [Qemu-devel] [PATCH 2/3 RFC] s390: implement pci instructions


From: Thomas Huth
Subject: Re: [Qemu-devel] [PATCH 2/3 RFC] s390: implement pci instructions
Date: Thu, 23 Oct 2014 15:12:48 +0200

 Hi Frank,

On Wed, 22 Oct 2014 17:11:59 +0200
Frank Blaschka <address@hidden> wrote:

> From: Frank Blaschka <address@hidden>
> 
> 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 <address@hidden>
> ---
>  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 <address@hidden>
> + *            Hong Bo Li <address@hidden>
> + *            Yi Min Zhao <address@hidden>
> + *
> + * 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




reply via email to

[Prev in Thread] Current Thread [Next in Thread]