Commit c2cb92f9 authored by Peter Maydell's avatar Peter Maydell
Browse files

Merge remote-tracking branch 'remotes/borntraeger/tags/kvm-s390-20140227' into staging



Several features, fixes and cleanups for kvm/s390:

- sclp event facility: cleanup structure. This allows to use
  realize/unrealize   as well as migration support via vmsd
- reboot: Two fixes that make reboot much more reliable
- ipl: make elf loading more robust
- flic interrupt controller: This allows to migrate floating
  interrupts, as well as clear them on reset etc.
- enable async_pf feature of KVM on s390
- several sclp fixes and cleanups
- several sigp fixes and cleanups

* remotes/borntraeger/tags/kvm-s390-20140227: (22 commits)
  s390x/ipl: Fix crash of ELF images with arbitrary entry points
  s390x/kvm: Rework priv instruction handlers
  s390x/kvm: Add missing SIGP CPU RESET order
  s390x/kvm: Rework SIGP INITIAL CPU RESET handler
  s390x/cpu: Use ioctl to reset state in the kernel
  s390-ccw.img: new binary rom to match latest fixes
  s390-ccw.img: Fix sporadic errors with ccw boot image - initialize css
  s390-ccw.img: Fix sporadic reboot hangs: Initialize next_idx
  s390x/event-facility: exploit realize/unrealize
  s390x/event-facility: add support for live migration
  s390x/event-facility: code restructure
  s390x/event-facility: some renaming
  s390x/sclp: Fixed setting of condition code register
  s390x/sclp: Add missing checks to SCLP handler
  s390x/sclp: Fixed the size of sccb and code parameter
  s390x/eventfacility: mask out commands
  s390x/virtio-hcall: Specification exception for illegal subcodes
  s390x/virtio-hcall: Add range check for hypervisor call
  s390x/kvm: Fixed bad SIGP SET-ARCHITECTURE handler
  s390x/async_pf: Check for apf extension and enable pfault
  ...

Conflicts:
	linux-headers/linux/kvm.h

Signed-off-by: default avatarPeter Maydell <peter.maydell@linaro.org>
parents 739aa555 7f00eb30
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
CONFIG_VIRTIO=y
CONFIG_SCLPCONSOLE=y
CONFIG_S390_FLIC=$(CONFIG_KVM)
+1 −0
Original line number Diff line number Diff line
@@ -25,3 +25,4 @@ obj-$(CONFIG_SH4) += sh_intc.o
obj-$(CONFIG_XICS) += xics.o
obj-$(CONFIG_XICS_KVM) += xics_kvm.o
obj-$(CONFIG_ALLWINNER_A10_PIC) += allwinner-a10-pic.o
obj-$(CONFIG_S390_FLIC) += s390_flic.o

hw/intc/s390_flic.c

0 → 100644
+322 −0
Original line number Diff line number Diff line
/*
 * QEMU S390x KVM floating interrupt controller (flic)
 *
 * Copyright 2014 IBM Corp.
 * Author(s): Jens Freimann <jfrei@linux.vnet.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 <sys/ioctl.h>
#include "qemu/error-report.h"
#include "hw/sysbus.h"
#include "sysemu/kvm.h"
#include "migration/qemu-file.h"
#include "hw/s390x/s390_flic.h"
#include "trace.h"

#define FLIC_SAVE_INITIAL_SIZE getpagesize()
#define FLIC_FAILED (-1UL)
#define FLIC_SAVEVM_VERSION 1

void s390_flic_init(void)
{
    DeviceState *dev;
    int r;

    if (kvm_enabled()) {
        dev = qdev_create(NULL, "s390-flic");
        object_property_add_child(qdev_get_machine(), "s390-flic",
                                OBJECT(dev), NULL);
        r = qdev_init(dev);
        if (r) {
            error_report("flic: couldn't create qdev");
        }
    }
}

/**
 * flic_get_all_irqs - store all pending irqs in buffer
 * @buf: pointer to buffer which is passed to kernel
 * @len: length of buffer
 * @flic: pointer to flic device state
 *
 * Returns: -ENOMEM if buffer is too small,
 * -EINVAL if attr.group is invalid,
 * -EFAULT if copying to userspace failed,
 * on success return number of stored interrupts
 */
