Commit a8d2b068 authored by Peter Maydell's avatar Peter Maydell
Browse files

Merge remote-tracking branch 'remotes/cohuck/tags/s390x-20190118' into staging



s390x updates:
- clang compilation fixes
- fixes in zpci hotplug code
- handle unimplemented diag 308 subcodes correctly
- add common fmb in zpci

# gpg: Signature made Fri 18 Jan 2019 12:13:26 GMT
# gpg:                using RSA key DECF6B93C6F02FAF
# gpg: Good signature from "Cornelia Huck <conny@cornelia-huck.de>"
# gpg:                 aka "Cornelia Huck <huckc@linux.vnet.ibm.com>"
# gpg:                 aka "Cornelia Huck <cornelia.huck@de.ibm.com>"
# gpg:                 aka "Cornelia Huck <cohuck@kernel.org>"
# gpg:                 aka "Cornelia Huck <cohuck@redhat.com>"
# Primary key fingerprint: C3D0 D66D C362 4FF6 A8C0  18CE DECF 6B93 C6F0 2FAF

* remotes/cohuck/tags/s390x-20190118:
  s390x/pci: add common function measurement block
  s390x/pci: Ignore the unplug call if we already have a release_timer
  s390x/pci: Always delete and free the release_timer
  s390x/pci: Move some hotplug checks to the pre_plug handler
  s390x/pci: Use hotplug_dev instead of looking up the host bridge
  s390x/pci: Set the iommu region size mpcifc request
  s390x/pci: Send correct event on hotplug
  configure: Only build the s390-ccw bios if the compiler supports -march=z900
  s390x: Return specification exception for unimplemented diag 308 subcodes
  pc-bios/s390-ccw: Use proper register names for Clang
  s390: avoid potential null dereference in s390_pcihost_unplug()

Signed-off-by: default avatarPeter Maydell <peter.maydell@linaro.org>
parents 9bd641b1 6e92c70c
Loading
Loading
Loading
Loading
+5 −1
Original line number Diff line number Diff line
@@ -5892,9 +5892,13 @@ if test "$cpu" = "ppc64" -a "$targetos" != "Darwin" ; then
  roms="$roms spapr-rtas"
fi

# Only build s390-ccw bios if we're on s390x and the compiler has -march=z900
if test "$cpu" = "s390x" ; then
  write_c_skeleton
  if compile_prog "-march=z900" ""; then
    roms="$roms s390-ccw"
  fi
fi

# Probe for the need for relocating the user-only binary.
if ( [ "$linux_user" = yes ] || [ "$bsd_user" = yes ] ) && [ "$pie" = no ]; then
+40 −22
Original line number Diff line number Diff line
@@ -660,7 +660,7 @@ void s390_pci_iommu_enable(S390PCIIOMMU *iommu)
    char *name = g_strdup_printf("iommu-s390-%04x", iommu->pbdev->uid);
    memory_region_init_iommu(&iommu->iommu_mr, sizeof(iommu->iommu_mr),
                             TYPE_S390_IOMMU_MEMORY_REGION, OBJECT(&iommu->mr),
                             name, iommu->pal + 1);
                             name, iommu->pal - iommu->pba + 1);
    iommu->enabled = true;
    memory_region_add_subregion(&iommu->mr, 0, MEMORY_REGION(&iommu->iommu_mr));
    g_free(name);
@@ -818,28 +818,43 @@ static bool s390_pci_alloc_idx(S390pciState *s, S390PCIBusDevice *pbdev)
    }

    pbdev->idx = idx;
    s->next_idx = (idx + 1) & FH_MASK_INDEX;

    return true;
}

static void s390_pcihost_pre_plug(HotplugHandler *hotplug_dev, DeviceState *dev,
                                   Error **errp)
{
    S390pciState *s = S390_PCI_HOST_BRIDGE(hotplug_dev);

    if (object_dynamic_cast(OBJECT(dev), TYPE_PCI_DEVICE)) {
        PCIDevice *pdev = PCI_DEVICE(dev);

        if (pdev->cap_present & QEMU_PCI_CAP_MULTIFUNCTION) {
            error_setg(errp, "multifunction not supported in s390");
            return;
        }
    } else if (object_dynamic_cast(OBJECT(dev), TYPE_S390_PCI_DEVICE)) {
        S390PCIBusDevice *pbdev = S390_PCI_DEVICE(dev);

        if (!s390_pci_alloc_idx(s, pbdev)) {
            error_setg(errp, "no slot for plugging zpci device");
            return;
        }
    }
}

