diff options
Diffstat (limited to 'hw')
-rw-r--r-- | hw/s390x/css.c | 10 | ||||
-rw-r--r-- | hw/s390x/css.h | 2 | ||||
-rw-r--r-- | hw/s390x/virtio-ccw.c | 102 | ||||
-rw-r--r-- | hw/s390x/virtio-ccw.h | 4 |
4 files changed, 111 insertions, 7 deletions
diff --git a/hw/s390x/css.c b/hw/s390x/css.c index 75b04b4..7074d2b 100644 --- a/hw/s390x/css.c +++ b/hw/s390x/css.c @@ -116,6 +116,15 @@ void css_conditional_io_interrupt(SubchDev *sch) } } +void css_adapter_interrupt(uint8_t isc) +{ + S390CPU *cpu = s390_cpu_addr2state(0); + uint32_t io_int_word = (isc << 27) | IO_INT_WORD_AI; + + trace_css_adapter_interrupt(isc); + s390_io_interrupt(cpu, 0, 0, 0, io_int_word); +} + static void sch_handle_clear_func(SubchDev *sch) { PMCW *p = &sch->curr_status.pmcw; @@ -1259,6 +1268,7 @@ void css_reset_sch(SubchDev *sch) sch->channel_prog = 0x0; sch->last_cmd_valid = false; sch->orb = NULL; + sch->thinint_active = false; } void css_reset(void) diff --git a/hw/s390x/css.h b/hw/s390x/css.h index b536ab5..e9b4405 100644 --- a/hw/s390x/css.h +++ b/hw/s390x/css.h @@ -77,6 +77,7 @@ struct SubchDev { CCW1 last_cmd; bool last_cmd_valid; ORB *orb; + bool thinint_active; /* transport-provided data: */ int (*ccw_cb) (SubchDev *, CCW1); SenseId id; @@ -97,4 +98,5 @@ void css_queue_crw(uint8_t rsc, uint8_t erc, int chain, uint16_t rsid); void css_generate_sch_crws(uint8_t cssid, uint8_t ssid, uint16_t schid, int hotplugged, int add); void css_generate_chp_crws(uint8_t cssid, uint8_t chpid); +void css_adapter_interrupt(uint8_t isc); #endif diff --git a/hw/s390x/virtio-ccw.c b/hw/s390x/virtio-ccw.c index f6e0e3e..a01801e 100644 --- a/hw/s390x/virtio-ccw.c +++ b/hw/s390x/virtio-ccw.c @@ -1,7 +1,7 @@ /* * virtio ccw target implementation * - * Copyright 2012 IBM Corp. + * Copyright 2012,2014 IBM Corp. * Author(s): Cornelia Huck <cornelia.huck@de.ibm.com> * * This work is licensed under the terms of the GNU GPL, version 2 or (at @@ -188,6 +188,13 @@ typedef struct VirtioFeatDesc { uint8_t index; } QEMU_PACKED VirtioFeatDesc; +typedef struct VirtioThinintInfo { + hwaddr summary_indicator; + hwaddr device_indicator; + uint64_t ind_bit; + uint8_t isc; +} QEMU_PACKED VirtioThinintInfo; + /* Specify where the virtqueues for the subchannel are in guest memory. */ static int virtio_ccw_set_vqs(SubchDev *sch, uint64_t addr, uint32_t align, uint16_t index, uint16_t num) @@ -237,6 +244,7 @@ static int virtio_ccw_cb(SubchDev *sch, CCW1 ccw) bool check_len; int len; hwaddr hw_len; + VirtioThinintInfo *thinint; if (!dev) { return -EINVAL; @@ -428,6 +436,11 @@ static int virtio_ccw_cb(SubchDev *sch, CCW1 ccw) ret = -EINVAL; break; } + if (sch->thinint_active) { + /* Trigger a command reject. */ + ret = -ENOSYS; + break; + } if (!ccw.cda) { ret = -EFAULT; } else { @@ -480,6 +493,42 @@ static int virtio_ccw_cb(SubchDev *sch, CCW1 ccw) ret = 0; } break; + case CCW_CMD_SET_IND_ADAPTER: + if (check_len) { + if (ccw.count != sizeof(*thinint)) { + ret = -EINVAL; + break; + } + } else if (ccw.count < sizeof(*thinint)) { + /* Can't execute command. */ + ret = -EINVAL; + break; + } + len = sizeof(*thinint); + hw_len = len; + if (!ccw.cda) { + ret = -EFAULT; + } else if (dev->indicators && !sch->thinint_active) { + /* Trigger a command reject. */ + ret = -ENOSYS; + } else { + thinint = cpu_physical_memory_map(ccw.cda, &hw_len, 0); + if (!thinint) { + ret = -EFAULT; + } else { + len = hw_len; + dev->summary_indicator = thinint->summary_indicator; + dev->indicators = thinint->device_indicator; + dev->thinint_isc = thinint->isc; + dev->ind_bit = thinint->ind_bit; + cpu_physical_memory_unmap(thinint, hw_len, 0, hw_len); + sch->thinint_active = ((dev->indicators != 0) && + (dev->summary_indicator != 0)); + sch->curr_status.scsw.count = ccw.count - len; + ret = 0; + } + } + break; default: ret = -ENOSYS; break; @@ -511,6 +560,7 @@ static int virtio_ccw_device_init(VirtioCcwDevice *dev, VirtIODevice *vdev) sch->channel_prog = 0x0; sch->last_cmd_valid = false; sch->orb = NULL; + sch->thinint_active = false; /* * Use a device number if provided. Otherwise, fall back to subchannel * number. @@ -858,6 +908,28 @@ static inline VirtioCcwDevice *to_virtio_ccw_dev_fast(DeviceState *d) return container_of(d, VirtioCcwDevice, parent_obj); } +static uint8_t virtio_set_ind_atomic(SubchDev *sch, uint64_t ind_loc, + uint8_t to_be_set) +{ + uint8_t ind_old, ind_new; + hwaddr len = 1; + uint8_t *ind_addr; + + ind_addr = cpu_physical_memory_map(ind_loc, &len, 1); + if (!ind_addr) { + error_report("%s(%x.%x.%04x): unable to access indicator", + __func__, sch->cssid, sch->ssid, sch->schid); + return -1; + } + do { + ind_old = *ind_addr; + ind_new = ind_old | to_be_set; + } while (atomic_cmpxchg(ind_addr, ind_old, ind_new) != ind_old); + cpu_physical_memory_unmap(ind_addr, len, 1, len); + + return ind_old; +} + static void virtio_ccw_notify(DeviceState *d, uint16_t vector) { VirtioCcwDevice *dev = to_virtio_ccw_dev_fast(d); @@ -872,9 +944,26 @@ static void virtio_ccw_notify(DeviceState *d, uint16_t vector) if (!dev->indicators) { return; } - indicators = ldq_phys(&address_space_memory, dev->indicators); - indicators |= 1ULL << vector; - stq_phys(&address_space_memory, dev->indicators, indicators); + if (sch->thinint_active) { + /* + * In the adapter interrupt case, indicators points to a + * memory area that may be (way) larger than 64 bit and + * ind_bit indicates the start of the indicators in a big + * endian notation. + */ + virtio_set_ind_atomic(sch, dev->indicators + + (dev->ind_bit + vector) / 8, + 0x80 >> ((dev->ind_bit + vector) % 8)); + if (!virtio_set_ind_atomic(sch, dev->summary_indicator, + 0x01)) { + css_adapter_interrupt(dev->thinint_isc); + } + } else { + indicators = ldq_phys(&address_space_memory, dev->indicators); + indicators |= 1ULL << vector; + stq_phys(&address_space_memory, dev->indicators, indicators); + css_conditional_io_interrupt(sch); + } } else { if (!dev->indicators2) { return; @@ -883,10 +972,8 @@ static void virtio_ccw_notify(DeviceState *d, uint16_t vector) indicators = ldq_phys(&address_space_memory, dev->indicators2); indicators |= 1ULL << vector; stq_phys(&address_space_memory, dev->indicators2, indicators); + css_conditional_io_interrupt(sch); } - - css_conditional_io_interrupt(sch); - } static unsigned virtio_ccw_get_features(DeviceState *d) @@ -907,6 +994,7 @@ static void virtio_ccw_reset(DeviceState *d) css_reset_sch(dev->sch); dev->indicators = 0; dev->indicators2 = 0; + dev->summary_indicator = 0; } static void virtio_ccw_vmstate_change(DeviceState *d, bool running) diff --git a/hw/s390x/virtio-ccw.h b/hw/s390x/virtio-ccw.h index 00932c7..4393e44 100644 --- a/hw/s390x/virtio-ccw.h +++ b/hw/s390x/virtio-ccw.h @@ -38,6 +38,7 @@ #define CCW_CMD_SET_IND 0x43 #define CCW_CMD_SET_CONF_IND 0x53 #define CCW_CMD_READ_VQ_CONF 0x32 +#define CCW_CMD_SET_IND_ADAPTER 0x73 #define TYPE_VIRTIO_CCW_DEVICE "virtio-ccw-device" #define VIRTIO_CCW_DEVICE(obj) \ @@ -83,9 +84,12 @@ struct VirtioCcwDevice { bool ioeventfd_started; bool ioeventfd_disabled; uint32_t flags; + uint8_t thinint_isc; /* Guest provided values: */ hwaddr indicators; hwaddr indicators2; + hwaddr summary_indicator; + uint64_t ind_bit; }; /* virtual css bus type */ |