Commit 5d1abf23 authored by Yi Min Zhao's avatar Yi Min Zhao Committed by Cornelia Huck
Browse files

s390x/pci: enforce zPCI state checking



Current code uses some fields combinatorially to indicate the state of
a s390 pci device. This patch introduces device states in order to make
the code more readable and more logical.

Signed-off-by: default avatarYi Min Zhao <zyimin@linux.vnet.ibm.com>
Reviewed-by: default avatarPierre Morel <pmorel@linux.vnet.ibm.com>
Signed-off-by: default avatarCornelia Huck <cornelia.huck@de.ibm.com>
parent 06a96dae
Loading
Loading
Loading
Loading
+55 −49
Original line number Diff line number Diff line
@@ -116,16 +116,22 @@ void s390_pci_sclp_configure(SCCB *sccb)
        goto out;
    }

    if (pbdev) {
        if (pbdev->configured) {
            rc = SCLP_RC_NO_ACTION_REQUIRED;
        } else {
            pbdev->configured = true;
            rc = SCLP_RC_NORMAL_COMPLETION;
        }
    } else {
    if (!pbdev) {
        DPRINTF("sclp config no dev found\n");
        rc = SCLP_RC_ADAPTER_ID_NOT_RECOGNIZED;
        goto out;
    }

    switch (pbdev->state) {
    case ZPCI_FS_RESERVED:
        rc = SCLP_RC_ADAPTER_IN_RESERVED_STATE;
        break;
    case ZPCI_FS_STANDBY:
        pbdev->state = ZPCI_FS_DISABLED;
        rc = SCLP_RC_NORMAL_COMPLETION;
        break;
    default:
        rc = SCLP_RC_NO_ACTION_REQUIRED;
    }
out:
    psccb->header.response_code = cpu_to_be16(rc);
@@ -142,23 +148,29 @@ void s390_pci_sclp_deconfigure(SCCB *sccb)
        goto out;
    }

    if (pbdev) {
        if (!pbdev->configured) {
    if (!pbdev) {
        DPRINTF("sclp deconfig no dev found\n");
        rc = SCLP_RC_ADAPTER_ID_NOT_RECOGNIZED;
        goto out;
    }

    switch (pbdev->state) {
    case ZPCI_FS_RESERVED:
        rc = SCLP_RC_ADAPTER_IN_RESERVED_STATE;
        break;
    case ZPCI_FS_STANDBY:
        rc = SCLP_RC_NO_ACTION_REQUIRED;
        } else {
        break;
    default:
        if (pbdev->summary_ind) {
            pci_dereg_irqs(pbdev);
        }
        if (pbdev->iommu_enabled) {
            pci_dereg_ioat(pbdev);
        }
            pbdev->configured = false;
        pbdev->state = ZPCI_FS_STANDBY;
        rc = SCLP_RC_NORMAL_COMPLETION;
    }
    } else {
        DPRINTF("sclp deconfig no dev found\n");
        rc = SCLP_RC_ADAPTER_ID_NOT_RECOGNIZED;
    }
out:
    psccb->header.response_code = cpu_to_be16(rc);
}
@@ -183,7 +195,7 @@ S390PCIBusDevice *s390_pci_find_dev_by_idx(uint32_t idx)
    for (i = 0; i < PCI_SLOT_MAX; i++) {
        pbdev = &s->pbdev[i];

        if (pbdev->fh == 0) {
        if (pbdev->state == ZPCI_FS_RESERVED) {
            continue;
        }

@@ -233,9 +245,8 @@ static void s390_pci_generate_plug_event(uint16_t pec, uint32_t fh,
    s390_pci_generate_event(2, pec, fh, fid, 0, 0);
}

static void s390_pci_generate_error_event(uint16_t pec, uint32_t fh,
                                          uint32_t fid, uint64_t faddr,
                                          uint32_t e)
void s390_pci_generate_error_event(uint16_t pec, uint32_t fh, uint32_t fid,
                                   uint64_t faddr, uint32_t e)
{
    s390_pci_generate_event(1, pec, fh, fid, faddr, e);
}
@@ -337,8 +348,14 @@ static IOMMUTLBEntry s390_translate_iommu(MemoryRegion *iommu, hwaddr addr,
        .perm = IOMMU_NONE,
    };

    if (!pbdev->configured || !pbdev->pdev ||
        !(pbdev->fh & FH_MASK_ENABLE) || !pbdev->iommu_enabled) {
    switch (pbdev->state) {
    case ZPCI_FS_ENABLED:
    case ZPCI_FS_BLOCKED:
        if (!pbdev->iommu_enabled) {
            return ret;
        }
        break;
    default:
        return ret;
    }

@@ -357,30 +374,13 @@ static IOMMUTLBEntry s390_translate_iommu(MemoryRegion *iommu, hwaddr addr,
        return ret;
    }

    if (!pbdev->g_iota) {
        pbdev->error_state = true;
        pbdev->lgstg_blocked = true;
        s390_pci_generate_error_event(ERR_EVENT_INVALAS, pbdev->fh, pbdev->fid,
                                      addr, 0);
        return ret;
    }

    if (addr < pbdev->pba || addr > pbdev->pal) {
        pbdev->error_state = true;
        pbdev->lgstg_blocked = true;
        s390_pci_generate_error_event(ERR_EVENT_OORANGE, pbdev->fh, pbdev->fid,
                                      addr, 0);
        return ret;
    }

    pte = s390_guest_io_table_walk(s390_pci_get_table_origin(pbdev->g_iota),
                                   addr);

    if (!pte) {
        pbdev->error_state = true;
        pbdev->lgstg_blocked = true;
        s390_pci_generate_error_event(ERR_EVENT_SERR, pbdev->fh, pbdev->fid,
                                      addr, ERR_EVENT_Q_BIT);
        return ret;
    }

@@ -449,7 +449,7 @@ static void s390_msi_ctrl_write(void *opaque, hwaddr addr, uint64_t data,
        return;
    }

    if (!(pbdev->fh & FH_MASK_ENABLE)) {
    if (pbdev->state != ZPCI_FS_ENABLED) {
        return;
    }

@@ -571,7 +571,7 @@ static void s390_pcihost_hot_plug(HotplugHandler *hotplug_dev,

    pbdev->fid = s390_pci_get_pfid(pci_dev);
    pbdev->pdev = pci_dev;
    pbdev->configured = true;
    pbdev->state = ZPCI_FS_DISABLED;
    pbdev->fh = s390_pci_get_pfh(pci_dev);

    s390_pcihost_setup_msix(pbdev);
@@ -592,8 +592,12 @@ static void s390_pcihost_hot_unplug(HotplugHandler *hotplug_dev,
                                           ->qbus.parent);
    S390PCIBusDevice *pbdev = &s->pbdev[PCI_SLOT(pci_dev->devfn)];

    if (pbdev->configured) {
        pbdev->configured = false;
    switch (pbdev->state) {
    case ZPCI_FS_RESERVED:
        goto out;
    case ZPCI_FS_STANDBY:
        break;
    default:
        s390_pci_generate_plug_event(HP_EVENT_CONFIGURED_TO_STBRES,
                                     pbdev->fh, pbdev->fid);
    }
@@ -603,6 +607,8 @@ static void s390_pcihost_hot_unplug(HotplugHandler *hotplug_dev,
    pbdev->fh = 0;
    pbdev->fid = 0;
    pbdev->pdev = NULL;
    pbdev->state = ZPCI_FS_RESERVED;
out:
    object_unparent(OBJECT(pci_dev));
}

+31 −3
Original line number Diff line number Diff line
@@ -153,6 +153,34 @@ enum ZpciIoatDtype {
#define ZPCI_TABLE_VALID_MASK           0x20
#define ZPCI_TABLE_PROT_MASK            0x200

/* PCI Function States
 *
 * reserved: default; device has just been plugged or is in progress of being
 *           unplugged
 * standby: device is present but not configured; transition from any
 *          configured state/to this state via sclp configure/deconfigure
 *
 * The following states make up the "configured" meta-state:
 * disabled: device is configured but not enabled; transition between this
 *           state and enabled via clp enable/disable
 * enbaled: device is ready for use; transition to disabled via clp disable;
 *          may enter an error state
 * blocked: ignore all DMA and interrupts; transition back to enabled or from
 *          error state via mpcifc
 * error: an error occured; transition back to enabled via mpcifc
 * permanent error: an unrecoverable error occured; transition to standby via
 *                  sclp deconfigure
 */
typedef enum {
    ZPCI_FS_RESERVED,
    ZPCI_FS_STANDBY,
    ZPCI_FS_DISABLED,
    ZPCI_FS_ENABLED,
    ZPCI_FS_BLOCKED,
    ZPCI_FS_ERROR,
    ZPCI_FS_PERMANENT_ERROR,
} ZpciState;

typedef struct SeiContainer {
    QTAILQ_ENTRY(SeiContainer) link;
    uint32_t fid;
@@ -219,9 +247,7 @@ typedef struct S390MsixInfo {

typedef struct S390PCIBusDevice {
    PCIDevice *pdev;
    bool configured;
    bool error_state;
    bool lgstg_blocked;
    ZpciState state;
    bool iommu_enabled;
    uint32_t fh;
    uint32_t fid;
@@ -255,6 +281,8 @@ void s390_pci_sclp_configure(SCCB *sccb);
void s390_pci_sclp_deconfigure(SCCB *sccb);
void s390_pci_iommu_enable(S390PCIBusDevice *pbdev);
void s390_pci_iommu_disable(S390PCIBusDevice *pbdev);
void s390_pci_generate_error_event(uint16_t pec, uint32_t fh, uint32_t fid,
                                   uint64_t faddr, uint32_t e);
S390PCIBusDevice *s390_pci_find_dev_by_idx(uint32_t idx);
S390PCIBusDevice *s390_pci_find_dev_by_fh(uint32_t fh);
S390PCIBusDevice *s390_pci_find_dev_by_fid(uint32_t fid);
+138 −30
Original line number Diff line number Diff line
@@ -108,8 +108,9 @@ static int list_pci(ClpReqRspListPci *rrb, uint8_t *cc)
            pci_get_word(pbdev->pdev->config + PCI_DEVICE_ID));
        stw_p(&rrb->response.fh_list[idx - resume_token].vendor_id,
            pci_get_word(pbdev->pdev->config + PCI_VENDOR_ID));
        /* Ignore RESERVED devices. */
        stl_p(&rrb->response.fh_list[idx - resume_token].config,
            pbdev->configured << 31);
            pbdev->state == ZPCI_FS_STANDBY ? 0 : 1 << 31);
        stl_p(&rrb->response.fh_list[idx - resume_token].fid, pbdev->fid);
        stl_p(&rrb->response.fh_list[idx - resume_token].fh, pbdev->fh);

@@ -213,13 +214,13 @@ int clp_service_call(S390CPU *cpu, uint8_t r2)
        switch (reqsetpci->oc) {
        case CLP_SET_ENABLE_PCI_FN:
            pbdev->fh |= FH_MASK_ENABLE;
            pbdev->state = ZPCI_FS_ENABLED;
            stl_p(&ressetpci->fh, pbdev->fh);
            stw_p(&ressetpci->hdr.rsp, CLP_RC_OK);
            break;
        case CLP_SET_DISABLE_PCI_FN:
            pbdev->fh &= ~FH_MASK_ENABLE;
            pbdev->error_state = false;
            pbdev->lgstg_blocked = false;
            pbdev->state = ZPCI_FS_DISABLED;
            stl_p(&ressetpci->fh, pbdev->fh);
            stw_p(&ressetpci->hdr.rsp, CLP_RC_OK);
            break;
@@ -318,16 +319,25 @@ int pcilg_service_call(S390CPU *cpu, uint8_t r1, uint8_t r2)
    offset = env->regs[r2 + 1];

    pbdev = s390_pci_find_dev_by_fh(fh);
    if (!pbdev || !(pbdev->fh & FH_MASK_ENABLE)) {
    if (!pbdev) {
        DPRINTF("pcilg no pci dev\n");
        setcc(cpu, ZPCI_PCI_LS_INVAL_HANDLE);
        return 0;
    }

    if (pbdev->lgstg_blocked) {
    switch (pbdev->state) {
    case ZPCI_FS_RESERVED:
    case ZPCI_FS_STANDBY:
    case ZPCI_FS_DISABLED:
    case ZPCI_FS_PERMANENT_ERROR:
        setcc(cpu, ZPCI_PCI_LS_INVAL_HANDLE);
        return 0;
    case ZPCI_FS_ERROR:
        setcc(cpu, ZPCI_PCI_LS_ERR);
        s390_set_status_code(env, r2, ZPCI_PCI_ST_BLOCKED);
        return 0;
    default:
        break;
    }

    if (pcias < 6) {
@@ -435,16 +445,25 @@ int pcistg_service_call(S390CPU *cpu, uint8_t r1, uint8_t r2)
    offset = env->regs[r2 + 1];

    pbdev = s390_pci_find_dev_by_fh(fh);
    if (!pbdev || !(pbdev->fh & FH_MASK_ENABLE)) {
    if (!pbdev) {
        DPRINTF("pcistg no pci dev\n");
        setcc(cpu, ZPCI_PCI_LS_INVAL_HANDLE);
        return 0;
    }

    if (pbdev->lgstg_blocked) {
    switch (pbdev->state) {
    case ZPCI_FS_RESERVED:
    case ZPCI_FS_STANDBY:
    case ZPCI_FS_DISABLED:
    case ZPCI_FS_PERMANENT_ERROR:
        setcc(cpu, ZPCI_PCI_LS_INVAL_HANDLE);
        return 0;
    case ZPCI_FS_ERROR:
        setcc(cpu, ZPCI_PCI_LS_ERR);
        s390_set_status_code(env, r2, ZPCI_PCI_ST_BLOCKED);
        return 0;
    default:
        break;
    }

    data = env->regs[r1];
@@ -526,18 +545,55 @@ int rpcit_service_call(S390CPU *cpu, uint8_t r1, uint8_t r2)
    end = start + env->regs[r2 + 1];

    pbdev = s390_pci_find_dev_by_fh(fh);
    if (!pbdev || !(pbdev->fh & FH_MASK_ENABLE)) {
    if (!pbdev) {
        DPRINTF("rpcit no pci dev\n");
        setcc(cpu, ZPCI_PCI_LS_INVAL_HANDLE);
        goto out;
    }

    switch (pbdev->state) {
    case ZPCI_FS_RESERVED:
    case ZPCI_FS_STANDBY:
    case ZPCI_FS_DISABLED:
    case ZPCI_FS_PERMANENT_ERROR:
        setcc(cpu, ZPCI_PCI_LS_INVAL_HANDLE);
        return 0;
    case ZPCI_FS_ERROR:
        setcc(cpu, ZPCI_PCI_LS_ERR);
        s390_set_status_code(env, r1, ZPCI_MOD_ST_ERROR_RECOVER);
        return 0;
    default:
        break;
    }

    if (!pbdev->g_iota) {
        pbdev->state = ZPCI_FS_ERROR;
        setcc(cpu, ZPCI_PCI_LS_ERR);
        s390_set_status_code(env, r1, ZPCI_PCI_ST_INSUF_RES);
        s390_pci_generate_error_event(ERR_EVENT_INVALAS, pbdev->fh, pbdev->fid,
                                      start, 0);
        goto out;
    }

    if (end < pbdev->pba || start > pbdev->pal) {
        pbdev->state = ZPCI_FS_ERROR;
        setcc(cpu, ZPCI_PCI_LS_ERR);
        s390_set_status_code(env, r1, ZPCI_PCI_ST_INSUF_RES);
        s390_pci_generate_error_event(ERR_EVENT_OORANGE, pbdev->fh, pbdev->fid,
                                      start, 0);
        goto out;
    }

    mr = &pbdev->iommu_mr;
    while (start < end) {
        entry = mr->iommu_ops->translate(mr, start, 0);

        if (!entry.translated_addr) {
            pbdev->state = ZPCI_FS_ERROR;
            setcc(cpu, ZPCI_PCI_LS_ERR);
            s390_set_status_code(env, r1, ZPCI_PCI_ST_INSUF_RES);
            s390_pci_generate_error_event(ERR_EVENT_SERR, pbdev->fh, pbdev->fid,
                                          start, ERR_EVENT_Q_BIT);
            goto out;
        }

@@ -590,16 +646,25 @@ int pcistb_service_call(S390CPU *cpu, uint8_t r1, uint8_t r3, uint64_t gaddr,
    }

    pbdev = s390_pci_find_dev_by_fh(fh);
    if (!pbdev || !(pbdev->fh & FH_MASK_ENABLE)) {
    if (!pbdev) {
        DPRINTF("pcistb no pci dev fh 0x%x\n", fh);
        setcc(cpu, ZPCI_PCI_LS_INVAL_HANDLE);
        return 0;
    }

    if (pbdev->lgstg_blocked) {
    switch (pbdev->state) {
    case ZPCI_FS_RESERVED:
    case ZPCI_FS_STANDBY:
    case ZPCI_FS_DISABLED:
    case ZPCI_FS_PERMANENT_ERROR:
        setcc(cpu, ZPCI_PCI_LS_INVAL_HANDLE);
        return 0;
    case ZPCI_FS_ERROR:
        setcc(cpu, ZPCI_PCI_LS_ERR);
        s390_set_status_code(env, r1, ZPCI_PCI_ST_BLOCKED);
        return 0;
    default:
        break;
    }

    mr = pbdev->pdev->io_regions[pcias].memory;
@@ -743,12 +808,23 @@ int mpcifc_service_call(S390CPU *cpu, uint8_t r1, uint64_t fiba, uint8_t ar)
    }

    pbdev = s390_pci_find_dev_by_fh(fh);
    if (!pbdev || !(pbdev->fh & FH_MASK_ENABLE)) {
    if (!pbdev) {
        DPRINTF("mpcifc no pci dev fh 0x%x\n", fh);
        setcc(cpu, ZPCI_PCI_LS_INVAL_HANDLE);
        return 0;
    }

    switch (pbdev->state) {
    case ZPCI_FS_RESERVED:
    case ZPCI_FS_STANDBY:
    case ZPCI_FS_DISABLED:
    case ZPCI_FS_PERMANENT_ERROR:
        setcc(cpu, ZPCI_PCI_LS_INVAL_HANDLE);
        return 0;
    default:
        break;
    }

    if (s390_cpu_virt_mem_read(cpu, fiba, ar, (uint8_t *)&fib, sizeof(fib))) {
        return 0;
    }
@@ -815,11 +891,25 @@ int mpcifc_service_call(S390CPU *cpu, uint8_t r1, uint64_t fiba, uint8_t ar)
        }
        break;
    case ZPCI_MOD_FC_RESET_ERROR:
        pbdev->error_state = false;
        pbdev->lgstg_blocked = false;
        switch (pbdev->state) {
        case ZPCI_FS_BLOCKED:
        case ZPCI_FS_ERROR:
            pbdev->state = ZPCI_FS_ENABLED;
            break;
        default:
            cc = ZPCI_PCI_LS_ERR;
            s390_set_status_code(env, r1, ZPCI_MOD_ST_SEQUENCE);
        }
        break;
    case ZPCI_MOD_FC_RESET_BLOCK:
        pbdev->lgstg_blocked = false;
        switch (pbdev->state) {
        case ZPCI_FS_ERROR:
            pbdev->state = ZPCI_FS_BLOCKED;
            break;
        default:
            cc = ZPCI_PCI_LS_ERR;
            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);
@@ -861,6 +951,39 @@ int stpcifc_service_call(S390CPU *cpu, uint8_t r1, uint64_t fiba, uint8_t ar)
    }

    memset(&fib, 0, sizeof(fib));

    switch (pbdev->state) {
    case ZPCI_FS_RESERVED:
    case ZPCI_FS_STANDBY:
        setcc(cpu, ZPCI_PCI_LS_INVAL_HANDLE);
        return 0;
    case ZPCI_FS_DISABLED:
        if (fh & FH_MASK_ENABLE) {
            setcc(cpu, ZPCI_PCI_LS_INVAL_HANDLE);
            return 0;
        }
        goto out;
    /* BLOCKED bit is set to one coincident with the setting of ERROR bit.
     * FH Enabled bit is set to one in states of ENABLED, BLOCKED or ERROR. */
    case ZPCI_FS_ERROR:
        fib.fc |= 0x20;
    case ZPCI_FS_BLOCKED:
        fib.fc |= 0x40;
    case ZPCI_FS_ENABLED:
        fib.fc |= 0x80;
        if (pbdev->iommu_enabled) {
            fib.fc |= 0x10;
        }
        if (!(fh & FH_MASK_ENABLE)) {
            env->regs[r1] |= 1ULL << 63;
        }
        break;
    case ZPCI_FS_PERMANENT_ERROR:
        setcc(cpu, ZPCI_PCI_LS_ERR);
        s390_set_status_code(env, r1, ZPCI_STPCIFC_ST_PERM_ERROR);
        return 0;
    }

    stq_p(&fib.pba, pbdev->pba);
    stq_p(&fib.pal, pbdev->pal);
    stq_p(&fib.iota, pbdev->g_iota);
@@ -873,22 +996,7 @@ int stpcifc_service_call(S390CPU *cpu, uint8_t r1, uint64_t fiba, uint8_t ar)
           ((uint32_t)pbdev->sum << 7) | pbdev->routes.adapter.summary_offset;
    stl_p(&fib.data, data);

    if (pbdev->fh & FH_MASK_ENABLE) {
        fib.fc |= 0x80;
    }

    if (pbdev->error_state) {
        fib.fc |= 0x40;
    }

    if (pbdev->lgstg_blocked) {
        fib.fc |= 0x20;
    }

    if (pbdev->g_iota) {
        fib.fc |= 0x10;
    }

out:
    if (s390_cpu_virt_mem_write(cpu, fiba, ar, (uint8_t *)&fib, sizeof(fib))) {
        return 0;
    }
+5 −0
Original line number Diff line number Diff line
@@ -249,6 +249,11 @@ typedef struct ClpReqRspQueryPciGrp {
#define ZPCI_MOD_FC_RESET_BLOCK 9
#define ZPCI_MOD_FC_SET_MEASURE 10

/* Store PCI Function Controls status codes */
#define ZPCI_STPCIFC_ST_PERM_ERROR    8
#define ZPCI_STPCIFC_ST_INVAL_DMAAS   28
#define ZPCI_STPCIFC_ST_ERROR_RECOVER 40

/* FIB function controls */
#define ZPCI_FIB_FC_ENABLED     0x80
#define ZPCI_FIB_FC_ERROR       0x40
+1 −0
Original line number Diff line number Diff line
@@ -58,6 +58,7 @@
#define SCLP_RC_CONTAINED_EQUIPMENT_CHECK       0x0340
#define SCLP_RC_INSUFFICIENT_SCCB_LENGTH        0x0300
#define SCLP_RC_STANDBY_READ_COMPLETION         0x0410
#define SCLP_RC_ADAPTER_IN_RESERVED_STATE       0x05f0
#define SCLP_RC_ADAPTER_ID_NOT_RECOGNIZED       0x09f0
#define SCLP_RC_INVALID_FUNCTION                0x40f0
#define SCLP_RC_NO_EVENT_BUFFERS_STORED         0x60f0