static void s390_pcihost_plug(HotplugHandler *hotplug_dev, DeviceState *dev,
                              Error **errp)
{
    S390pciState *s = S390_PCI_HOST_BRIDGE(hotplug_dev);
    PCIDevice *pdev = NULL;
    S390PCIBusDevice *pbdev = NULL;
    S390pciState *s = s390_get_phb();

    if (object_dynamic_cast(OBJECT(dev), TYPE_PCI_BRIDGE)) {
        BusState *bus;
        PCIBridge *pb = PCI_BRIDGE(dev);
        PCIDevice *pdev = PCI_DEVICE(dev);

        if (pdev->cap_present & QEMU_PCI_CAP_MULTIFUNCTION) {
            error_setg(errp, "multifunction not supported in s390");
            return;
        }

        pci_bridge_map_irq(pb, dev->id, s390_pci_map_irq);
        pci_setup_iommu(&pb->sec_bus, s390_pci_dma_iommu, s);

@@ -859,11 +874,6 @@ static void s390_pcihost_plug(HotplugHandler *hotplug_dev, DeviceState *dev,
    } else if (object_dynamic_cast(OBJECT(dev), TYPE_PCI_DEVICE)) {
        pdev = PCI_DEVICE(dev);

        if (pdev->cap_present & QEMU_PCI_CAP_MULTIFUNCTION) {
            error_setg(errp, "multifunction not supported in s390");
            return;
        }

        if (!dev->id) {
            /* In the case the PCI device does not define an id */
            /* we generate one based on the PCI address         */
@@ -899,19 +909,19 @@ static void s390_pcihost_plug(HotplugHandler *hotplug_dev, DeviceState *dev,
        }

        if (dev->hotplugged) {
            s390_pci_generate_plug_event(HP_EVENT_RESERVED_TO_STANDBY,
            s390_pci_generate_plug_event(HP_EVENT_TO_CONFIGURED ,
                                         pbdev->fh, pbdev->fid);
        }
    } else if (object_dynamic_cast(OBJECT(dev), TYPE_S390_PCI_DEVICE)) {
        pbdev = S390_PCI_DEVICE(dev);

        if (!s390_pci_alloc_idx(s, pbdev)) {
            error_setg(errp, "no slot for plugging zpci device");
            return;
        }
        /* the allocated idx is actually getting used */
        s->next_idx = (pbdev->idx + 1) & FH_MASK_INDEX;
        pbdev->fh = pbdev->idx;
        QTAILQ_INSERT_TAIL(&s->zpci_devs, pbdev, link);
        g_hash_table_insert(s->zpci_table, &pbdev->idx, pbdev);
    } else {
        g_assert_not_reached();
    }
}

@@ -935,11 +945,11 @@ static void s390_pcihost_timer_cb(void *opaque)
static void s390_pcihost_unplug(HotplugHandler *hotplug_dev, DeviceState *dev,
                                Error **errp)
{
    S390pciState *s = S390_PCI_HOST_BRIDGE(hotplug_dev);
    PCIDevice *pci_dev = NULL;
    PCIBus *bus;
    int32_t devfn;
    S390PCIBusDevice *pbdev = NULL;
    S390pciState *s = s390_get_phb();

    if (object_dynamic_cast(OBJECT(dev), TYPE_PCI_BRIDGE)) {
        error_setg(errp, "PCI bridge hot unplug currently not supported");
@@ -956,6 +966,8 @@ static void s390_pcihost_unplug(HotplugHandler *hotplug_dev, DeviceState *dev,
    } else if (object_dynamic_cast(OBJECT(dev), TYPE_S390_PCI_DEVICE)) {
        pbdev = S390_PCI_DEVICE(dev);
        pci_dev = pbdev->pdev;
    } else {
        g_assert_not_reached();
    }

    switch (pbdev->state) {
@@ -964,6 +976,9 @@ static void s390_pcihost_unplug(HotplugHandler *hotplug_dev, DeviceState *dev,
    case ZPCI_FS_STANDBY:
        break;
    default:
        if (pbdev->release_timer) {
            return;
        }
        s390_pci_generate_plug_event(HP_EVENT_DECONFIGURE_REQUEST,
                                     pbdev->fh, pbdev->fid);
        pbdev->release_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL,
@@ -974,7 +989,7 @@ static void s390_pcihost_unplug(HotplugHandler *hotplug_dev, DeviceState *dev,
        return;
    }

    if (pbdev->release_timer && timer_pending(pbdev->release_timer)) {
    if (pbdev->release_timer) {
        timer_del(pbdev->release_timer);
        timer_free(pbdev->release_timer);
        pbdev->release_timer = NULL;
@@ -985,6 +1000,7 @@ static void s390_pcihost_unplug(HotplugHandler *hotplug_dev, DeviceState *dev,
    bus = pci_get_bus(pci_dev);
    devfn = pci_dev->devfn;
    object_unparent(OBJECT(pci_dev));
    fmb_timer_free(pbdev);
    s390_pci_msix_free(pbdev);
    s390_pci_iommu_free(s, bus, devfn);
    pbdev->pdev = NULL;
@@ -1041,6 +1057,7 @@ static void s390_pcihost_class_init(ObjectClass *klass, void *data)

    dc->reset = s390_pcihost_reset;
    dc->realize = s390_pcihost_realize;
    hc->pre_plug = s390_pcihost_pre_plug;
    hc->plug = s390_pcihost_plug;
    hc->unplug = s390_pcihost_unplug;
    msi_nonbroken = true;
@@ -1132,6 +1149,7 @@ static void s390_pci_device_realize(DeviceState *dev, Error **errp)
    }

    zpci->state = ZPCI_FS_RESERVED;
    zpci->fmb.format = ZPCI_FMB_FORMAT;
}

static void s390_pci_device_reset(DeviceState *dev)
@@ -1156,7 +1174,7 @@ static void s390_pci_device_reset(DeviceState *dev)
        pci_dereg_ioat(pbdev->iommu);
    }

    pbdev->fmb_addr = 0;
    fmb_timer_free(pbdev);
}

static void s390_pci_get_fid(Object *obj, Visitor *v, const char *name,
+29 −0
Original line number Diff line number Diff line
@@ -285,6 +285,33 @@ typedef struct S390PCIIOMMUTable {
    S390PCIIOMMU *iommu[PCI_SLOT_MAX];
} S390PCIIOMMUTable;

/* Function Measurement Block */
#define DEFAULT_MUI 4000
#define UPDATE_U_BIT 0x1ULL
#define FMBK_MASK 0xfULL

typedef struct ZpciFmbFmt0 {
    uint64_t dma_rbytes;
    uint64_t dma_wbytes;
} ZpciFmbFmt0;

#define ZPCI_FMB_CNT_LD    0
#define ZPCI_FMB_CNT_ST    1
#define ZPCI_FMB_CNT_STB   2
#define ZPCI_FMB_CNT_RPCIT 3
#define ZPCI_FMB_CNT_MAX   4

#define ZPCI_FMB_FORMAT    0

typedef struct ZpciFmb {
    uint32_t format;
    uint32_t sample;
    uint64_t last_update;
    uint64_t counter[ZPCI_FMB_CNT_MAX];
    ZpciFmbFmt0 fmt0;
} ZpciFmb;
QEMU_BUILD_BUG_MSG(offsetof(ZpciFmb, fmt0) != 48, "padding in ZpciFmb");

struct S390PCIBusDevice {
    DeviceState qdev;
    PCIDevice *pdev;
@@ -296,6 +323,8 @@ struct S390PCIBusDevice {
    uint32_t fid;
    bool fid_defined;
    uint64_t fmb_addr;
    ZpciFmb fmb;
    QEMUTimer *fmb_timer;
    uint8_t isc;
    uint16_t noi;
    uint16_t maxstbl;
+130 −3
Original line number Diff line number Diff line
@@ -19,6 +19,7 @@
#include "exec/memory-internal.h"
#include "qemu/error-report.h"
#include "sysemu/hw_accel.h"
#include "hw/s390x/tod.h"

#ifndef DEBUG_S390PCI_INST
#define DEBUG_S390PCI_INST  0
@@ -293,7 +294,7 @@ int clp_service_call(S390CPU *cpu, uint8_t r2, uintptr_t ra)
        resgrp->fr = 1;
        stq_p(&resgrp->dasm, 0);
        stq_p(&resgrp->msia, ZPCI_MSI_ADDR);
        stw_p(&resgrp->mui, 0);
        stw_p(&resgrp->mui, DEFAULT_MUI);
        stw_p(&resgrp->i, 128);
        stw_p(&resgrp->maxstbl, 128);
        resgrp->version = 0;
@@ -456,6 +457,8 @@ int pcilg_service_call(S390CPU *cpu, uint8_t r1, uint8_t r2, uintptr_t ra)
        return 0;
    }

    pbdev->fmb.counter[ZPCI_FMB_CNT_LD]++;

    env->regs[r1] = data;
    setcc(cpu, ZPCI_PCI_LS_OK);
    return 0;
@@ -561,6 +564,8 @@ int pcistg_service_call(S390CPU *cpu, uint8_t r1, uint8_t r2, uintptr_t ra)
        return 0;
    }

    pbdev->fmb.counter[ZPCI_FMB_CNT_ST]++;

    setcc(cpu, ZPCI_PCI_LS_OK);
    return 0;
}
@@ -681,6 +686,7 @@ err:
        s390_set_status_code(env, r1, ZPCI_PCI_ST_FUNC_IN_ERR);
        s390_pci_generate_error_event(error, pbdev->fh, pbdev->fid, start, 0);
    } else {
        pbdev->fmb.counter[ZPCI_FMB_CNT_RPCIT]++;
        setcc(cpu, ZPCI_PCI_LS_OK);
    }
    return 0;
@@ -783,6 +789,8 @@ int pcistb_service_call(S390CPU *cpu, uint8_t r1, uint8_t r3, uint64_t gaddr,
        }
    }

    pbdev->fmb.counter[ZPCI_FMB_CNT_STB]++;

    setcc(cpu, ZPCI_PCI_LS_OK);
    return 0;

@@ -889,6 +897,99 @@ void pci_dereg_ioat(S390PCIIOMMU *iommu)
    iommu->g_iota = 0;
}

void fmb_timer_free(S390PCIBusDevice *pbdev)
{
    if (pbdev->fmb_timer) {
        timer_del(pbdev->fmb_timer);
        timer_free(pbdev->fmb_timer);
        pbdev->fmb_timer = NULL;
    }
    pbdev->fmb_addr = 0;
    memset(&pbdev->fmb, 0, sizeof(ZpciFmb));
}

static int fmb_do_update(S390PCIBusDevice *pbdev, int offset, uint64_t val,
                         int len)
{
    MemTxResult ret;
    uint64_t dst = pbdev->fmb_addr + offset;

    switch (len) {
    case 8:
        address_space_stq_be(&address_space_memory, dst, val,
                             MEMTXATTRS_UNSPECIFIED,
                             &ret);
        break;
    case 4:
        address_space_stl_be(&address_space_memory, dst, val,
                             MEMTXATTRS_UNSPECIFIED,
                             &ret);
        break;
    case 2:
        address_space_stw_be(&address_space_memory, dst, val,
                             MEMTXATTRS_UNSPECIFIED,
                             &ret);
        break;
    case 1:
        address_space_stb(&address_space_memory, dst, val,
                          MEMTXATTRS_UNSPECIFIED,
                          &ret);
        break;
    default:
        ret = MEMTX_ERROR;
        break;
    }
    if (ret != MEMTX_OK) {
        s390_pci_generate_error_event(ERR_EVENT_FMBA, pbdev->fh, pbdev->fid,
                                      pbdev->fmb_addr, 0);
        fmb_timer_free(pbdev);
    }

    return ret;
}

static void fmb_update(void *opaque)
{
    S390PCIBusDevice *pbdev = opaque;
    int64_t t = qemu_clock_get_ms(QEMU_CLOCK_VIRTUAL);
    int i;

    /* Update U bit */
    pbdev->fmb.last_update *= 2;
    pbdev->fmb.last_update |= UPDATE_U_BIT;
    if (fmb_do_update(pbdev, offsetof(ZpciFmb, last_update),
                      pbdev->fmb.last_update,
                      sizeof(pbdev->fmb.last_update))) {
        return;
    }

    /* Update FMB sample count */
    if (fmb_do_update(pbdev, offsetof(ZpciFmb, sample),
                      pbdev->fmb.sample++,
                      sizeof(pbdev->fmb.sample))) {
        return;
    }

    /* Update FMB counters */
    for (i = 0; i < ZPCI_FMB_CNT_MAX; i++) {
        if (fmb_do_update(pbdev, offsetof(ZpciFmb, counter[i]),
                          pbdev->fmb.counter[i],
                          sizeof(pbdev->fmb.counter[0]))) {
            return;
        }
    }

    /* Clear U bit and update the time */
    pbdev->fmb.last_update = time2tod(qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL));
    pbdev->fmb.last_update *= 2;
    if (fmb_do_update(pbdev, offsetof(ZpciFmb, last_update),
                      pbdev->fmb.last_update,
                      sizeof(pbdev->fmb.last_update))) {
        return;
    }
    timer_mod(pbdev->fmb_timer, t + DEFAULT_MUI);
}

int mpcifc_service_call(S390CPU *cpu, uint8_t r1, uint64_t fiba, uint8_t ar,
                        uintptr_t ra)
{
@@ -1018,9 +1119,35 @@ int mpcifc_service_call(S390CPU *cpu, uint8_t r1, uint64_t fiba, uint8_t ar,
            s390_set_status_code(env, r1, ZPCI_MOD_ST_SEQUENCE);
        }
        break;
    case ZPCI_MOD_FC_SET_MEASURE:
        pbdev->fmb_addr = ldq_p(&fib.fmb_addr);
    case ZPCI_MOD_FC_SET_MEASURE: {
        uint64_t fmb_addr = ldq_p(&fib.fmb_addr);

        if (fmb_addr & FMBK_MASK) {
            cc = ZPCI_PCI_LS_ERR;
            s390_pci_generate_error_event(ERR_EVENT_FMBPRO, pbdev->fh,
                                          pbdev->fid, fmb_addr, 0);
            fmb_timer_free(pbdev);
            break;
        }

        if (!fmb_addr) {
            /* Stop updating FMB. */
            fmb_timer_free(pbdev);
            break;
        }

        if (!pbdev->fmb_timer) {
            pbdev->fmb_timer = timer_new_ms(QEMU_CLOCK_VIRTUAL,
                                            fmb_update, pbdev);
        } else if (timer_pending(pbdev->fmb_timer)) {
            /* Remove pending timer to update FMB address. */
            timer_del(pbdev->fmb_timer);
        }
        pbdev->fmb_addr = fmb_addr;
        timer_mod(pbdev->fmb_timer,
                  qemu_clock_get_ms(QEMU_CLOCK_VIRTUAL) + DEFAULT_MUI);
        break;
    }
    default:
        s390_program_interrupt(&cpu->env, PGM_OPERAND, 6, ra);
        cc = ZPCI_PCI_LS_ERR;
+1 −0
Original line number Diff line number Diff line
@@ -303,6 +303,7 @@ int mpcifc_service_call(S390CPU *cpu, uint8_t r1, uint64_t fiba, uint8_t ar,
                        uintptr_t ra);
int stpcifc_service_call(S390CPU *cpu, uint8_t r1, uint64_t fiba, uint8_t ar,
                         uintptr_t ra);
void fmb_timer_free(S390PCIBusDevice *pbdev);

#define ZPCI_IO_BAR_MIN 0
#define ZPCI_IO_BAR_MAX 5
Loading