| 1 | /* |
| 2 | * Support for QEMU/KVM hypercalls on s390 |
| 3 | * |
| 4 | * Copyright 2012 IBM Corp. |
| 5 | * Author(s): Cornelia Huck <cornelia.huck@de.ibm.com> |
| 6 | * |
| 7 | * This work is licensed under the terms of the GNU GPL, version 2 or (at |
| 8 | * your option) any later version. See the COPYING file in the top-level |
| 9 | * directory. |
| 10 | */ |
| 11 | |
| 12 | #include "qemu/osdep.h" |
| 13 | #include "qemu/error-report.h" |
| 14 | #include "target/s390x/cpu.h" |
| 15 | #include "hw/s390x/s390-virtio-ccw.h" |
| 16 | #include "hw/s390x/s390-hypercall.h" |
| 17 | #include "hw/s390x/ioinst.h" |
| 18 | #include "hw/s390x/css.h" |
| 19 | #include "virtio-ccw.h" |
| 20 | |
| 21 | static int handle_virtio_notify(uint64_t mem) |
| 22 | { |
| 23 | MachineState *ms = MACHINE(qdev_get_machine()); |
| 24 | |
| 25 | if (mem < ms->ram_size) { |
| 26 | /* Early printk */ |
| 27 | return 0; |
| 28 | } |
| 29 | return -EINVAL; |
| 30 | } |
| 31 | |
| 32 | static int handle_virtio_ccw_notify(uint64_t subch_id, uint64_t data) |
| 33 | { |
| 34 | SubchDev *sch; |
| 35 | VirtIODevice *vdev; |
| 36 | int cssid, ssid, schid, m; |
| 37 | uint16_t vq_idx = data; |
| 38 | |
| 39 | if (ioinst_disassemble_sch_ident(subch_id, &m, &cssid, &ssid, &schid)) { |
| 40 | return -EINVAL; |
| 41 | } |
| 42 | sch = css_find_subch(m, cssid, ssid, schid); |
| 43 | if (!sch || !css_subch_visible(sch)) { |
| 44 | return -EINVAL; |
| 45 | } |
| 46 | if (sch->id.cu_type != VIRTIO_CCW_CU_TYPE) { |
| 47 | /* |
| 48 | * This might happen in nested setups: If the L1 host defined the |
| 49 | * L2 guest with a virtio device (e.g. virtio-keyboard), and the |
| 50 | * L2 guest passes this device through to the L3 guest, the L3 guest |
| 51 | * might send virtio notifications to the QEMU in L2 for that device. |
| 52 | * But since the QEMU in L2 defined this device as vfio-ccw, it's not |
| 53 | * a VirtIODevice that we can handle here! |
| 54 | */ |
| 55 | warn_report_once("Got virtio notification for unsupported device " |
| 56 | "on subchannel %02x.%1x.%04x!", cssid, ssid, schid); |
| 57 | return -EINVAL; |
| 58 | } |
| 59 | |
| 60 | vdev = virtio_ccw_get_vdev(sch); |
| 61 | if (vq_idx >= VIRTIO_QUEUE_MAX || !virtio_queue_get_num(vdev, vq_idx)) { |
| 62 | return -EINVAL; |
| 63 | } |
| 64 | |
| 65 | if (virtio_vdev_has_feature(vdev, VIRTIO_F_NOTIFICATION_DATA)) { |
| 66 | virtio_queue_set_shadow_avail_idx(virtio_get_queue(vdev, vq_idx), |
| 67 | (data >> 16) & 0xFFFF); |
| 68 | } |
| 69 | |
| 70 | virtio_queue_notify(vdev, vq_idx); |
| 71 | return 0; |
| 72 | } |
| 73 | |
| 74 | static uint64_t handle_storage_limit(void) |
| 75 | { |
| 76 | S390CcwMachineState *s390ms = S390_CCW_MACHINE(qdev_get_machine()); |
| 77 | |
| 78 | return s390_get_memory_limit(s390ms) - 1; |
| 79 | } |
| 80 | |
| 81 | void handle_diag_500(S390CPU *cpu, uintptr_t ra) |
| 82 | { |
| 83 | CPUS390XState *env = &cpu->env; |
| 84 | const uint64_t subcode = env->regs[1]; |
| 85 | |
| 86 | switch (subcode) { |
| 87 | case DIAG500_VIRTIO_NOTIFY: |
| 88 | env->regs[2] = handle_virtio_notify(env->regs[2]); |
| 89 | break; |
| 90 | case DIAG500_VIRTIO_CCW_NOTIFY: |
| 91 | env->regs[2] = handle_virtio_ccw_notify(env->regs[2], env->regs[3]); |
| 92 | break; |
| 93 | case DIAG500_STORAGE_LIMIT: |
| 94 | env->regs[2] = handle_storage_limit(); |
| 95 | break; |
| 96 | default: |
| 97 | s390_program_interrupt(env, PGM_SPECIFICATION, ra); |
| 98 | } |
| 99 | } |