static int flic_get_all_irqs(KVMS390FLICState *flic,
                             void *buf, int len)
{
    struct kvm_device_attr attr = {
        .group = KVM_DEV_FLIC_GET_ALL_IRQS,
        .addr = (uint64_t) buf,
        .attr = len,
    };
    int rc;

    rc = ioctl(flic->fd, KVM_GET_DEVICE_ATTR, &attr);

    return rc == -1 ? -errno : rc;
}

static void flic_enable_pfault(KVMS390FLICState *flic)
{
    struct kvm_device_attr attr = {
        .group = KVM_DEV_FLIC_APF_ENABLE,
    };
    int rc;

    rc = ioctl(flic->fd, KVM_SET_DEVICE_ATTR, &attr);

    if (rc) {
        fprintf(stderr, "flic: couldn't enable pfault\n");
    }
}

static void flic_disable_wait_pfault(KVMS390FLICState *flic)
{
    struct kvm_device_attr attr = {
        .group = KVM_DEV_FLIC_APF_DISABLE_WAIT,
    };
    int rc;

    rc = ioctl(flic->fd, KVM_SET_DEVICE_ATTR, &attr);

    if (rc) {
        fprintf(stderr, "flic: couldn't disable pfault\n");
    }
}

/** flic_enqueue_irqs - returns 0 on success
 * @buf: pointer to buffer which is passed to kernel
 * @len: length of buffer
 * @flic: pointer to flic device state
 *
 * Returns: -EINVAL if attr.group is unknown
 */
static int flic_enqueue_irqs(void *buf, uint64_t len,
                            KVMS390FLICState *flic)
{
    int rc;
    struct kvm_device_attr attr = {
        .group = KVM_DEV_FLIC_ENQUEUE,
        .addr = (uint64_t) buf,
        .attr = len,
    };

    rc = ioctl(flic->fd, KVM_SET_DEVICE_ATTR, &attr);

    return rc ? -errno : 0;
}

/**
 * __get_all_irqs - store all pending irqs in buffer
 * @flic: pointer to flic device state
 * @buf: pointer to pointer to a buffer
 * @len: length of buffer
 *
 * Returns: return value of flic_get_all_irqs
 * Note: Retry and increase buffer size until flic_get_all_irqs
 * either returns a value >= 0 or a negative error code.
 * -ENOMEM is an exception, which means the buffer is too small
 * and we should try again. Other negative error codes can be
 * -EFAULT and -EINVAL which we ignore at this point
 */
static int __get_all_irqs(KVMS390FLICState *flic,
                          void **buf, int len)
{
    int r;

    do {
        /* returns -ENOMEM if buffer is too small and number
         * of queued interrupts on success */
        r = flic_get_all_irqs(flic, *buf, len);
        if (r >= 0) {
            break;
        }
        len *= 2;
        *buf = g_try_realloc(*buf, len);
        if (!buf) {
            return -ENOMEM;
        }
    } while (r == -ENOMEM && len <= KVM_S390_FLIC_MAX_BUFFER);

    return r;
}

/**
 * kvm_flic_save - Save pending floating interrupts
 * @f: QEMUFile containing migration state
 * @opaque: pointer to flic device state
 *
 * Note: Pass buf and len to kernel. Start with one page and
 * increase until buffer is sufficient or maxium size is
 * reached
 */
static void kvm_flic_save(QEMUFile *f, void *opaque)
{
    KVMS390FLICState *flic = opaque;
    int len = FLIC_SAVE_INITIAL_SIZE;
    void *buf;
    int count;

    flic_disable_wait_pfault((struct KVMS390FLICState *) opaque);

    buf = g_try_malloc0(len);
    if (!buf) {
        /* Storing FLIC_FAILED into the count field here will cause the
         * target system to fail when attempting to load irqs from the
         * migration state */
        error_report("flic: couldn't allocate memory");
        qemu_put_be64(f, FLIC_FAILED);
        return;
    }

    count = __get_all_irqs(flic, &buf, len);
    if (count < 0) {
        error_report("flic: couldn't retrieve irqs from kernel, rc %d",
                     count);
        /* Storing FLIC_FAILED into the count field here will cause the
         * target system to fail when attempting to load irqs from the
         * migration state */
        qemu_put_be64(f, FLIC_FAILED);
    } else {
        qemu_put_be64(f, count);
        qemu_put_buffer(f, (uint8_t *) buf,
                        count * sizeof(struct kvm_s390_irq));
    }
    g_free(buf);
}

