diff options
Diffstat (limited to 'hw/s390x/s390-pci-inst.c')
-rw-r--r-- | hw/s390x/s390-pci-inst.c | 133 |
1 files changed, 130 insertions, 3 deletions
diff --git a/hw/s390x/s390-pci-inst.c b/hw/s390x/s390-pci-inst.c index 7b61367ee3..be2896232d 100644 --- a/hw/s390x/s390-pci-inst.c +++ b/hw/s390x/s390-pci-inst.c @@ -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; |