diff options
Diffstat (limited to 'hw/s390x/s390-hypercall.c')
-rw-r--r-- | hw/s390x/s390-hypercall.c | 85 |
1 files changed, 85 insertions, 0 deletions
diff --git a/hw/s390x/s390-hypercall.c b/hw/s390x/s390-hypercall.c new file mode 100644 index 0000000..ac1b08b --- /dev/null +++ b/hw/s390x/s390-hypercall.c @@ -0,0 +1,85 @@ +/* + * Support for QEMU/KVM hypercalls on s390 + * + * Copyright 2012 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 + * your option) any later version. See the COPYING file in the top-level + * directory. + */ + +#include "qemu/osdep.h" +#include "cpu.h" +#include "hw/s390x/s390-virtio-ccw.h" +#include "hw/s390x/s390-hypercall.h" +#include "hw/s390x/ioinst.h" +#include "hw/s390x/css.h" +#include "virtio-ccw.h" + +static int handle_virtio_notify(uint64_t mem) +{ + MachineState *ms = MACHINE(qdev_get_machine()); + + if (mem < ms->ram_size) { + /* Early printk */ + return 0; + } + return -EINVAL; +} + +static int handle_virtio_ccw_notify(uint64_t subch_id, uint64_t data) +{ + 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; + } + + 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; +} + +static uint64_t handle_storage_limit(void) +{ + S390CcwMachineState *s390ms = S390_CCW_MACHINE(qdev_get_machine()); + + return s390_get_memory_limit(s390ms) - 1; +} + +void handle_diag_500(S390CPU *cpu, uintptr_t ra) +{ + CPUS390XState *env = &cpu->env; + const uint64_t subcode = env->regs[1]; + + switch (subcode) { + case DIAG500_VIRTIO_NOTIFY: + env->regs[2] = handle_virtio_notify(env->regs[2]); + break; + case DIAG500_VIRTIO_CCW_NOTIFY: + env->regs[2] = handle_virtio_ccw_notify(env->regs[2], env->regs[3]); + break; + case DIAG500_STORAGE_LIMIT: + env->regs[2] = handle_storage_limit(); + break; + default: + s390_program_interrupt(env, PGM_SPECIFICATION, ra); + } +} |