diff options
Diffstat (limited to 'hw/s390x/s390-virtio-hcall.c')
| -rw-r--r-- | hw/s390x/s390-virtio-hcall.c | 65 |
1 files changed, 49 insertions, 16 deletions
diff --git a/hw/s390x/s390-virtio-hcall.c b/hw/s390x/s390-virtio-hcall.c index ec7cf8beb3..ca49e3cd22 100644 --- a/hw/s390x/s390-virtio-hcall.c +++ b/hw/s390x/s390-virtio-hcall.c @@ -11,31 +11,64 @@ #include "qemu/osdep.h" #include "cpu.h" +#include "hw/boards.h" #include "hw/s390x/s390-virtio-hcall.h" +#include "hw/s390x/ioinst.h" +#include "hw/s390x/css.h" +#include "virtio-ccw.h" -#define MAX_DIAG_SUBCODES 255 +static int handle_virtio_notify(uint64_t mem) +{ + MachineState *ms = MACHINE(qdev_get_machine()); -static s390_virtio_fn s390_diag500_table[MAX_DIAG_SUBCODES]; + if (mem < ms->ram_size) { + /* Early printk */ + return 0; + } + return -EINVAL; +} -void s390_register_virtio_hypercall(uint64_t code, s390_virtio_fn fn) +static int handle_virtio_ccw_notify(uint64_t subch_id, uint64_t data) { - assert(code < MAX_DIAG_SUBCODES); - assert(!s390_diag500_table[code]); + SubchDev *sch; + VirtIODevice *vdev; + int cssid, ssid, schid, m; + uint16_t vq_idx = data; + + if (ioinst_disassemble_sch_ident(subch_id, &m, &cssid, &ssid, &schid)) { + return -EINVAL; + } + sch = css_find_subch(m, cssid, ssid, schid); + if (!sch || !css_subch_visible(sch)) { + return -EINVAL; + } - s390_diag500_table[code] = fn; + vdev = virtio_ccw_get_vdev(sch); + if (vq_idx >= VIRTIO_QUEUE_MAX || !virtio_queue_get_num(vdev, vq_idx)) { + return -EINVAL; + } + + if (virtio_vdev_has_feature(vdev, VIRTIO_F_NOTIFICATION_DATA)) { + virtio_queue_set_shadow_avail_idx(virtio_get_queue(vdev, vq_idx), + (data >> 16) & 0xFFFF); + } + + virtio_queue_notify(vdev, vq_idx); + return 0; } int s390_virtio_hypercall(CPUS390XState *env) { - s390_virtio_fn fn; - - if (env->regs[1] < MAX_DIAG_SUBCODES) { - fn = s390_diag500_table[env->regs[1]]; - if (fn) { - env->regs[2] = fn(&env->regs[2]); - return 0; - } - } + const uint64_t subcode = env->regs[1]; - return -EINVAL; + switch (subcode) { + case KVM_S390_VIRTIO_NOTIFY: + env->regs[2] = handle_virtio_notify(env->regs[2]); + return 0; + case KVM_S390_VIRTIO_CCW_NOTIFY: + env->regs[2] = handle_virtio_ccw_notify(env->regs[2], env->regs[3]); + return 0; + default: + return -EINVAL; + } } |