/**
 * kvm_flic_load - Load pending floating interrupts
 * @f: QEMUFile containing migration state
 * @opaque: pointer to flic device state
 * @version_id: version id for migration
 *
 * Returns: value of flic_enqueue_irqs, -EINVAL on error
 * Note: Do nothing when no interrupts where stored
 * in QEMUFile
 */
static int kvm_flic_load(QEMUFile *f, void *opaque, int version_id)
{
    uint64_t len = 0;
    uint64_t count = 0;
    void *buf = NULL;
    int r = 0;

    if (version_id != FLIC_SAVEVM_VERSION) {
        r = -EINVAL;
        goto out;
    }

    flic_enable_pfault((struct KVMS390FLICState *) opaque);

    count = qemu_get_be64(f);
    len = count * sizeof(struct kvm_s390_irq);
    if (count == FLIC_FAILED) {
        r = -EINVAL;
        goto out;
    }
    if (count == 0) {
        r = 0;
        goto out;
    }
    buf = g_try_malloc0(len);
    if (!buf) {
        r = -ENOMEM;
        goto out;
    }

    if (qemu_get_buffer(f, (uint8_t *) buf, len) != len) {
        r = -EINVAL;
        goto out_free;
    }
    r = flic_enqueue_irqs(buf, len, (struct KVMS390FLICState *) opaque);

out_free:
    g_free(buf);
out:
    return r;
}

static void kvm_s390_flic_realize(DeviceState *dev, Error **errp)
{
    KVMS390FLICState *flic_state = KVM_S390_FLIC(dev);
    struct kvm_create_device cd = {0};
    int ret;

    flic_state->fd = -1;
    if (!kvm_check_extension(kvm_state, KVM_CAP_DEVICE_CTRL)) {
        trace_flic_no_device_api(errno);
        return;
    }

    cd.type = KVM_DEV_TYPE_FLIC;
    ret = kvm_vm_ioctl(kvm_state, KVM_CREATE_DEVICE, &cd);
    if (ret < 0) {
        trace_flic_create_device(errno);
        return;
    }
    flic_state->fd = cd.fd;

    /* Register savevm handler for floating interrupts */
    register_savevm(NULL, "s390-flic", 0, 1, kvm_flic_save,
                    kvm_flic_load, (void *) flic_state);
}

static void kvm_s390_flic_unrealize(DeviceState *dev, Error **errp)
{
    KVMS390FLICState *flic_state = KVM_S390_FLIC(dev);

    unregister_savevm(DEVICE(flic_state), "s390-flic", flic_state);
}

static void kvm_s390_flic_reset(DeviceState *dev)
{
    KVMS390FLICState *flic = KVM_S390_FLIC(dev);
    struct kvm_device_attr attr = {
        .group = KVM_DEV_FLIC_CLEAR_IRQS,
    };
    int rc = 0;

    if (flic->fd == -1) {
        return;
    }

    flic_disable_wait_pfault(flic);

    rc = ioctl(flic->fd, KVM_SET_DEVICE_ATTR, &attr);
    if (rc) {
        trace_flic_reset_failed(errno);
    }

    flic_enable_pfault(flic);
}

static void kvm_s390_flic_class_init(ObjectClass *oc, void *data)
{
    DeviceClass *dc = DEVICE_CLASS(oc);

    dc->realize = kvm_s390_flic_realize;
    dc->unrealize = kvm_s390_flic_unrealize;
    dc->reset = kvm_s390_flic_reset;
}

static const TypeInfo kvm_s390_flic_info = {
    .name          = TYPE_KVM_S390_FLIC,
    .parent        = TYPE_SYS_BUS_DEVICE,
    .instance_size = sizeof(KVMS390FLICState),
    .class_init    = kvm_s390_flic_class_init,
};

static void kvm_s390_flic_register_types(void)
{
    type_register_static(&kvm_s390_flic_info);
}

type_init(kvm_s390_flic_register_types)
+56 −37
Original line number Diff line number Diff line
@@ -21,13 +21,13 @@
#include "hw/s390x/sclp.h"
#include "hw/s390x/event-facility.h"

