*/
#include "qemu/osdep.h"
-#include "qemu-common.h"
#include "cpu.h"
#include "s390-pci-inst.h"
#include "s390-pci-bus.h"
+#include "exec/memop.h"
#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
int i;
if (env->psw.mask & PSW_MASK_PSTATE) {
- s390_program_interrupt(env, PGM_PRIVILEGED, 4, ra);
+ s390_program_interrupt(env, PGM_PRIVILEGED, ra);
return 0;
}
reqh = (ClpReqHdr *)buffer;
req_len = lduw_p(&reqh->len);
if (req_len < 16 || req_len > 8184 || (req_len % 8 != 0)) {
- s390_program_interrupt(env, PGM_OPERAND, 4, ra);
+ s390_program_interrupt(env, PGM_OPERAND, ra);
return 0;
}
resh = (ClpRspHdr *)(buffer + req_len);
res_len = lduw_p(&resh->len);
if (res_len < 8 || res_len > 8176 || (res_len % 8 != 0)) {
- s390_program_interrupt(env, PGM_OPERAND, 4, ra);
+ s390_program_interrupt(env, PGM_OPERAND, ra);
return 0;
}
if ((req_len + res_len) > 8192) {
- s390_program_interrupt(env, PGM_OPERAND, 4, ra);
+ s390_program_interrupt(env, PGM_OPERAND, ra);
return 0;
}
stw_p(&ressetpci->hdr.rsp, CLP_RC_SETPCIFN_FHOP);
goto out;
}
- device_reset(DEVICE(pbdev));
+ device_legacy_reset(DEVICE(pbdev));
pbdev->fh &= ~FH_MASK_ENABLE;
pbdev->state = ZPCI_FS_DISABLED;
stl_p(&ressetpci->fh, pbdev->fh);
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;
mr = pbdev->pdev->io_regions[pcias].memory;
mr = s390_get_subregion(mr, offset, len);
offset -= mr->addr;
- return memory_region_dispatch_read(mr, offset, data, len,
+ return memory_region_dispatch_read(mr, offset, data,
+ size_memop(len) | MO_BE,
MEMTXATTRS_UNSPECIFIED);
}
uint8_t pcias;
if (env->psw.mask & PSW_MASK_PSTATE) {
- s390_program_interrupt(env, PGM_PRIVILEGED, 4, ra);
+ s390_program_interrupt(env, PGM_PRIVILEGED, ra);
return 0;
}
if (r2 & 0x1) {
- s390_program_interrupt(env, PGM_SPECIFICATION, 4, ra);
+ s390_program_interrupt(env, PGM_SPECIFICATION, ra);
return 0;
}
switch (pcias) {
case ZPCI_IO_BAR_MIN...ZPCI_IO_BAR_MAX:
if (!len || (len > (8 - (offset & 0x7)))) {
- s390_program_interrupt(env, PGM_OPERAND, 4, ra);
+ s390_program_interrupt(env, PGM_OPERAND, ra);
return 0;
}
result = zpci_read_bar(pbdev, pcias, offset, &data, len);
if (result != MEMTX_OK) {
- s390_program_interrupt(env, PGM_OPERAND, 4, ra);
+ s390_program_interrupt(env, PGM_OPERAND, ra);
return 0;
}
break;
case ZPCI_CONFIG_BAR:
if (!len || (len > (4 - (offset & 0x3))) || len == 3) {
- s390_program_interrupt(env, PGM_OPERAND, 4, ra);
+ s390_program_interrupt(env, PGM_OPERAND, ra);
return 0;
}
data = pci_host_config_read_common(
pbdev->pdev, offset, pci_config_size(pbdev->pdev), len);
if (zpci_endian_swap(&data, len)) {
- s390_program_interrupt(env, PGM_OPERAND, 4, ra);
+ s390_program_interrupt(env, PGM_OPERAND, ra);
return 0;
}
break;
return 0;
}
+ pbdev->fmb.counter[ZPCI_FMB_CNT_LD]++;
+
env->regs[r1] = data;
setcc(cpu, ZPCI_PCI_LS_OK);
return 0;
mr = pbdev->pdev->io_regions[pcias].memory;
mr = s390_get_subregion(mr, offset, len);
offset -= mr->addr;
- return memory_region_dispatch_write(mr, offset, data, len,
+ return memory_region_dispatch_write(mr, offset, data,
+ size_memop(len) | MO_BE,
MEMTXATTRS_UNSPECIFIED);
}
uint8_t pcias;
if (env->psw.mask & PSW_MASK_PSTATE) {
- s390_program_interrupt(env, PGM_PRIVILEGED, 4, ra);
+ s390_program_interrupt(env, PGM_PRIVILEGED, ra);
return 0;
}
if (r2 & 0x1) {
- s390_program_interrupt(env, PGM_SPECIFICATION, 4, ra);
+ s390_program_interrupt(env, PGM_SPECIFICATION, ra);
return 0;
}
* A length of 0 is invalid and length should not cross a double word
*/
if (!len || (len > (8 - (offset & 0x7)))) {
- s390_program_interrupt(env, PGM_OPERAND, 4, ra);
+ s390_program_interrupt(env, PGM_OPERAND, ra);
return 0;
}
result = zpci_write_bar(pbdev, pcias, offset, data, len);
if (result != MEMTX_OK) {
- s390_program_interrupt(env, PGM_OPERAND, 4, ra);
+ s390_program_interrupt(env, PGM_OPERAND, ra);
return 0;
}
break;
/* ZPCI uses the pseudo BAR number 15 as configuration space */
/* possible access lengths are 1,2,4 and must not cross a word */
if (!len || (len > (4 - (offset & 0x3))) || len == 3) {
- s390_program_interrupt(env, PGM_OPERAND, 4, ra);
+ s390_program_interrupt(env, PGM_OPERAND, ra);
return 0;
}
/* len = 1,2,4 so we do not need to test */
return 0;
}
+ pbdev->fmb.counter[ZPCI_FMB_CNT_ST]++;
+
setcc(cpu, ZPCI_PCI_LS_OK);
return 0;
}
}
notify.perm = IOMMU_NONE;
- memory_region_notify_iommu(&iommu->iommu_mr, notify);
+ memory_region_notify_iommu(&iommu->iommu_mr, 0, notify);
notify.perm = entry->perm;
}
g_hash_table_replace(iommu->iotlb, &cache->iova, cache);
}
- memory_region_notify_iommu(&iommu->iommu_mr, notify);
+ memory_region_notify_iommu(&iommu->iommu_mr, 0, notify);
}
int rpcit_service_call(S390CPU *cpu, uint8_t r1, uint8_t r2, uintptr_t ra)
hwaddr start, end;
if (env->psw.mask & PSW_MASK_PSTATE) {
- s390_program_interrupt(env, PGM_PRIVILEGED, 4, ra);
+ s390_program_interrupt(env, PGM_PRIVILEGED, ra);
return 0;
}
if (r2 & 0x1) {
- s390_program_interrupt(env, PGM_SPECIFICATION, 4, ra);
+ s390_program_interrupt(env, PGM_SPECIFICATION, ra);
return 0;
}
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;
uint8_t buffer[128];
if (env->psw.mask & PSW_MASK_PSTATE) {
- s390_program_interrupt(env, PGM_PRIVILEGED, 6, ra);
+ s390_program_interrupt(env, PGM_PRIVILEGED, ra);
return 0;
}
mr = s390_get_subregion(mr, offset, len);
offset -= mr->addr;
- if (!memory_region_access_valid(mr, offset, len, true)) {
- s390_program_interrupt(env, PGM_OPERAND, 6, ra);
+ if (!memory_region_access_valid(mr, offset, len, true,
+ MEMTXATTRS_UNSPECIFIED)) {
+ s390_program_interrupt(env, PGM_OPERAND, ra);
return 0;
}
for (i = 0; i < len / 8; i++) {
result = memory_region_dispatch_write(mr, offset + i * 8,
- ldq_p(buffer + i * 8), 8,
- MEMTXATTRS_UNSPECIFIED);
+ ldq_p(buffer + i * 8),
+ MO_64, MEMTXATTRS_UNSPECIFIED);
if (result != MEMTX_OK) {
- s390_program_interrupt(env, PGM_OPERAND, 6, ra);
+ s390_program_interrupt(env, PGM_OPERAND, ra);
return 0;
}
}
+ pbdev->fmb.counter[ZPCI_FMB_CNT_STB]++;
+
setcc(cpu, ZPCI_PCI_LS_OK);
return 0;
specification_error:
- s390_program_interrupt(env, PGM_SPECIFICATION, 6, ra);
+ s390_program_interrupt(env, PGM_SPECIFICATION, ra);
return 0;
}
pba &= ~0xfff;
pal |= 0xfff;
if (pba > pal || pba < ZPCI_SDMA_ADDR || pal > ZPCI_EDMA_ADDR) {
- s390_program_interrupt(env, PGM_OPERAND, 6, ra);
+ s390_program_interrupt(env, PGM_OPERAND, ra);
return -EINVAL;
}
/* currently we only support designation type 1 with translation */
if (!(dt == ZPCI_IOTA_RTTO && t)) {
error_report("unsupported ioat dt %d t %d", dt, t);
- s390_program_interrupt(env, PGM_OPERAND, 6, ra);
+ s390_program_interrupt(env, PGM_OPERAND, ra);
return -EINVAL;
}
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)
{
uint64_t cc = ZPCI_PCI_LS_OK;
if (env->psw.mask & PSW_MASK_PSTATE) {
- s390_program_interrupt(env, PGM_PRIVILEGED, 6, ra);
+ s390_program_interrupt(env, PGM_PRIVILEGED, ra);
return 0;
}
fh = env->regs[r1] >> 32;
if (fiba & 0x7) {
- s390_program_interrupt(env, PGM_SPECIFICATION, 6, ra);
+ s390_program_interrupt(env, PGM_SPECIFICATION, ra);
return 0;
}
}
if (fib.fmt != 0) {
- s390_program_interrupt(env, PGM_OPERAND, 6, ra);
+ s390_program_interrupt(env, PGM_OPERAND, ra);
return 0;
}
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);
+ s390_program_interrupt(&cpu->env, PGM_OPERAND, ra);
cc = ZPCI_PCI_LS_ERR;
}
uint64_t cc = ZPCI_PCI_LS_OK;
if (env->psw.mask & PSW_MASK_PSTATE) {
- s390_program_interrupt(env, PGM_PRIVILEGED, 6, ra);
+ s390_program_interrupt(env, PGM_PRIVILEGED, ra);
return 0;
}
}
if (fiba & 0x7) {
- s390_program_interrupt(env, PGM_SPECIFICATION, 6, ra);
+ s390_program_interrupt(env, PGM_SPECIFICATION, ra);
return 0;
}
* FH Enabled bit is set to one in states of ENABLED, BLOCKED or ERROR. */
case ZPCI_FS_ERROR:
fib.fc |= 0x20;
+ /* fallthrough */
case ZPCI_FS_BLOCKED:
fib.fc |= 0x40;
+ /* fallthrough */
case ZPCI_FS_ENABLED:
fib.fc |= 0x80;
if (pbdev->iommu->enabled) {