xref: /openbmc/qemu/hw/s390x/s390-hypercall.c (revision aa3a285b5bc56a4208b3b57d4a55291e9c260107)
185489fc3SDavid Hildenbrand /*
285489fc3SDavid Hildenbrand  * Support for QEMU/KVM hypercalls on s390
385489fc3SDavid Hildenbrand  *
485489fc3SDavid Hildenbrand  * Copyright 2012 IBM Corp.
585489fc3SDavid Hildenbrand  * Author(s): Cornelia Huck <cornelia.huck@de.ibm.com>
685489fc3SDavid Hildenbrand  *
785489fc3SDavid Hildenbrand  * This work is licensed under the terms of the GNU GPL, version 2 or (at
885489fc3SDavid Hildenbrand  * your option) any later version. See the COPYING file in the top-level
985489fc3SDavid Hildenbrand  * directory.
1085489fc3SDavid Hildenbrand  */
1185489fc3SDavid Hildenbrand 
1285489fc3SDavid Hildenbrand #include "qemu/osdep.h"
1385489fc3SDavid Hildenbrand #include "cpu.h"
14*f7c16865SDavid Hildenbrand #include "hw/s390x/s390-virtio-ccw.h"
1585489fc3SDavid Hildenbrand #include "hw/s390x/s390-hypercall.h"
1685489fc3SDavid Hildenbrand #include "hw/s390x/ioinst.h"
1785489fc3SDavid Hildenbrand #include "hw/s390x/css.h"
1885489fc3SDavid Hildenbrand #include "virtio-ccw.h"
1985489fc3SDavid Hildenbrand 
handle_virtio_notify(uint64_t mem)2085489fc3SDavid Hildenbrand static int handle_virtio_notify(uint64_t mem)
2185489fc3SDavid Hildenbrand {
2285489fc3SDavid Hildenbrand     MachineState *ms = MACHINE(qdev_get_machine());
2385489fc3SDavid Hildenbrand 
2485489fc3SDavid Hildenbrand     if (mem < ms->ram_size) {
2585489fc3SDavid Hildenbrand         /* Early printk */
2685489fc3SDavid Hildenbrand         return 0;
2785489fc3SDavid Hildenbrand     }
2885489fc3SDavid Hildenbrand     return -EINVAL;
2985489fc3SDavid Hildenbrand }
3085489fc3SDavid Hildenbrand 
handle_virtio_ccw_notify(uint64_t subch_id,uint64_t data)3185489fc3SDavid Hildenbrand static int handle_virtio_ccw_notify(uint64_t subch_id, uint64_t data)
3285489fc3SDavid Hildenbrand {
3385489fc3SDavid Hildenbrand     SubchDev *sch;
3485489fc3SDavid Hildenbrand     VirtIODevice *vdev;
3585489fc3SDavid Hildenbrand     int cssid, ssid, schid, m;
3685489fc3SDavid Hildenbrand     uint16_t vq_idx = data;
3785489fc3SDavid Hildenbrand 
3885489fc3SDavid Hildenbrand     if (ioinst_disassemble_sch_ident(subch_id, &m, &cssid, &ssid, &schid)) {
3985489fc3SDavid Hildenbrand         return -EINVAL;
4085489fc3SDavid Hildenbrand     }
4185489fc3SDavid Hildenbrand     sch = css_find_subch(m, cssid, ssid, schid);
4285489fc3SDavid Hildenbrand     if (!sch || !css_subch_visible(sch)) {
4385489fc3SDavid Hildenbrand         return -EINVAL;
4485489fc3SDavid Hildenbrand     }
4585489fc3SDavid Hildenbrand 
4685489fc3SDavid Hildenbrand     vdev = virtio_ccw_get_vdev(sch);
4785489fc3SDavid Hildenbrand     if (vq_idx >= VIRTIO_QUEUE_MAX || !virtio_queue_get_num(vdev, vq_idx)) {
4885489fc3SDavid Hildenbrand         return -EINVAL;
4985489fc3SDavid Hildenbrand     }
5085489fc3SDavid Hildenbrand 
5185489fc3SDavid Hildenbrand     if (virtio_vdev_has_feature(vdev, VIRTIO_F_NOTIFICATION_DATA)) {
5285489fc3SDavid Hildenbrand         virtio_queue_set_shadow_avail_idx(virtio_get_queue(vdev, vq_idx),
5385489fc3SDavid Hildenbrand                                           (data >> 16) & 0xFFFF);
5485489fc3SDavid Hildenbrand     }
5585489fc3SDavid Hildenbrand 
5685489fc3SDavid Hildenbrand     virtio_queue_notify(vdev, vq_idx);
5785489fc3SDavid Hildenbrand     return 0;
5885489fc3SDavid Hildenbrand }
5985489fc3SDavid Hildenbrand 
handle_storage_limit(void)60*f7c16865SDavid Hildenbrand static uint64_t handle_storage_limit(void)
61*f7c16865SDavid Hildenbrand {
62*f7c16865SDavid Hildenbrand     S390CcwMachineState *s390ms = S390_CCW_MACHINE(qdev_get_machine());
63*f7c16865SDavid Hildenbrand 
64*f7c16865SDavid Hildenbrand     return s390_get_memory_limit(s390ms) - 1;
65*f7c16865SDavid Hildenbrand }
66*f7c16865SDavid Hildenbrand 
handle_diag_500(S390CPU * cpu,uintptr_t ra)6785489fc3SDavid Hildenbrand void handle_diag_500(S390CPU *cpu, uintptr_t ra)
6885489fc3SDavid Hildenbrand {
6985489fc3SDavid Hildenbrand     CPUS390XState *env = &cpu->env;
7085489fc3SDavid Hildenbrand     const uint64_t subcode = env->regs[1];
7185489fc3SDavid Hildenbrand 
7285489fc3SDavid Hildenbrand     switch (subcode) {
7385489fc3SDavid Hildenbrand     case DIAG500_VIRTIO_NOTIFY:
7485489fc3SDavid Hildenbrand         env->regs[2] = handle_virtio_notify(env->regs[2]);
7585489fc3SDavid Hildenbrand         break;
7685489fc3SDavid Hildenbrand     case DIAG500_VIRTIO_CCW_NOTIFY:
7785489fc3SDavid Hildenbrand         env->regs[2] = handle_virtio_ccw_notify(env->regs[2], env->regs[3]);
7885489fc3SDavid Hildenbrand         break;
79*f7c16865SDavid Hildenbrand     case DIAG500_STORAGE_LIMIT:
80*f7c16865SDavid Hildenbrand         env->regs[2] = handle_storage_limit();
81*f7c16865SDavid Hildenbrand         break;
8285489fc3SDavid Hildenbrand     default:
8385489fc3SDavid Hildenbrand         s390_program_interrupt(env, PGM_SPECIFICATION, ra);
8485489fc3SDavid Hildenbrand     }
8585489fc3SDavid Hildenbrand }
86