typedef struct EventTypesBus {
typedef struct SCLPEventsBus {
    BusState qbus;
} EventTypesBus;
} SCLPEventsBus;

struct SCLPEventFacility {
    EventTypesBus sbus;
    DeviceState *qdev;
    SysBusDevice parent_obj;
    SCLPEventsBus sbus;
    /* guest' receive mask */
    unsigned int receive_mask;
};
@@ -291,7 +291,7 @@ static void sclp_events_bus_class_init(ObjectClass *klass, void *data)
{
}

static const TypeInfo s390_sclp_events_bus_info = {
static const TypeInfo sclp_events_bus_info = {
    .name = TYPE_SCLP_EVENTS_BUS,
    .parent = TYPE_BUS,
    .class_init = sclp_events_bus_class_init,
@@ -299,7 +299,7 @@ static const TypeInfo s390_sclp_events_bus_info = {

static void command_handler(SCLPEventFacility *ef, SCCB *sccb, uint64_t code)
{
    switch (code) {
    switch (code & SCLP_CMD_CODE_MASK) {
    case SCLP_CMD_READ_EVENT_DATA:
        read_event_data(ef, sccb);
        break;
@@ -315,21 +315,26 @@ static void command_handler(SCLPEventFacility *ef, SCCB *sccb, uint64_t code)
    }
}

static int init_event_facility(S390SCLPDevice *sdev)
static const VMStateDescription vmstate_event_facility = {
    .name = "vmstate-event-facility",
    .version_id = 0,
    .minimum_version_id = 0,
    .minimum_version_id_old = 0,
    .fields      = (VMStateField[]) {
        VMSTATE_UINT32(receive_mask, SCLPEventFacility),
        VMSTATE_END_OF_LIST()
     }
};

static int init_event_facility(SCLPEventFacility *event_facility)
{
    SCLPEventFacility *event_facility;
    DeviceState *sdev = DEVICE(event_facility);
    DeviceState *quiesce;

    event_facility = g_malloc0(sizeof(SCLPEventFacility));
    sdev->ef = event_facility;
    sdev->sclp_command_handler = command_handler;
    sdev->event_pending = event_pending;

    /* Spawn a new sclp-events facility */
    /* Spawn a new bus for SCLP events */
    qbus_create_inplace(&event_facility->sbus, sizeof(event_facility->sbus),
                        TYPE_SCLP_EVENTS_BUS, DEVICE(sdev), NULL);
                        TYPE_SCLP_EVENTS_BUS, sdev, NULL);
    event_facility->sbus.qbus.allow_hotplug = 0;
    event_facility->qdev = (DeviceState *) sdev;

    quiesce = qdev_create(&event_facility->sbus.qbus, "sclpquiesce");
    if (!quiesce) {
@@ -346,43 +351,57 @@ static int init_event_facility(S390SCLPDevice *sdev)

static void reset_event_facility(DeviceState *dev)
{
    S390SCLPDevice *sdev = SCLP_S390_DEVICE(dev);
    SCLPEventFacility *sdev = EVENT_FACILITY(dev);

    sdev->ef->receive_mask = 0;
    sdev->receive_mask = 0;
}

static void init_event_facility_class(ObjectClass *klass, void *data)
{
    DeviceClass *dc = DEVICE_CLASS(klass);
    S390SCLPDeviceClass *k = SCLP_S390_DEVICE_CLASS(klass);
    SysBusDeviceClass *sbdc = SYS_BUS_DEVICE_CLASS(klass);
    DeviceClass *dc = DEVICE_CLASS(sbdc);
    SCLPEventFacilityClass *k = EVENT_FACILITY_CLASS(dc);

    dc->reset = reset_event_facility;
    dc->vmsd = &vmstate_event_facility;
    k->init = init_event_facility;
    k->command_handler = command_handler;
    k->event_pending = event_pending;
}

static const TypeInfo s390_sclp_event_facility_info = {
    .name          = "s390-sclp-event-facility",
    .parent        = TYPE_DEVICE_S390_SCLP,
    .instance_size = sizeof(S390SCLPDevice),
static const TypeInfo sclp_event_facility_info = {
    .name          = TYPE_SCLP_EVENT_FACILITY,
    .parent        = TYPE_SYS_BUS_DEVICE,
    .instance_size = sizeof(SCLPEventFacility),
    .class_init    = init_event_facility_class,
    .class_size    = sizeof(SCLPEventFacilityClass),
};

static int event_qdev_init(DeviceState *qdev)
static void event_realize(DeviceState *qdev, Error **errp)
{
    SCLPEvent *event = DO_UPCAST(SCLPEvent, qdev, qdev);
    SCLPEvent *event = SCLP_EVENT(qdev);
    SCLPEventClass *child = SCLP_EVENT_GET_CLASS(event);

    return child->init(event);
    if (child->init) {
        int rc = child->init(event);
        if (rc < 0) {
            error_setg(errp, "SCLP event initialization failed.");
            return;
        }
    }
}

static int event_qdev_exit(DeviceState *qdev)
static void event_unrealize(DeviceState *qdev, Error **errp)
{
    SCLPEvent *event = DO_UPCAST(SCLPEvent, qdev, qdev);
    SCLPEvent *event = SCLP_EVENT(qdev);
    SCLPEventClass *child = SCLP_EVENT_GET_CLASS(event);
    if (child->exit) {
        child->exit(event);
        int rc = child->exit(event);
        if (rc < 0) {
            error_setg(errp, "SCLP event exit failed.");
            return;
        }
    }
    return 0;
}

static void event_class_init(ObjectClass *klass, void *data)
@@ -391,11 +410,11 @@ static void event_class_init(ObjectClass *klass, void *data)

    dc->bus_type = TYPE_SCLP_EVENTS_BUS;
    dc->unplug = qdev_simple_unplug_cb;
    dc->init = event_qdev_init;
    dc->exit = event_qdev_exit;
    dc->realize = event_realize;
    dc->unrealize = event_unrealize;
}

static const TypeInfo s390_sclp_event_type_info = {
static const TypeInfo sclp_event_type_info = {
    .name = TYPE_SCLP_EVENT,
    .parent = TYPE_DEVICE,
    .instance_size = sizeof(SCLPEvent),
@@ -406,9 +425,9 @@ static const TypeInfo s390_sclp_event_type_info = {

static void register_types(void)
{
    type_register_static(&s390_sclp_events_bus_info);
    type_register_static(&s390_sclp_event_facility_info);
    type_register_static(&s390_sclp_event_type_info);
    type_register_static(&sclp_events_bus_info);
    type_register_static(&sclp_event_facility_info);
    type_register_static(&sclp_event_type_info);
}

type_init(register_types)
+13 −8
Original line number Diff line number Diff line
@@ -95,7 +95,8 @@ static int s390_ipl_init(SysBusDevice *dev)
        }
        return 0;
    } else {
        kernel_size = load_elf(ipl->kernel, NULL, NULL, NULL, NULL,
        uint64_t pentry = KERN_IMAGE_START;
        kernel_size = load_elf(ipl->kernel, NULL, NULL, &pentry, NULL,
                               NULL, 1, ELF_MACHINE, 0);
        if (kernel_size == -1) {
            kernel_size = load_image_targphys(ipl->kernel, 0, ram_size);
@@ -104,15 +105,19 @@ static int s390_ipl_init(SysBusDevice *dev)
            fprintf(stderr, "could not load kernel '%s'\n", ipl->kernel);
            return -1;
        }
        /* we have to overwrite values in the kernel image, which are "rom" */
        strcpy(rom_ptr(KERN_PARM_AREA), ipl->cmdline);

        /*
         * we can not rely on the ELF entry point, since up to 3.2 this
         * value was 0x800 (the SALIPL loader) and it wont work. For
         * all (Linux) cases 0x10000 (KERN_IMAGE_START) should be fine.
         * Is it a Linux kernel (starting at 0x10000)? If yes, we fill in the
         * kernel parameters here as well. Note: For old kernels (up to 3.2)
         * we can not rely on the ELF entry point - it was 0x800 (the SALIPL
         * loader) and it won't work. For this case we force it to 0x10000, too.
         */
        if (pentry == KERN_IMAGE_START || pentry == 0x800) {
            ipl->start_addr = KERN_IMAGE_START;
            /* Overwrite parameters in the kernel image, which are "rom" */
            strcpy(rom_ptr(KERN_PARM_AREA), ipl->cmdline);
        } else {
            ipl->start_addr = pentry;
        }
    }
    if (ipl->initrd) {
        ram_addr_t initrd_offset;
Loading