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