| /* | 
 |  * 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); | 
 |     } | 
 | } |