From 76ed4b18debfe597329d1f6a9eb2ec9ffa751ecd Mon Sep 17 00:00:00 2001 From: Christian Borntraeger Date: Fri, 22 Jun 2018 12:29:28 +0200 Subject: s390/ipl: fix ipl with -no-reboot kexec/kdump as well as the bootloader use a subcode of diagnose 308 that is supposed to reset the I/O subsystem but not comprise a full "reboot". With the latest refactoring this is now broken when -no-reboot is used or when libvirt acts on a reboot QMP event, for example a virt-install from iso images. We need to mark these "subsystem resets" as special. Fixes: a30fb811cbe9 (s390x: refactor reset/reipl handling) Signed-off-by: Christian Borntraeger Reviewed-by: David Hildenbrand Message-Id: <20180622102928.173420-1-borntraeger@de.ibm.com> Acked-by: Paolo Bonzini Signed-off-by: Cornelia Huck --- include/sysemu/sysemu.h | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'include') diff --git a/include/sysemu/sysemu.h b/include/sysemu/sysemu.h index b921c6f3b7..76ef6196a7 100644 --- a/include/sysemu/sysemu.h +++ b/include/sysemu/sysemu.h @@ -44,6 +44,10 @@ typedef enum ShutdownCause { turns that into a shutdown */ SHUTDOWN_CAUSE_GUEST_PANIC, /* Guest panicked, and command line turns that into a shutdown */ + SHUTDOWN_CAUSE_SUBSYSTEM_RESET,/* Partial guest reset that does not trigger + QMP events and ignores --no-reboot. This + is useful for sanitize hypercalls on s390 + that are used during kexec/kdump/boot */ SHUTDOWN_CAUSE__MAX, } ShutdownCause; -- cgit v1.2.3-55-g7522 From 0f0f8b611eeea663c8d3b6021918033e257411a1 Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Tue, 26 Jun 2018 11:35:40 +0200 Subject: loader: Check access size when calling rom_ptr() to avoid crashes The rom_ptr() function allows direct access to the ROM blobs that we load during startup. However, there are currently no checks for the size of the accesses, so it's currently possible to crash QEMU for example with: $ echo "Insane in the mainframe" > /tmp/test.txt $ s390x-softmmu/qemu-system-s390x -kernel /tmp/test.txt -append xyz Segmentation fault (core dumped) $ s390x-softmmu/qemu-system-s390x -kernel /tmp/test.txt -initrd /tmp/test.txt Segmentation fault (core dumped) $ echo -n HdrS > /tmp/hdr.txt $ sparc64-softmmu/qemu-system-sparc64 -kernel /tmp/hdr.txt -initrd /tmp/hdr.txt Segmentation fault (core dumped) We need a possibility to check the size of the ROM area that we want to access, thus let's add a size parameter to the rom_ptr() function to avoid these problems. Acked-by: Christian Borntraeger Signed-off-by: Thomas Huth Message-Id: <1530005740-25254-1-git-send-email-thuth@redhat.com> Signed-off-by: Cornelia Huck --- hw/core/loader.c | 10 +++++----- hw/mips/mips_malta.c | 6 ++++-- hw/s390x/ipl.c | 18 ++++++++++++------ hw/sparc/sun4m.c | 4 ++-- hw/sparc64/sun4u.c | 4 ++-- include/hw/loader.h | 2 +- target/arm/cpu.c | 2 +- 7 files changed, 27 insertions(+), 19 deletions(-) (limited to 'include') diff --git a/hw/core/loader.c b/hw/core/loader.c index 06bdbca537..bbb6e65bb5 100644 --- a/hw/core/loader.c +++ b/hw/core/loader.c @@ -191,7 +191,7 @@ void pstrcpy_targphys(const char *name, hwaddr dest, int buf_size, rom_add_blob_fixed(name, source, (nulp - source) + 1, dest); } else { rom_add_blob_fixed(name, source, buf_size, dest); - ptr = rom_ptr(dest + buf_size - 1); + ptr = rom_ptr(dest + buf_size - 1, sizeof(*ptr)); *ptr = 0; } } @@ -1165,7 +1165,7 @@ void rom_reset_order_override(void) fw_cfg_reset_order_override(fw_cfg); } -static Rom *find_rom(hwaddr addr) +static Rom *find_rom(hwaddr addr, size_t size) { Rom *rom; @@ -1179,7 +1179,7 @@ static Rom *find_rom(hwaddr addr) if (rom->addr > addr) { continue; } - if (rom->addr + rom->romsize < addr) { + if (rom->addr + rom->romsize < addr + size) { continue; } return rom; @@ -1249,11 +1249,11 @@ int rom_copy(uint8_t *dest, hwaddr addr, size_t size) return (d + l) - dest; } -void *rom_ptr(hwaddr addr) +void *rom_ptr(hwaddr addr, size_t size) { Rom *rom; - rom = find_rom(addr); + rom = find_rom(addr, size); if (!rom || !rom->data) return NULL; return rom->data + (addr - rom->addr); diff --git a/hw/mips/mips_malta.c b/hw/mips/mips_malta.c index b9d92bf47e..1b4e32e58e 100644 --- a/hw/mips/mips_malta.c +++ b/hw/mips/mips_malta.c @@ -1133,11 +1133,13 @@ void mips_malta_init(MachineState *machine) a neat trick which allows bi-endian firmware. */ #ifndef TARGET_WORDS_BIGENDIAN { - uint32_t *end, *addr = rom_ptr(FLASH_ADDRESS); + uint32_t *end, *addr; + const size_t swapsize = MIN(bios_size, 0x3e0000); + addr = rom_ptr(FLASH_ADDRESS, swapsize); if (!addr) { addr = memory_region_get_ram_ptr(bios); } - end = (void *)addr + MIN(bios_size, 0x3e0000); + end = (void *)addr + swapsize; while (addr < end) { bswap32s(addr); addr++; diff --git a/hw/s390x/ipl.c b/hw/s390x/ipl.c index f278036fa7..21f64ad26a 100644 --- a/hw/s390x/ipl.c +++ b/hw/s390x/ipl.c @@ -33,7 +33,6 @@ #define KERN_PARM_AREA 0x010480UL #define INITRD_START 0x800000UL #define INITRD_PARM_START 0x010408UL -#define INITRD_PARM_SIZE 0x010410UL #define PARMFILE_START 0x001000UL #define ZIPL_IMAGE_START 0x009000UL #define IPL_PSW_MASK (PSW_MASK_32 | PSW_MASK_64) @@ -165,12 +164,12 @@ static void s390_ipl_realize(DeviceState *dev, Error **errp) goto error; } /* if this is Linux use KERN_IMAGE_START */ - magic = rom_ptr(LINUX_MAGIC_ADDR); + magic = rom_ptr(LINUX_MAGIC_ADDR, 6); if (magic && !memcmp(magic, "S390EP", 6)) { pentry = KERN_IMAGE_START; } else { /* if not Linux load the address of the (short) IPL PSW */ - ipl_psw = rom_ptr(4); + ipl_psw = rom_ptr(4, 4); if (ipl_psw) { pentry = be32_to_cpu(*ipl_psw) & 0x7fffffffUL; } else { @@ -186,9 +185,12 @@ static void s390_ipl_realize(DeviceState *dev, Error **errp) * loader) and it won't work. For this case we force it to 0x10000, too. */ if (pentry == KERN_IMAGE_START || pentry == 0x800) { + char *parm_area = rom_ptr(KERN_PARM_AREA, strlen(ipl->cmdline) + 1); ipl->start_addr = KERN_IMAGE_START; /* Overwrite parameters in the kernel image, which are "rom" */ - strcpy(rom_ptr(KERN_PARM_AREA), ipl->cmdline); + if (parm_area) { + strcpy(parm_area, ipl->cmdline); + } } else { ipl->start_addr = pentry; } @@ -196,6 +198,7 @@ static void s390_ipl_realize(DeviceState *dev, Error **errp) if (ipl->initrd) { ram_addr_t initrd_offset; int initrd_size; + uint64_t *romptr; initrd_offset = INITRD_START; while (kernel_size + 0x100000 > initrd_offset) { @@ -212,8 +215,11 @@ static void s390_ipl_realize(DeviceState *dev, Error **errp) * we have to overwrite values in the kernel image, * which are "rom" */ - stq_p(rom_ptr(INITRD_PARM_START), initrd_offset); - stq_p(rom_ptr(INITRD_PARM_SIZE), initrd_size); + romptr = rom_ptr(INITRD_PARM_START, 16); + if (romptr) { + stq_p(romptr, initrd_offset); + stq_p(romptr + 1, initrd_size); + } } } /* diff --git a/hw/sparc/sun4m.c b/hw/sparc/sun4m.c index b984d2da0e..21078cc121 100644 --- a/hw/sparc/sun4m.c +++ b/hw/sparc/sun4m.c @@ -272,8 +272,8 @@ static unsigned long sun4m_load_kernel(const char *kernel_filename, } if (initrd_size > 0) { for (i = 0; i < 64 * TARGET_PAGE_SIZE; i += TARGET_PAGE_SIZE) { - ptr = rom_ptr(KERNEL_LOAD_ADDR + i); - if (ldl_p(ptr) == 0x48647253) { // HdrS + ptr = rom_ptr(KERNEL_LOAD_ADDR + i, 24); + if (ptr && ldl_p(ptr) == 0x48647253) { /* HdrS */ stl_p(ptr + 16, INITRD_LOAD_ADDR); stl_p(ptr + 20, initrd_size); break; diff --git a/hw/sparc64/sun4u.c b/hw/sparc64/sun4u.c index 3975a7b65a..334dd7008e 100644 --- a/hw/sparc64/sun4u.c +++ b/hw/sparc64/sun4u.c @@ -186,8 +186,8 @@ static uint64_t sun4u_load_kernel(const char *kernel_filename, } if (*initrd_size > 0) { for (i = 0; i < 64 * TARGET_PAGE_SIZE; i += TARGET_PAGE_SIZE) { - ptr = rom_ptr(*kernel_addr + i); - if (ldl_p(ptr + 8) == 0x48647253) { /* HdrS */ + ptr = rom_ptr(*kernel_addr + i, 32); + if (ptr && ldl_p(ptr + 8) == 0x48647253) { /* HdrS */ stl_p(ptr + 24, *initrd_addr + *kernel_addr); stl_p(ptr + 28, *initrd_size); break; diff --git a/include/hw/loader.h b/include/hw/loader.h index 5ed3fd8ae6..e98b84b8f9 100644 --- a/include/hw/loader.h +++ b/include/hw/loader.h @@ -226,7 +226,7 @@ void rom_set_fw(FWCfgState *f); void rom_set_order_override(int order); void rom_reset_order_override(void); int rom_copy(uint8_t *dest, hwaddr addr, size_t size); -void *rom_ptr(hwaddr addr); +void *rom_ptr(hwaddr addr, size_t size); void hmp_info_roms(Monitor *mon, const QDict *qdict); #define rom_add_file_fixed(_f, _a, _i) \ diff --git a/target/arm/cpu.c b/target/arm/cpu.c index 82ff450f9a..64a8005a4b 100644 --- a/target/arm/cpu.c +++ b/target/arm/cpu.c @@ -239,7 +239,7 @@ static void arm_cpu_reset(CPUState *s) /* Load the initial SP and PC from offset 0 and 4 in the vector table */ vecbase = env->v7m.vecbase[env->v7m.secure]; - rom = rom_ptr(vecbase); + rom = rom_ptr(vecbase, 8); if (rom) { /* Address zero is covered by ROM which hasn't yet been * copied into physical memory. -- cgit v1.2.3-55-g7522 From 8046f374a64b81fdf4f71f7a433bf4035d501521 Mon Sep 17 00:00:00 2001 From: David Hildenbrand Date: Wed, 27 Jun 2018 15:44:04 +0200 Subject: s390x/tod: factor out TOD into separate device Let's treat this like a separate device. TCG will have to store the actual state/time later on. Include cpu-qom.h in kvm_s390x.h (due to S390CPU) to compile tod-kvm.c. Reviewed-by: Thomas Huth Signed-off-by: David Hildenbrand Message-Id: <20180627134410.4901-4-david@redhat.com> Signed-off-by: Cornelia Huck --- hw/s390x/Makefile.objs | 3 ++ hw/s390x/s390-virtio-ccw.c | 57 ++-------------------- hw/s390x/tod-kvm.c | 64 ++++++++++++++++++++++++ hw/s390x/tod-qemu.c | 49 +++++++++++++++++++ hw/s390x/tod.c | 119 +++++++++++++++++++++++++++++++++++++++++++++ include/hw/s390x/tod.h | 46 ++++++++++++++++++ target/s390x/cpu.c | 32 ------------ target/s390x/cpu.h | 2 - target/s390x/kvm_s390x.h | 2 + 9 files changed, 286 insertions(+), 88 deletions(-) create mode 100644 hw/s390x/tod-kvm.c create mode 100644 hw/s390x/tod-qemu.c create mode 100644 hw/s390x/tod.c create mode 100644 include/hw/s390x/tod.h (limited to 'include') diff --git a/hw/s390x/Makefile.objs b/hw/s390x/Makefile.objs index dc704b57d6..93282f7c59 100644 --- a/hw/s390x/Makefile.objs +++ b/hw/s390x/Makefile.objs @@ -14,6 +14,9 @@ obj-$(CONFIG_PCI) += s390-pci-bus.o s390-pci-inst.o obj-$(call lnot,$(CONFIG_PCI)) += s390-pci-stub.o obj-y += s390-skeys.o obj-y += s390-stattrib.o +obj-y += tod.o +obj-$(CONFIG_KVM) += tod-kvm.o +obj-$(CONFIG_TCG) += tod-qemu.o obj-$(CONFIG_KVM) += s390-skeys-kvm.o obj-$(CONFIG_KVM) += s390-stattrib-kvm.o obj-y += s390-ccw.o diff --git a/hw/s390x/s390-virtio-ccw.c b/hw/s390x/s390-virtio-ccw.c index f8f58c8acb..7983185d04 100644 --- a/hw/s390x/s390-virtio-ccw.c +++ b/hw/s390x/s390-virtio-ccw.c @@ -35,6 +35,7 @@ #include "migration/register.h" #include "cpu_models.h" #include "hw/nmi.h" +#include "hw/s390x/tod.h" S390CPU *s390_cpu_addr2state(uint16_t cpu_addr) { @@ -187,58 +188,6 @@ static void s390_memory_init(ram_addr_t mem_size) s390_stattrib_init(); } -#define S390_TOD_CLOCK_VALUE_MISSING 0x00 -#define S390_TOD_CLOCK_VALUE_PRESENT 0x01 - -static void gtod_save(QEMUFile *f, void *opaque) -{ - uint64_t tod_low; - uint8_t tod_high; - int r; - - r = s390_get_clock(&tod_high, &tod_low); - if (r) { - warn_report("Unable to get guest clock for migration: %s", - strerror(-r)); - error_printf("Guest clock will not be migrated " - "which could cause the guest to hang."); - qemu_put_byte(f, S390_TOD_CLOCK_VALUE_MISSING); - return; - } - - qemu_put_byte(f, S390_TOD_CLOCK_VALUE_PRESENT); - qemu_put_byte(f, tod_high); - qemu_put_be64(f, tod_low); -} - -static int gtod_load(QEMUFile *f, void *opaque, int version_id) -{ - uint64_t tod_low; - uint8_t tod_high; - int r; - - if (qemu_get_byte(f) == S390_TOD_CLOCK_VALUE_MISSING) { - warn_report("Guest clock was not migrated. This could " - "cause the guest to hang."); - return 0; - } - - tod_high = qemu_get_byte(f); - tod_low = qemu_get_be64(f); - - r = s390_set_clock(&tod_high, &tod_low); - if (r) { - error_report("Unable to set KVM guest TOD clock: %s", strerror(-r)); - } - - return r; -} - -static SaveVMHandlers savevm_gtod = { - .save_state = gtod_save, - .load_state = gtod_load, -}; - static void s390_init_ipl_dev(const char *kernel_filename, const char *kernel_cmdline, const char *initrd_filename, const char *firmware, @@ -363,8 +312,8 @@ static void ccw_init(MachineState *machine) s390_create_sclpconsole("sclplmconsole", serial_hd(1)); } - /* Register savevm handler for guest TOD clock */ - register_savevm_live(NULL, "todclock", 0, 1, &savevm_gtod, NULL); + /* init the TOD clock */ + s390_init_tod(); } static void s390_cpu_plug(HotplugHandler *hotplug_dev, diff --git a/hw/s390x/tod-kvm.c b/hw/s390x/tod-kvm.c new file mode 100644 index 0000000000..df564ab89c --- /dev/null +++ b/hw/s390x/tod-kvm.c @@ -0,0 +1,64 @@ +/* + * TOD (Time Of Day) clock - KVM implementation + * + * Copyright 2018 Red Hat, Inc. + * Author(s): David Hildenbrand + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + */ + +#include "qemu/osdep.h" +#include "qapi/error.h" +#include "hw/s390x/tod.h" +#include "kvm_s390x.h" + +static void kvm_s390_tod_get(const S390TODState *td, S390TOD *tod, Error **errp) +{ + int r; + + r = kvm_s390_get_clock_ext(&tod->high, &tod->low); + if (r == -ENXIO) { + r = kvm_s390_get_clock(&tod->high, &tod->low); + } + if (r) { + error_setg(errp, "Unable to get KVM guest TOD clock: %s", + strerror(-r)); + } +} + +static void kvm_s390_tod_set(S390TODState *td, const S390TOD *tod, Error **errp) +{ + int r; + + r = kvm_s390_set_clock_ext(tod->high, tod->low); + if (r == -ENXIO) { + r = kvm_s390_set_clock(tod->high, tod->low); + } + if (r) { + error_setg(errp, "Unable to set KVM guest TOD clock: %s", + strerror(-r)); + } +} + +static void kvm_s390_tod_class_init(ObjectClass *oc, void *data) +{ + S390TODClass *tdc = S390_TOD_CLASS(oc); + + tdc->get = kvm_s390_tod_get; + tdc->set = kvm_s390_tod_set; +} + +static TypeInfo kvm_s390_tod_info = { + .name = TYPE_KVM_S390_TOD, + .parent = TYPE_S390_TOD, + .instance_size = sizeof(S390TODState), + .class_init = kvm_s390_tod_class_init, + .class_size = sizeof(S390TODClass), +}; + +static void register_types(void) +{ + type_register_static(&kvm_s390_tod_info); +} +type_init(register_types); diff --git a/hw/s390x/tod-qemu.c b/hw/s390x/tod-qemu.c new file mode 100644 index 0000000000..03ea1ce4e8 --- /dev/null +++ b/hw/s390x/tod-qemu.c @@ -0,0 +1,49 @@ +/* + * TOD (Time Of Day) clock - QEMU implementation + * + * Copyright 2018 Red Hat, Inc. + * Author(s): David Hildenbrand + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + */ + +#include "qemu/osdep.h" +#include "qapi/error.h" +#include "hw/s390x/tod.h" + +static void qemu_s390_tod_get(const S390TODState *td, S390TOD *tod, + Error **errp) +{ + /* FIXME */ + tod->high = 0; + tod->low = 0; +} + +static void qemu_s390_tod_set(S390TODState *td, const S390TOD *tod, + Error **errp) +{ + /* FIXME */ +} + +static void qemu_s390_tod_class_init(ObjectClass *oc, void *data) +{ + S390TODClass *tdc = S390_TOD_CLASS(oc); + + tdc->get = qemu_s390_tod_get; + tdc->set = qemu_s390_tod_set; +} + +static TypeInfo qemu_s390_tod_info = { + .name = TYPE_QEMU_S390_TOD, + .parent = TYPE_S390_TOD, + .instance_size = sizeof(S390TODState), + .class_init = qemu_s390_tod_class_init, + .class_size = sizeof(S390TODClass), +}; + +static void register_types(void) +{ + type_register_static(&qemu_s390_tod_info); +} +type_init(register_types); diff --git a/hw/s390x/tod.c b/hw/s390x/tod.c new file mode 100644 index 0000000000..0501affa7f --- /dev/null +++ b/hw/s390x/tod.c @@ -0,0 +1,119 @@ +/* + * TOD (Time Of Day) clock + * + * Copyright 2018 Red Hat, Inc. + * Author(s): David Hildenbrand + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + */ + +#include "qemu/osdep.h" +#include "hw/s390x/tod.h" +#include "qapi/error.h" +#include "qemu/error-report.h" +#include "sysemu/kvm.h" +#include "migration/register.h" + +void s390_init_tod(void) +{ + Object *obj; + + if (kvm_enabled()) { + obj = object_new(TYPE_KVM_S390_TOD); + } else { + obj = object_new(TYPE_QEMU_S390_TOD); + } + object_property_add_child(qdev_get_machine(), TYPE_S390_TOD, obj, NULL); + object_unref(obj); + + qdev_init_nofail(DEVICE(obj)); +} + +#define S390_TOD_CLOCK_VALUE_MISSING 0x00 +#define S390_TOD_CLOCK_VALUE_PRESENT 0x01 + +static void s390_tod_save(QEMUFile *f, void *opaque) +{ + S390TODState *td = opaque; + S390TODClass *tdc = S390_TOD_GET_CLASS(td); + Error *err = NULL; + S390TOD tod; + + tdc->get(td, &tod, &err); + if (err) { + warn_report_err(err); + error_printf("Guest clock will not be migrated " + "which could cause the guest to hang."); + qemu_put_byte(f, S390_TOD_CLOCK_VALUE_MISSING); + return; + } + + qemu_put_byte(f, S390_TOD_CLOCK_VALUE_PRESENT); + qemu_put_byte(f, tod.high); + qemu_put_be64(f, tod.low); +} + +static int s390_tod_load(QEMUFile *f, void *opaque, int version_id) +{ + S390TODState *td = opaque; + S390TODClass *tdc = S390_TOD_GET_CLASS(td); + Error *err = NULL; + S390TOD tod; + + if (qemu_get_byte(f) == S390_TOD_CLOCK_VALUE_MISSING) { + warn_report("Guest clock was not migrated. This could " + "cause the guest to hang."); + return 0; + } + + tod.high = qemu_get_byte(f); + tod.low = qemu_get_be64(f); + + tdc->set(td, &tod, &err); + if (err) { + error_report_err(err); + return -1; + } + return 0; +} + +static SaveVMHandlers savevm_tod = { + .save_state = s390_tod_save, + .load_state = s390_tod_load, +}; + +static void s390_tod_realize(DeviceState *dev, Error **errp) +{ + S390TODState *td = S390_TOD(dev); + + /* Legacy migration interface */ + register_savevm_live(NULL, "todclock", 0, 1, &savevm_tod, td); +} + +static void s390_tod_class_init(ObjectClass *oc, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(oc); + + dc->desc = "TOD (Time Of Day) Clock"; + dc->realize = s390_tod_realize; + set_bit(DEVICE_CATEGORY_MISC, dc->categories); + + /* We only have one TOD clock in the system attached to the machine */ + dc->user_creatable = false; +} + +static TypeInfo s390_tod_info = { + .name = TYPE_S390_TOD, + .parent = TYPE_DEVICE, + .instance_size = sizeof(S390TODState), + .class_init = s390_tod_class_init, + .class_size = sizeof(S390TODClass), + .abstract = true, +}; + +static void register_types(void) +{ + type_register_static(&s390_tod_info); +} +type_init(register_types); diff --git a/include/hw/s390x/tod.h b/include/hw/s390x/tod.h new file mode 100644 index 0000000000..7096b574ae --- /dev/null +++ b/include/hw/s390x/tod.h @@ -0,0 +1,46 @@ +/* + * TOD (Time Of Day) clock + * + * Copyright 2018 Red Hat, Inc. + * Author(s): David Hildenbrand + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + */ + +#ifndef HW_S390_TOD_H +#define HW_S390_TOD_H + +#include "hw/qdev.h" + +typedef struct S390TOD { + uint8_t high; + uint64_t low; +} S390TOD; + +#define TYPE_S390_TOD "s390-tod" +#define S390_TOD(obj) OBJECT_CHECK(S390TODState, (obj), TYPE_S390_TOD) +#define S390_TOD_CLASS(oc) OBJECT_CLASS_CHECK(S390TODClass, (oc), \ + TYPE_S390_TOD) +#define S390_TOD_GET_CLASS(obj) OBJECT_GET_CLASS(S390TODClass, (obj), \ + TYPE_S390_TOD) +#define TYPE_KVM_S390_TOD TYPE_S390_TOD "-kvm" +#define TYPE_QEMU_S390_TOD TYPE_S390_TOD "-qemu" + +typedef struct S390TODState { + /* private */ + DeviceState parent_obj; +} S390TODState; + +typedef struct S390TODClass { + /* private */ + DeviceClass parent_class; + + /* public */ + void (*get)(const S390TODState *td, S390TOD *tod, Error **errp); + void (*set)(S390TODState *td, const S390TOD *tod, Error **errp); +} S390TODClass; + +void s390_init_tod(void); + +#endif diff --git a/target/s390x/cpu.c b/target/s390x/cpu.c index 68512e3e54..03ea6eafa7 100644 --- a/target/s390x/cpu.c +++ b/target/s390x/cpu.c @@ -390,38 +390,6 @@ unsigned int s390_cpu_set_state(uint8_t cpu_state, S390CPU *cpu) return s390_count_running_cpus(); } -int s390_get_clock(uint8_t *tod_high, uint64_t *tod_low) -{ - int r = 0; - - if (kvm_enabled()) { - r = kvm_s390_get_clock_ext(tod_high, tod_low); - if (r == -ENXIO) { - return kvm_s390_get_clock(tod_high, tod_low); - } - } else { - /* Fixme TCG */ - *tod_high = 0; - *tod_low = 0; - } - - return r; -} - -int s390_set_clock(uint8_t *tod_high, uint64_t *tod_low) -{ - int r = 0; - - if (kvm_enabled()) { - r = kvm_s390_set_clock_ext(*tod_high, *tod_low); - if (r == -ENXIO) { - return kvm_s390_set_clock(*tod_high, *tod_low); - } - } - /* Fixme TCG */ - return r; -} - int s390_set_memory_limit(uint64_t new_limit, uint64_t *hw_limit) { if (kvm_enabled()) { diff --git a/target/s390x/cpu.h b/target/s390x/cpu.h index 6629a533f3..ac51c17fb4 100644 --- a/target/s390x/cpu.h +++ b/target/s390x/cpu.h @@ -714,8 +714,6 @@ static inline void s390_do_cpu_load_normal(CPUState *cs, run_on_cpu_data arg) /* cpu.c */ -int s390_get_clock(uint8_t *tod_high, uint64_t *tod_low); -int s390_set_clock(uint8_t *tod_high, uint64_t *tod_low); void s390_crypto_reset(void); bool s390_get_squash_mcss(void); int s390_set_memory_limit(uint64_t new_limit, uint64_t *hw_limit); diff --git a/target/s390x/kvm_s390x.h b/target/s390x/kvm_s390x.h index 36eb34bf99..6e52287da3 100644 --- a/target/s390x/kvm_s390x.h +++ b/target/s390x/kvm_s390x.h @@ -10,6 +10,8 @@ #ifndef KVM_S390X_H #define KVM_S390X_H +#include "cpu-qom.h" + struct kvm_s390_irq; void kvm_s390_floating_interrupt_legacy(struct kvm_s390_irq *irq); -- cgit v1.2.3-55-g7522 From 7de3b1cdc67dcb572c1761c2051252e91a438b22 Mon Sep 17 00:00:00 2001 From: David Hildenbrand Date: Wed, 27 Jun 2018 15:44:06 +0200 Subject: s390x/tcg: properly implement the TOD Right now, each CPU has its own TOD. Especially, the TOD will differ based on creation time of a CPU - e.g. when hotplugging a CPU the times will differ quite a lot, resulting in stall warnings in the guest. Let's use a single TOD by implementing our new TOD device. Prepare it for TOD-clock epoch extension. Most importantly, whenever we set the TOD, we have to update the CKC timer. Introduce "tcg_s390x.h" just like "kvm_s390x.h" for tcg specific function declarations that should not go into cpu.h. Reviewed-by: Thomas Huth Signed-off-by: David Hildenbrand Message-Id: <20180627134410.4901-6-david@redhat.com> Signed-off-by: Cornelia Huck --- hw/s390x/tod-qemu.c | 46 ++++++++++++++++++++++++++++++++++++++++++---- hw/s390x/tod.c | 11 +++++++++++ include/hw/s390x/tod.h | 19 +++++++++++++++++++ target/s390x/cpu.c | 7 ------- target/s390x/cpu.h | 1 - target/s390x/internal.h | 16 ---------------- target/s390x/misc_helper.c | 25 +++++++++++++++++++------ target/s390x/tcg_s390x.h | 18 ++++++++++++++++++ 8 files changed, 109 insertions(+), 34 deletions(-) create mode 100644 target/s390x/tcg_s390x.h (limited to 'include') diff --git a/hw/s390x/tod-qemu.c b/hw/s390x/tod-qemu.c index 03ea1ce4e8..59c015c69d 100644 --- a/hw/s390x/tod-qemu.c +++ b/hw/s390x/tod-qemu.c @@ -11,19 +11,43 @@ #include "qemu/osdep.h" #include "qapi/error.h" #include "hw/s390x/tod.h" +#include "qemu/timer.h" +#include "qemu/cutils.h" +#include "cpu.h" +#include "tcg_s390x.h" static void qemu_s390_tod_get(const S390TODState *td, S390TOD *tod, Error **errp) { - /* FIXME */ - tod->high = 0; - tod->low = 0; + *tod = td->base; + + tod->low += time2tod(qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL)); + if (tod->low < td->base.low) { + tod->high++; + } } static void qemu_s390_tod_set(S390TODState *td, const S390TOD *tod, Error **errp) { - /* FIXME */ + CPUState *cpu; + + td->base = *tod; + + td->base.low -= time2tod(qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL)); + if (td->base.low > tod->low) { + td->base.high--; + } + + /* + * The TOD has been changed and we have to recalculate the CKC values + * for all CPUs. We do this asynchronously, as "SET CLOCK should be + * issued only while all other activity on all CPUs .. has been + * suspended". + */ + CPU_FOREACH(cpu) { + async_run_on_cpu(cpu, tcg_s390_tod_updated, RUN_ON_CPU_NULL); + } } static void qemu_s390_tod_class_init(ObjectClass *oc, void *data) @@ -34,10 +58,24 @@ static void qemu_s390_tod_class_init(ObjectClass *oc, void *data) tdc->set = qemu_s390_tod_set; } +static void qemu_s390_tod_init(Object *obj) +{ + S390TODState *td = S390_TOD(obj); + struct tm tm; + + qemu_get_timedate(&tm, 0); + td->base.high = 0; + td->base.low = TOD_UNIX_EPOCH + (time2tod(mktimegm(&tm)) * 1000000000ULL); + if (td->base.low < TOD_UNIX_EPOCH) { + td->base.high += 1; + } +} + static TypeInfo qemu_s390_tod_info = { .name = TYPE_QEMU_S390_TOD, .parent = TYPE_S390_TOD, .instance_size = sizeof(S390TODState), + .instance_init = qemu_s390_tod_init, .class_init = qemu_s390_tod_class_init, .class_size = sizeof(S390TODClass), }; diff --git a/hw/s390x/tod.c b/hw/s390x/tod.c index 0501affa7f..1c63f411e6 100644 --- a/hw/s390x/tod.c +++ b/hw/s390x/tod.c @@ -30,6 +30,17 @@ void s390_init_tod(void) qdev_init_nofail(DEVICE(obj)); } +S390TODState *s390_get_todstate(void) +{ + static S390TODState *ts; + + if (!ts) { + ts = S390_TOD(object_resolve_path_type("", TYPE_S390_TOD, NULL)); + } + + return ts; +} + #define S390_TOD_CLOCK_VALUE_MISSING 0x00 #define S390_TOD_CLOCK_VALUE_PRESENT 0x01 diff --git a/include/hw/s390x/tod.h b/include/hw/s390x/tod.h index 7096b574ae..413c0d7c02 100644 --- a/include/hw/s390x/tod.h +++ b/include/hw/s390x/tod.h @@ -30,6 +30,9 @@ typedef struct S390TOD { typedef struct S390TODState { /* private */ DeviceState parent_obj; + + /* unused by KVM implementation */ + S390TOD base; } S390TODState; typedef struct S390TODClass { @@ -41,6 +44,22 @@ typedef struct S390TODClass { void (*set)(S390TODState *td, const S390TOD *tod, Error **errp); } S390TODClass; +/* The value of the TOD clock for 1.1.1970. */ +#define TOD_UNIX_EPOCH 0x7d91048bca000000ULL + +/* Converts ns to s390's clock format */ +static inline uint64_t time2tod(uint64_t ns) +{ + return (ns << 9) / 125 + (((ns & 0xff10000000000000ull) / 125) << 9); +} + +/* Converts s390's clock format to ns */ +static inline uint64_t tod2time(uint64_t t) +{ + return ((t >> 9) * 125) + (((t & 0x1ff) * 125) >> 9); +} + void s390_init_tod(void); +S390TODState *s390_get_todstate(void); #endif diff --git a/target/s390x/cpu.c b/target/s390x/cpu.c index a41b3f34ab..40d6980229 100644 --- a/target/s390x/cpu.c +++ b/target/s390x/cpu.c @@ -30,7 +30,6 @@ #include "kvm_s390x.h" #include "sysemu/kvm.h" #include "qemu-common.h" -#include "qemu/cutils.h" #include "qemu/timer.h" #include "qemu/error-report.h" #include "trace.h" @@ -275,9 +274,6 @@ static void s390_cpu_initfn(Object *obj) CPUState *cs = CPU(obj); S390CPU *cpu = S390_CPU(obj); CPUS390XState *env = &cpu->env; -#if !defined(CONFIG_USER_ONLY) - struct tm tm; -#endif cs->env_ptr = env; cs->halted = 1; @@ -286,9 +282,6 @@ static void s390_cpu_initfn(Object *obj) s390_cpu_get_crash_info_qom, NULL, NULL, NULL, NULL); s390_cpu_model_register_props(obj); #if !defined(CONFIG_USER_ONLY) - qemu_get_timedate(&tm, 0); - env->tod_offset = TOD_UNIX_EPOCH + - (time2tod(mktimegm(&tm)) * 1000000000ULL); env->tod_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, s390x_tod_timer, cpu); env->cpu_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, s390x_cpu_timer, cpu); s390_cpu_set_state(S390_CPU_STATE_STOPPED, cpu); diff --git a/target/s390x/cpu.h b/target/s390x/cpu.h index 4abfe88a3d..2c3dd2d189 100644 --- a/target/s390x/cpu.h +++ b/target/s390x/cpu.h @@ -130,7 +130,6 @@ struct CPUS390XState { uint64_t cpuid; #endif - uint64_t tod_offset; QEMUTimer *tod_timer; QEMUTimer *cpu_timer; diff --git a/target/s390x/internal.h b/target/s390x/internal.h index 6cf63340bf..f2a771e2b4 100644 --- a/target/s390x/internal.h +++ b/target/s390x/internal.h @@ -237,22 +237,6 @@ enum cc_op { CC_OP_MAX }; -/* The value of the TOD clock for 1.1.1970. */ -#define TOD_UNIX_EPOCH 0x7d91048bca000000ULL - -/* Converts ns to s390's clock format */ -static inline uint64_t time2tod(uint64_t ns) -{ - return (ns << 9) / 125 + (((ns & 0xff10000000000000ull) / 125) << 9); - -} - -/* Converts s390's clock format to ns */ -static inline uint64_t tod2time(uint64_t t) -{ - return ((t >> 9) * 125) + (((t & 0x1ff) * 125) >> 9); -} - static inline hwaddr decode_basedisp_s(CPUS390XState *env, uint32_t ipb, uint8_t *ar) { diff --git a/target/s390x/misc_helper.c b/target/s390x/misc_helper.c index dd5273949b..229f14d3da 100644 --- a/target/s390x/misc_helper.c +++ b/target/s390x/misc_helper.c @@ -28,6 +28,8 @@ #include "qemu/timer.h" #include "exec/exec-all.h" #include "exec/cpu_ldst.h" +#include "qapi/error.h" +#include "tcg_s390x.h" #if !defined(CONFIG_USER_ONLY) #include "sysemu/cpus.h" @@ -39,6 +41,7 @@ #include "hw/s390x/ioinst.h" #include "hw/s390x/s390-pci-inst.h" #include "hw/boards.h" +#include "hw/s390x/tod.h" #endif /* #define DEBUG_HELPER */ @@ -138,17 +141,19 @@ void HELPER(spx)(CPUS390XState *env, uint64_t a1) /* Store Clock */ uint64_t HELPER(stck)(CPUS390XState *env) { - uint64_t time; + S390TODState *td = s390_get_todstate(); + S390TODClass *tdc = S390_TOD_GET_CLASS(td); + S390TOD tod; - time = env->tod_offset + - time2tod(qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL)); - - return time; + tdc->get(td, &tod, &error_abort); + return tod.low; } /* Set Clock Comparator */ void HELPER(sckc)(CPUS390XState *env, uint64_t time) { + S390TODState *td = s390_get_todstate(); + if (time == -1ULL) { return; } @@ -156,7 +161,7 @@ void HELPER(sckc)(CPUS390XState *env, uint64_t time) env->ckc = time; /* difference between origins */ - time -= env->tod_offset; + time -= td->base.low; /* nanoseconds */ time = tod2time(time); @@ -164,6 +169,14 @@ void HELPER(sckc)(CPUS390XState *env, uint64_t time) timer_mod(env->tod_timer, time); } +void tcg_s390_tod_updated(CPUState *cs, run_on_cpu_data opaque) +{ + S390CPU *cpu = S390_CPU(cs); + CPUS390XState *env = &cpu->env; + + helper_sckc(env, env->ckc); +} + /* Set Tod Programmable Field */ void HELPER(sckpf)(CPUS390XState *env, uint64_t r0) { diff --git a/target/s390x/tcg_s390x.h b/target/s390x/tcg_s390x.h new file mode 100644 index 0000000000..4e308aa0ce --- /dev/null +++ b/target/s390x/tcg_s390x.h @@ -0,0 +1,18 @@ +/* + * QEMU TCG support -- s390x specific functions. + * + * Copyright 2018 Red Hat, Inc. + * + * Authors: + * David Hildenbrand + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + */ + +#ifndef TCG_S390X_H +#define TCG_S390X_H + +void tcg_s390_tod_updated(CPUState *cs, run_on_cpu_data opaque); + +#endif /* TCG_S390X_H */ -- cgit v1.2.3-55-g7522