diff options
63 files changed, 3198 insertions, 1072 deletions
diff --git a/MAINTAINERS b/MAINTAINERS index 79fb830031..b7c4130388 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -556,7 +556,7 @@ F: hw/misc/arm_sysctl.c Xilinx Zynq M: Edgar E. Iglesias <edgar.iglesias@gmail.com> -M: Alistair Francis <alistair.francis@xilinx.com> +M: Alistair Francis <alistair@alistair23.me> L: qemu-arm@nongnu.org S: Maintained F: hw/*/xilinx_* @@ -566,7 +566,7 @@ F: include/hw/misc/zynq* X: hw/ssi/xilinx_* Xilinx ZynqMP -M: Alistair Francis <alistair.francis@xilinx.com> +M: Alistair Francis <alistair@alistair23.me> M: Edgar E. Iglesias <edgar.iglesias@gmail.com> L: qemu-arm@nongnu.org S: Maintained @@ -1075,7 +1075,7 @@ T: git git://github.com/bonzini/qemu.git scsi-next SSI M: Peter Crosthwaite <crosthwaite.peter@gmail.com> -M: Alistair Francis <alistair.francis@xilinx.com> +M: Alistair Francis <alistair@alistair23.me> S: Maintained F: hw/ssi/* F: hw/block/m25p80.c @@ -1084,7 +1084,7 @@ X: hw/ssi/xilinx_* F: tests/m25p80-test.c Xilinx SPI -M: Alistair Francis <alistair.francis@xilinx.com> +M: Alistair Francis <alistair@alistair23.me> M: Peter Crosthwaite <crosthwaite.peter@gmail.com> S: Maintained F: hw/ssi/xilinx_* @@ -1254,7 +1254,7 @@ S: Maintained F: hw/net/eepro100.c Generic Loader -M: Alistair Francis <alistair.francis@xilinx.com> +M: Alistair Francis <alistair@alistair23.me> S: Maintained F: hw/core/generic-loader.c F: include/hw/core/generic-loader.h @@ -1600,7 +1600,7 @@ F: tests/qmp-test.c T: git git://repo.or.cz/qemu/armbru.git qapi-next Register API -M: Alistair Francis <alistair.francis@xilinx.com> +M: Alistair Francis <alistair@alistair23.me> S: Maintained F: hw/core/register.c F: include/hw/register.h @@ -2100,6 +2100,9 @@ CpuInfoList *qmp_query_cpus(Error **errp) #elif defined(TARGET_TRICORE) TriCoreCPU *tricore_cpu = TRICORE_CPU(cpu); CPUTriCoreState *env = &tricore_cpu->env; +#elif defined(TARGET_S390X) + S390CPU *s390_cpu = S390_CPU(cpu); + CPUS390XState *env = &s390_cpu->env; #endif cpu_synchronize_state(cpu); @@ -2127,6 +2130,9 @@ CpuInfoList *qmp_query_cpus(Error **errp) #elif defined(TARGET_TRICORE) info->value->arch = CPU_INFO_ARCH_TRICORE; info->value->u.tricore.PC = env->PC; +#elif defined(TARGET_S390X) + info->value->arch = CPU_INFO_ARCH_S390; + info->value->u.s390.cpu_state = env->cpu_state; #else info->value->arch = CPU_INFO_ARCH_OTHER; #endif @@ -2150,6 +2156,54 @@ CpuInfoList *qmp_query_cpus(Error **errp) return head; } +/* + * fast means: we NEVER interrupt vCPU threads to retrieve + * information from KVM. + */ +CpuInfoFastList *qmp_query_cpus_fast(Error **errp) +{ + MachineState *ms = MACHINE(qdev_get_machine()); + MachineClass *mc = MACHINE_GET_CLASS(ms); + CpuInfoFastList *head = NULL, *cur_item = NULL; + CPUState *cpu; +#if defined(TARGET_S390X) + S390CPU *s390_cpu; + CPUS390XState *env; +#endif + + CPU_FOREACH(cpu) { + CpuInfoFastList *info = g_malloc0(sizeof(*info)); + info->value = g_malloc0(sizeof(*info->value)); + + info->value->cpu_index = cpu->cpu_index; + info->value->qom_path = object_get_canonical_path(OBJECT(cpu)); + info->value->thread_id = cpu->thread_id; + + info->value->has_props = !!mc->cpu_index_to_instance_props; + if (info->value->has_props) { + CpuInstanceProperties *props; + props = g_malloc0(sizeof(*props)); + *props = mc->cpu_index_to_instance_props(ms, cpu->cpu_index); + info->value->props = props; + } + +#if defined(TARGET_S390X) + s390_cpu = S390_CPU(cpu); + env = &s390_cpu->env; + info->value->arch = CPU_INFO_ARCH_S390; + info->value->u.s390.cpu_state = env->cpu_state; +#endif + if (!cur_item) { + head = cur_item = info; + } else { + cur_item->next = info; + cur_item = info; + } + } + + return head; +} + void qmp_memsave(int64_t addr, int64_t size, const char *filename, bool has_cpu, int64_t cpu_index, Error **errp) { diff --git a/default-configs/arm-softmmu.mak b/default-configs/arm-softmmu.mak index ca34cf4462..54f855d072 100644 --- a/default-configs/arm-softmmu.mak +++ b/default-configs/arm-softmmu.mak @@ -21,6 +21,8 @@ CONFIG_STELLARIS_INPUT=y CONFIG_STELLARIS_ENET=y CONFIG_SSD0303=y CONFIG_SSD0323=y +CONFIG_DDC=y +CONFIG_SII9022=y CONFIG_ADS7846=y CONFIG_MAX111X=y CONFIG_SSI=y @@ -360,50 +360,23 @@ void hmp_info_migrate_cache_size(Monitor *mon, const QDict *qdict) void hmp_info_cpus(Monitor *mon, const QDict *qdict) { - CpuInfoList *cpu_list, *cpu; + CpuInfoFastList *cpu_list, *cpu; - cpu_list = qmp_query_cpus(NULL); + cpu_list = qmp_query_cpus_fast(NULL); for (cpu = cpu_list; cpu; cpu = cpu->next) { int active = ' '; - if (cpu->value->CPU == monitor_get_cpu_index()) { + if (cpu->value->cpu_index == monitor_get_cpu_index()) { active = '*'; } - monitor_printf(mon, "%c CPU #%" PRId64 ":", active, cpu->value->CPU); - - switch (cpu->value->arch) { - case CPU_INFO_ARCH_X86: - monitor_printf(mon, " pc=0x%016" PRIx64, cpu->value->u.x86.pc); - break; - case CPU_INFO_ARCH_PPC: - monitor_printf(mon, " nip=0x%016" PRIx64, cpu->value->u.ppc.nip); - break; - case CPU_INFO_ARCH_SPARC: - monitor_printf(mon, " pc=0x%016" PRIx64, - cpu->value->u.q_sparc.pc); - monitor_printf(mon, " npc=0x%016" PRIx64, - cpu->value->u.q_sparc.npc); - break; - case CPU_INFO_ARCH_MIPS: - monitor_printf(mon, " PC=0x%016" PRIx64, cpu->value->u.q_mips.PC); - break; - case CPU_INFO_ARCH_TRICORE: - monitor_printf(mon, " PC=0x%016" PRIx64, cpu->value->u.tricore.PC); - break; - default: - break; - } - - if (cpu->value->halted) { - monitor_printf(mon, " (halted)"); - } - - monitor_printf(mon, " thread_id=%" PRId64 "\n", cpu->value->thread_id); + monitor_printf(mon, "%c CPU #%" PRId64 ":", active, + cpu->value->cpu_index); + monitor_printf(mon, " thread-id=%" PRId64 "\n", cpu->value->thread_id); } - qapi_free_CpuInfoList(cpu_list); + qapi_free_CpuInfoFastList(cpu_list); } static void print_block_info(Monitor *mon, BlockInfo *info, diff --git a/hw/arm/vexpress.c b/hw/arm/vexpress.c index dc5928ae1a..9fad79177a 100644 --- a/hw/arm/vexpress.c +++ b/hw/arm/vexpress.c @@ -29,6 +29,7 @@ #include "hw/arm/arm.h" #include "hw/arm/primecell.h" #include "hw/devices.h" +#include "hw/i2c/i2c.h" #include "net/net.h" #include "sysemu/sysemu.h" #include "hw/boards.h" @@ -537,6 +538,7 @@ static void vexpress_common_init(MachineState *machine) uint32_t sys_id; DriveInfo *dinfo; pflash_t *pflash0; + I2CBus *i2c; ram_addr_t vram_size, sram_size; MemoryRegion *sysmem = get_system_memory(); MemoryRegion *vram = g_new(MemoryRegion, 1); @@ -628,7 +630,9 @@ static void vexpress_common_init(MachineState *machine) sysbus_create_simple("sp804", map[VE_TIMER01], pic[2]); sysbus_create_simple("sp804", map[VE_TIMER23], pic[3]); - /* VE_SERIALDVI: not modelled */ + dev = sysbus_create_simple("versatile_i2c", map[VE_SERIALDVI], NULL); + i2c = (I2CBus *)qdev_get_child_bus(dev, "i2c"); + i2c_create_slave(i2c, "sii9022", 0x39); sysbus_create_simple("pl031", map[VE_RTC], pic[4]); /* RTC */ diff --git a/hw/core/register.c b/hw/core/register.c index 900294b9c4..0741a1af32 100644 --- a/hw/core/register.c +++ b/hw/core/register.c @@ -159,13 +159,21 @@ uint64_t register_read(RegisterInfo *reg, uint64_t re, const char* prefix, void register_reset(RegisterInfo *reg) { + const RegisterAccessInfo *ac; + g_assert(reg); if (!reg->data || !reg->access) { return; } + ac = reg->access; + register_write_val(reg, reg->access->reset); + + if (ac->post_write) { + ac->post_write(reg, reg->access->reset); + } } void register_init(RegisterInfo *reg) diff --git a/hw/display/Makefile.objs b/hw/display/Makefile.objs index d3a4cb396e..3c7c75b94d 100644 --- a/hw/display/Makefile.objs +++ b/hw/display/Makefile.objs @@ -3,6 +3,7 @@ common-obj-$(CONFIG_VGA_CIRRUS) += cirrus_vga.o common-obj-$(CONFIG_G364FB) += g364fb.o common-obj-$(CONFIG_JAZZ_LED) += jazz_led.o common-obj-$(CONFIG_PL110) += pl110.o +common-obj-$(CONFIG_SII9022) += sii9022.o common-obj-$(CONFIG_SSD0303) += ssd0303.o common-obj-$(CONFIG_SSD0323) += ssd0323.o common-obj-$(CONFIG_XEN) += xenfb.o diff --git a/hw/display/sii9022.c b/hw/display/sii9022.c new file mode 100644 index 0000000000..eaf11a6e7b --- /dev/null +++ b/hw/display/sii9022.c @@ -0,0 +1,191 @@ +/* + * Silicon Image SiI9022 + * + * This is a pretty hollow emulation: all we do is acknowledge that we + * exist (chip ID) and confirm that we get switched over into DDC mode + * so the emulated host can proceed to read out EDID data. All subsequent + * set-up of connectors etc will be acknowledged and ignored. + * + * Copyright (C) 2018 Linus Walleij + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "qemu/osdep.h" +#include "qemu-common.h" +#include "hw/i2c/i2c.h" +#include "hw/i2c/i2c-ddc.h" +#include "trace.h" + +#define SII9022_SYS_CTRL_DATA 0x1a +#define SII9022_SYS_CTRL_PWR_DWN 0x10 +#define SII9022_SYS_CTRL_AV_MUTE 0x08 +#define SII9022_SYS_CTRL_DDC_BUS_REQ 0x04 +#define SII9022_SYS_CTRL_DDC_BUS_GRTD 0x02 +#define SII9022_SYS_CTRL_OUTPUT_MODE 0x01 +#define SII9022_SYS_CTRL_OUTPUT_HDMI 1 +#define SII9022_SYS_CTRL_OUTPUT_DVI 0 +#define SII9022_REG_CHIPID 0x1b +#define SII9022_INT_ENABLE 0x3c +#define SII9022_INT_STATUS 0x3d +#define SII9022_INT_STATUS_HOTPLUG 0x01; +#define SII9022_INT_STATUS_PLUGGED 0x04; + +#define TYPE_SII9022 "sii9022" +#define SII9022(obj) OBJECT_CHECK(sii9022_state, (obj), TYPE_SII9022) + +typedef struct sii9022_state { + I2CSlave parent_obj; + uint8_t ptr; + bool addr_byte; + bool ddc_req; + bool ddc_skip_finish; + bool ddc; +} sii9022_state; + +static const VMStateDescription vmstate_sii9022 = { + .name = "sii9022", + .version_id = 1, + .minimum_version_id = 1, + .fields = (VMStateField[]) { + VMSTATE_I2C_SLAVE(parent_obj, sii9022_state), + VMSTATE_UINT8(ptr, sii9022_state), + VMSTATE_BOOL(addr_byte, sii9022_state), + VMSTATE_BOOL(ddc_req, sii9022_state), + VMSTATE_BOOL(ddc_skip_finish, sii9022_state), + VMSTATE_BOOL(ddc, sii9022_state), + VMSTATE_END_OF_LIST() + } +}; + +static int sii9022_event(I2CSlave *i2c, enum i2c_event event) +{ + sii9022_state *s = SII9022(i2c); + + switch (event) { + case I2C_START_SEND: + s->addr_byte = true; + break; + case I2C_START_RECV: + break; + case I2C_FINISH: + break; + case I2C_NACK: + break; + } + + return 0; +} + +static int sii9022_rx(I2CSlave *i2c) +{ + sii9022_state *s = SII9022(i2c); + uint8_t res = 0x00; + + switch (s->ptr) { + case SII9022_SYS_CTRL_DATA: + if (s->ddc_req) { + /* Acknowledge DDC bus request */ + res = SII9022_SYS_CTRL_DDC_BUS_GRTD | SII9022_SYS_CTRL_DDC_BUS_REQ; + } + break; + case SII9022_REG_CHIPID: + res = 0xb0; + break; + case SII9022_INT_STATUS: + /* Something is cold-plugged in, no interrupts */ + res = SII9022_INT_STATUS_PLUGGED; + break; + default: + break; + } + + trace_sii9022_read_reg(s->ptr, res); + s->ptr++; + + return res; +} + +static int sii9022_tx(I2CSlave *i2c, uint8_t data) +{ + sii9022_state *s = SII9022(i2c); + + if (s->addr_byte) { + s->ptr = data; + s->addr_byte = false; + return 0; + } + + switch (s->ptr) { + case SII9022_SYS_CTRL_DATA: + if (data & SII9022_SYS_CTRL_DDC_BUS_REQ) { + s->ddc_req = true; + if (data & SII9022_SYS_CTRL_DDC_BUS_GRTD) { + s->ddc = true; + /* Skip this finish since we just switched to DDC */ + s->ddc_skip_finish = true; + trace_sii9022_switch_mode("DDC"); + } + } else { + s->ddc_req = false; + s->ddc = false; + trace_sii9022_switch_mode("normal"); + } + break; + default: + break; + } + + trace_sii9022_write_reg(s->ptr, data); + s->ptr++; + + return 0; +} + +static void sii9022_reset(DeviceState *dev) +{ + sii9022_state *s = SII9022(dev); + + s->ptr = 0; + s->addr_byte = false; + s->ddc_req = false; + s->ddc_skip_finish = false; + s->ddc = false; +} + +static void sii9022_realize(DeviceState *dev, Error **errp) +{ + I2CBus *bus; + + bus = I2C_BUS(qdev_get_parent_bus(dev)); + i2c_create_slave(bus, TYPE_I2CDDC, 0x50); +} + +static void sii9022_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + I2CSlaveClass *k = I2C_SLAVE_CLASS(klass); + + k->event = sii9022_event; + k->recv = sii9022_rx; + k->send = sii9022_tx; + dc->reset = sii9022_reset; + dc->realize = sii9022_realize; + dc->vmsd = &vmstate_sii9022; +} + +static const TypeInfo sii9022_info = { + .name = TYPE_SII9022, + .parent = TYPE_I2C_SLAVE, + .instance_size = sizeof(sii9022_state), + .class_init = sii9022_class_init, +}; + +static void sii9022_register_types(void) +{ + type_register_static(&sii9022_info); +} + +type_init(sii9022_register_types) diff --git a/hw/display/trace-events b/hw/display/trace-events index da498c1def..5a48c6cb6a 100644 --- a/hw/display/trace-events +++ b/hw/display/trace-events @@ -132,3 +132,8 @@ vga_cirrus_read_io(uint32_t addr, uint32_t val) "addr 0x%x, val 0x%x" vga_cirrus_write_io(uint32_t addr, uint32_t val) "addr 0x%x, val 0x%x" vga_cirrus_read_blt(uint32_t offset, uint32_t val) "offset 0x%x, val 0x%x" vga_cirrus_write_blt(uint32_t offset, uint32_t val) "offset 0x%x, val 0x%x" + +# hw/display/sii9022.c +sii9022_read_reg(uint8_t addr, uint8_t val) "addr 0x%02x, val 0x%02x" +sii9022_write_reg(uint8_t addr, uint8_t val) "addr 0x%02x, val 0x%02x" +sii9022_switch_mode(const char *mode) "mode: %s" diff --git a/hw/i2c/core.c b/hw/i2c/core.c index 59068f157e..cfccefca3d 100644 --- a/hw/i2c/core.c +++ b/hw/i2c/core.c @@ -10,31 +10,13 @@ #include "qemu/osdep.h" #include "hw/i2c/i2c.h" -typedef struct I2CNode I2CNode; - -struct I2CNode { - I2CSlave *elt; - QLIST_ENTRY(I2CNode) next; -}; - #define I2C_BROADCAST 0x00 -struct I2CBus -{ - BusState qbus; - QLIST_HEAD(, I2CNode) current_devs; - uint8_t saved_address; - bool broadcast; -}; - static Property i2c_props[] = { DEFINE_PROP_UINT8("address", struct I2CSlave, address, 0), DEFINE_PROP_END_OF_LIST(), }; -#define TYPE_I2C_BUS "i2c-bus" -#define I2C_BUS(obj) OBJECT_CHECK(I2CBus, (obj), TYPE_I2C_BUS) - static const TypeInfo i2c_bus_info = { .name = TYPE_I2C_BUS, .parent = TYPE_BUS, diff --git a/hw/i2c/i2c-ddc.c b/hw/i2c/i2c-ddc.c index 199dac9e41..bec0c91e2d 100644 --- a/hw/i2c/i2c-ddc.c +++ b/hw/i2c/i2c-ddc.c @@ -259,12 +259,12 @@ static int i2c_ddc_tx(I2CSlave *i2c, uint8_t data) s->reg = data; s->firstbyte = false; DPRINTF("[EDID] Written new pointer: %u\n", data); - return 1; + return 0; } /* Ignore all writes */ s->reg++; - return 1; + return 0; } static void i2c_ddc_init(Object *obj) diff --git a/hw/intc/s390_flic.c b/hw/intc/s390_flic.c index a85a149c6d..5f8168f0f0 100644 --- a/hw/intc/s390_flic.c +++ b/hw/intc/s390_flic.c @@ -192,8 +192,8 @@ static void qemu_s390_flic_notify(uint32_t type) cs->interrupt_request |= CPU_INTERRUPT_HARD; /* ignore CPUs that are not sleeping */ - if (s390_cpu_get_state(cpu) != CPU_STATE_OPERATING && - s390_cpu_get_state(cpu) != CPU_STATE_LOAD) { + if (s390_cpu_get_state(cpu) != S390_CPU_STATE_OPERATING && + s390_cpu_get_state(cpu) != S390_CPU_STATE_LOAD) { continue; } diff --git a/hw/s390x/ipl.c b/hw/s390x/ipl.c index 0d06fc12b6..798e99aadf 100644 --- a/hw/s390x/ipl.c +++ b/hw/s390x/ipl.c @@ -23,6 +23,9 @@ #include "hw/s390x/ebcdic.h" #include "ipl.h" #include "qemu/error-report.h" +#include "qemu/config-file.h" +#include "qemu/cutils.h" +#include "qemu/option.h" #define KERN_IMAGE_START 0x010000UL #define KERN_PARM_AREA 0x010480UL @@ -219,6 +222,61 @@ static Property s390_ipl_properties[] = { DEFINE_PROP_END_OF_LIST(), }; +static void s390_ipl_set_boot_menu(S390IPLState *ipl) +{ + QemuOptsList *plist = qemu_find_opts("boot-opts"); + QemuOpts *opts = QTAILQ_FIRST(&plist->head); + uint8_t *flags = &ipl->qipl.qipl_flags; + uint32_t *timeout = &ipl->qipl.boot_menu_timeout; + const char *tmp; + unsigned long splash_time = 0; + + if (!get_boot_device(0)) { + if (boot_menu) { + error_report("boot menu requires a bootindex to be specified for " + "the IPL device."); + } + return; + } + + switch (ipl->iplb.pbt) { + case S390_IPL_TYPE_CCW: + /* In the absence of -boot menu, use zipl parameters */ + if (!qemu_opt_get(opts, "menu")) { + *flags |= QIPL_FLAG_BM_OPTS_ZIPL; + return; + } + break; + case S390_IPL_TYPE_QEMU_SCSI: + break; + default: + error_report("boot menu is not supported for this device type."); + return; + } + + if (!boot_menu) { + return; + } + + *flags |= QIPL_FLAG_BM_OPTS_CMD; + + tmp = qemu_opt_get(opts, "splash-time"); + + if (tmp && qemu_strtoul(tmp, NULL, 10, &splash_time)) { + error_report("splash-time is invalid, forcing it to 0."); + *timeout = 0; + return; + } + + if (splash_time > 0xffffffff) { + error_report("splash-time is too large, forcing it to max value."); + *timeout = 0xffffffff; + return; + } + + *timeout = cpu_to_be32(splash_time); +} + static bool s390_gen_initial_iplb(S390IPLState *ipl) { DeviceState *dev_st; @@ -399,6 +457,21 @@ void s390_reipl_request(void) qemu_system_reset_request(SHUTDOWN_CAUSE_GUEST_RESET); } +static void s390_ipl_prepare_qipl(S390CPU *cpu) +{ + S390IPLState *ipl = get_ipl_device(); + uint8_t *addr; + uint64_t len = 4096; + + addr = cpu_physical_memory_map(cpu->env.psa, &len, 1); + if (!addr || len < QIPL_ADDRESS + sizeof(QemuIplParameters)) { + error_report("Cannot set QEMU IPL parameters"); + return; + } + memcpy(addr + QIPL_ADDRESS, &ipl->qipl, sizeof(QemuIplParameters)); + cpu_physical_memory_unmap(addr, len, 1, len); +} + void s390_ipl_prepare_cpu(S390CPU *cpu) { S390IPLState *ipl = get_ipl_device(); @@ -418,8 +491,10 @@ void s390_ipl_prepare_cpu(S390CPU *cpu) error_report_err(err); vm_stop(RUN_STATE_INTERNAL_ERROR); } - ipl->iplb.ccw.netboot_start_addr = cpu_to_be64(ipl->start_addr); + ipl->qipl.netboot_start_addr = cpu_to_be64(ipl->start_addr); } + s390_ipl_set_boot_menu(ipl); + s390_ipl_prepare_qipl(cpu); } static void s390_ipl_reset(DeviceState *dev) diff --git a/hw/s390x/ipl.h b/hw/s390x/ipl.h index 8a705e0428..0570d0ad75 100644 --- a/hw/s390x/ipl.h +++ b/hw/s390x/ipl.h @@ -16,8 +16,7 @@ #include "cpu.h" struct IplBlockCcw { - uint64_t netboot_start_addr; - uint8_t reserved0[77]; + uint8_t reserved0[85]; uint8_t ssid; uint16_t devno; uint8_t vm_flags; @@ -90,6 +89,33 @@ void s390_ipl_prepare_cpu(S390CPU *cpu); IplParameterBlock *s390_ipl_get_iplb(void); void s390_reipl_request(void); +#define QIPL_ADDRESS 0xcc + +/* Boot Menu flags */ +#define QIPL_FLAG_BM_OPTS_CMD 0x80 +#define QIPL_FLAG_BM_OPTS_ZIPL 0x40 + +/* + * The QEMU IPL Parameters will be stored at absolute address + * 204 (0xcc) which means it is 32-bit word aligned but not + * double-word aligned. + * Placement of data fields in this area must account for + * their alignment needs. E.g., netboot_start_address must + * have an offset of 4 + n * 8 bytes within the struct in order + * to keep it double-word aligned. + * The total size of the struct must never exceed 28 bytes. + * This definition must be kept in sync with the defininition + * in pc-bios/s390-ccw/iplb.h. + */ +struct QemuIplParameters { + uint8_t qipl_flags; + uint8_t reserved1[3]; + uint64_t netboot_start_addr; + uint32_t boot_menu_timeout; + uint8_t reserved2[12]; +} QEMU_PACKED; +typedef struct QemuIplParameters QemuIplParameters; + #define TYPE_S390_IPL "s390-ipl" #define S390_IPL(obj) OBJECT_CHECK(S390IPLState, (obj), TYPE_S390_IPL) @@ -105,6 +131,7 @@ struct S390IPLState { bool iplb_valid; bool reipl_requested; bool netboot; + QemuIplParameters qipl; /*< public >*/ char *kernel; diff --git a/hw/s390x/s390-stattrib.c b/hw/s390x/s390-stattrib.c index a1d2135a60..adf07ef312 100644 --- a/hw/s390x/s390-stattrib.c +++ b/hw/s390x/s390-stattrib.c @@ -365,22 +365,22 @@ static inline void s390_stattrib_set_migration_enabled(Object *obj, bool value, s->migration_enabled = value; } +static SaveVMHandlers savevm_s390_stattrib_handlers = { + .save_setup = cmma_save_setup, + .save_live_iterate = cmma_save_iterate, + .save_live_complete_precopy = cmma_save_complete, + .save_live_pending = cmma_save_pending, + .save_cleanup = cmma_save_cleanup, + .load_state = cmma_load, + .is_active = cmma_active, +}; + static void s390_stattrib_instance_init(Object *obj) { S390StAttribState *sas = S390_STATTRIB(obj); - SaveVMHandlers *ops; - - /* ops will always be freed by qemu when unregistering */ - ops = g_new0(SaveVMHandlers, 1); - - ops->save_setup = cmma_save_setup; - ops->save_live_iterate = cmma_save_iterate; - ops->save_live_complete_precopy = cmma_save_complete; - ops->save_live_pending = cmma_save_pending; - ops->save_cleanup = cmma_save_cleanup; - ops->load_state = cmma_load; - ops->is_active = cmma_active; - register_savevm_live(NULL, TYPE_S390_STATTRIB, 0, 0, ops, sas); + + register_savevm_live(NULL, TYPE_S390_STATTRIB, 0, 0, + &savevm_s390_stattrib_handlers, sas); object_property_add_bool(obj, "migration-enabled", s390_stattrib_get_migration_enabled, diff --git a/hw/s390x/s390-virtio-ccw.c b/hw/s390x/s390-virtio-ccw.c index 4abbe89847..4d0c3deba6 100644 --- a/hw/s390x/s390-virtio-ccw.c +++ b/hw/s390x/s390-virtio-ccw.c @@ -368,7 +368,7 @@ static void s390_machine_reset(void) /* all cpus are stopped - configure and start the ipl cpu only */ s390_ipl_prepare_cpu(ipl_cpu); - s390_cpu_set_state(CPU_STATE_OPERATING, ipl_cpu); + s390_cpu_set_state(S390_CPU_STATE_OPERATING, ipl_cpu); } static void s390_machine_device_plug(HotplugHandler *hotplug_dev, diff --git a/hw/s390x/sclp.c b/hw/s390x/sclp.c index 276972b59f..047d577313 100644 --- a/hw/s390x/sclp.c +++ b/hw/s390x/sclp.c @@ -15,9 +15,7 @@ #include "qemu/osdep.h" #include "qapi/error.h" #include "cpu.h" -#include "exec/memory.h" #include "sysemu/sysemu.h" -#include "exec/address-spaces.h" #include "hw/boards.h" #include "hw/s390x/sclp.h" #include "hw/s390x/event-facility.h" @@ -57,10 +55,8 @@ static void read_SCP_info(SCLPDevice *sclp, SCCB *sccb) { ReadInfo *read_info = (ReadInfo *) sccb; MachineState *machine = MACHINE(qdev_get_machine()); - sclpMemoryHotplugDev *mhd = get_sclp_memory_hotplug_dev(); int cpu_count; int rnsize, rnmax; - int slots = MIN(machine->ram_slots, s390_get_memslot_count()); IplParameterBlock *ipib = s390_ipl_get_iplb(); /* CPU information */ @@ -80,36 +76,6 @@ static void read_SCP_info(SCLPDevice *sclp, SCCB *sccb) read_info->facilities = cpu_to_be64(SCLP_HAS_CPU_INFO | SCLP_HAS_IOA_RECONFIG); - /* Memory Hotplug is only supported for the ccw machine type */ - if (mhd) { - mhd->standby_subregion_size = MEM_SECTION_SIZE; - /* Deduct the memory slot already used for core */ - if (slots > 0) { - while ((mhd->standby_subregion_size * (slots - 1) - < mhd->standby_mem_size)) { - mhd->standby_subregion_size = mhd->standby_subregion_size << 1; - } - } - /* - * Initialize mapping of guest standby memory sections indicating which - * are and are not online. Assume all standby memory begins offline. - */ - if (mhd->standby_state_map == 0) { - if (mhd->standby_mem_size % mhd->standby_subregion_size) { - mhd->standby_state_map = g_malloc0((mhd->standby_mem_size / - mhd->standby_subregion_size + 1) * - (mhd->standby_subregion_size / - MEM_SECTION_SIZE)); - } else { - mhd->standby_state_map = g_malloc0(mhd->standby_mem_size / - MEM_SECTION_SIZE); - } - } - mhd->padded_ram_size = ram_size + mhd->pad_size; - mhd->rzm = 1 << mhd->increment_size; - - read_info->facilities |= cpu_to_be64(SCLP_FC_ASSIGN_ATTACH_READ_STOR); - } read_info->mha_pow = s390_get_mha_pow(); read_info->hmfai = cpu_to_be32(s390_get_hmfai()); @@ -121,7 +87,8 @@ static void read_SCP_info(SCLPDevice *sclp, SCCB *sccb) read_info->rnsize2 = cpu_to_be32(rnsize); } - rnmax = machine->maxram_size >> sclp->increment_size; + /* we don't support standby memory, maxram_size is never exposed */ + rnmax = machine->ram_size >> sclp->increment_size; if (rnmax < 0x10000) { read_info->rnmax = cpu_to_be16(rnmax); } else { @@ -139,195 +106,6 @@ static void read_SCP_info(SCLPDevice *sclp, SCCB *sccb) sccb->h.response_code = cpu_to_be16(SCLP_RC_NORMAL_READ_COMPLETION); } -static void read_storage_element0_info(SCLPDevice *sclp, SCCB *sccb) -{ - int i, assigned; - int subincrement_id = SCLP_STARTING_SUBINCREMENT_ID; - ReadStorageElementInfo *storage_info = (ReadStorageElementInfo *) sccb; - sclpMemoryHotplugDev *mhd = get_sclp_memory_hotplug_dev(); - - if (!mhd) { - sccb->h.response_code = cpu_to_be16(SCLP_RC_INVALID_SCLP_COMMAND); - return; - } - - if ((ram_size >> mhd->increment_size) >= 0x10000) { - sccb->h.response_code = cpu_to_be16(SCLP_RC_SCCB_BOUNDARY_VIOLATION); - return; - } - - /* Return information regarding core memory */ - storage_info->max_id = cpu_to_be16(mhd->standby_mem_size ? 1 : 0); - assigned = ram_size >> mhd->increment_size; - storage_info->assigned = cpu_to_be16(assigned); - - for (i = 0; i < assigned; i++) { - storage_info->entries[i] = cpu_to_be32(subincrement_id); - subincrement_id += SCLP_INCREMENT_UNIT; - } - sccb->h.response_code = cpu_to_be16(SCLP_RC_NORMAL_READ_COMPLETION); -} - -static void read_storage_element1_info(SCLPDevice *sclp, SCCB *sccb) -{ - ReadStorageElementInfo *storage_info = (ReadStorageElementInfo *) sccb; - sclpMemoryHotplugDev *mhd = get_sclp_memory_hotplug_dev(); - - if (!mhd) { - sccb->h.response_code = cpu_to_be16(SCLP_RC_INVALID_SCLP_COMMAND); - return; - } - - if ((mhd->standby_mem_size >> mhd->increment_size) >= 0x10000) { - sccb->h.response_code = cpu_to_be16(SCLP_RC_SCCB_BOUNDARY_VIOLATION); - return; - } - - /* Return information regarding standby memory */ - storage_info->max_id = cpu_to_be16(mhd->standby_mem_size ? 1 : 0); - storage_info->assigned = cpu_to_be16(mhd->standby_mem_size >> - mhd->increment_size); - storage_info->standby = cpu_to_be16(mhd->standby_mem_size >> - mhd->increment_size); - sccb->h.response_code = cpu_to_be16(SCLP_RC_STANDBY_READ_COMPLETION); -} - -static void attach_storage_element(SCLPDevice *sclp, SCCB *sccb, - uint16_t element) -{ - int i, assigned, subincrement_id; - AttachStorageElement *attach_info = (AttachStorageElement *) sccb; - sclpMemoryHotplugDev *mhd = get_sclp_memory_hotplug_dev(); - - if (!mhd) { - sccb->h.response_code = cpu_to_be16(SCLP_RC_INVALID_SCLP_COMMAND); - return; - } - - if (element != 1) { - sccb->h.response_code = cpu_to_be16(SCLP_RC_INVALID_SCLP_COMMAND); - return; - } - - assigned = mhd->standby_mem_size >> mhd->increment_size; - attach_info->assigned = cpu_to_be16(assigned); - subincrement_id = ((ram_size >> mhd->increment_size) << 16) - + SCLP_STARTING_SUBINCREMENT_ID; - for (i = 0; i < assigned; i++) { - attach_info->entries[i] = cpu_to_be32(subincrement_id); - subincrement_id += SCLP_INCREMENT_UNIT; - } - sccb->h.response_code = cpu_to_be16(SCLP_RC_NORMAL_COMPLETION); -} - -static void assign_storage(SCLPDevice *sclp, SCCB *sccb) -{ - MemoryRegion *mr = NULL; - uint64_t this_subregion_size; - AssignStorage *assign_info = (AssignStorage *) sccb; - sclpMemoryHotplugDev *mhd = get_sclp_memory_hotplug_dev(); - ram_addr_t assign_addr; - MemoryRegion *sysmem = get_system_memory(); - - if (!mhd) { - sccb->h.response_code = cpu_to_be16(SCLP_RC_INVALID_SCLP_COMMAND); - return; - } - assign_addr = (be16_to_cpu(assign_info->rn) - 1) * mhd->rzm; - - if ((assign_addr % MEM_SECTION_SIZE == 0) && - (assign_addr >= mhd->padded_ram_size)) { - /* Re-use existing memory region if found */ - mr = memory_region_find(sysmem, assign_addr, 1).mr; - memory_region_unref(mr); - if (!mr) { - - MemoryRegion *standby_ram = g_new(MemoryRegion, 1); - - /* offset to align to standby_subregion_size for allocation */ - ram_addr_t offset = assign_addr - - (assign_addr - mhd->padded_ram_size) - % mhd->standby_subregion_size; - - /* strlen("standby.ram") + 4 (Max of KVM_MEMORY_SLOTS) + NULL */ - char id[16]; - snprintf(id, 16, "standby.ram%d", - (int)((offset - mhd->padded_ram_size) / - mhd->standby_subregion_size) + 1); - - /* Allocate a subregion of the calculated standby_subregion_size */ - if (offset + mhd->standby_subregion_size > - mhd->padded_ram_size + mhd->standby_mem_size) { - this_subregion_size = mhd->padded_ram_size + - mhd->standby_mem_size - offset; - } else { - this_subregion_size = mhd->standby_subregion_size; - } - - memory_region_init_ram(standby_ram, NULL, id, this_subregion_size, - &error_fatal); - /* This is a hack to make memory hotunplug work again. Once we have - * subdevices, we have to unparent them when unassigning memory, - * instead of doing it via the ref count of the MemoryRegion. */ - object_ref(OBJECT(standby_ram)); - object_unparent(OBJECT(standby_ram)); - memory_region_add_subregion(sysmem, offset, standby_ram); - } - /* The specified subregion is no longer in standby */ - mhd->standby_state_map[(assign_addr - mhd->padded_ram_size) - / MEM_SECTION_SIZE] = 1; - } - sccb->h.response_code = cpu_to_be16(SCLP_RC_NORMAL_COMPLETION); -} - -static void unassign_storage(SCLPDevice *sclp, SCCB *sccb) -{ - MemoryRegion *mr = NULL; - AssignStorage *assign_info = (AssignStorage *) sccb; - sclpMemoryHotplugDev *mhd = get_sclp_memory_hotplug_dev(); - ram_addr_t unassign_addr; - MemoryRegion *sysmem = get_system_memory(); - - if (!mhd) { - sccb->h.response_code = cpu_to_be16(SCLP_RC_INVALID_SCLP_COMMAND); - return; - } - unassign_addr = (be16_to_cpu(assign_info->rn) - 1) * mhd->rzm; - - /* if the addr is a multiple of 256 MB */ - if ((unassign_addr % MEM_SECTION_SIZE == 0) && - (unassign_addr >= mhd->padded_ram_size)) { - mhd->standby_state_map[(unassign_addr - - mhd->padded_ram_size) / MEM_SECTION_SIZE] = 0; - - /* find the specified memory region and destroy it */ - mr = memory_region_find(sysmem, unassign_addr, 1).mr; - memory_region_unref(mr); - if (mr) { - int i; - int is_removable = 1; - ram_addr_t map_offset = (unassign_addr - mhd->padded_ram_size - - (unassign_addr - mhd->padded_ram_size) - % mhd->standby_subregion_size); - /* Mark all affected subregions as 'standby' once again */ - for (i = 0; - i < (mhd->standby_subregion_size / MEM_SECTION_SIZE); - i++) { - - if (mhd->standby_state_map[i + map_offset / MEM_SECTION_SIZE]) { - is_removable = 0; - break; - } - } - if (is_removable) { - memory_region_del_subregion(sysmem, mr); - object_unref(OBJECT(mr)); - } - } - } - sccb->h.response_code = cpu_to_be16(SCLP_RC_NORMAL_COMPLETION); -} - /* Provide information about the CPU */ static void sclp_read_cpu_info(SCLPDevice *sclp, SCCB *sccb) { @@ -390,22 +168,6 @@ static void sclp_execute(SCLPDevice *sclp, SCCB *sccb, uint32_t code) case SCLP_CMDW_READ_CPU_INFO: sclp_c->read_cpu_info(sclp, sccb); break; - case SCLP_READ_STORAGE_ELEMENT_INFO: - if (code & 0xff00) { - sclp_c->read_storage_element1_info(sclp, sccb); - } else { - sclp_c->read_storage_element0_info(sclp, sccb); - } - break; - case SCLP_ATTACH_STORAGE_ELEMENT: - sclp_c->attach_storage_element(sclp, sccb, (code & 0xff00) >> 8); - break; - case SCLP_ASSIGN_STORAGE: - sclp_c->assign_storage(sclp, sccb); - break; - case SCLP_UNASSIGN_STORAGE: - sclp_c->unassign_storage(sclp, sccb); - break; case SCLP_CMDW_CONFIGURE_IOA: sclp_configure_io_adapter(sclp, sccb, true); break; @@ -540,9 +302,6 @@ static void sclp_memory_init(SCLPDevice *sclp) { MachineState *machine = MACHINE(qdev_get_machine()); ram_addr_t initial_mem = machine->ram_size; - ram_addr_t max_mem = machine->maxram_size; - ram_addr_t standby_mem = max_mem - initial_mem; - ram_addr_t pad_mem = 0; int increment_size = 20; /* The storage increment size is a multiple of 1M and is a power of 2. @@ -552,34 +311,14 @@ static void sclp_memory_init(SCLPDevice *sclp) while ((initial_mem >> increment_size) > MAX_STORAGE_INCREMENTS) { increment_size++; } - if (machine->ram_slots) { - while ((standby_mem >> increment_size) > MAX_STORAGE_INCREMENTS) { - increment_size++; - } - } sclp->increment_size = increment_size; - /* The core and standby memory areas need to be aligned with - * the increment size. In effect, this can cause the - * user-specified memory size to be rounded down to align - * with the nearest increment boundary. */ + /* The core memory area needs to be aligned with the increment size. + * In effect, this can cause the user-specified memory size to be rounded + * down to align with the nearest increment boundary. */ initial_mem = initial_mem >> increment_size << increment_size; - standby_mem = standby_mem >> increment_size << increment_size; - - /* If the size of ram is not on a MEM_SECTION_SIZE boundary, - calculate the pad size necessary to force this boundary. */ - if (machine->ram_slots && standby_mem) { - sclpMemoryHotplugDev *mhd = init_sclp_memory_hotplug_dev(); - if (initial_mem % MEM_SECTION_SIZE) { - pad_mem = MEM_SECTION_SIZE - initial_mem % MEM_SECTION_SIZE; - } - mhd->increment_size = increment_size; - mhd->pad_size = pad_mem; - mhd->standby_mem_size = standby_mem; - } machine->ram_size = initial_mem; - machine->maxram_size = initial_mem + pad_mem + standby_mem; /* let's propagate the changed ram size into the global variable. */ ram_size = initial_mem; } @@ -613,11 +352,6 @@ static void sclp_class_init(ObjectClass *oc, void *data) dc->user_creatable = false; sc->read_SCP_info = read_SCP_info; - sc->read_storage_element0_info = read_storage_element0_info; - sc->read_storage_element1_info = read_storage_element1_info; - sc->attach_storage_element = attach_storage_element; - sc->assign_storage = assign_storage; - sc->unassign_storage = unassign_storage; sc->read_cpu_info = sclp_read_cpu_info; sc->execute = sclp_execute; sc->service_interrupt = service_interrupt; @@ -632,42 +366,8 @@ static TypeInfo sclp_info = { .class_size = sizeof(SCLPDeviceClass), }; -sclpMemoryHotplugDev *init_sclp_memory_hotplug_dev(void) -{ - DeviceState *dev; - dev = qdev_create(NULL, TYPE_SCLP_MEMORY_HOTPLUG_DEV); - object_property_add_child(qdev_get_machine(), - TYPE_SCLP_MEMORY_HOTPLUG_DEV, - OBJECT(dev), NULL); - qdev_init_nofail(dev); - return SCLP_MEMORY_HOTPLUG_DEV(object_resolve_path( - TYPE_SCLP_MEMORY_HOTPLUG_DEV, NULL)); -} - -sclpMemoryHotplugDev *get_sclp_memory_hotplug_dev(void) -{ - return SCLP_MEMORY_HOTPLUG_DEV(object_resolve_path( - TYPE_SCLP_MEMORY_HOTPLUG_DEV, NULL)); -} - -static void sclp_memory_hotplug_dev_class_init(ObjectClass *klass, - void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - - set_bit(DEVICE_CATEGORY_MISC, dc->categories); -} - -static TypeInfo sclp_memory_hotplug_dev_info = { - .name = TYPE_SCLP_MEMORY_HOTPLUG_DEV, - .parent = TYPE_SYS_BUS_DEVICE, - .instance_size = sizeof(sclpMemoryHotplugDev), - .class_init = sclp_memory_hotplug_dev_class_init, -}; - static void register_types(void) { - type_register_static(&sclp_memory_hotplug_dev_info); type_register_static(&sclp_info); } type_init(register_types); diff --git a/hw/ssi/xilinx_spips.c b/hw/ssi/xilinx_spips.c index 8af36ca3d4..426f971311 100644 --- a/hw/ssi/xilinx_spips.c +++ b/hw/ssi/xilinx_spips.c @@ -223,7 +223,7 @@ static void xilinx_spips_update_cs(XilinxSPIPS *s, int field) { int i; - for (i = 0; i < s->num_cs; i++) { + for (i = 0; i < s->num_cs * s->num_busses; i++) { bool old_state = s->cs_lines_state[i]; bool new_state = field & (1 << i); @@ -234,7 +234,7 @@ static void xilinx_spips_update_cs(XilinxSPIPS *s, int field) } qemu_set_irq(s->cs_lines[i], !new_state); } - if (!(field & ((1 << s->num_cs) - 1))) { + if (!(field & ((1 << (s->num_cs * s->num_busses)) - 1))) { s->snoop_state = SNOOP_CHECKING; s->cmd_dummies = 0; s->link_state = 1; @@ -248,7 +248,40 @@ static void xlnx_zynqmp_qspips_update_cs_lines(XlnxZynqMPQSPIPS *s) { if (s->regs[R_GQSPI_GF_SNAPSHOT]) { int field = ARRAY_FIELD_EX32(s->regs, GQSPI_GF_SNAPSHOT, CHIP_SELECT); - xilinx_spips_update_cs(XILINX_SPIPS(s), field); + bool upper_cs_sel = field & (1 << 1); + bool lower_cs_sel = field & 1; + bool bus0_enabled; + bool bus1_enabled; + uint8_t buses; + int cs = 0; + + buses = ARRAY_FIELD_EX32(s->regs, GQSPI_GF_SNAPSHOT, DATA_BUS_SELECT); + bus0_enabled = buses & 1; + bus1_enabled = buses & (1 << 1); + + if (bus0_enabled && bus1_enabled) { + if (lower_cs_sel) { + cs |= 1; + } + if (upper_cs_sel) { + cs |= 1 << 3; + } + } else if (bus0_enabled) { + if (lower_cs_sel) { + cs |= 1; + } + if (upper_cs_sel) { + cs |= 1 << 1; + } + } else if (bus1_enabled) { + if (lower_cs_sel) { + cs |= 1 << 2; + } + if (upper_cs_sel) { + cs |= 1 << 3; + } + } + xilinx_spips_update_cs(XILINX_SPIPS(s), cs); } } @@ -260,7 +293,7 @@ static void xilinx_spips_update_cs_lines(XilinxSPIPS *s) if (num_effective_busses(s) == 2) { /* Single bit chip-select for qspi */ field &= 0x1; - field |= field << 1; + field |= field << 3; /* Dual stack U-Page */ } else if (s->regs[R_LQSPI_CFG] & LQSPI_CFG_TWO_MEM && s->regs[R_LQSPI_STS] & LQSPI_CFG_U_PAGE) { @@ -544,7 +577,7 @@ static int xilinx_spips_num_dummies(XilinxQSPIPS *qs, uint8_t command) return 2; case QIOR: case QIOR_4: - return 5; + return 4; default: return -1; } diff --git a/include/exec/helper-head.h b/include/exec/helper-head.h index e1fd08f2ba..15b6a68de3 100644 --- a/include/exec/helper-head.h +++ b/include/exec/helper-head.h @@ -26,6 +26,7 @@ #define dh_alias_int i32 #define dh_alias_i64 i64 #define dh_alias_s64 i64 +#define dh_alias_f16 i32 #define dh_alias_f32 i32 #define dh_alias_f64 i64 #define dh_alias_ptr ptr @@ -38,6 +39,7 @@ #define dh_ctype_int int #define dh_ctype_i64 uint64_t #define dh_ctype_s64 int64_t +#define dh_ctype_f16 float16 #define dh_ctype_f32 float32 #define dh_ctype_f64 float64 #define dh_ctype_ptr void * @@ -94,6 +96,7 @@ #define dh_is_signed_s32 1 #define dh_is_signed_i64 0 #define dh_is_signed_s64 1 +#define dh_is_signed_f16 0 #define dh_is_signed_f32 0 #define dh_is_signed_f64 0 #define dh_is_signed_tl 0 diff --git a/include/fpu/softfloat.h b/include/fpu/softfloat.h index 9b7b5e34e2..27876e711c 100644 --- a/include/fpu/softfloat.h +++ b/include/fpu/softfloat.h @@ -306,8 +306,11 @@ static inline float16 float16_set_sign(float16 a, int sign) } #define float16_zero make_float16(0) -#define float16_one make_float16(0x3c00) #define float16_half make_float16(0x3800) +#define float16_one make_float16(0x3c00) +#define float16_one_point_five make_float16(0x3e00) +#define float16_two make_float16(0x4000) +#define float16_three make_float16(0x4200) #define float16_infinity make_float16(0x7c00) /*---------------------------------------------------------------------------- @@ -415,11 +418,13 @@ static inline float32 float32_set_sign(float32 a, int sign) } #define float32_zero make_float32(0) -#define float32_one make_float32(0x3f800000) #define float32_half make_float32(0x3f000000) +#define float32_one make_float32(0x3f800000) +#define float32_one_point_five make_float32(0x3fc00000) +#define float32_two make_float32(0x40000000) +#define float32_three make_float32(0x40400000) #define float32_infinity make_float32(0x7f800000) - /*---------------------------------------------------------------------------- | The pattern for a default generated single-precision NaN. *----------------------------------------------------------------------------*/ @@ -526,9 +531,12 @@ static inline float64 float64_set_sign(float64 a, int sign) } #define float64_zero make_float64(0) +#define float64_half make_float64(0x3fe0000000000000LL) #define float64_one make_float64(0x3ff0000000000000LL) +#define float64_one_point_five make_float64(0x3FF8000000000000ULL) +#define float64_two make_float64(0x4000000000000000ULL) +#define float64_three make_float64(0x4008000000000000ULL) #define float64_ln2 make_float64(0x3fe62e42fefa39efLL) -#define float64_half make_float64(0x3fe0000000000000LL) #define float64_infinity make_float64(0x7ff0000000000000LL) /*---------------------------------------------------------------------------- diff --git a/include/hw/i2c/i2c.h b/include/hw/i2c/i2c.h index 24e95d0155..d727379b48 100644 --- a/include/hw/i2c/i2c.h +++ b/include/hw/i2c/i2c.h @@ -25,8 +25,7 @@ typedef struct I2CSlave I2CSlave; #define I2C_SLAVE_GET_CLASS(obj) \ OBJECT_GET_CLASS(I2CSlaveClass, (obj), TYPE_I2C_SLAVE) -typedef struct I2CSlaveClass -{ +typedef struct I2CSlaveClass { DeviceClass parent_class; /* Callbacks provided by the device. */ @@ -50,14 +49,30 @@ typedef struct I2CSlaveClass int (*event)(I2CSlave *s, enum i2c_event event); } I2CSlaveClass; -struct I2CSlave -{ +struct I2CSlave { DeviceState qdev; /* Remaining fields for internal use by the I2C code. */ uint8_t address; }; +#define TYPE_I2C_BUS "i2c-bus" +#define I2C_BUS(obj) OBJECT_CHECK(I2CBus, (obj), TYPE_I2C_BUS) + +typedef struct I2CNode I2CNode; + +struct I2CNode { + I2CSlave *elt; + QLIST_ENTRY(I2CNode) next; +}; + +struct I2CBus { + BusState qbus; + QLIST_HEAD(, I2CNode) current_devs; + uint8_t saved_address; + bool broadcast; +}; + I2CBus *i2c_init_bus(DeviceState *parent, const char *name); void i2c_set_slave_address(I2CSlave *dev, uint8_t address); int i2c_bus_busy(I2CBus *bus); diff --git a/include/hw/register.h b/include/hw/register.h index de2414e6b4..5796584588 100644 --- a/include/hw/register.h +++ b/include/hw/register.h @@ -34,7 +34,7 @@ typedef struct RegisterInfoArray RegisterInfoArray; * immediately before the actual write. The returned value is what is written, * giving the handler a chance to modify the written value. * @post_write: Post write callback. Passed the written value. Most write side - * effects should be implemented here. + * effects should be implemented here. This is called during device reset. * * @post_read: Post read callback. Passes the value that is about to be returned * for a read. The return value from this function is what is ultimately read, @@ -135,8 +135,8 @@ uint64_t register_read(RegisterInfo *reg, uint64_t re, const char* prefix, bool debug); /** - * reset a register - * @reg: register to reset + * Resets a register. This will also call the post_write hook if it exists. + * @reg: The register to reset. */ void register_reset(RegisterInfo *reg); diff --git a/include/hw/s390x/sclp.h b/include/hw/s390x/sclp.h index 847ff32f85..f9db243484 100644 --- a/include/hw/s390x/sclp.h +++ b/include/hw/s390x/sclp.h @@ -35,7 +35,6 @@ #define SCLP_FC_ASSIGN_ATTACH_READ_STOR 0xE00000000000ULL #define SCLP_STARTING_SUBINCREMENT_ID 0x10001 #define SCLP_INCREMENT_UNIT 0x10000 -#define MAX_AVAIL_SLOTS 32 #define MAX_STORAGE_INCREMENTS 1020 /* CPU hotplug SCLP codes */ @@ -202,12 +201,6 @@ typedef struct SCLPDeviceClass { /* private */ DeviceClass parent_class; void (*read_SCP_info)(SCLPDevice *sclp, SCCB *sccb); - void (*read_storage_element0_info)(SCLPDevice *sclp, SCCB *sccb); - void (*read_storage_element1_info)(SCLPDevice *sclp, SCCB *sccb); - void (*attach_storage_element)(SCLPDevice *sclp, SCCB *sccb, - uint16_t element); - void (*assign_storage)(SCLPDevice *sclp, SCCB *sccb); - void (*unassign_storage)(SCLPDevice *sclp, SCCB *sccb); void (*read_cpu_info)(SCLPDevice *sclp, SCCB *sccb); /* public */ @@ -215,23 +208,6 @@ typedef struct SCLPDeviceClass { void (*service_interrupt)(SCLPDevice *sclp, uint32_t sccb); } SCLPDeviceClass; -typedef struct sclpMemoryHotplugDev sclpMemoryHotplugDev; - -#define TYPE_SCLP_MEMORY_HOTPLUG_DEV "sclp-memory-hotplug-dev" -#define SCLP_MEMORY_HOTPLUG_DEV(obj) \ - OBJECT_CHECK(sclpMemoryHotplugDev, (obj), TYPE_SCLP_MEMORY_HOTPLUG_DEV) - -struct sclpMemoryHotplugDev { - SysBusDevice parent; - ram_addr_t standby_mem_size; - ram_addr_t padded_ram_size; - ram_addr_t pad_size; - ram_addr_t standby_subregion_size; - ram_addr_t rzm; - int increment_size; - char *standby_state_map; -}; - static inline int sccb_data_len(SCCB *sccb) { return be16_to_cpu(sccb->h.length) - sizeof(sccb->h); @@ -239,8 +215,6 @@ static inline int sccb_data_len(SCCB *sccb) void s390_sclp_init(void); -sclpMemoryHotplugDev *init_sclp_memory_hotplug_dev(void); -sclpMemoryHotplugDev *get_sclp_memory_hotplug_dev(void); void sclp_service_interrupt(uint32_t sccb); void raise_irq_cpu_hotplug(void); int sclp_service_call(CPUS390XState *env, uint64_t sccb, uint32_t code); diff --git a/linux-user/elfload.c b/linux-user/elfload.c index 0208022445..6689089cd2 100644 --- a/linux-user/elfload.c +++ b/linux-user/elfload.c @@ -551,6 +551,8 @@ static uint32_t get_elf_hwcap(void) GET_FEATURE(ARM_FEATURE_V8_SM3, ARM_HWCAP_A64_SM3); GET_FEATURE(ARM_FEATURE_V8_SM4, ARM_HWCAP_A64_SM4); GET_FEATURE(ARM_FEATURE_V8_SHA512, ARM_HWCAP_A64_SHA512); + GET_FEATURE(ARM_FEATURE_V8_FP16, + ARM_HWCAP_A64_FPHP | ARM_HWCAP_A64_ASIMDHP); #undef GET_FEATURE return hwcaps; @@ -1055,7 +1055,7 @@ int monitor_set_cpu(int cpu_index) return 0; } -CPUState *mon_get_cpu(void) +static CPUState *mon_get_cpu_sync(bool synchronize) { CPUState *cpu; @@ -1074,10 +1074,17 @@ CPUState *mon_get_cpu(void) monitor_set_cpu(first_cpu->cpu_index); cpu = first_cpu; } - cpu_synchronize_state(cpu); + if (synchronize) { + cpu_synchronize_state(cpu); + } return cpu; } +CPUState *mon_get_cpu(void) +{ + return mon_get_cpu_sync(true); +} + CPUArchState *mon_get_cpu_env(void) { CPUState *cs = mon_get_cpu(); @@ -1087,7 +1094,7 @@ CPUArchState *mon_get_cpu_env(void) int monitor_get_cpu_index(void) { - CPUState *cs = mon_get_cpu(); + CPUState *cs = mon_get_cpu_sync(false); return cs ? cs->cpu_index : UNASSIGNED_CPU_INDEX; } diff --git a/pc-bios/s390-ccw.img b/pc-bios/s390-ccw.img Binary files differindex 97155d2638..fbd76bb55e 100644 --- a/pc-bios/s390-ccw.img +++ b/pc-bios/s390-ccw.img diff --git a/pc-bios/s390-ccw/Makefile b/pc-bios/s390-ccw/Makefile index 6d0c2ee691..1712c2d95d 100644 --- a/pc-bios/s390-ccw/Makefile +++ b/pc-bios/s390-ccw/Makefile @@ -9,7 +9,7 @@ $(call set-vpath, $(SRC_PATH)/pc-bios/s390-ccw) .PHONY : all clean build-all -OBJECTS = start.o main.o bootmap.o sclp.o virtio.o virtio-scsi.o virtio-blkdev.o +OBJECTS = start.o main.o bootmap.o sclp.o virtio.o virtio-scsi.o virtio-blkdev.o libc.o menu.o QEMU_CFLAGS := $(filter -W%, $(QEMU_CFLAGS)) QEMU_CFLAGS += -ffreestanding -fno-delete-null-pointer-checks -msoft-float QEMU_CFLAGS += -march=z900 -fPIE -fno-strict-aliasing diff --git a/pc-bios/s390-ccw/bootmap.c b/pc-bios/s390-ccw/bootmap.c index 67a6123ed4..29bfd8c875 100644 --- a/pc-bios/s390-ccw/bootmap.c +++ b/pc-bios/s390-ccw/bootmap.c @@ -83,6 +83,10 @@ static void jump_to_IPL_code(uint64_t address) static unsigned char _bprs[8*1024]; /* guessed "max" ECKD sector size */ static const int max_bprs_entries = sizeof(_bprs) / sizeof(ExtEckdBlockPtr); +static uint8_t _s2[MAX_SECTOR_SIZE * 3] __attribute__((__aligned__(PAGE_SIZE))); +static void *s2_prev_blk = _s2; +static void *s2_cur_blk = _s2 + MAX_SECTOR_SIZE; +static void *s2_next_blk = _s2 + MAX_SECTOR_SIZE * 2; static inline void verify_boot_info(BootInfo *bip) { @@ -95,32 +99,32 @@ static inline void verify_boot_info(BootInfo *bip) "Bad block size in zIPL section of the 1st record."); } -static block_number_t eckd_block_num(BootMapPointer *p) +static block_number_t eckd_block_num(EckdCHS *chs) { const uint64_t sectors = virtio_get_sectors(); const uint64_t heads = virtio_get_heads(); - const uint64_t cylinder = p->eckd.cylinder - + ((p->eckd.head & 0xfff0) << 12); - const uint64_t head = p->eckd.head & 0x000f; + const uint64_t cylinder = chs->cylinder + + ((chs->head & 0xfff0) << 12); + const uint64_t head = chs->head & 0x000f; const block_number_t block = sectors * heads * cylinder + sectors * head - + p->eckd.sector + + chs->sector - 1; /* block nr starts with zero */ return block; } static bool eckd_valid_address(BootMapPointer *p) { - const uint64_t head = p->eckd.head & 0x000f; + const uint64_t head = p->eckd.chs.head & 0x000f; if (head >= virtio_get_heads() - || p->eckd.sector > virtio_get_sectors() - || p->eckd.sector <= 0) { + || p->eckd.chs.sector > virtio_get_sectors() + || p->eckd.chs.sector <= 0) { return false; } if (!virtio_guessed_disk_nature() && - eckd_block_num(p) >= virtio_get_blocks()) { + eckd_block_num(&p->eckd.chs) >= virtio_get_blocks()) { return false; } @@ -140,7 +144,7 @@ static block_number_t load_eckd_segments(block_number_t blk, uint64_t *address) do { more_data = false; for (j = 0;; j++) { - block_nr = eckd_block_num((void *)&(bprs[j].xeckd)); + block_nr = eckd_block_num(&bprs[j].xeckd.bptr.chs); if (is_null_block_number(block_nr)) { /* end of chunk */ break; } @@ -182,31 +186,105 @@ static block_number_t load_eckd_segments(block_number_t blk, uint64_t *address) return block_nr; } -static void run_eckd_boot_script(block_number_t mbr_block_nr) +static bool find_zipl_boot_menu_banner(int *offset) +{ + int i; + + /* Menu banner starts with "zIPL" */ + for (i = 0; i < virtio_get_block_size() - 4; i++) { + if (magic_match(s2_cur_blk + i, ZIPL_MAGIC_EBCDIC)) { + *offset = i; + return true; + } + } + + return false; +} + +static int eckd_get_boot_menu_index(block_number_t s1b_block_nr) +{ + block_number_t cur_block_nr; + block_number_t prev_block_nr = 0; + block_number_t next_block_nr = 0; + EckdStage1b *s1b = (void *)sec; + int banner_offset; + int i; + + /* Get Stage1b data */ + memset(sec, FREE_SPACE_FILLER, sizeof(sec)); + read_block(s1b_block_nr, s1b, "Cannot read stage1b boot loader"); + + memset(_s2, FREE_SPACE_FILLER, sizeof(_s2)); + + /* Get Stage2 data */ + for (i = 0; i < STAGE2_BLK_CNT_MAX; i++) { + cur_block_nr = eckd_block_num(&s1b->seek[i].chs); + + if (!cur_block_nr) { + break; + } + + read_block(cur_block_nr, s2_cur_blk, "Cannot read stage2 boot loader"); + + if (find_zipl_boot_menu_banner(&banner_offset)) { + /* + * Load the adjacent blocks to account for the + * possibility of menu data spanning multiple blocks. + */ + if (prev_block_nr) { + read_block(prev_block_nr, s2_prev_blk, + "Cannot read stage2 boot loader"); + } + + if (i + 1 < STAGE2_BLK_CNT_MAX) { + next_block_nr = eckd_block_num(&s1b->seek[i + 1].chs); + } + + if (next_block_nr) { + read_block(next_block_nr, s2_next_blk, + "Cannot read stage2 boot loader"); + } + + return menu_get_zipl_boot_index(s2_cur_blk + banner_offset); + } + + prev_block_nr = cur_block_nr; + } + + sclp_print("No zipl boot menu data found. Booting default entry."); + return 0; +} + +static void run_eckd_boot_script(block_number_t bmt_block_nr, + block_number_t s1b_block_nr) { int i; unsigned int loadparm = get_loadparm_index(); block_number_t block_nr; uint64_t address; - ScsiMbr *bte = (void *)sec; /* Eckd bootmap table entry */ + BootMapTable *bmt = (void *)sec; BootMapScript *bms = (void *)sec; + if (menu_is_enabled_zipl()) { + loadparm = eckd_get_boot_menu_index(s1b_block_nr); + } + debug_print_int("loadparm", loadparm); - IPL_assert(loadparm < 31, "loadparm value greater than" + IPL_assert(loadparm <= MAX_TABLE_ENTRIES, "loadparm value greater than" " maximum number of boot entries allowed"); memset(sec, FREE_SPACE_FILLER, sizeof(sec)); - read_block(mbr_block_nr, sec, "Cannot read MBR"); + read_block(bmt_block_nr, sec, "Cannot read Boot Map Table"); - block_nr = eckd_block_num((void *)&(bte->blockptr[loadparm])); - IPL_assert(block_nr != -1, "No Boot Map"); + block_nr = eckd_block_num(&bmt->entry[loadparm].xeckd.bptr.chs); + IPL_assert(block_nr != -1, "Cannot find Boot Map Table Entry"); memset(sec, FREE_SPACE_FILLER, sizeof(sec)); read_block(block_nr, sec, "Cannot read Boot Map Script"); for (i = 0; bms->entry[i].type == BOOT_SCRIPT_LOAD; i++) { address = bms->entry[i].address.load_address; - block_nr = eckd_block_num(&(bms->entry[i].blkptr)); + block_nr = eckd_block_num(&bms->entry[i].blkptr.xeckd.bptr.chs); do { block_nr = load_eckd_segments(block_nr, &address); @@ -221,9 +299,9 @@ static void run_eckd_boot_script(block_number_t mbr_block_nr) static void ipl_eckd_cdl(void) { XEckdMbr *mbr; - Ipl2 *ipl2 = (void *)sec; + EckdCdlIpl2 *ipl2 = (void *)sec; IplVolumeLabel *vlbl = (void *)sec; - block_number_t block_nr; + block_number_t bmt_block_nr, s1b_block_nr; /* we have just read the block #0 and recognized it as "IPL1" */ sclp_print("CDL\n"); @@ -231,15 +309,18 @@ static void ipl_eckd_cdl(void) memset(sec, FREE_SPACE_FILLER, sizeof(sec)); read_block(1, ipl2, "Cannot read IPL2 record at block 1"); - mbr = &ipl2->u.x.mbr; + mbr = &ipl2->mbr; IPL_assert(magic_match(mbr, ZIPL_MAGIC), "No zIPL section in IPL2 record."); IPL_assert(block_size_ok(mbr->blockptr.xeckd.bptr.size), "Bad block size in zIPL section of IPL2 record."); IPL_assert(mbr->dev_type == DEV_TYPE_ECKD, "Non-ECKD device type in zIPL section of IPL2 record."); - /* save pointer to Boot Script */ - block_nr = eckd_block_num((void *)&(mbr->blockptr)); + /* save pointer to Boot Map Table */ + bmt_block_nr = eckd_block_num(&mbr->blockptr.xeckd.bptr.chs); + + /* save pointer to Stage1b Data */ + s1b_block_nr = eckd_block_num(&ipl2->stage1.seek[0].chs); memset(sec, FREE_SPACE_FILLER, sizeof(sec)); read_block(2, vlbl, "Cannot read Volume Label at block 2"); @@ -249,7 +330,7 @@ static void ipl_eckd_cdl(void) "Invalid magic of volser block"); print_volser(vlbl->f.volser); - run_eckd_boot_script(block_nr); + run_eckd_boot_script(bmt_block_nr, s1b_block_nr); /* no return */ } @@ -280,8 +361,8 @@ static void print_eckd_ldl_msg(ECKD_IPL_mode_t mode) static void ipl_eckd_ldl(ECKD_IPL_mode_t mode) { - block_number_t block_nr; - BootInfo *bip = (void *)(sec + 0x70); /* BootInfo is MBR for LDL */ + block_number_t bmt_block_nr, s1b_block_nr; + EckdLdlIpl1 *ipl1 = (void *)sec; if (mode != ECKD_LDL_UNLABELED) { print_eckd_ldl_msg(mode); @@ -292,15 +373,20 @@ static void ipl_eckd_ldl(ECKD_IPL_mode_t mode) memset(sec, FREE_SPACE_FILLER, sizeof(sec)); read_block(0, sec, "Cannot read block 0 to grab boot info."); if (mode == ECKD_LDL_UNLABELED) { - if (!magic_match(bip->magic, ZIPL_MAGIC)) { + if (!magic_match(ipl1->bip.magic, ZIPL_MAGIC)) { return; /* not applicable layout */ } sclp_print("unlabeled LDL.\n"); } - verify_boot_info(bip); + verify_boot_info(&ipl1->bip); - block_nr = eckd_block_num((void *)&(bip->bp.ipl.bm_ptr.eckd.bptr)); - run_eckd_boot_script(block_nr); + /* save pointer to Boot Map Table */ + bmt_block_nr = eckd_block_num(&ipl1->bip.bp.ipl.bm_ptr.eckd.bptr.chs); + + /* save pointer to Stage1b Data */ + s1b_block_nr = eckd_block_num(&ipl1->stage1.seek[0].chs); + + run_eckd_boot_script(bmt_block_nr, s1b_block_nr); /* no return */ } @@ -325,7 +411,7 @@ static void print_eckd_msg(void) static void ipl_eckd(void) { - ScsiMbr *mbr = (void *)sec; + XEckdMbr *mbr = (void *)sec; LDL_VTOC *vlbl = (void *)sec; print_eckd_msg(); @@ -449,10 +535,8 @@ static void zipl_run(ScsiBlockPtr *pte) static void ipl_scsi(void) { ScsiMbr *mbr = (void *)sec; - uint8_t *ns, *ns_end; int program_table_entries = 0; - const int pte_len = sizeof(ScsiBlockPtr); - ScsiBlockPtr *prog_table_entry = NULL; + BootMapTable *prog_table = (void *)sec; unsigned int loadparm = get_loadparm_index(); /* Grab the MBR */ @@ -467,34 +551,32 @@ static void ipl_scsi(void) debug_print_int("MBR Version", mbr->version_id); IPL_check(mbr->version_id == 1, "Unknown MBR layout version, assuming version 1"); - debug_print_int("program table", mbr->blockptr[0].blockno); - IPL_assert(mbr->blockptr[0].blockno, "No Program Table"); + debug_print_int("program table", mbr->pt.blockno); + IPL_assert(mbr->pt.blockno, "No Program Table"); /* Parse the program table */ - read_block(mbr->blockptr[0].blockno, sec, - "Error reading Program Table"); - + read_block(mbr->pt.blockno, sec, "Error reading Program Table"); IPL_assert(magic_match(sec, ZIPL_MAGIC), "No zIPL magic in PT"); - debug_print_int("loadparm index", loadparm); - ns_end = sec + virtio_get_block_size(); - for (ns = (sec + pte_len); (ns + pte_len) < ns_end; ns += pte_len) { - prog_table_entry = (ScsiBlockPtr *)ns; - if (!prog_table_entry->blockno) { + while (program_table_entries <= MAX_TABLE_ENTRIES) { + if (!prog_table->entry[program_table_entries].scsi.blockno) { break; } - program_table_entries++; - if (program_table_entries == loadparm + 1) { - break; /* selected entry found */ - } } debug_print_int("program table entries", program_table_entries); - IPL_assert(program_table_entries != 0, "Empty Program Table"); - zipl_run(prog_table_entry); /* no return */ + if (menu_is_enabled_enum()) { + loadparm = menu_get_enum_boot_index(program_table_entries); + } + + debug_print_int("loadparm", loadparm); + IPL_assert(loadparm <= MAX_TABLE_ENTRIES, "loadparm value greater than" + " maximum number of boot entries allowed"); + + zipl_run(&prog_table->entry[loadparm].scsi); /* no return */ } /*********************************************************************** @@ -512,7 +594,7 @@ static bool is_iso_bc_entry_compatible(IsoBcSection *s) "Failed to read image sector 0"); /* Checking bytes 8 - 32 for S390 Linux magic */ - return !_memcmp(magic_sec + 8, linux_s390_magic, 24); + return !memcmp(magic_sec + 8, linux_s390_magic, 24); } /* Location of the current sector of the directory */ @@ -641,7 +723,7 @@ static uint32_t find_iso_bc(void) if (vd->type == VOL_DESC_TYPE_BOOT) { IsoVdElTorito *et = &vd->vd.boot; - if (!_memcmp(&et->el_torito[0], el_torito_magic, 32)) { + if (!memcmp(&et->el_torito[0], el_torito_magic, 32)) { return bswap32(et->bc_offset); } } diff --git a/pc-bios/s390-ccw/bootmap.h b/pc-bios/s390-ccw/bootmap.h index cf99a4c728..c636626f1a 100644 --- a/pc-bios/s390-ccw/bootmap.h +++ b/pc-bios/s390-ccw/bootmap.h @@ -32,10 +32,14 @@ typedef struct FbaBlockPtr { uint16_t blockct; } __attribute__ ((packed)) FbaBlockPtr; -typedef struct EckdBlockPtr { - uint16_t cylinder; /* cylinder/head/sector is an address of the block */ +typedef struct EckdCHS { + uint16_t cylinder; uint16_t head; uint8_t sector; +} __attribute__ ((packed)) EckdCHS; + +typedef struct EckdBlockPtr { + EckdCHS chs; /* cylinder/head/sector is an address of the block */ uint16_t size; uint8_t count; /* (size_in_blocks-1); * it's 0 for TablePtr, ScriptPtr, and SectionPtr */ @@ -53,6 +57,15 @@ typedef union BootMapPointer { ExtEckdBlockPtr xeckd; } __attribute__ ((packed)) BootMapPointer; +#define MAX_TABLE_ENTRIES 30 + +/* aka Program Table */ +typedef struct BootMapTable { + uint8_t magic[4]; + uint8_t reserved[12]; + BootMapPointer entry[]; +} __attribute__ ((packed)) BootMapTable; + typedef struct ComponentEntry { ScsiBlockPtr data; uint8_t pad[7]; @@ -70,10 +83,11 @@ typedef struct ScsiMbr { uint8_t magic[4]; uint32_t version_id; uint8_t reserved[8]; - ScsiBlockPtr blockptr[]; + ScsiBlockPtr pt; /* block pointer to program table */ } __attribute__ ((packed)) ScsiMbr; #define ZIPL_MAGIC "zIPL" +#define ZIPL_MAGIC_EBCDIC "\xa9\xc9\xd7\xd3" #define IPL1_MAGIC "\xc9\xd7\xd3\xf1" /* == "IPL1" in EBCDIC */ #define IPL2_MAGIC "\xc9\xd7\xd3\xf2" /* == "IPL2" in EBCDIC */ #define VOL1_MAGIC "\xe5\xd6\xd3\xf1" /* == "VOL1" in EBCDIC */ @@ -226,22 +240,45 @@ typedef struct BootInfo { /* @ 0x70, record #0 */ } bp; } __attribute__ ((packed)) BootInfo; /* see also XEckdMbr */ -typedef struct Ipl1 { - unsigned char key[4]; /* == "IPL1" */ - unsigned char data[24]; -} __attribute__((packed)) Ipl1; - -typedef struct Ipl2 { - unsigned char key[4]; /* == "IPL2" */ - union { - unsigned char data[144]; - struct { - unsigned char reserved1[92-4]; - XEckdMbr mbr; - unsigned char reserved2[144-(92-4)-sizeof(XEckdMbr)]; - } x; - } u; -} __attribute__((packed)) Ipl2; +/* + * Structs for IPL + */ +#define STAGE2_BLK_CNT_MAX 24 /* Stage 1b can load up to 24 blocks */ + +typedef struct EckdCdlIpl1 { + uint8_t key[4]; /* == "IPL1" */ + uint8_t data[24]; +} __attribute__((packed)) EckdCdlIpl1; + +typedef struct EckdSeekArg { + uint16_t pad; + EckdCHS chs; + uint8_t pad2; +} __attribute__ ((packed)) EckdSeekArg; + +typedef struct EckdStage1b { + uint8_t reserved[32 * STAGE2_BLK_CNT_MAX]; + struct EckdSeekArg seek[STAGE2_BLK_CNT_MAX]; + uint8_t unused[64]; +} __attribute__ ((packed)) EckdStage1b; + +typedef struct EckdStage1 { + uint8_t reserved[72]; + struct EckdSeekArg seek[2]; +} __attribute__ ((packed)) EckdStage1; + +typedef struct EckdCdlIpl2 { + uint8_t key[4]; /* == "IPL2" */ + struct EckdStage1 stage1; + XEckdMbr mbr; + uint8_t reserved[24]; +} __attribute__((packed)) EckdCdlIpl2; + +typedef struct EckdLdlIpl1 { + uint8_t reserved[24]; + struct EckdStage1 stage1; + BootInfo bip; /* BootInfo is MBR for LDL */ +} __attribute__((packed)) EckdLdlIpl1; typedef struct IplVolumeLabel { unsigned char key[4]; /* == "VOL1" */ @@ -310,20 +347,6 @@ static inline bool magic_match(const void *data, const void *magic) return *((uint32_t *)data) == *((uint32_t *)magic); } -static inline int _memcmp(const void *s1, const void *s2, size_t n) -{ - int i; - const uint8_t *p1 = s1, *p2 = s2; - - for (i = 0; i < n; i++) { - if (p1[i] != p2[i]) { - return p1[i] > p2[i] ? 1 : -1; - } - } - - return 0; -} - static inline uint32_t iso_733_to_u32(uint64_t x) { return (uint32_t)x; @@ -416,7 +439,7 @@ const uint8_t vol_desc_magic[] = "CD001"; static inline bool is_iso_vd_valid(IsoVolDesc *vd) { - return !_memcmp(&vd->ident[0], vol_desc_magic, 5) && + return !memcmp(&vd->ident[0], vol_desc_magic, 5) && vd->version == 0x1 && vd->type <= VOL_DESC_TYPE_PARTITION; } diff --git a/pc-bios/s390-ccw/iplb.h b/pc-bios/s390-ccw/iplb.h index 890aed9ece..7dfce4fbcf 100644 --- a/pc-bios/s390-ccw/iplb.h +++ b/pc-bios/s390-ccw/iplb.h @@ -13,8 +13,7 @@ #define IPLB_H struct IplBlockCcw { - uint64_t netboot_start_addr; - uint8_t reserved0[77]; + uint8_t reserved0[85]; uint8_t ssid; uint16_t devno; uint8_t vm_flags; @@ -73,6 +72,27 @@ typedef struct IplParameterBlock IplParameterBlock; extern IplParameterBlock iplb __attribute__((__aligned__(PAGE_SIZE))); +#define QIPL_ADDRESS 0xcc + +/* Boot Menu flags */ +#define QIPL_FLAG_BM_OPTS_CMD 0x80 +#define QIPL_FLAG_BM_OPTS_ZIPL 0x40 + +/* + * This definition must be kept in sync with the defininition + * in hw/s390x/ipl.h + */ +struct QemuIplParameters { + uint8_t qipl_flags; + uint8_t reserved1[3]; + uint64_t netboot_start_addr; + uint32_t boot_menu_timeout; + uint8_t reserved2[12]; +} __attribute__ ((packed)); +typedef struct QemuIplParameters QemuIplParameters; + +extern QemuIplParameters qipl; + #define S390_IPL_TYPE_FCP 0x00 #define S390_IPL_TYPE_CCW 0x02 #define S390_IPL_TYPE_QEMU_SCSI 0xff diff --git a/pc-bios/s390-ccw/libc.c b/pc-bios/s390-ccw/libc.c new file mode 100644 index 0000000000..38ea77d7aa --- /dev/null +++ b/pc-bios/s390-ccw/libc.c @@ -0,0 +1,88 @@ +/* + * libc-style definitions and functions + * + * Copyright 2018 IBM Corp. + * Author(s): Collin L. Walling <walling@linux.vnet.ibm.com> + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + */ + +#include "libc.h" +#include "s390-ccw.h" + +/** + * atoui: + * @str: the string to be converted. + * + * Given a string @str, convert it to an integer. Leading spaces are + * ignored. Any other non-numerical value will terminate the conversion + * and return 0. This function only handles numbers between 0 and + * UINT64_MAX inclusive. + * + * Returns: an integer converted from the string @str, or the number 0 + * if an error occurred. + */ +uint64_t atoui(const char *str) +{ + int val = 0; + + if (!str || !str[0]) { + return 0; + } + + while (*str == ' ') { + str++; + } + + while (*str) { + if (!isdigit(*str)) { + break; + } + val = val * 10 + *str - '0'; + str++; + } + + return val; +} + +/** + * uitoa: + * @num: an integer (base 10) to be converted. + * @str: a pointer to a string to store the conversion. + * @len: the length of the passed string. + * + * Given an integer @num, convert it to a string. The string @str must be + * allocated beforehand. The resulting string will be null terminated and + * returned. This function only handles numbers between 0 and UINT64_MAX + * inclusive. + * + * Returns: the string @str of the converted integer @num + */ +char *uitoa(uint64_t num, char *str, size_t len) +{ + size_t num_idx = 1; /* account for NUL */ + uint64_t tmp = num; + + IPL_assert(str != NULL, "uitoa: no space allocated to store string"); + + /* Count indices of num */ + while ((tmp /= 10) != 0) { + num_idx++; + } + + /* Check if we have enough space for num and NUL */ + IPL_assert(len > num_idx, "uitoa: array too small for conversion"); + + str[num_idx--] = '\0'; + + /* Convert int to string */ + while (num_idx >= 0) { + str[num_idx--] = num % 10 + '0'; + num /= 10; + } + + return str; +} diff --git a/pc-bios/s390-ccw/libc.h b/pc-bios/s390-ccw/libc.h index 0142ea8e7b..63ece70c6b 100644 --- a/pc-bios/s390-ccw/libc.h +++ b/pc-bios/s390-ccw/libc.h @@ -1,6 +1,8 @@ /* * libc-style definitions and functions * + * Copyright (c) 2013 Alexander Graf <agraf@suse.de> + * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the * Free Software Foundation; either version 2 of the License, or (at your @@ -19,7 +21,7 @@ typedef unsigned long long uint64_t; static inline void *memset(void *s, int c, size_t n) { - int i; + size_t i; unsigned char *p = s; for (i = 0; i < n; i++) { @@ -33,7 +35,7 @@ static inline void *memcpy(void *s1, const void *s2, size_t n) { uint8_t *dest = s1; const uint8_t *src = s2; - int i; + size_t i; for (i = 0; i < n; i++) { dest[i] = src[i]; @@ -42,4 +44,35 @@ static inline void *memcpy(void *s1, const void *s2, size_t n) return s1; } +static inline int memcmp(const void *s1, const void *s2, size_t n) +{ + size_t i; + const uint8_t *p1 = s1, *p2 = s2; + + for (i = 0; i < n; i++) { + if (p1[i] != p2[i]) { + return p1[i] > p2[i] ? 1 : -1; + } + } + + return 0; +} + +static inline size_t strlen(const char *str) +{ + size_t i; + for (i = 0; *str; i++) { + str++; + } + return i; +} + +static inline int isdigit(int c) +{ + return (c >= '0') && (c <= '9'); +} + +uint64_t atoui(const char *str); +char *uitoa(uint64_t num, char *str, size_t len); + #endif diff --git a/pc-bios/s390-ccw/main.c b/pc-bios/s390-ccw/main.c index 401e9dbb5f..9d9f8cf4d3 100644 --- a/pc-bios/s390-ccw/main.c +++ b/pc-bios/s390-ccw/main.c @@ -16,6 +16,11 @@ char stack[PAGE_SIZE * 8] __attribute__((__aligned__(PAGE_SIZE))); static SubChannelId blk_schid = { .one = 1 }; IplParameterBlock iplb __attribute__((__aligned__(PAGE_SIZE))); static char loadparm[8] = { 0, 0, 0, 0, 0, 0, 0, 0 }; +QemuIplParameters qipl; + +#define LOADPARM_PROMPT "PROMPT " +#define LOADPARM_EMPTY "........" +#define BOOT_MENU_FLAG_MASK (QIPL_FLAG_BM_OPTS_CMD | QIPL_FLAG_BM_OPTS_ZIPL) /* * Priniciples of Operations (SA22-7832-09) chapter 17 requires that @@ -40,22 +45,7 @@ void panic(const char *string) unsigned int get_loadparm_index(void) { - const char *lp = loadparm; - int i; - unsigned int idx = 0; - - for (i = 0; i < 8; i++) { - char c = lp[i]; - - if (c < '0' || c > '9') { - break; - } - - idx *= 10; - idx += c - '0'; - } - - return idx; + return atoui(loadparm); } static bool find_dev(Schib *schib, int dev_no) @@ -88,6 +78,27 @@ static bool find_dev(Schib *schib, int dev_no) return false; } +static void menu_setup(void) +{ + if (memcmp(loadparm, LOADPARM_PROMPT, 8) == 0) { + menu_set_parms(QIPL_FLAG_BM_OPTS_CMD, 0); + return; + } + + /* If loadparm was set to any other value, then do not enable menu */ + if (memcmp(loadparm, LOADPARM_EMPTY, 8) != 0) { + return; + } + + switch (iplb.pbt) { + case S390_IPL_TYPE_CCW: + case S390_IPL_TYPE_QEMU_SCSI: + menu_set_parms(qipl.qipl_flags & BOOT_MENU_FLAG_MASK, + qipl.boot_menu_timeout); + return; + } +} + static void virtio_setup(void) { Schib schib; @@ -96,6 +107,7 @@ static void virtio_setup(void) uint16_t dev_no; char ldp[] = "LOADPARM=[________]\n"; VDev *vdev = virtio_get_device(); + QemuIplParameters *early_qipl = (QemuIplParameters *)QIPL_ADDRESS; /* * We unconditionally enable mss support. In every sane configuration, @@ -108,6 +120,8 @@ static void virtio_setup(void) memcpy(ldp + 10, loadparm, 8); sclp_print(ldp); + memcpy(&qipl, early_qipl, sizeof(QemuIplParameters)); + if (store_iplb(&iplb)) { switch (iplb.pbt) { case S390_IPL_TYPE_CCW: @@ -128,6 +142,7 @@ static void virtio_setup(void) default: panic("List-directed IPL not supported yet!\n"); } + menu_setup(); } else { for (ssid = 0; ssid < 0x3; ssid++) { blk_schid.ssid = ssid; @@ -142,7 +157,7 @@ static void virtio_setup(void) if (virtio_get_device_type() == VIRTIO_ID_NET) { sclp_print("Network boot device detected\n"); - vdev->netboot_start_addr = iplb.ccw.netboot_start_addr; + vdev->netboot_start_addr = qipl.netboot_start_addr; } else { virtio_blk_setup_device(blk_schid); diff --git a/pc-bios/s390-ccw/menu.c b/pc-bios/s390-ccw/menu.c new file mode 100644 index 0000000000..96eec81e84 --- /dev/null +++ b/pc-bios/s390-ccw/menu.c @@ -0,0 +1,249 @@ +/* + * QEMU S390 Interactive Boot Menu + * + * Copyright 2018 IBM Corp. + * Author: Collin L. Walling <walling@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 "libc.h" +#include "s390-ccw.h" +#include "sclp.h" + +#define KEYCODE_NO_INP '\0' +#define KEYCODE_ESCAPE '\033' +#define KEYCODE_BACKSP '\177' +#define KEYCODE_ENTER '\r' + +/* Offsets from zipl fields to zipl banner start */ +#define ZIPL_TIMEOUT_OFFSET 138 +#define ZIPL_FLAG_OFFSET 140 + +#define TOD_CLOCK_MILLISECOND 0x3e8000 + +#define LOW_CORE_EXTERNAL_INT_ADDR 0x86 +#define CLOCK_COMPARATOR_INT 0X1004 + +static uint8_t flag; +static uint64_t timeout; + +static inline void enable_clock_int(void) +{ + uint64_t tmp = 0; + + asm volatile( + "stctg 0,0,%0\n" + "oi 6+%0, 0x8\n" + "lctlg 0,0,%0" + : : "Q" (tmp) : "memory" + ); +} + +static inline void disable_clock_int(void) +{ + uint64_t tmp = 0; + + asm volatile( + "stctg 0,0,%0\n" + "ni 6+%0, 0xf7\n" + "lctlg 0,0,%0" + : : "Q" (tmp) : "memory" + ); +} + +static inline void set_clock_comparator(uint64_t time) +{ + asm volatile("sckc %0" : : "Q" (time)); +} + +static inline bool check_clock_int(void) +{ + uint16_t *code = (uint16_t *)LOW_CORE_EXTERNAL_INT_ADDR; + + consume_sclp_int(); + + return *code == CLOCK_COMPARATOR_INT; +} + +static int read_prompt(char *buf, size_t len) +{ + char inp[2] = {}; + uint8_t idx = 0; + uint64_t time; + + if (timeout) { + time = get_clock() + timeout * TOD_CLOCK_MILLISECOND; + set_clock_comparator(time); + enable_clock_int(); + timeout = 0; + } + + while (!check_clock_int()) { + + sclp_read(inp, 1); /* Process only one character at a time */ + + switch (inp[0]) { + case KEYCODE_NO_INP: + case KEYCODE_ESCAPE: + continue; + case KEYCODE_BACKSP: + if (idx > 0) { + buf[--idx] = 0; + sclp_print("\b \b"); + } + continue; + case KEYCODE_ENTER: + disable_clock_int(); + return idx; + default: + /* Echo input and add to buffer */ + if (idx < len) { + buf[idx++] = inp[0]; + sclp_print(inp); + } + } + } + + disable_clock_int(); + *buf = 0; + + return 0; +} + +static int get_index(void) +{ + char buf[11]; + int len; + int i; + + memset(buf, 0, sizeof(buf)); + + sclp_set_write_mask(SCLP_EVENT_MASK_MSG_ASCII, SCLP_EVENT_MASK_MSG_ASCII); + + len = read_prompt(buf, sizeof(buf) - 1); + + sclp_set_write_mask(0, SCLP_EVENT_MASK_MSG_ASCII); + + /* If no input, boot default */ + if (len == 0) { + return 0; + } + + /* Check for erroneous input */ + for (i = 0; i < len; i++) { + if (!isdigit(buf[i])) { + return -1; + } + } + + return atoui(buf); +} + +static void boot_menu_prompt(bool retry) +{ + char tmp[11]; + + if (retry) { + sclp_print("\nError: undefined configuration" + "\nPlease choose:\n"); + } else if (timeout > 0) { + sclp_print("Please choose (default will boot in "); + sclp_print(uitoa(timeout / 1000, tmp, sizeof(tmp))); + sclp_print(" seconds):\n"); + } else { + sclp_print("Please choose:\n"); + } +} + +static int get_boot_index(int entries) +{ + int boot_index; + bool retry = false; + char tmp[5]; + + do { + boot_menu_prompt(retry); + boot_index = get_index(); + retry = true; + } while (boot_index < 0 || boot_index >= entries); + + sclp_print("\nBooting entry #"); + sclp_print(uitoa(boot_index, tmp, sizeof(tmp))); + + return boot_index; +} + +static void zipl_println(const char *data, size_t len) +{ + char buf[len + 2]; + + ebcdic_to_ascii(data, buf, len); + buf[len] = '\n'; + buf[len + 1] = '\0'; + + sclp_print(buf); +} + +int menu_get_zipl_boot_index(const char *menu_data) +{ + size_t len; + int entries; + uint16_t zipl_flag = *(uint16_t *)(menu_data - ZIPL_FLAG_OFFSET); + uint16_t zipl_timeout = *(uint16_t *)(menu_data - ZIPL_TIMEOUT_OFFSET); + + if (flag == QIPL_FLAG_BM_OPTS_ZIPL) { + if (!zipl_flag) { + return 0; /* Boot default */ + } + /* zipl stores timeout as seconds */ + timeout = zipl_timeout * 1000; + } + + /* Print and count all menu items, including the banner */ + for (entries = 0; *menu_data; entries++) { + len = strlen(menu_data); + zipl_println(menu_data, len); + menu_data += len + 1; + + if (entries < 2) { + sclp_print("\n"); + } + } + + sclp_print("\n"); + return get_boot_index(entries - 1); /* subtract 1 to exclude banner */ +} + + +int menu_get_enum_boot_index(int entries) +{ + char tmp[4]; + + sclp_print("s390x Enumerated Boot Menu.\n\n"); + + sclp_print(uitoa(entries, tmp, sizeof(tmp))); + sclp_print(" entries detected. Select from boot index 0 to "); + sclp_print(uitoa(entries - 1, tmp, sizeof(tmp))); + sclp_print(".\n\n"); + + return get_boot_index(entries); +} + +void menu_set_parms(uint8_t boot_menu_flag, uint32_t boot_menu_timeout) +{ + flag = boot_menu_flag; + timeout = boot_menu_timeout; +} + +bool menu_is_enabled_zipl(void) +{ + return flag & (QIPL_FLAG_BM_OPTS_CMD | QIPL_FLAG_BM_OPTS_ZIPL); +} + +bool menu_is_enabled_enum(void) +{ + return flag & QIPL_FLAG_BM_OPTS_CMD; +} diff --git a/pc-bios/s390-ccw/s390-ccw.h b/pc-bios/s390-ccw/s390-ccw.h index 25d4d213ea..fd18da22c6 100644 --- a/pc-bios/s390-ccw/s390-ccw.h +++ b/pc-bios/s390-ccw/s390-ccw.h @@ -69,8 +69,10 @@ unsigned int get_loadparm_index(void); /* sclp.c */ void sclp_print(const char *string); +void sclp_set_write_mask(uint32_t receive_mask, uint32_t send_mask); void sclp_setup(void); void sclp_get_loadparm_ascii(char *loadparm); +int sclp_read(char *str, size_t count); /* virtio.c */ unsigned long virtio_load_direct(ulong rec_list1, ulong rec_list2, @@ -79,11 +81,19 @@ bool virtio_is_supported(SubChannelId schid); void virtio_blk_setup_device(SubChannelId schid); int virtio_read(ulong sector, void *load_addr); int enable_mss_facility(void); +u64 get_clock(void); ulong get_second(void); /* bootmap.c */ void zipl_load(void); +/* menu.c */ +void menu_set_parms(uint8_t boot_menu_flag, uint32_t boot_menu_timeout); +int menu_get_zipl_boot_index(const char *menu_data); +bool menu_is_enabled_zipl(void); +int menu_get_enum_boot_index(int entries); +bool menu_is_enabled_enum(void); + static inline void fill_hex(char *out, unsigned char val) { const char hex[] = "0123456789abcdef"; diff --git a/pc-bios/s390-ccw/sclp.c b/pc-bios/s390-ccw/sclp.c index 90d1bc3147..3836cb4716 100644 --- a/pc-bios/s390-ccw/sclp.c +++ b/pc-bios/s390-ccw/sclp.c @@ -46,31 +46,21 @@ static int sclp_service_call(unsigned int command, void *sccb) return 0; } -static void sclp_set_write_mask(void) +void sclp_set_write_mask(uint32_t receive_mask, uint32_t send_mask) { WriteEventMask *sccb = (void *)_sccb; sccb->h.length = sizeof(WriteEventMask); sccb->mask_length = sizeof(unsigned int); - sccb->receive_mask = SCLP_EVENT_MASK_MSG_ASCII; - sccb->cp_receive_mask = SCLP_EVENT_MASK_MSG_ASCII; - sccb->send_mask = SCLP_EVENT_MASK_MSG_ASCII; - sccb->cp_send_mask = SCLP_EVENT_MASK_MSG_ASCII; + sccb->cp_receive_mask = receive_mask; + sccb->cp_send_mask = send_mask; sclp_service_call(SCLP_CMD_WRITE_EVENT_MASK, sccb); } void sclp_setup(void) { - sclp_set_write_mask(); -} - -static int _strlen(const char *str) -{ - int i; - for (i = 0; *str; i++) - str++; - return i; + sclp_set_write_mask(0, SCLP_EVENT_MASK_MSG_ASCII); } long write(int fd, const void *str, size_t len) @@ -113,7 +103,7 @@ long write(int fd, const void *str, size_t len) void sclp_print(const char *str) { - write(1, str, _strlen(str)); + write(1, str, strlen(str)); } void sclp_get_loadparm_ascii(char *loadparm) @@ -127,3 +117,22 @@ void sclp_get_loadparm_ascii(char *loadparm) ebcdic_to_ascii((char *) sccb->loadparm, loadparm, 8); } } + +int sclp_read(char *str, size_t count) +{ + ReadEventData *sccb = (void *)_sccb; + char *buf = (char *)(&sccb->ebh) + 7; + + /* If count exceeds max buffer size, then restrict it to the max size */ + if (count > SCCB_SIZE - 8) { + count = SCCB_SIZE - 8; + } + + sccb->h.length = SCCB_SIZE; + sccb->h.function_code = SCLP_UNCONDITIONAL_READ; + + sclp_service_call(SCLP_CMD_READ_EVENT_DATA, sccb); + memcpy(str, buf, count); + + return sccb->ebh.length - 7; +} diff --git a/pc-bios/s390-ccw/virtio.c b/pc-bios/s390-ccw/virtio.c index c890a0330b..817e7f56ea 100644 --- a/pc-bios/s390-ccw/virtio.c +++ b/pc-bios/s390-ccw/virtio.c @@ -176,7 +176,7 @@ void vring_send_buf(VRing *vr, void *p, int len, int flags) } } -static u64 get_clock(void) +u64 get_clock(void) { u64 r; diff --git a/pc-bios/s390-netboot.img b/pc-bios/s390-netboot.img Binary files differindex 24f40feae6..9f5926b534 100755..100644 --- a/pc-bios/s390-netboot.img +++ b/pc-bios/s390-netboot.img diff --git a/qapi-schema.json b/qapi-schema.json index 0262b9f20b..cd98a94388 100644 --- a/qapi-schema.json +++ b/qapi-schema.json @@ -408,12 +408,14 @@ # @CpuInfoArch: # # An enumeration of cpu types that enable additional information during -# @query-cpus. +# @query-cpus and @query-cpus-fast. +# +# @s390: since 2.12 # # Since: 2.6 ## { 'enum': 'CpuInfoArch', - 'data': ['x86', 'sparc', 'ppc', 'mips', 'tricore', 'other' ] } + 'data': ['x86', 'sparc', 'ppc', 'mips', 'tricore', 's390', 'other' ] } ## # @CpuInfo: @@ -452,6 +454,7 @@ 'ppc': 'CpuInfoPPC', 'mips': 'CpuInfoMIPS', 'tricore': 'CpuInfoTricore', + 's390': 'CpuInfoS390', 'other': 'CpuInfoOther' } } ## @@ -522,10 +525,39 @@ { 'struct': 'CpuInfoOther', 'data': { } } ## +# @CpuS390State: +# +# An enumeration of cpu states that can be assumed by a virtual +# S390 CPU +# +# Since: 2.12 +## +{ 'enum': 'CpuS390State', + 'prefix': 'S390_CPU_STATE', + 'data': [ 'uninitialized', 'stopped', 'check-stop', 'operating', 'load' ] } + +## +# @CpuInfoS390: +# +# Additional information about a virtual S390 CPU +# +# @cpu-state: the virtual CPU's state +# +# Since: 2.12 +## +{ 'struct': 'CpuInfoS390', 'data': { 'cpu-state': 'CpuS390State' } } + +## # @query-cpus: # # Returns a list of information about each virtual CPU. # +# This command causes vCPU threads to exit to userspace, which causes +# a small interruption to guest CPU execution. This will have a negative +# impact on realtime guests and other latency sensitive guest workloads. +# It is recommended to use @query-cpus-fast instead of this command to +# avoid the vCPU interruption. +# # Returns: a list of @CpuInfo for each virtual CPU # # Since: 0.14.0 @@ -555,10 +587,89 @@ # ] # } # +# Notes: This interface is deprecated (since 2.12.0), and it is strongly +# recommended that you avoid using it. Use @query-cpus-fast to +# obtain information about virtual CPUs. +# ## { 'command': 'query-cpus', 'returns': ['CpuInfo'] } ## +# @CpuInfoFast: +# +# Information about a virtual CPU +# +# @cpu-index: index of the virtual CPU +# +# @qom-path: path to the CPU object in the QOM tree +# +# @thread-id: ID of the underlying host thread +# +# @props: properties describing to which node/socket/core/thread +# virtual CPU belongs to, provided if supported by board +# +# @arch: architecture of the cpu, which determines which additional fields +# will be listed +# +# Since: 2.12 +# +## +{ 'union': 'CpuInfoFast', + 'base': {'cpu-index': 'int', 'qom-path': 'str', + 'thread-id': 'int', '*props': 'CpuInstanceProperties', + 'arch': 'CpuInfoArch' }, + 'discriminator': 'arch', + 'data': { 'x86': 'CpuInfoOther', + 'sparc': 'CpuInfoOther', + 'ppc': 'CpuInfoOther', + 'mips': 'CpuInfoOther', + 'tricore': 'CpuInfoOther', + 's390': 'CpuInfoS390', + 'other': 'CpuInfoOther' } } + +## +# @query-cpus-fast: +# +# Returns information about all virtual CPUs. This command does not +# incur a performance penalty and should be used in production +# instead of query-cpus. +# +# Returns: list of @CpuInfoFast +# +# Since: 2.12 +# +# Example: +# +# -> { "execute": "query-cpus-fast" } +# <- { "return": [ +# { +# "thread-id": 25627, +# "props": { +# "core-id": 0, +# "thread-id": 0, +# "socket-id": 0 +# }, +# "qom-path": "/machine/unattached/device[0]", +# "arch":"x86", +# "cpu-index": 0 +# }, +# { +# "thread-id": 25628, +# "props": { +# "core-id": 0, +# "thread-id": 0, +# "socket-id": 1 +# }, +# "qom-path": "/machine/unattached/device[2]", +# "arch":"x86", +# "cpu-index": 1 +# } +# ] +# } +## +{ 'command': 'query-cpus-fast', 'returns': [ 'CpuInfoFast' ] } + +## # @IOThreadInfo: # # Information about an iothread diff --git a/qapi/run-state.json b/qapi/run-state.json index bca46a8785..92d29fd571 100644 --- a/qapi/run-state.json +++ b/qapi/run-state.json @@ -320,22 +320,29 @@ # # An enumeration of the guest panic information types # +# @hyper-v: hyper-v guest panic information type +# +# @s390: s390 guest panic information type (Since: 2.12) +# # Since: 2.9 ## { 'enum': 'GuestPanicInformationType', - 'data': [ 'hyper-v'] } + 'data': [ 'hyper-v', 's390' ] } ## # @GuestPanicInformation: # # Information about a guest panic # +# @type: Crash type that defines the hypervisor specific information +# # Since: 2.9 ## {'union': 'GuestPanicInformation', 'base': {'type': 'GuestPanicInformationType'}, 'discriminator': 'type', - 'data': { 'hyper-v': 'GuestPanicInformationHyperV' } } + 'data': { 'hyper-v': 'GuestPanicInformationHyperV', + 's390': 'GuestPanicInformationS390' } } ## # @GuestPanicInformationHyperV: @@ -350,3 +357,47 @@ 'arg3': 'uint64', 'arg4': 'uint64', 'arg5': 'uint64' } } + +## +# @S390CrashReason: +# +# Reason why the CPU is in a crashed state. +# +# @unknown: no crash reason was set +# +# @disabled-wait: the CPU has entered a disabled wait state +# +# @extint-loop: clock comparator or cpu timer interrupt with new PSW enabled +# for external interrupts +# +# @pgmint-loop: program interrupt with BAD new PSW +# +# @opint-loop: operation exception interrupt with invalid code at the program +# interrupt new PSW +# +# Since: 2.12 +## +{ 'enum': 'S390CrashReason', + 'data': [ 'unknown', + 'disabled-wait', + 'extint-loop', + 'pgmint-loop', + 'opint-loop' ] } + +## +# @GuestPanicInformationS390: +# +# S390 specific guest panic information (PSW) +# +# @core: core id of the CPU that crashed +# @psw-mask: control fields of guest PSW +# @psw-addr: guest instruction address +# @reason: guest crash reason +# +# Since: 2.12 +## +{'struct': 'GuestPanicInformationS390', + 'data': { 'core': 'uint32', + 'psw-mask': 'uint64', + 'psw-addr': 'uint64', + 'reason': 'S390CrashReason' } } diff --git a/qemu-doc.texi b/qemu-doc.texi index 8e3556976b..b3c2763624 100644 --- a/qemu-doc.texi +++ b/qemu-doc.texi @@ -2762,6 +2762,10 @@ by the ``convert -l snapshot_param'' argument instead. "autoload" parameter is now ignored. All bitmaps are automatically loaded from qcow2 images. +@subsection query-cpus (since 2.12.0) + +The ``query-cpus'' command is replaced by the ``query-cpus-fast'' command. + @section System emulator human monitor commands @subsection host_net_add (since 2.10.0) diff --git a/target/arm/cpu.h b/target/arm/cpu.h index 8c839faa8f..2b9740878b 100644 --- a/target/arm/cpu.h +++ b/target/arm/cpu.h @@ -168,6 +168,7 @@ typedef struct { * Qn = regs[n].d[1]:regs[n].d[0] * Dn = regs[n].d[0] * Sn = regs[n].d[0] bits 31..0 + * Hn = regs[n].d[0] bits 15..0 * * This corresponds to the architecturally defined mapping between * the two execution states, and means we do not need to explicitly @@ -537,19 +538,29 @@ typedef struct CPUARMState { /* scratch space when Tn are not sufficient. */ uint32_t scratch[8]; - /* fp_status is the "normal" fp status. standard_fp_status retains - * values corresponding to the ARM "Standard FPSCR Value", ie - * default-NaN, flush-to-zero, round-to-nearest and is used by - * any operations (generally Neon) which the architecture defines - * as controlled by the standard FPSCR value rather than the FPSCR. + /* There are a number of distinct float control structures: + * + * fp_status: is the "normal" fp status. + * fp_status_fp16: used for half-precision calculations + * standard_fp_status : the ARM "Standard FPSCR Value" + * + * Half-precision operations are governed by a separate + * flush-to-zero control bit in FPSCR:FZ16. We pass a separate + * status structure to control this. + * + * The "Standard FPSCR", ie default-NaN, flush-to-zero, + * round-to-nearest and is used by any operations (generally + * Neon) which the architecture defines as controlled by the + * standard FPSCR value rather than the FPSCR. * * To avoid having to transfer exception bits around, we simply * say that the FPSCR cumulative exception flags are the logical - * OR of the flags in the two fp statuses. This relies on the + * OR of the flags in the three fp statuses. This relies on the * only thing which needs to read the exception flags being * an explicit FPSCR read. */ float_status fp_status; + float_status fp_status_f16; float_status standard_fp_status; /* ZCR_EL[1-3] */ @@ -1189,12 +1200,20 @@ static inline void xpsr_write(CPUARMState *env, uint32_t val, uint32_t mask) uint32_t vfp_get_fpscr(CPUARMState *env); void vfp_set_fpscr(CPUARMState *env, uint32_t val); -/* For A64 the FPSCR is split into two logically distinct registers, +/* FPCR, Floating Point Control Register + * FPSR, Floating Poiht Status Register + * + * For A64 the FPSCR is split into two logically distinct registers, * FPCR and FPSR. However since they still use non-overlapping bits * we store the underlying state in fpscr and just mask on read/write. */ #define FPSR_MASK 0xf800009f #define FPCR_MASK 0x07f79f00 + +#define FPCR_FZ16 (1 << 19) /* ARMv8.2+, FP16 flush-to-zero */ +#define FPCR_FZ (1 << 24) /* Flush-to-zero enable bit */ +#define FPCR_DN (1 << 25) /* Default NaN enable bit */ + static inline uint32_t vfp_get_fpsr(CPUARMState *env) { return vfp_get_fpscr(env) & FPSR_MASK; @@ -1408,6 +1427,7 @@ enum arm_features { ARM_FEATURE_V8_SHA3, /* implements SHA3 part of v8 Crypto Extensions */ ARM_FEATURE_V8_SM3, /* implements SM3 part of v8 Crypto Extensions */ ARM_FEATURE_V8_SM4, /* implements SM4 part of v8 Crypto Extensions */ + ARM_FEATURE_V8_FP16, /* implements v8.2 half-precision float */ }; static inline int arm_feature(CPUARMState *env, int feature) diff --git a/target/arm/cpu64.c b/target/arm/cpu64.c index 1c330adc28..9743bdc8c3 100644 --- a/target/arm/cpu64.c +++ b/target/arm/cpu64.c @@ -230,6 +230,7 @@ static void aarch64_any_initfn(Object *obj) set_feature(&cpu->env, ARM_FEATURE_V8_SM4); set_feature(&cpu->env, ARM_FEATURE_V8_PMULL); set_feature(&cpu->env, ARM_FEATURE_CRC); + set_feature(&cpu->env, ARM_FEATURE_V8_FP16); cpu->ctr = 0x80038003; /* 32 byte I and D cacheline size, VIPT icache */ cpu->dcz_blocksize = 7; /* 512 bytes */ } diff --git a/target/arm/helper-a64.c b/target/arm/helper-a64.c index 10e08bdc1f..afb25ad20c 100644 --- a/target/arm/helper-a64.c +++ b/target/arm/helper-a64.c @@ -192,6 +192,10 @@ uint64_t HELPER(neon_cgt_f64)(float64 a, float64 b, void *fpstp) * versions, these do a fully fused multiply-add or * multiply-add-and-halve. */ +#define float16_two make_float16(0x4000) +#define float16_three make_float16(0x4200) +#define float16_one_point_five make_float16(0x3e00) + #define float32_two make_float32(0x40000000) #define float32_three make_float32(0x40400000) #define float32_one_point_five make_float32(0x3fc00000) @@ -200,6 +204,21 @@ uint64_t HELPER(neon_cgt_f64)(float64 a, float64 b, void *fpstp) #define float64_three make_float64(0x4008000000000000ULL) #define float64_one_point_five make_float64(0x3FF8000000000000ULL) +float16 HELPER(recpsf_f16)(float16 a, float16 b, void *fpstp) +{ + float_status *fpst = fpstp; + + a = float16_squash_input_denormal(a, fpst); + b = float16_squash_input_denormal(b, fpst); + + a = float16_chs(a); + if ((float16_is_infinity(a) && float16_is_zero(b)) || + (float16_is_infinity(b) && float16_is_zero(a))) { + return float16_two; + } + return float16_muladd(a, b, float16_two, 0, fpst); +} + float32 HELPER(recpsf_f32)(float32 a, float32 b, void *fpstp) { float_status *fpst = fpstp; @@ -230,6 +249,21 @@ float64 HELPER(recpsf_f64)(float64 a, float64 b, void *fpstp) return float64_muladd(a, b, float64_two, 0, fpst); } +float16 HELPER(rsqrtsf_f16)(float16 a, float16 b, void *fpstp) +{ + float_status *fpst = fpstp; + + a = float16_squash_input_denormal(a, fpst); + b = float16_squash_input_denormal(b, fpst); + + a = float16_chs(a); + if ((float16_is_infinity(a) && float16_is_zero(b)) || + (float16_is_infinity(b) && float16_is_zero(a))) { + return float16_one_point_five; + } + return float16_muladd(a, b, float16_three, float_muladd_halve_result, fpst); +} + float32 HELPER(rsqrtsf_f32)(float32 a, float32 b, void *fpstp) { float_status *fpst = fpstp; @@ -322,6 +356,35 @@ uint64_t HELPER(neon_addlp_u16)(uint64_t a) } /* Floating-point reciprocal exponent - see FPRecpX in ARM ARM */ +float16 HELPER(frecpx_f16)(float16 a, void *fpstp) +{ + float_status *fpst = fpstp; + uint16_t val16, sbit; + int16_t exp; + + if (float16_is_any_nan(a)) { + float16 nan = a; + if (float16_is_signaling_nan(a, fpst)) { + float_raise(float_flag_invalid, fpst); + nan = float16_maybe_silence_nan(a, fpst); + } + if (fpst->default_nan_mode) { + nan = float16_default_nan(fpst); + } + return nan; + } + + val16 = float16_val(a); + sbit = 0x8000 & val16; + exp = extract32(val16, 10, 5); + + if (exp == 0) { + return make_float16(deposit32(sbit, 10, 5, 0x1e)); + } else { + return make_float16(deposit32(sbit, 10, 5, ~exp)); + } +} + float32 HELPER(frecpx_f32)(float32 a, void *fpstp) { float_status *fpst = fpstp; @@ -572,3 +635,209 @@ uint64_t HELPER(paired_cmpxchg64_be_parallel)(CPUARMState *env, uint64_t addr, { return do_paired_cmpxchg64_be(env, addr, new_lo, new_hi, true, GETPC()); } + +/* + * AdvSIMD half-precision + */ + +#define ADVSIMD_HELPER(name, suffix) HELPER(glue(glue(advsimd_, name), suffix)) + +#define ADVSIMD_HALFOP(name) \ +float16 ADVSIMD_HELPER(name, h)(float16 a, float16 b, void *fpstp) \ +{ \ + float_status *fpst = fpstp; \ + return float16_ ## name(a, b, fpst); \ +} + +ADVSIMD_HALFOP(add) +ADVSIMD_HALFOP(sub) +ADVSIMD_HALFOP(mul) +ADVSIMD_HALFOP(div) +ADVSIMD_HALFOP(min) +ADVSIMD_HALFOP(max) +ADVSIMD_HALFOP(minnum) +ADVSIMD_HALFOP(maxnum) + +#define ADVSIMD_TWOHALFOP(name) \ +uint32_t ADVSIMD_HELPER(name, 2h)(uint32_t two_a, uint32_t two_b, void *fpstp) \ +{ \ + float16 a1, a2, b1, b2; \ + uint32_t r1, r2; \ + float_status *fpst = fpstp; \ + a1 = extract32(two_a, 0, 16); \ + a2 = extract32(two_a, 16, 16); \ + b1 = extract32(two_b, 0, 16); \ + b2 = extract32(two_b, 16, 16); \ + r1 = float16_ ## name(a1, b1, fpst); \ + r2 = float16_ ## name(a2, b2, fpst); \ + return deposit32(r1, 16, 16, r2); \ +} + +ADVSIMD_TWOHALFOP(add) +ADVSIMD_TWOHALFOP(sub) +ADVSIMD_TWOHALFOP(mul) +ADVSIMD_TWOHALFOP(div) +ADVSIMD_TWOHALFOP(min) +ADVSIMD_TWOHALFOP(max) +ADVSIMD_TWOHALFOP(minnum) +ADVSIMD_TWOHALFOP(maxnum) + +/* Data processing - scalar floating-point and advanced SIMD */ +static float16 float16_mulx(float16 a, float16 b, void *fpstp) +{ + float_status *fpst = fpstp; + + a = float16_squash_input_denormal(a, fpst); + b = float16_squash_input_denormal(b, fpst); + + if ((float16_is_zero(a) && float16_is_infinity(b)) || + (float16_is_infinity(a) && float16_is_zero(b))) { + /* 2.0 with the sign bit set to sign(A) XOR sign(B) */ + return make_float16((1U << 14) | + ((float16_val(a) ^ float16_val(b)) & (1U << 15))); + } + return float16_mul(a, b, fpst); +} + +ADVSIMD_HALFOP(mulx) +ADVSIMD_TWOHALFOP(mulx) + +/* fused multiply-accumulate */ +float16 HELPER(advsimd_muladdh)(float16 a, float16 b, float16 c, void *fpstp) +{ + float_status *fpst = fpstp; + return float16_muladd(a, b, c, 0, fpst); +} + +uint32_t HELPER(advsimd_muladd2h)(uint32_t two_a, uint32_t two_b, + uint32_t two_c, void *fpstp) +{ + float_status *fpst = fpstp; + float16 a1, a2, b1, b2, c1, c2; + uint32_t r1, r2; + a1 = extract32(two_a, 0, 16); + a2 = extract32(two_a, 16, 16); + b1 = extract32(two_b, 0, 16); + b2 = extract32(two_b, 16, 16); + c1 = extract32(two_c, 0, 16); + c2 = extract32(two_c, 16, 16); + r1 = float16_muladd(a1, b1, c1, 0, fpst); + r2 = float16_muladd(a2, b2, c2, 0, fpst); + return deposit32(r1, 16, 16, r2); +} + +/* + * Floating point comparisons produce an integer result. Softfloat + * routines return float_relation types which we convert to the 0/-1 + * Neon requires. + */ + +#define ADVSIMD_CMPRES(test) (test) ? 0xffff : 0 + +uint32_t HELPER(advsimd_ceq_f16)(float16 a, float16 b, void *fpstp) +{ + float_status *fpst = fpstp; + int compare = float16_compare_quiet(a, b, fpst); + return ADVSIMD_CMPRES(compare == float_relation_equal); +} + +uint32_t HELPER(advsimd_cge_f16)(float16 a, float16 b, void *fpstp) +{ + float_status *fpst = fpstp; + int compare = float16_compare(a, b, fpst); + return ADVSIMD_CMPRES(compare == float_relation_greater || + compare == float_relation_equal); +} + +uint32_t HELPER(advsimd_cgt_f16)(float16 a, float16 b, void *fpstp) +{ + float_status *fpst = fpstp; + int compare = float16_compare(a, b, fpst); + return ADVSIMD_CMPRES(compare == float_relation_greater); +} + +uint32_t HELPER(advsimd_acge_f16)(float16 a, float16 b, void *fpstp) +{ + float_status *fpst = fpstp; + float16 f0 = float16_abs(a); + float16 f1 = float16_abs(b); + int compare = float16_compare(f0, f1, fpst); + return ADVSIMD_CMPRES(compare == float_relation_greater || + compare == float_relation_equal); +} + +uint32_t HELPER(advsimd_acgt_f16)(float16 a, float16 b, void *fpstp) +{ + float_status *fpst = fpstp; + float16 f0 = float16_abs(a); + float16 f1 = float16_abs(b); + int compare = float16_compare(f0, f1, fpst); + return ADVSIMD_CMPRES(compare == float_relation_greater); +} + +/* round to integral */ +float16 HELPER(advsimd_rinth_exact)(float16 x, void *fp_status) +{ + return float16_round_to_int(x, fp_status); +} + +float16 HELPER(advsimd_rinth)(float16 x, void *fp_status) +{ + int old_flags = get_float_exception_flags(fp_status), new_flags; + float16 ret; + + ret = float16_round_to_int(x, fp_status); + + /* Suppress any inexact exceptions the conversion produced */ + if (!(old_flags & float_flag_inexact)) { + new_flags = get_float_exception_flags(fp_status); + set_float_exception_flags(new_flags & ~float_flag_inexact, fp_status); + } + + return ret; +} + +/* + * Half-precision floating point conversion functions + * + * There are a multitude of conversion functions with various + * different rounding modes. This is dealt with by the calling code + * setting the mode appropriately before calling the helper. + */ + +uint32_t HELPER(advsimd_f16tosinth)(float16 a, void *fpstp) +{ + float_status *fpst = fpstp; + + /* Invalid if we are passed a NaN */ + if (float16_is_any_nan(a)) { + float_raise(float_flag_invalid, fpst); + return 0; + } + return float16_to_int16(a, fpst); +} + +uint32_t HELPER(advsimd_f16touinth)(float16 a, void *fpstp) +{ + float_status *fpst = fpstp; + + /* Invalid if we are passed a NaN */ + if (float16_is_any_nan(a)) { + float_raise(float_flag_invalid, fpst); + return 0; + } + return float16_to_uint16(a, fpst); +} + +/* + * Square Root and Reciprocal square root + */ + +float16 HELPER(sqrt_f16)(float16 a, void *fpstp) +{ + float_status *s = fpstp; + + return float16_sqrt(a, s); +} + + diff --git a/target/arm/helper-a64.h b/target/arm/helper-a64.h index 85d86741db..ef4ddfe9d8 100644 --- a/target/arm/helper-a64.h +++ b/target/arm/helper-a64.h @@ -29,8 +29,10 @@ DEF_HELPER_FLAGS_3(vfp_mulxd, TCG_CALL_NO_RWG, f64, f64, f64, ptr) DEF_HELPER_FLAGS_3(neon_ceq_f64, TCG_CALL_NO_RWG, i64, i64, i64, ptr) DEF_HELPER_FLAGS_3(neon_cge_f64, TCG_CALL_NO_RWG, i64, i64, i64, ptr) DEF_HELPER_FLAGS_3(neon_cgt_f64, TCG_CALL_NO_RWG, i64, i64, i64, ptr) +DEF_HELPER_FLAGS_3(recpsf_f16, TCG_CALL_NO_RWG, f16, f16, f16, ptr) DEF_HELPER_FLAGS_3(recpsf_f32, TCG_CALL_NO_RWG, f32, f32, f32, ptr) DEF_HELPER_FLAGS_3(recpsf_f64, TCG_CALL_NO_RWG, f64, f64, f64, ptr) +DEF_HELPER_FLAGS_3(rsqrtsf_f16, TCG_CALL_NO_RWG, f16, f16, f16, ptr) DEF_HELPER_FLAGS_3(rsqrtsf_f32, TCG_CALL_NO_RWG, f32, f32, f32, ptr) DEF_HELPER_FLAGS_3(rsqrtsf_f64, TCG_CALL_NO_RWG, f64, f64, f64, ptr) DEF_HELPER_FLAGS_1(neon_addlp_s8, TCG_CALL_NO_RWG_SE, i64, i64) @@ -39,6 +41,7 @@ DEF_HELPER_FLAGS_1(neon_addlp_s16, TCG_CALL_NO_RWG_SE, i64, i64) DEF_HELPER_FLAGS_1(neon_addlp_u16, TCG_CALL_NO_RWG_SE, i64, i64) DEF_HELPER_FLAGS_2(frecpx_f64, TCG_CALL_NO_RWG, f64, f64, ptr) DEF_HELPER_FLAGS_2(frecpx_f32, TCG_CALL_NO_RWG, f32, f32, ptr) +DEF_HELPER_FLAGS_2(frecpx_f16, TCG_CALL_NO_RWG, f16, f16, ptr) DEF_HELPER_FLAGS_2(fcvtx_f64_to_f32, TCG_CALL_NO_RWG, f32, f64, env) DEF_HELPER_FLAGS_3(crc32_64, TCG_CALL_NO_RWG_SE, i64, i64, i64, i32) DEF_HELPER_FLAGS_3(crc32c_64, TCG_CALL_NO_RWG_SE, i64, i64, i64, i32) @@ -48,3 +51,33 @@ DEF_HELPER_FLAGS_4(paired_cmpxchg64_le_parallel, TCG_CALL_NO_WG, DEF_HELPER_FLAGS_4(paired_cmpxchg64_be, TCG_CALL_NO_WG, i64, env, i64, i64, i64) DEF_HELPER_FLAGS_4(paired_cmpxchg64_be_parallel, TCG_CALL_NO_WG, i64, env, i64, i64, i64) +DEF_HELPER_FLAGS_3(advsimd_maxh, TCG_CALL_NO_RWG, f16, f16, f16, ptr) +DEF_HELPER_FLAGS_3(advsimd_minh, TCG_CALL_NO_RWG, f16, f16, f16, ptr) +DEF_HELPER_FLAGS_3(advsimd_maxnumh, TCG_CALL_NO_RWG, f16, f16, f16, ptr) +DEF_HELPER_FLAGS_3(advsimd_minnumh, TCG_CALL_NO_RWG, f16, f16, f16, ptr) +DEF_HELPER_3(advsimd_addh, f16, f16, f16, ptr) +DEF_HELPER_3(advsimd_subh, f16, f16, f16, ptr) +DEF_HELPER_3(advsimd_mulh, f16, f16, f16, ptr) +DEF_HELPER_3(advsimd_divh, f16, f16, f16, ptr) +DEF_HELPER_3(advsimd_ceq_f16, i32, f16, f16, ptr) +DEF_HELPER_3(advsimd_cge_f16, i32, f16, f16, ptr) +DEF_HELPER_3(advsimd_cgt_f16, i32, f16, f16, ptr) +DEF_HELPER_3(advsimd_acge_f16, i32, f16, f16, ptr) +DEF_HELPER_3(advsimd_acgt_f16, i32, f16, f16, ptr) +DEF_HELPER_3(advsimd_mulxh, f16, f16, f16, ptr) +DEF_HELPER_4(advsimd_muladdh, f16, f16, f16, f16, ptr) +DEF_HELPER_3(advsimd_add2h, i32, i32, i32, ptr) +DEF_HELPER_3(advsimd_sub2h, i32, i32, i32, ptr) +DEF_HELPER_3(advsimd_mul2h, i32, i32, i32, ptr) +DEF_HELPER_3(advsimd_div2h, i32, i32, i32, ptr) +DEF_HELPER_3(advsimd_max2h, i32, i32, i32, ptr) +DEF_HELPER_3(advsimd_min2h, i32, i32, i32, ptr) +DEF_HELPER_3(advsimd_maxnum2h, i32, i32, i32, ptr) +DEF_HELPER_3(advsimd_minnum2h, i32, i32, i32, ptr) +DEF_HELPER_3(advsimd_mulx2h, i32, i32, i32, ptr) +DEF_HELPER_4(advsimd_muladd2h, i32, i32, i32, i32, ptr) +DEF_HELPER_2(advsimd_rinth_exact, f16, f16, ptr) +DEF_HELPER_2(advsimd_rinth, f16, f16, ptr) +DEF_HELPER_2(advsimd_f16tosinth, i32, f16, ptr) +DEF_HELPER_2(advsimd_f16touinth, i32, f16, ptr) +DEF_HELPER_2(sqrt_f16, f16, f16, ptr) diff --git a/target/arm/helper.c b/target/arm/helper.c index c5bc69b961..c82f63d440 100644 --- a/target/arm/helper.c +++ b/target/arm/helper.c @@ -11103,6 +11103,7 @@ uint32_t HELPER(vfp_get_fpscr)(CPUARMState *env) | (env->vfp.vec_stride << 20); i = get_float_exception_flags(&env->vfp.fp_status); i |= get_float_exception_flags(&env->vfp.standard_fp_status); + i |= get_float_exception_flags(&env->vfp.fp_status_f16); fpscr |= vfp_exceptbits_from_host(i); return fpscr; } @@ -11160,16 +11161,31 @@ void HELPER(vfp_set_fpscr)(CPUARMState *env, uint32_t val) break; } set_float_rounding_mode(i, &env->vfp.fp_status); + set_float_rounding_mode(i, &env->vfp.fp_status_f16); } - if (changed & (1 << 24)) { - set_flush_to_zero((val & (1 << 24)) != 0, &env->vfp.fp_status); - set_flush_inputs_to_zero((val & (1 << 24)) != 0, &env->vfp.fp_status); + if (changed & FPCR_FZ16) { + bool ftz_enabled = val & FPCR_FZ16; + set_flush_to_zero(ftz_enabled, &env->vfp.fp_status_f16); + set_flush_inputs_to_zero(ftz_enabled, &env->vfp.fp_status_f16); + } + if (changed & FPCR_FZ) { + bool ftz_enabled = val & FPCR_FZ; + set_flush_to_zero(ftz_enabled, &env->vfp.fp_status); + set_flush_inputs_to_zero(ftz_enabled, &env->vfp.fp_status); + } + if (changed & FPCR_DN) { + bool dnan_enabled = val & FPCR_DN; + set_default_nan_mode(dnan_enabled, &env->vfp.fp_status); + set_default_nan_mode(dnan_enabled, &env->vfp.fp_status_f16); } - if (changed & (1 << 25)) - set_default_nan_mode((val & (1 << 25)) != 0, &env->vfp.fp_status); + /* The exception flags are ORed together when we read fpscr so we + * only need to preserve the current state in one of our + * float_status values. + */ i = vfp_exceptbits_to_host(val); set_float_exception_flags(i, &env->vfp.fp_status); + set_float_exception_flags(0, &env->vfp.fp_status_f16); set_float_exception_flags(0, &env->vfp.standard_fp_status); } @@ -11286,8 +11302,10 @@ CONV_ITOF(vfp_##name##to##p, fsz, sign) \ CONV_FTOI(vfp_to##name##p, fsz, sign, ) \ CONV_FTOI(vfp_to##name##z##p, fsz, sign, _round_to_zero) +FLOAT_CONVS(si, h, 16, ) FLOAT_CONVS(si, s, 32, ) FLOAT_CONVS(si, d, 64, ) +FLOAT_CONVS(ui, h, 16, u) FLOAT_CONVS(ui, s, 32, u) FLOAT_CONVS(ui, d, 64, u) @@ -11370,6 +11388,8 @@ VFP_CONV_FIX_A64(sq, s, 32, 64, int64) VFP_CONV_FIX(uh, s, 32, 32, uint16) VFP_CONV_FIX(ul, s, 32, 32, uint32) VFP_CONV_FIX_A64(uq, s, 32, 64, uint64) +VFP_CONV_FIX_A64(sl, h, 16, 32, int32) +VFP_CONV_FIX_A64(ul, h, 16, 32, uint32) #undef VFP_CONV_FIX #undef VFP_CONV_FIX_FLOAT #undef VFP_CONV_FLOAT_FIX_ROUND @@ -11377,9 +11397,9 @@ VFP_CONV_FIX_A64(uq, s, 32, 64, uint64) /* Set the current fp rounding mode and return the old one. * The argument is a softfloat float_round_ value. */ -uint32_t HELPER(set_rmode)(uint32_t rmode, CPUARMState *env) +uint32_t HELPER(set_rmode)(uint32_t rmode, void *fpstp) { - float_status *fp_status = &env->vfp.fp_status; + float_status *fp_status = fpstp; uint32_t prev_rmode = get_float_rounding_mode(fp_status); set_float_rounding_mode(rmode, fp_status); @@ -11503,80 +11523,75 @@ float32 HELPER(rsqrts_f32)(float32 a, float32 b, CPUARMState *env) * int->float conversions at run-time. */ #define float64_256 make_float64(0x4070000000000000LL) #define float64_512 make_float64(0x4080000000000000LL) +#define float16_maxnorm make_float16(0x7bff) #define float32_maxnorm make_float32(0x7f7fffff) #define float64_maxnorm make_float64(0x7fefffffffffffffLL) /* Reciprocal functions * * The algorithm that must be used to calculate the estimate - * is specified by the ARM ARM, see FPRecipEstimate() + * is specified by the ARM ARM, see FPRecipEstimate()/RecipEstimate */ -static float64 recip_estimate(float64 a, float_status *real_fp_status) -{ - /* These calculations mustn't set any fp exception flags, - * so we use a local copy of the fp_status. - */ - float_status dummy_status = *real_fp_status; - float_status *s = &dummy_status; - /* q = (int)(a * 512.0) */ - float64 q = float64_mul(float64_512, a, s); - int64_t q_int = float64_to_int64_round_to_zero(q, s); - - /* r = 1.0 / (((double)q + 0.5) / 512.0) */ - q = int64_to_float64(q_int, s); - q = float64_add(q, float64_half, s); - q = float64_div(q, float64_512, s); - q = float64_div(float64_one, q, s); - - /* s = (int)(256.0 * r + 0.5) */ - q = float64_mul(q, float64_256, s); - q = float64_add(q, float64_half, s); - q_int = float64_to_int64_round_to_zero(q, s); +/* See RecipEstimate() + * + * input is a 9 bit fixed point number + * input range 256 .. 511 for a number from 0.5 <= x < 1.0. + * result range 256 .. 511 for a number from 1.0 to 511/256. + */ - /* return (double)s / 256.0 */ - return float64_div(int64_to_float64(q_int, s), float64_256, s); +static int recip_estimate(int input) +{ + int a, b, r; + assert(256 <= input && input < 512); + a = (input * 2) + 1; + b = (1 << 19) / a; + r = (b + 1) >> 1; + assert(256 <= r && r < 512); + return r; } -/* Common wrapper to call recip_estimate */ -static float64 call_recip_estimate(float64 num, int off, float_status *fpst) +/* + * Common wrapper to call recip_estimate + * + * The parameters are exponent and 64 bit fraction (without implicit + * bit) where the binary point is nominally at bit 52. Returns a + * float64 which can then be rounded to the appropriate size by the + * callee. + */ + +static uint64_t call_recip_estimate(int *exp, int exp_off, uint64_t frac) { - uint64_t val64 = float64_val(num); - uint64_t frac = extract64(val64, 0, 52); - int64_t exp = extract64(val64, 52, 11); - uint64_t sbit; - float64 scaled, estimate; + uint32_t scaled, estimate; + uint64_t result_frac; + int result_exp; - /* Generate the scaled number for the estimate function */ - if (exp == 0) { + /* Handle sub-normals */ + if (*exp == 0) { if (extract64(frac, 51, 1) == 0) { - exp = -1; - frac = extract64(frac, 0, 50) << 2; + *exp = -1; + frac <<= 2; } else { - frac = extract64(frac, 0, 51) << 1; + frac <<= 1; } } - /* scaled = '0' : '01111111110' : fraction<51:44> : Zeros(44); */ - scaled = make_float64((0x3feULL << 52) - | extract64(frac, 44, 8) << 44); - - estimate = recip_estimate(scaled, fpst); - - /* Build new result */ - val64 = float64_val(estimate); - sbit = 0x8000000000000000ULL & val64; - exp = off - exp; - frac = extract64(val64, 0, 52); + /* scaled = UInt('1':fraction<51:44>) */ + scaled = deposit32(1 << 8, 0, 8, extract64(frac, 44, 8)); + estimate = recip_estimate(scaled); - if (exp == 0) { - frac = 1ULL << 51 | extract64(frac, 1, 51); - } else if (exp == -1) { - frac = 1ULL << 50 | extract64(frac, 2, 50); - exp = 0; + result_exp = exp_off - *exp; + result_frac = deposit64(0, 44, 8, estimate); + if (result_exp == 0) { + result_frac = deposit64(result_frac >> 1, 51, 1, 1); + } else if (result_exp == -1) { + result_frac = deposit64(result_frac >> 2, 50, 2, 1); + result_exp = 0; } - return make_float64(sbit | (exp << 52) | frac); + *exp = result_exp; + + return result_frac; } static bool round_to_inf(float_status *fpst, bool sign_bit) @@ -11595,18 +11610,63 @@ static bool round_to_inf(float_status *fpst, bool sign_bit) g_assert_not_reached(); } +float16 HELPER(recpe_f16)(float16 input, void *fpstp) +{ + float_status *fpst = fpstp; + float16 f16 = float16_squash_input_denormal(input, fpst); + uint32_t f16_val = float16_val(f16); + uint32_t f16_sign = float16_is_neg(f16); + int f16_exp = extract32(f16_val, 10, 5); + uint32_t f16_frac = extract32(f16_val, 0, 10); + uint64_t f64_frac; + + if (float16_is_any_nan(f16)) { + float16 nan = f16; + if (float16_is_signaling_nan(f16, fpst)) { + float_raise(float_flag_invalid, fpst); + nan = float16_maybe_silence_nan(f16, fpst); + } + if (fpst->default_nan_mode) { + nan = float16_default_nan(fpst); + } + return nan; + } else if (float16_is_infinity(f16)) { + return float16_set_sign(float16_zero, float16_is_neg(f16)); + } else if (float16_is_zero(f16)) { + float_raise(float_flag_divbyzero, fpst); + return float16_set_sign(float16_infinity, float16_is_neg(f16)); + } else if (float16_abs(f16) < (1 << 8)) { + /* Abs(value) < 2.0^-16 */ + float_raise(float_flag_overflow | float_flag_inexact, fpst); + if (round_to_inf(fpst, f16_sign)) { + return float16_set_sign(float16_infinity, f16_sign); + } else { + return float16_set_sign(float16_maxnorm, f16_sign); + } + } else if (f16_exp >= 29 && fpst->flush_to_zero) { + float_raise(float_flag_underflow, fpst); + return float16_set_sign(float16_zero, float16_is_neg(f16)); + } + + f64_frac = call_recip_estimate(&f16_exp, 29, + ((uint64_t) f16_frac) << (52 - 10)); + + /* result = sign : result_exp<4:0> : fraction<51:42> */ + f16_val = deposit32(0, 15, 1, f16_sign); + f16_val = deposit32(f16_val, 10, 5, f16_exp); + f16_val = deposit32(f16_val, 0, 10, extract64(f64_frac, 52 - 10, 10)); + return make_float16(f16_val); +} + float32 HELPER(recpe_f32)(float32 input, void *fpstp) { float_status *fpst = fpstp; float32 f32 = float32_squash_input_denormal(input, fpst); uint32_t f32_val = float32_val(f32); - uint32_t f32_sbit = 0x80000000ULL & f32_val; - int32_t f32_exp = extract32(f32_val, 23, 8); + bool f32_sign = float32_is_neg(f32); + int f32_exp = extract32(f32_val, 23, 8); uint32_t f32_frac = extract32(f32_val, 0, 23); - float64 f64, r64; - uint64_t r64_val; - int64_t r64_exp; - uint64_t r64_frac; + uint64_t f64_frac; if (float32_is_any_nan(f32)) { float32 nan = f32; @@ -11623,30 +11683,27 @@ float32 HELPER(recpe_f32)(float32 input, void *fpstp) } else if (float32_is_zero(f32)) { float_raise(float_flag_divbyzero, fpst); return float32_set_sign(float32_infinity, float32_is_neg(f32)); - } else if ((f32_val & ~(1ULL << 31)) < (1ULL << 21)) { + } else if (float32_abs(f32) < (1ULL << 21)) { /* Abs(value) < 2.0^-128 */ float_raise(float_flag_overflow | float_flag_inexact, fpst); - if (round_to_inf(fpst, f32_sbit)) { - return float32_set_sign(float32_infinity, float32_is_neg(f32)); + if (round_to_inf(fpst, f32_sign)) { + return float32_set_sign(float32_infinity, f32_sign); } else { - return float32_set_sign(float32_maxnorm, float32_is_neg(f32)); + return float32_set_sign(float32_maxnorm, f32_sign); } } else if (f32_exp >= 253 && fpst->flush_to_zero) { float_raise(float_flag_underflow, fpst); return float32_set_sign(float32_zero, float32_is_neg(f32)); } + f64_frac = call_recip_estimate(&f32_exp, 253, + ((uint64_t) f32_frac) << (52 - 23)); - f64 = make_float64(((int64_t)(f32_exp) << 52) | (int64_t)(f32_frac) << 29); - r64 = call_recip_estimate(f64, 253, fpst); - r64_val = float64_val(r64); - r64_exp = extract64(r64_val, 52, 11); - r64_frac = extract64(r64_val, 0, 52); - - /* result = sign : result_exp<7:0> : fraction<51:29>; */ - return make_float32(f32_sbit | - (r64_exp & 0xff) << 23 | - extract64(r64_frac, 29, 24)); + /* result = sign : result_exp<7:0> : fraction<51:29> */ + f32_val = deposit32(0, 31, 1, f32_sign); + f32_val = deposit32(f32_val, 23, 8, f32_exp); + f32_val = deposit32(f32_val, 0, 23, extract64(f64_frac, 52 - 23, 23)); + return make_float32(f32_val); } float64 HELPER(recpe_f64)(float64 input, void *fpstp) @@ -11654,12 +11711,9 @@ float64 HELPER(recpe_f64)(float64 input, void *fpstp) float_status *fpst = fpstp; float64 f64 = float64_squash_input_denormal(input, fpst); uint64_t f64_val = float64_val(f64); - uint64_t f64_sbit = 0x8000000000000000ULL & f64_val; - int64_t f64_exp = extract64(f64_val, 52, 11); - float64 r64; - uint64_t r64_val; - int64_t r64_exp; - uint64_t r64_frac; + bool f64_sign = float64_is_neg(f64); + int f64_exp = extract64(f64_val, 52, 11); + uint64_t f64_frac = extract64(f64_val, 0, 52); /* Deal with any special cases */ if (float64_is_any_nan(f64)) { @@ -11680,80 +11734,119 @@ float64 HELPER(recpe_f64)(float64 input, void *fpstp) } else if ((f64_val & ~(1ULL << 63)) < (1ULL << 50)) { /* Abs(value) < 2.0^-1024 */ float_raise(float_flag_overflow | float_flag_inexact, fpst); - if (round_to_inf(fpst, f64_sbit)) { - return float64_set_sign(float64_infinity, float64_is_neg(f64)); + if (round_to_inf(fpst, f64_sign)) { + return float64_set_sign(float64_infinity, f64_sign); } else { - return float64_set_sign(float64_maxnorm, float64_is_neg(f64)); + return float64_set_sign(float64_maxnorm, f64_sign); } } else if (f64_exp >= 2045 && fpst->flush_to_zero) { float_raise(float_flag_underflow, fpst); return float64_set_sign(float64_zero, float64_is_neg(f64)); } - r64 = call_recip_estimate(f64, 2045, fpst); - r64_val = float64_val(r64); - r64_exp = extract64(r64_val, 52, 11); - r64_frac = extract64(r64_val, 0, 52); + f64_frac = call_recip_estimate(&f64_exp, 2045, f64_frac); - /* result = sign : result_exp<10:0> : fraction<51:0> */ - return make_float64(f64_sbit | - ((r64_exp & 0x7ff) << 52) | - r64_frac); + /* result = sign : result_exp<10:0> : fraction<51:0>; */ + f64_val = deposit64(0, 63, 1, f64_sign); + f64_val = deposit64(f64_val, 52, 11, f64_exp); + f64_val = deposit64(f64_val, 0, 52, f64_frac); + return make_float64(f64_val); } /* The algorithm that must be used to calculate the estimate * is specified by the ARM ARM. */ -static float64 recip_sqrt_estimate(float64 a, float_status *real_fp_status) -{ - /* These calculations mustn't set any fp exception flags, - * so we use a local copy of the fp_status. - */ - float_status dummy_status = *real_fp_status; - float_status *s = &dummy_status; - float64 q; - int64_t q_int; - - if (float64_lt(a, float64_half, s)) { - /* range 0.25 <= a < 0.5 */ - - /* a in units of 1/512 rounded down */ - /* q0 = (int)(a * 512.0); */ - q = float64_mul(float64_512, a, s); - q_int = float64_to_int64_round_to_zero(q, s); - - /* reciprocal root r */ - /* r = 1.0 / sqrt(((double)q0 + 0.5) / 512.0); */ - q = int64_to_float64(q_int, s); - q = float64_add(q, float64_half, s); - q = float64_div(q, float64_512, s); - q = float64_sqrt(q, s); - q = float64_div(float64_one, q, s); + +static int do_recip_sqrt_estimate(int a) +{ + int b, estimate; + + assert(128 <= a && a < 512); + if (a < 256) { + a = a * 2 + 1; + } else { + a = (a >> 1) << 1; + a = (a + 1) * 2; + } + b = 512; + while (a * (b + 1) * (b + 1) < (1 << 28)) { + b += 1; + } + estimate = (b + 1) / 2; + assert(256 <= estimate && estimate < 512); + + return estimate; +} + + +static uint64_t recip_sqrt_estimate(int *exp , int exp_off, uint64_t frac) +{ + int estimate; + uint32_t scaled; + + if (*exp == 0) { + while (extract64(frac, 51, 1) == 0) { + frac = frac << 1; + *exp -= 1; + } + frac = extract64(frac, 0, 51) << 1; + } + + if (*exp & 1) { + /* scaled = UInt('01':fraction<51:45>) */ + scaled = deposit32(1 << 7, 0, 7, extract64(frac, 45, 7)); } else { - /* range 0.5 <= a < 1.0 */ + /* scaled = UInt('1':fraction<51:44>) */ + scaled = deposit32(1 << 8, 0, 8, extract64(frac, 44, 8)); + } + estimate = do_recip_sqrt_estimate(scaled); + + *exp = (exp_off - *exp) / 2; + return extract64(estimate, 0, 8) << 44; +} - /* a in units of 1/256 rounded down */ - /* q1 = (int)(a * 256.0); */ - q = float64_mul(float64_256, a, s); - int64_t q_int = float64_to_int64_round_to_zero(q, s); +float16 HELPER(rsqrte_f16)(float16 input, void *fpstp) +{ + float_status *s = fpstp; + float16 f16 = float16_squash_input_denormal(input, s); + uint16_t val = float16_val(f16); + bool f16_sign = float16_is_neg(f16); + int f16_exp = extract32(val, 10, 5); + uint16_t f16_frac = extract32(val, 0, 10); + uint64_t f64_frac; - /* reciprocal root r */ - /* r = 1.0 /sqrt(((double)q1 + 0.5) / 256); */ - q = int64_to_float64(q_int, s); - q = float64_add(q, float64_half, s); - q = float64_div(q, float64_256, s); - q = float64_sqrt(q, s); - q = float64_div(float64_one, q, s); + if (float16_is_any_nan(f16)) { + float16 nan = f16; + if (float16_is_signaling_nan(f16, s)) { + float_raise(float_flag_invalid, s); + nan = float16_maybe_silence_nan(f16, s); + } + if (s->default_nan_mode) { + nan = float16_default_nan(s); + } + return nan; + } else if (float16_is_zero(f16)) { + float_raise(float_flag_divbyzero, s); + return float16_set_sign(float16_infinity, f16_sign); + } else if (f16_sign) { + float_raise(float_flag_invalid, s); + return float16_default_nan(s); + } else if (float16_is_infinity(f16)) { + return float16_zero; } - /* r in units of 1/256 rounded to nearest */ - /* s = (int)(256.0 * r + 0.5); */ - q = float64_mul(q, float64_256,s ); - q = float64_add(q, float64_half, s); - q_int = float64_to_int64_round_to_zero(q, s); + /* Scale and normalize to a double-precision value between 0.25 and 1.0, + * preserving the parity of the exponent. */ + + f64_frac = ((uint64_t) f16_frac) << (52 - 10); + + f64_frac = recip_sqrt_estimate(&f16_exp, 44, f64_frac); - /* return (double)s / 256.0;*/ - return float64_div(int64_to_float64(q_int, s), float64_256, s); + /* result = sign : result_exp<4:0> : estimate<7:0> : Zeros(2) */ + val = deposit32(0, 15, 1, f16_sign); + val = deposit32(val, 10, 5, f16_exp); + val = deposit32(val, 2, 8, extract64(f64_frac, 52 - 8, 8)); + return make_float16(val); } float32 HELPER(rsqrte_f32)(float32 input, void *fpstp) @@ -11761,13 +11854,10 @@ float32 HELPER(rsqrte_f32)(float32 input, void *fpstp) float_status *s = fpstp; float32 f32 = float32_squash_input_denormal(input, s); uint32_t val = float32_val(f32); - uint32_t f32_sbit = 0x80000000 & val; - int32_t f32_exp = extract32(val, 23, 8); + uint32_t f32_sign = float32_is_neg(f32); + int f32_exp = extract32(val, 23, 8); uint32_t f32_frac = extract32(val, 0, 23); uint64_t f64_frac; - uint64_t val64; - int result_exp; - float64 f64; if (float32_is_any_nan(f32)) { float32 nan = f32; @@ -11793,32 +11883,13 @@ float32 HELPER(rsqrte_f32)(float32 input, void *fpstp) * preserving the parity of the exponent. */ f64_frac = ((uint64_t) f32_frac) << 29; - if (f32_exp == 0) { - while (extract64(f64_frac, 51, 1) == 0) { - f64_frac = f64_frac << 1; - f32_exp = f32_exp-1; - } - f64_frac = extract64(f64_frac, 0, 51) << 1; - } - - if (extract64(f32_exp, 0, 1) == 0) { - f64 = make_float64(((uint64_t) f32_sbit) << 32 - | (0x3feULL << 52) - | f64_frac); - } else { - f64 = make_float64(((uint64_t) f32_sbit) << 32 - | (0x3fdULL << 52) - | f64_frac); - } - - result_exp = (380 - f32_exp) / 2; - f64 = recip_sqrt_estimate(f64, s); + f64_frac = recip_sqrt_estimate(&f32_exp, 380, f64_frac); - val64 = float64_val(f64); - - val = ((result_exp & 0xff) << 23) - | ((val64 >> 29) & 0x7fffff); + /* result = sign : result_exp<4:0> : estimate<7:0> : Zeros(15) */ + val = deposit32(0, 31, 1, f32_sign); + val = deposit32(val, 23, 8, f32_exp); + val = deposit32(val, 15, 8, extract64(f64_frac, 52 - 8, 8)); return make_float32(val); } @@ -11827,11 +11898,9 @@ float64 HELPER(rsqrte_f64)(float64 input, void *fpstp) float_status *s = fpstp; float64 f64 = float64_squash_input_denormal(input, s); uint64_t val = float64_val(f64); - uint64_t f64_sbit = 0x8000000000000000ULL & val; - int64_t f64_exp = extract64(val, 52, 11); + bool f64_sign = float64_is_neg(f64); + int f64_exp = extract64(val, 52, 11); uint64_t f64_frac = extract64(val, 0, 52); - int64_t result_exp; - uint64_t result_frac; if (float64_is_any_nan(f64)) { float64 nan = f64; @@ -11853,75 +11922,41 @@ float64 HELPER(rsqrte_f64)(float64 input, void *fpstp) return float64_zero; } - /* Scale and normalize to a double-precision value between 0.25 and 1.0, - * preserving the parity of the exponent. */ - - if (f64_exp == 0) { - while (extract64(f64_frac, 51, 1) == 0) { - f64_frac = f64_frac << 1; - f64_exp = f64_exp - 1; - } - f64_frac = extract64(f64_frac, 0, 51) << 1; - } - - if (extract64(f64_exp, 0, 1) == 0) { - f64 = make_float64(f64_sbit - | (0x3feULL << 52) - | f64_frac); - } else { - f64 = make_float64(f64_sbit - | (0x3fdULL << 52) - | f64_frac); - } - - result_exp = (3068 - f64_exp) / 2; - - f64 = recip_sqrt_estimate(f64, s); + f64_frac = recip_sqrt_estimate(&f64_exp, 3068, f64_frac); - result_frac = extract64(float64_val(f64), 0, 52); - - return make_float64(f64_sbit | - ((result_exp & 0x7ff) << 52) | - result_frac); + /* result = sign : result_exp<4:0> : estimate<7:0> : Zeros(44) */ + val = deposit64(0, 61, 1, f64_sign); + val = deposit64(val, 52, 11, f64_exp); + val = deposit64(val, 44, 8, extract64(f64_frac, 52 - 8, 8)); + return make_float64(val); } uint32_t HELPER(recpe_u32)(uint32_t a, void *fpstp) { - float_status *s = fpstp; - float64 f64; + /* float_status *s = fpstp; */ + int input, estimate; if ((a & 0x80000000) == 0) { return 0xffffffff; } - f64 = make_float64((0x3feULL << 52) - | ((int64_t)(a & 0x7fffffff) << 21)); - - f64 = recip_estimate(f64, s); + input = extract32(a, 23, 9); + estimate = recip_estimate(input); - return 0x80000000 | ((float64_val(f64) >> 21) & 0x7fffffff); + return deposit32(0, (32 - 9), 9, estimate); } uint32_t HELPER(rsqrte_u32)(uint32_t a, void *fpstp) { - float_status *fpst = fpstp; - float64 f64; + int estimate; if ((a & 0xc0000000) == 0) { return 0xffffffff; } - if (a & 0x80000000) { - f64 = make_float64((0x3feULL << 52) - | ((uint64_t)(a & 0x7fffffff) << 21)); - } else { /* bits 31-30 == '01' */ - f64 = make_float64((0x3fdULL << 52) - | ((uint64_t)(a & 0x3fffffff) << 22)); - } - - f64 = recip_sqrt_estimate(f64, fpst); + estimate = do_recip_sqrt_estimate(extract32(a, 23, 9)); - return 0x80000000 | ((float64_val(f64) >> 21) & 0x7fffffff); + return deposit32(0, 23, 9, estimate); } /* VFPv4 fused multiply-accumulate */ diff --git a/target/arm/helper.h b/target/arm/helper.h index 6383d7d09e..6dd8504ec3 100644 --- a/target/arm/helper.h +++ b/target/arm/helper.h @@ -120,17 +120,23 @@ DEF_HELPER_3(vfp_cmped, void, f64, f64, env) DEF_HELPER_2(vfp_fcvtds, f64, f32, env) DEF_HELPER_2(vfp_fcvtsd, f32, f64, env) +DEF_HELPER_2(vfp_uitoh, f16, i32, ptr) DEF_HELPER_2(vfp_uitos, f32, i32, ptr) DEF_HELPER_2(vfp_uitod, f64, i32, ptr) +DEF_HELPER_2(vfp_sitoh, f16, i32, ptr) DEF_HELPER_2(vfp_sitos, f32, i32, ptr) DEF_HELPER_2(vfp_sitod, f64, i32, ptr) +DEF_HELPER_2(vfp_touih, i32, f16, ptr) DEF_HELPER_2(vfp_touis, i32, f32, ptr) DEF_HELPER_2(vfp_touid, i32, f64, ptr) +DEF_HELPER_2(vfp_touizh, i32, f16, ptr) DEF_HELPER_2(vfp_touizs, i32, f32, ptr) DEF_HELPER_2(vfp_touizd, i32, f64, ptr) +DEF_HELPER_2(vfp_tosih, i32, f16, ptr) DEF_HELPER_2(vfp_tosis, i32, f32, ptr) DEF_HELPER_2(vfp_tosid, i32, f64, ptr) +DEF_HELPER_2(vfp_tosizh, i32, f16, ptr) DEF_HELPER_2(vfp_tosizs, i32, f32, ptr) DEF_HELPER_2(vfp_tosizd, i32, f64, ptr) @@ -142,6 +148,8 @@ DEF_HELPER_3(vfp_toshd_round_to_zero, i64, f64, i32, ptr) DEF_HELPER_3(vfp_tosld_round_to_zero, i64, f64, i32, ptr) DEF_HELPER_3(vfp_touhd_round_to_zero, i64, f64, i32, ptr) DEF_HELPER_3(vfp_tould_round_to_zero, i64, f64, i32, ptr) +DEF_HELPER_3(vfp_toulh, i32, f16, i32, ptr) +DEF_HELPER_3(vfp_toslh, i32, f16, i32, ptr) DEF_HELPER_3(vfp_toshs, i32, f32, i32, ptr) DEF_HELPER_3(vfp_tosls, i32, f32, i32, ptr) DEF_HELPER_3(vfp_tosqs, i64, f32, i32, ptr) @@ -166,8 +174,10 @@ DEF_HELPER_3(vfp_sqtod, f64, i64, i32, ptr) DEF_HELPER_3(vfp_uhtod, f64, i64, i32, ptr) DEF_HELPER_3(vfp_ultod, f64, i64, i32, ptr) DEF_HELPER_3(vfp_uqtod, f64, i64, i32, ptr) +DEF_HELPER_3(vfp_sltoh, f16, i32, i32, ptr) +DEF_HELPER_3(vfp_ultoh, f16, i32, i32, ptr) -DEF_HELPER_FLAGS_2(set_rmode, TCG_CALL_NO_RWG, i32, i32, env) +DEF_HELPER_FLAGS_2(set_rmode, TCG_CALL_NO_RWG, i32, i32, ptr) DEF_HELPER_FLAGS_2(set_neon_rmode, TCG_CALL_NO_RWG, i32, i32, env) DEF_HELPER_2(vfp_fcvt_f16_to_f32, f32, i32, env) @@ -182,8 +192,10 @@ DEF_HELPER_4(vfp_muladds, f32, f32, f32, f32, ptr) DEF_HELPER_3(recps_f32, f32, f32, f32, env) DEF_HELPER_3(rsqrts_f32, f32, f32, f32, env) +DEF_HELPER_FLAGS_2(recpe_f16, TCG_CALL_NO_RWG, f16, f16, ptr) DEF_HELPER_FLAGS_2(recpe_f32, TCG_CALL_NO_RWG, f32, f32, ptr) DEF_HELPER_FLAGS_2(recpe_f64, TCG_CALL_NO_RWG, f64, f64, ptr) +DEF_HELPER_FLAGS_2(rsqrte_f16, TCG_CALL_NO_RWG, f16, f16, ptr) DEF_HELPER_FLAGS_2(rsqrte_f32, TCG_CALL_NO_RWG, f32, f32, ptr) DEF_HELPER_FLAGS_2(rsqrte_f64, TCG_CALL_NO_RWG, f64, f64, ptr) DEF_HELPER_2(recpe_u32, i32, i32, ptr) diff --git a/target/arm/translate-a64.c b/target/arm/translate-a64.c index 1c88539d62..32811dc8b0 100644 --- a/target/arm/translate-a64.c +++ b/target/arm/translate-a64.c @@ -637,16 +637,21 @@ static void write_fp_sreg(DisasContext *s, int reg, TCGv_i32 v) tcg_temp_free_i64(tmp); } -static TCGv_ptr get_fpstatus_ptr(void) +static TCGv_ptr get_fpstatus_ptr(bool is_f16) { TCGv_ptr statusptr = tcg_temp_new_ptr(); int offset; - /* In A64 all instructions (both FP and Neon) use the FPCR; - * there is no equivalent of the A32 Neon "standard FPSCR value" - * and all operations use vfp.fp_status. + /* In A64 all instructions (both FP and Neon) use the FPCR; there + * is no equivalent of the A32 Neon "standard FPSCR value". + * However half-precision operations operate under a different + * FZ16 flag and use vfp.fp_status_f16 instead of vfp.fp_status. */ - offset = offsetof(CPUARMState, vfp.fp_status); + if (is_f16) { + offset = offsetof(CPUARMState, vfp.fp_status_f16); + } else { + offset = offsetof(CPUARMState, vfp.fp_status); + } tcg_gen_addi_ptr(statusptr, cpu_env, offset); return statusptr; } @@ -4423,7 +4428,7 @@ static void handle_fp_compare(DisasContext *s, bool is_double, bool cmp_with_zero, bool signal_all_nans) { TCGv_i64 tcg_flags = tcg_temp_new_i64(); - TCGv_ptr fpst = get_fpstatus_ptr(); + TCGv_ptr fpst = get_fpstatus_ptr(false); if (is_double) { TCGv_i64 tcg_vn, tcg_vm; @@ -4591,6 +4596,65 @@ static void disas_fp_csel(DisasContext *s, uint32_t insn) tcg_temp_free_i64(t_true); } +/* Floating-point data-processing (1 source) - half precision */ +static void handle_fp_1src_half(DisasContext *s, int opcode, int rd, int rn) +{ + TCGv_ptr fpst = NULL; + TCGv_i32 tcg_op = tcg_temp_new_i32(); + TCGv_i32 tcg_res = tcg_temp_new_i32(); + + read_vec_element_i32(s, tcg_op, rn, 0, MO_16); + + switch (opcode) { + case 0x0: /* FMOV */ + tcg_gen_mov_i32(tcg_res, tcg_op); + break; + case 0x1: /* FABS */ + tcg_gen_andi_i32(tcg_res, tcg_op, 0x7fff); + break; + case 0x2: /* FNEG */ + tcg_gen_xori_i32(tcg_res, tcg_op, 0x8000); + break; + case 0x3: /* FSQRT */ + gen_helper_sqrt_f16(tcg_res, tcg_op, cpu_env); + break; + case 0x8: /* FRINTN */ + case 0x9: /* FRINTP */ + case 0xa: /* FRINTM */ + case 0xb: /* FRINTZ */ + case 0xc: /* FRINTA */ + { + TCGv_i32 tcg_rmode = tcg_const_i32(arm_rmode_to_sf(opcode & 7)); + fpst = get_fpstatus_ptr(true); + + gen_helper_set_rmode(tcg_rmode, tcg_rmode, fpst); + gen_helper_advsimd_rinth(tcg_res, tcg_op, fpst); + + gen_helper_set_rmode(tcg_rmode, tcg_rmode, fpst); + tcg_temp_free_i32(tcg_rmode); + break; + } + case 0xe: /* FRINTX */ + fpst = get_fpstatus_ptr(true); + gen_helper_advsimd_rinth_exact(tcg_res, tcg_op, fpst); + break; + case 0xf: /* FRINTI */ + fpst = get_fpstatus_ptr(true); + gen_helper_advsimd_rinth(tcg_res, tcg_op, fpst); + break; + default: + abort(); + } + + write_fp_sreg(s, rd, tcg_res); + + if (fpst) { + tcg_temp_free_ptr(fpst); + } + tcg_temp_free_i32(tcg_op); + tcg_temp_free_i32(tcg_res); +} + /* Floating-point data-processing (1 source) - single precision */ static void handle_fp_1src_single(DisasContext *s, int opcode, int rd, int rn) { @@ -4598,7 +4662,7 @@ static void handle_fp_1src_single(DisasContext *s, int opcode, int rd, int rn) TCGv_i32 tcg_op; TCGv_i32 tcg_res; - fpst = get_fpstatus_ptr(); + fpst = get_fpstatus_ptr(false); tcg_op = read_fp_sreg(s, rn); tcg_res = tcg_temp_new_i32(); @@ -4623,10 +4687,10 @@ static void handle_fp_1src_single(DisasContext *s, int opcode, int rd, int rn) { TCGv_i32 tcg_rmode = tcg_const_i32(arm_rmode_to_sf(opcode & 7)); - gen_helper_set_rmode(tcg_rmode, tcg_rmode, cpu_env); + gen_helper_set_rmode(tcg_rmode, tcg_rmode, fpst); gen_helper_rints(tcg_res, tcg_op, fpst); - gen_helper_set_rmode(tcg_rmode, tcg_rmode, cpu_env); + gen_helper_set_rmode(tcg_rmode, tcg_rmode, fpst); tcg_temp_free_i32(tcg_rmode); break; } @@ -4660,7 +4724,7 @@ static void handle_fp_1src_double(DisasContext *s, int opcode, int rd, int rn) return; } - fpst = get_fpstatus_ptr(); + fpst = get_fpstatus_ptr(false); tcg_op = read_fp_dreg(s, rn); tcg_res = tcg_temp_new_i64(); @@ -4682,10 +4746,10 @@ static void handle_fp_1src_double(DisasContext *s, int opcode, int rd, int rn) { TCGv_i32 tcg_rmode = tcg_const_i32(arm_rmode_to_sf(opcode & 7)); - gen_helper_set_rmode(tcg_rmode, tcg_rmode, cpu_env); + gen_helper_set_rmode(tcg_rmode, tcg_rmode, fpst); gen_helper_rintd(tcg_res, tcg_op, fpst); - gen_helper_set_rmode(tcg_rmode, tcg_rmode, cpu_env); + gen_helper_set_rmode(tcg_rmode, tcg_rmode, fpst); tcg_temp_free_i32(tcg_rmode); break; } @@ -4820,6 +4884,18 @@ static void disas_fp_1src(DisasContext *s, uint32_t insn) handle_fp_1src_double(s, opcode, rd, rn); break; + case 3: + if (!arm_dc_feature(s, ARM_FEATURE_V8_FP16)) { + unallocated_encoding(s); + return; + } + + if (!fp_access_check(s)) { + return; + } + + handle_fp_1src_half(s, opcode, rd, rn); + break; default: unallocated_encoding(s); } @@ -4840,7 +4916,7 @@ static void handle_fp_2src_single(DisasContext *s, int opcode, TCGv_ptr fpst; tcg_res = tcg_temp_new_i32(); - fpst = get_fpstatus_ptr(); + fpst = get_fpstatus_ptr(false); tcg_op1 = read_fp_sreg(s, rn); tcg_op2 = read_fp_sreg(s, rm); @@ -4893,7 +4969,7 @@ static void handle_fp_2src_double(DisasContext *s, int opcode, TCGv_ptr fpst; tcg_res = tcg_temp_new_i64(); - fpst = get_fpstatus_ptr(); + fpst = get_fpstatus_ptr(false); tcg_op1 = read_fp_dreg(s, rn); tcg_op2 = read_fp_dreg(s, rm); @@ -4979,7 +5055,7 @@ static void handle_fp_3src_single(DisasContext *s, bool o0, bool o1, { TCGv_i32 tcg_op1, tcg_op2, tcg_op3; TCGv_i32 tcg_res = tcg_temp_new_i32(); - TCGv_ptr fpst = get_fpstatus_ptr(); + TCGv_ptr fpst = get_fpstatus_ptr(false); tcg_op1 = read_fp_sreg(s, rn); tcg_op2 = read_fp_sreg(s, rm); @@ -5017,7 +5093,7 @@ static void handle_fp_3src_double(DisasContext *s, bool o0, bool o1, { TCGv_i64 tcg_op1, tcg_op2, tcg_op3; TCGv_i64 tcg_res = tcg_temp_new_i64(); - TCGv_ptr fpst = get_fpstatus_ptr(); + TCGv_ptr fpst = get_fpstatus_ptr(false); tcg_op1 = read_fp_dreg(s, rn); tcg_op2 = read_fp_dreg(s, rm); @@ -5158,7 +5234,7 @@ static void handle_fpfpcvt(DisasContext *s, int rd, int rn, int opcode, TCGv_ptr tcg_fpstatus; TCGv_i32 tcg_shift; - tcg_fpstatus = get_fpstatus_ptr(); + tcg_fpstatus = get_fpstatus_ptr(false); tcg_shift = tcg_const_i32(64 - scale); @@ -5212,7 +5288,7 @@ static void handle_fpfpcvt(DisasContext *s, int rd, int rn, int opcode, tcg_rmode = tcg_const_i32(arm_rmode_to_sf(rmode)); - gen_helper_set_rmode(tcg_rmode, tcg_rmode, cpu_env); + gen_helper_set_rmode(tcg_rmode, tcg_rmode, tcg_fpstatus); if (is_double) { TCGv_i64 tcg_double = read_fp_dreg(s, rn); @@ -5259,7 +5335,7 @@ static void handle_fpfpcvt(DisasContext *s, int rd, int rn, int opcode, tcg_temp_free_i32(tcg_single); } - gen_helper_set_rmode(tcg_rmode, tcg_rmode, cpu_env); + gen_helper_set_rmode(tcg_rmode, tcg_rmode, tcg_fpstatus); tcg_temp_free_i32(tcg_rmode); if (!sf) { @@ -5736,26 +5812,75 @@ static void disas_simd_zip_trn(DisasContext *s, uint32_t insn) tcg_temp_free_i64(tcg_resh); } -static void do_minmaxop(DisasContext *s, TCGv_i32 tcg_elt1, TCGv_i32 tcg_elt2, - int opc, bool is_min, TCGv_ptr fpst) -{ - /* Helper function for disas_simd_across_lanes: do a single precision - * min/max operation on the specified two inputs, - * and return the result in tcg_elt1. - */ - if (opc == 0xc) { - if (is_min) { - gen_helper_vfp_minnums(tcg_elt1, tcg_elt1, tcg_elt2, fpst); - } else { - gen_helper_vfp_maxnums(tcg_elt1, tcg_elt1, tcg_elt2, fpst); - } +/* + * do_reduction_op helper + * + * This mirrors the Reduce() pseudocode in the ARM ARM. It is + * important for correct NaN propagation that we do these + * operations in exactly the order specified by the pseudocode. + * + * This is a recursive function, TCG temps should be freed by the + * calling function once it is done with the values. + */ +static TCGv_i32 do_reduction_op(DisasContext *s, int fpopcode, int rn, + int esize, int size, int vmap, TCGv_ptr fpst) +{ + if (esize == size) { + int element; + TCGMemOp msize = esize == 16 ? MO_16 : MO_32; + TCGv_i32 tcg_elem; + + /* We should have one register left here */ + assert(ctpop8(vmap) == 1); + element = ctz32(vmap); + assert(element < 8); + + tcg_elem = tcg_temp_new_i32(); + read_vec_element_i32(s, tcg_elem, rn, element, msize); + return tcg_elem; } else { - assert(opc == 0xf); - if (is_min) { - gen_helper_vfp_mins(tcg_elt1, tcg_elt1, tcg_elt2, fpst); - } else { - gen_helper_vfp_maxs(tcg_elt1, tcg_elt1, tcg_elt2, fpst); + int bits = size / 2; + int shift = ctpop8(vmap) / 2; + int vmap_lo = (vmap >> shift) & vmap; + int vmap_hi = (vmap & ~vmap_lo); + TCGv_i32 tcg_hi, tcg_lo, tcg_res; + + tcg_hi = do_reduction_op(s, fpopcode, rn, esize, bits, vmap_hi, fpst); + tcg_lo = do_reduction_op(s, fpopcode, rn, esize, bits, vmap_lo, fpst); + tcg_res = tcg_temp_new_i32(); + + switch (fpopcode) { + case 0x0c: /* fmaxnmv half-precision */ + gen_helper_advsimd_maxnumh(tcg_res, tcg_lo, tcg_hi, fpst); + break; + case 0x0f: /* fmaxv half-precision */ + gen_helper_advsimd_maxh(tcg_res, tcg_lo, tcg_hi, fpst); + break; + case 0x1c: /* fminnmv half-precision */ + gen_helper_advsimd_minnumh(tcg_res, tcg_lo, tcg_hi, fpst); + break; + case 0x1f: /* fminv half-precision */ + gen_helper_advsimd_minh(tcg_res, tcg_lo, tcg_hi, fpst); + break; + case 0x2c: /* fmaxnmv */ + gen_helper_vfp_maxnums(tcg_res, tcg_lo, tcg_hi, fpst); + break; + case 0x2f: /* fmaxv */ + gen_helper_vfp_maxs(tcg_res, tcg_lo, tcg_hi, fpst); + break; + case 0x3c: /* fminnmv */ + gen_helper_vfp_minnums(tcg_res, tcg_lo, tcg_hi, fpst); + break; + case 0x3f: /* fminv */ + gen_helper_vfp_mins(tcg_res, tcg_lo, tcg_hi, fpst); + break; + default: + g_assert_not_reached(); } + + tcg_temp_free_i32(tcg_hi); + tcg_temp_free_i32(tcg_lo); + return tcg_res; } } @@ -5797,16 +5922,21 @@ static void disas_simd_across_lanes(DisasContext *s, uint32_t insn) break; case 0xc: /* FMAXNMV, FMINNMV */ case 0xf: /* FMAXV, FMINV */ - if (!is_u || !is_q || extract32(size, 0, 1)) { - unallocated_encoding(s); - return; - } - /* Bit 1 of size field encodes min vs max, and actual size is always - * 32 bits: adjust the size variable so following code can rely on it + /* Bit 1 of size field encodes min vs max and the actual size + * depends on the encoding of the U bit. If not set (and FP16 + * enabled) then we do half-precision float instead of single + * precision. */ is_min = extract32(size, 1, 1); is_fp = true; - size = 2; + if (!is_u && arm_dc_feature(s, ARM_FEATURE_V8_FP16)) { + size = 1; + } else if (!is_u || !is_q || extract32(size, 0, 1)) { + unallocated_encoding(s); + return; + } else { + size = 2; + } break; default: unallocated_encoding(s); @@ -5863,38 +5993,18 @@ static void disas_simd_across_lanes(DisasContext *s, uint32_t insn) } } else { - /* Floating point ops which work on 32 bit (single) intermediates. + /* Floating point vector reduction ops which work across 32 + * bit (single) or 16 bit (half-precision) intermediates. * Note that correct NaN propagation requires that we do these * operations in exactly the order specified by the pseudocode. */ - TCGv_i32 tcg_elt1 = tcg_temp_new_i32(); - TCGv_i32 tcg_elt2 = tcg_temp_new_i32(); - TCGv_i32 tcg_elt3 = tcg_temp_new_i32(); - TCGv_ptr fpst = get_fpstatus_ptr(); - - assert(esize == 32); - assert(elements == 4); - - read_vec_element(s, tcg_elt, rn, 0, MO_32); - tcg_gen_extrl_i64_i32(tcg_elt1, tcg_elt); - read_vec_element(s, tcg_elt, rn, 1, MO_32); - tcg_gen_extrl_i64_i32(tcg_elt2, tcg_elt); - - do_minmaxop(s, tcg_elt1, tcg_elt2, opcode, is_min, fpst); - - read_vec_element(s, tcg_elt, rn, 2, MO_32); - tcg_gen_extrl_i64_i32(tcg_elt2, tcg_elt); - read_vec_element(s, tcg_elt, rn, 3, MO_32); - tcg_gen_extrl_i64_i32(tcg_elt3, tcg_elt); - - do_minmaxop(s, tcg_elt2, tcg_elt3, opcode, is_min, fpst); - - do_minmaxop(s, tcg_elt1, tcg_elt2, opcode, is_min, fpst); - - tcg_gen_extu_i32_i64(tcg_res, tcg_elt1); - tcg_temp_free_i32(tcg_elt1); - tcg_temp_free_i32(tcg_elt2); - tcg_temp_free_i32(tcg_elt3); + TCGv_ptr fpst = get_fpstatus_ptr(size == MO_16); + int fpopcode = opcode | is_min << 4 | is_u << 5; + int vmap = (1 << elements) - 1; + TCGv_i32 tcg_res32 = do_reduction_op(s, fpopcode, rn, esize, + (is_q ? 128 : 64), vmap, fpst); + tcg_gen_extu_i32_i64(tcg_res, tcg_res32); + tcg_temp_free_i32(tcg_res32); tcg_temp_free_ptr(fpst); } @@ -6195,6 +6305,8 @@ static void disas_simd_copy(DisasContext *s, uint32_t insn) * MVNI - move inverted (shifted) imm into register * ORR - bitwise OR of (shifted) imm with register * BIC - bitwise clear of (shifted) imm with register + * With ARMv8.2 we also have: + * FMOV half-precision */ static void disas_simd_mod_imm(DisasContext *s, uint32_t insn) { @@ -6209,8 +6321,11 @@ static void disas_simd_mod_imm(DisasContext *s, uint32_t insn) uint64_t imm = 0; if (o2 != 0 || ((cmode == 0xf) && is_neg && !is_q)) { - unallocated_encoding(s); - return; + /* Check for FMOV (vector, immediate) - half-precision */ + if (!(arm_dc_feature(s, ARM_FEATURE_V8_FP16) && o2 && cmode == 0xf)) { + unallocated_encoding(s); + return; + } } if (!fp_access_check(s)) { @@ -6268,19 +6383,29 @@ static void disas_simd_mod_imm(DisasContext *s, uint32_t insn) imm |= 0x4000000000000000ULL; } } else { - imm = (abcdefgh & 0x3f) << 19; - if (abcdefgh & 0x80) { - imm |= 0x80000000; - } - if (abcdefgh & 0x40) { - imm |= 0x3e000000; + if (o2) { + /* FMOV (vector, immediate) - half-precision */ + imm = vfp_expand_imm(MO_16, abcdefgh); + /* now duplicate across the lanes */ + imm = bitfield_replicate(imm, 16); } else { - imm |= 0x40000000; + imm = (abcdefgh & 0x3f) << 19; + if (abcdefgh & 0x80) { + imm |= 0x80000000; + } + if (abcdefgh & 0x40) { + imm |= 0x3e000000; + } else { + imm |= 0x40000000; + } + imm |= (imm << 32); } - imm |= (imm << 32); } } break; + default: + fprintf(stderr, "%s: cmode_3_1: %x\n", __func__, cmode_3_1); + g_assert_not_reached(); } if (cmode_3_1 != 7 && is_neg) { @@ -6362,24 +6487,30 @@ static void disas_simd_scalar_pairwise(DisasContext *s, uint32_t insn) case 0xf: /* FMAXP */ case 0x2c: /* FMINNMP */ case 0x2f: /* FMINP */ - /* FP op, size[0] is 32 or 64 bit */ + /* FP op, size[0] is 32 or 64 bit*/ if (!u) { - unallocated_encoding(s); - return; + if (!arm_dc_feature(s, ARM_FEATURE_V8_FP16)) { + unallocated_encoding(s); + return; + } else { + size = MO_16; + } + } else { + size = extract32(size, 0, 1) ? MO_64 : MO_32; } + if (!fp_access_check(s)) { return; } - size = extract32(size, 0, 1) ? 3 : 2; - fpst = get_fpstatus_ptr(); + fpst = get_fpstatus_ptr(size == MO_16); break; default: unallocated_encoding(s); return; } - if (size == 3) { + if (size == MO_64) { TCGv_i64 tcg_op1 = tcg_temp_new_i64(); TCGv_i64 tcg_op2 = tcg_temp_new_i64(); TCGv_i64 tcg_res = tcg_temp_new_i64(); @@ -6420,27 +6551,49 @@ static void disas_simd_scalar_pairwise(DisasContext *s, uint32_t insn) TCGv_i32 tcg_op2 = tcg_temp_new_i32(); TCGv_i32 tcg_res = tcg_temp_new_i32(); - read_vec_element_i32(s, tcg_op1, rn, 0, MO_32); - read_vec_element_i32(s, tcg_op2, rn, 1, MO_32); + read_vec_element_i32(s, tcg_op1, rn, 0, size); + read_vec_element_i32(s, tcg_op2, rn, 1, size); - switch (opcode) { - case 0xc: /* FMAXNMP */ - gen_helper_vfp_maxnums(tcg_res, tcg_op1, tcg_op2, fpst); - break; - case 0xd: /* FADDP */ - gen_helper_vfp_adds(tcg_res, tcg_op1, tcg_op2, fpst); - break; - case 0xf: /* FMAXP */ - gen_helper_vfp_maxs(tcg_res, tcg_op1, tcg_op2, fpst); - break; - case 0x2c: /* FMINNMP */ - gen_helper_vfp_minnums(tcg_res, tcg_op1, tcg_op2, fpst); - break; - case 0x2f: /* FMINP */ - gen_helper_vfp_mins(tcg_res, tcg_op1, tcg_op2, fpst); - break; - default: - g_assert_not_reached(); + if (size == MO_16) { + switch (opcode) { + case 0xc: /* FMAXNMP */ + gen_helper_advsimd_maxnumh(tcg_res, tcg_op1, tcg_op2, fpst); + break; + case 0xd: /* FADDP */ + gen_helper_advsimd_addh(tcg_res, tcg_op1, tcg_op2, fpst); + break; + case 0xf: /* FMAXP */ + gen_helper_advsimd_maxh(tcg_res, tcg_op1, tcg_op2, fpst); + break; + case 0x2c: /* FMINNMP */ + gen_helper_advsimd_minnumh(tcg_res, tcg_op1, tcg_op2, fpst); + break; + case 0x2f: /* FMINP */ + gen_helper_advsimd_minh(tcg_res, tcg_op1, tcg_op2, fpst); + break; + default: + g_assert_not_reached(); + } + } else { + switch (opcode) { + case 0xc: /* FMAXNMP */ + gen_helper_vfp_maxnums(tcg_res, tcg_op1, tcg_op2, fpst); + break; + case 0xd: /* FADDP */ + gen_helper_vfp_adds(tcg_res, tcg_op1, tcg_op2, fpst); + break; + case 0xf: /* FMAXP */ + gen_helper_vfp_maxs(tcg_res, tcg_op1, tcg_op2, fpst); + break; + case 0x2c: /* FMINNMP */ + gen_helper_vfp_minnums(tcg_res, tcg_op1, tcg_op2, fpst); + break; + case 0x2f: /* FMINP */ + gen_helper_vfp_mins(tcg_res, tcg_op1, tcg_op2, fpst); + break; + default: + g_assert_not_reached(); + } } write_fp_sreg(s, rd, tcg_res); @@ -6863,23 +7016,28 @@ static void handle_simd_intfp_conv(DisasContext *s, int rd, int rn, int elements, int is_signed, int fracbits, int size) { - bool is_double = size == 3 ? true : false; - TCGv_ptr tcg_fpst = get_fpstatus_ptr(); - TCGv_i32 tcg_shift = tcg_const_i32(fracbits); - TCGv_i64 tcg_int = tcg_temp_new_i64(); + TCGv_ptr tcg_fpst = get_fpstatus_ptr(size == MO_16); + TCGv_i32 tcg_shift = NULL; + TCGMemOp mop = size | (is_signed ? MO_SIGN : 0); int pass; - for (pass = 0; pass < elements; pass++) { - read_vec_element(s, tcg_int, rn, pass, mop); + if (fracbits || size == MO_64) { + tcg_shift = tcg_const_i32(fracbits); + } + + if (size == MO_64) { + TCGv_i64 tcg_int64 = tcg_temp_new_i64(); + TCGv_i64 tcg_double = tcg_temp_new_i64(); + + for (pass = 0; pass < elements; pass++) { + read_vec_element(s, tcg_int64, rn, pass, mop); - if (is_double) { - TCGv_i64 tcg_double = tcg_temp_new_i64(); if (is_signed) { - gen_helper_vfp_sqtod(tcg_double, tcg_int, + gen_helper_vfp_sqtod(tcg_double, tcg_int64, tcg_shift, tcg_fpst); } else { - gen_helper_vfp_uqtod(tcg_double, tcg_int, + gen_helper_vfp_uqtod(tcg_double, tcg_int64, tcg_shift, tcg_fpst); } if (elements == 1) { @@ -6887,28 +7045,72 @@ static void handle_simd_intfp_conv(DisasContext *s, int rd, int rn, } else { write_vec_element(s, tcg_double, rd, pass, MO_64); } - tcg_temp_free_i64(tcg_double); - } else { - TCGv_i32 tcg_single = tcg_temp_new_i32(); - if (is_signed) { - gen_helper_vfp_sqtos(tcg_single, tcg_int, - tcg_shift, tcg_fpst); - } else { - gen_helper_vfp_uqtos(tcg_single, tcg_int, - tcg_shift, tcg_fpst); + } + + tcg_temp_free_i64(tcg_int64); + tcg_temp_free_i64(tcg_double); + + } else { + TCGv_i32 tcg_int32 = tcg_temp_new_i32(); + TCGv_i32 tcg_float = tcg_temp_new_i32(); + + for (pass = 0; pass < elements; pass++) { + read_vec_element_i32(s, tcg_int32, rn, pass, mop); + + switch (size) { + case MO_32: + if (fracbits) { + if (is_signed) { + gen_helper_vfp_sltos(tcg_float, tcg_int32, + tcg_shift, tcg_fpst); + } else { + gen_helper_vfp_ultos(tcg_float, tcg_int32, + tcg_shift, tcg_fpst); + } + } else { + if (is_signed) { + gen_helper_vfp_sitos(tcg_float, tcg_int32, tcg_fpst); + } else { + gen_helper_vfp_uitos(tcg_float, tcg_int32, tcg_fpst); + } + } + break; + case MO_16: + if (fracbits) { + if (is_signed) { + gen_helper_vfp_sltoh(tcg_float, tcg_int32, + tcg_shift, tcg_fpst); + } else { + gen_helper_vfp_ultoh(tcg_float, tcg_int32, + tcg_shift, tcg_fpst); + } + } else { + if (is_signed) { + gen_helper_vfp_sitoh(tcg_float, tcg_int32, tcg_fpst); + } else { + gen_helper_vfp_uitoh(tcg_float, tcg_int32, tcg_fpst); + } + } + break; + default: + g_assert_not_reached(); } + if (elements == 1) { - write_fp_sreg(s, rd, tcg_single); + write_fp_sreg(s, rd, tcg_float); } else { - write_vec_element_i32(s, tcg_single, rd, pass, MO_32); + write_vec_element_i32(s, tcg_float, rd, pass, size); } - tcg_temp_free_i32(tcg_single); } + + tcg_temp_free_i32(tcg_int32); + tcg_temp_free_i32(tcg_float); } - tcg_temp_free_i64(tcg_int); tcg_temp_free_ptr(tcg_fpst); - tcg_temp_free_i32(tcg_shift); + if (tcg_shift) { + tcg_temp_free_i32(tcg_shift); + } clear_vec_high(s, elements << size == 16, rd); } @@ -6979,8 +7181,8 @@ static void handle_simd_shift_fpint_conv(DisasContext *s, bool is_scalar, assert(!(is_scalar && is_q)); tcg_rmode = tcg_const_i32(arm_rmode_to_sf(FPROUNDING_ZERO)); - gen_helper_set_rmode(tcg_rmode, tcg_rmode, cpu_env); - tcg_fpstatus = get_fpstatus_ptr(); + tcg_fpstatus = get_fpstatus_ptr(false); + gen_helper_set_rmode(tcg_rmode, tcg_rmode, tcg_fpstatus); tcg_shift = tcg_const_i32(fracbits); if (is_double) { @@ -7024,7 +7226,7 @@ static void handle_simd_shift_fpint_conv(DisasContext *s, bool is_scalar, tcg_temp_free_ptr(tcg_fpstatus); tcg_temp_free_i32(tcg_shift); - gen_helper_set_rmode(tcg_rmode, tcg_rmode, cpu_env); + gen_helper_set_rmode(tcg_rmode, tcg_rmode, tcg_fpstatus); tcg_temp_free_i32(tcg_rmode); } @@ -7239,8 +7441,7 @@ static void handle_3same_64(DisasContext *s, int opcode, bool u, /* Handle 64x64->64 opcodes which are shared between the scalar * and vector 3-same groups. We cover every opcode where size == 3 * is valid in either the three-reg-same (integer, not pairwise) - * or scalar-three-reg-same groups. (Some opcodes are not yet - * implemented.) + * or scalar-three-reg-same groups. */ TCGCond cond; @@ -7326,7 +7527,7 @@ static void handle_3same_float(DisasContext *s, int size, int elements, int fpopcode, int rd, int rn, int rm) { int pass; - TCGv_ptr fpst = get_fpstatus_ptr(); + TCGv_ptr fpst = get_fpstatus_ptr(false); for (pass = 0; pass < elements; pass++) { if (size) { @@ -7672,6 +7873,104 @@ static void disas_simd_scalar_three_reg_same(DisasContext *s, uint32_t insn) tcg_temp_free_i64(tcg_rd); } +/* AdvSIMD scalar three same FP16 + * 31 30 29 28 24 23 22 21 20 16 15 14 13 11 10 9 5 4 0 + * +-----+---+-----------+---+-----+------+-----+--------+---+----+----+ + * | 0 1 | U | 1 1 1 1 0 | a | 1 0 | Rm | 0 0 | opcode | 1 | Rn | Rd | + * +-----+---+-----------+---+-----+------+-----+--------+---+----+----+ + * v: 0101 1110 0100 0000 0000 0100 0000 0000 => 5e400400 + * m: 1101 1111 0110 0000 1100 0100 0000 0000 => df60c400 + */ +static void disas_simd_scalar_three_reg_same_fp16(DisasContext *s, + uint32_t insn) +{ + int rd = extract32(insn, 0, 5); + int rn = extract32(insn, 5, 5); + int opcode = extract32(insn, 11, 3); + int rm = extract32(insn, 16, 5); + bool u = extract32(insn, 29, 1); + bool a = extract32(insn, 23, 1); + int fpopcode = opcode | (a << 3) | (u << 4); + TCGv_ptr fpst; + TCGv_i32 tcg_op1; + TCGv_i32 tcg_op2; + TCGv_i32 tcg_res; + + switch (fpopcode) { + case 0x03: /* FMULX */ + case 0x04: /* FCMEQ (reg) */ + case 0x07: /* FRECPS */ + case 0x0f: /* FRSQRTS */ + case 0x14: /* FCMGE (reg) */ + case 0x15: /* FACGE */ + case 0x1a: /* FABD */ + case 0x1c: /* FCMGT (reg) */ + case 0x1d: /* FACGT */ + break; + default: + unallocated_encoding(s); + return; + } + + if (!arm_dc_feature(s, ARM_FEATURE_V8_FP16)) { + unallocated_encoding(s); + } + + if (!fp_access_check(s)) { + return; + } + + fpst = get_fpstatus_ptr(true); + + tcg_op1 = tcg_temp_new_i32(); + tcg_op2 = tcg_temp_new_i32(); + tcg_res = tcg_temp_new_i32(); + + read_vec_element_i32(s, tcg_op1, rn, 0, MO_16); + read_vec_element_i32(s, tcg_op2, rm, 0, MO_16); + + switch (fpopcode) { + case 0x03: /* FMULX */ + gen_helper_advsimd_mulxh(tcg_res, tcg_op1, tcg_op2, fpst); + break; + case 0x04: /* FCMEQ (reg) */ + gen_helper_advsimd_ceq_f16(tcg_res, tcg_op1, tcg_op2, fpst); + break; + case 0x07: /* FRECPS */ + gen_helper_recpsf_f16(tcg_res, tcg_op1, tcg_op2, fpst); + break; + case 0x0f: /* FRSQRTS */ + gen_helper_rsqrtsf_f16(tcg_res, tcg_op1, tcg_op2, fpst); + break; + case 0x14: /* FCMGE (reg) */ + gen_helper_advsimd_cge_f16(tcg_res, tcg_op1, tcg_op2, fpst); + break; + case 0x15: /* FACGE */ + gen_helper_advsimd_acge_f16(tcg_res, tcg_op1, tcg_op2, fpst); + break; + case 0x1a: /* FABD */ + gen_helper_advsimd_subh(tcg_res, tcg_op1, tcg_op2, fpst); + tcg_gen_andi_i32(tcg_res, tcg_res, 0x7fff); + break; + case 0x1c: /* FCMGT (reg) */ + gen_helper_advsimd_cgt_f16(tcg_res, tcg_op1, tcg_op2, fpst); + break; + case 0x1d: /* FACGT */ + gen_helper_advsimd_acgt_f16(tcg_res, tcg_op1, tcg_op2, fpst); + break; + default: + g_assert_not_reached(); + } + + write_fp_sreg(s, rd, tcg_res); + + + tcg_temp_free_i32(tcg_res); + tcg_temp_free_i32(tcg_op1); + tcg_temp_free_i32(tcg_op2); + tcg_temp_free_ptr(fpst); +} + static void handle_2misc_64(DisasContext *s, int opcode, bool u, TCGv_i64 tcg_rd, TCGv_i64 tcg_rn, TCGv_i32 tcg_rmode, TCGv_ptr tcg_fpstatus) @@ -7783,14 +8082,14 @@ static void handle_2misc_fcmp_zero(DisasContext *s, int opcode, bool is_scalar, bool is_u, bool is_q, int size, int rn, int rd) { - bool is_double = (size == 3); + bool is_double = (size == MO_64); TCGv_ptr fpst; if (!fp_access_check(s)) { return; } - fpst = get_fpstatus_ptr(); + fpst = get_fpstatus_ptr(size == MO_16); if (is_double) { TCGv_i64 tcg_op = tcg_temp_new_i64(); @@ -7842,34 +8141,57 @@ static void handle_2misc_fcmp_zero(DisasContext *s, int opcode, bool swap = false; int pass, maxpasses; - switch (opcode) { - case 0x2e: /* FCMLT (zero) */ - swap = true; - /* fall through */ - case 0x2c: /* FCMGT (zero) */ - genfn = gen_helper_neon_cgt_f32; - break; - case 0x2d: /* FCMEQ (zero) */ - genfn = gen_helper_neon_ceq_f32; - break; - case 0x6d: /* FCMLE (zero) */ - swap = true; - /* fall through */ - case 0x6c: /* FCMGE (zero) */ - genfn = gen_helper_neon_cge_f32; - break; - default: - g_assert_not_reached(); + if (size == MO_16) { + switch (opcode) { + case 0x2e: /* FCMLT (zero) */ + swap = true; + /* fall through */ + case 0x2c: /* FCMGT (zero) */ + genfn = gen_helper_advsimd_cgt_f16; + break; + case 0x2d: /* FCMEQ (zero) */ + genfn = gen_helper_advsimd_ceq_f16; + break; + case 0x6d: /* FCMLE (zero) */ + swap = true; + /* fall through */ + case 0x6c: /* FCMGE (zero) */ + genfn = gen_helper_advsimd_cge_f16; + break; + default: + g_assert_not_reached(); + } + } else { + switch (opcode) { + case 0x2e: /* FCMLT (zero) */ + swap = true; + /* fall through */ + case 0x2c: /* FCMGT (zero) */ + genfn = gen_helper_neon_cgt_f32; + break; + case 0x2d: /* FCMEQ (zero) */ + genfn = gen_helper_neon_ceq_f32; + break; + case 0x6d: /* FCMLE (zero) */ + swap = true; + /* fall through */ + case 0x6c: /* FCMGE (zero) */ + genfn = gen_helper_neon_cge_f32; + break; + default: + g_assert_not_reached(); + } } if (is_scalar) { maxpasses = 1; } else { - maxpasses = is_q ? 4 : 2; + int vector_size = 8 << is_q; + maxpasses = vector_size >> size; } for (pass = 0; pass < maxpasses; pass++) { - read_vec_element_i32(s, tcg_op, rn, pass, MO_32); + read_vec_element_i32(s, tcg_op, rn, pass, size); if (swap) { genfn(tcg_res, tcg_zero, tcg_op, fpst); } else { @@ -7878,7 +8200,7 @@ static void handle_2misc_fcmp_zero(DisasContext *s, int opcode, if (is_scalar) { write_fp_sreg(s, rd, tcg_res); } else { - write_vec_element_i32(s, tcg_res, rd, pass, MO_32); + write_vec_element_i32(s, tcg_res, rd, pass, size); } } tcg_temp_free_i32(tcg_res); @@ -7897,7 +8219,7 @@ static void handle_2misc_reciprocal(DisasContext *s, int opcode, int size, int rn, int rd) { bool is_double = (size == 3); - TCGv_ptr fpst = get_fpstatus_ptr(); + TCGv_ptr fpst = get_fpstatus_ptr(false); if (is_double) { TCGv_i64 tcg_op = tcg_temp_new_i64(); @@ -8295,8 +8617,8 @@ static void disas_simd_scalar_two_reg_misc(DisasContext *s, uint32_t insn) if (is_fcvt) { tcg_rmode = tcg_const_i32(arm_rmode_to_sf(rmode)); - gen_helper_set_rmode(tcg_rmode, tcg_rmode, cpu_env); - tcg_fpstatus = get_fpstatus_ptr(); + tcg_fpstatus = get_fpstatus_ptr(false); + gen_helper_set_rmode(tcg_rmode, tcg_rmode, tcg_fpstatus); } else { tcg_rmode = NULL; tcg_fpstatus = NULL; @@ -8361,7 +8683,7 @@ static void disas_simd_scalar_two_reg_misc(DisasContext *s, uint32_t insn) } if (is_fcvt) { - gen_helper_set_rmode(tcg_rmode, tcg_rmode, cpu_env); + gen_helper_set_rmode(tcg_rmode, tcg_rmode, tcg_fpstatus); tcg_temp_free_i32(tcg_rmode); tcg_temp_free_ptr(tcg_fpstatus); } @@ -9516,7 +9838,7 @@ static void handle_simd_3same_pair(DisasContext *s, int is_q, int u, int opcode, /* Floating point operations need fpst */ if (opcode >= 0x58) { - fpst = get_fpstatus_ptr(); + fpst = get_fpstatus_ptr(false); } else { fpst = NULL; } @@ -10190,6 +10512,200 @@ static void disas_simd_three_reg_same(DisasContext *s, uint32_t insn) } } +/* + * Advanced SIMD three same (ARMv8.2 FP16 variants) + * + * 31 30 29 28 24 23 22 21 20 16 15 14 13 11 10 9 5 4 0 + * +---+---+---+-----------+---------+------+-----+--------+---+------+------+ + * | 0 | Q | U | 0 1 1 1 0 | a | 1 0 | Rm | 0 0 | opcode | 1 | Rn | Rd | + * +---+---+---+-----------+---------+------+-----+--------+---+------+------+ + * + * This includes FMULX, FCMEQ (register), FRECPS, FRSQRTS, FCMGE + * (register), FACGE, FABD, FCMGT (register) and FACGT. + * + */ +static void disas_simd_three_reg_same_fp16(DisasContext *s, uint32_t insn) +{ + int opcode, fpopcode; + int is_q, u, a, rm, rn, rd; + int datasize, elements; + int pass; + TCGv_ptr fpst; + bool pairwise = false; + + if (!arm_dc_feature(s, ARM_FEATURE_V8_FP16)) { + unallocated_encoding(s); + return; + } + + if (!fp_access_check(s)) { + return; + } + + /* For these floating point ops, the U, a and opcode bits + * together indicate the operation. + */ + opcode = extract32(insn, 11, 3); + u = extract32(insn, 29, 1); + a = extract32(insn, 23, 1); + is_q = extract32(insn, 30, 1); + rm = extract32(insn, 16, 5); + rn = extract32(insn, 5, 5); + rd = extract32(insn, 0, 5); + + fpopcode = opcode | (a << 3) | (u << 4); + datasize = is_q ? 128 : 64; + elements = datasize / 16; + + switch (fpopcode) { + case 0x10: /* FMAXNMP */ + case 0x12: /* FADDP */ + case 0x16: /* FMAXP */ + case 0x18: /* FMINNMP */ + case 0x1e: /* FMINP */ + pairwise = true; + break; + } + + fpst = get_fpstatus_ptr(true); + + if (pairwise) { + int maxpass = is_q ? 8 : 4; + TCGv_i32 tcg_op1 = tcg_temp_new_i32(); + TCGv_i32 tcg_op2 = tcg_temp_new_i32(); + TCGv_i32 tcg_res[8]; + + for (pass = 0; pass < maxpass; pass++) { + int passreg = pass < (maxpass / 2) ? rn : rm; + int passelt = (pass << 1) & (maxpass - 1); + + read_vec_element_i32(s, tcg_op1, passreg, passelt, MO_16); + read_vec_element_i32(s, tcg_op2, passreg, passelt + 1, MO_16); + tcg_res[pass] = tcg_temp_new_i32(); + + switch (fpopcode) { + case 0x10: /* FMAXNMP */ + gen_helper_advsimd_maxnumh(tcg_res[pass], tcg_op1, tcg_op2, + fpst); + break; + case 0x12: /* FADDP */ + gen_helper_advsimd_addh(tcg_res[pass], tcg_op1, tcg_op2, fpst); + break; + case 0x16: /* FMAXP */ + gen_helper_advsimd_maxh(tcg_res[pass], tcg_op1, tcg_op2, fpst); + break; + case 0x18: /* FMINNMP */ + gen_helper_advsimd_minnumh(tcg_res[pass], tcg_op1, tcg_op2, + fpst); + break; + case 0x1e: /* FMINP */ + gen_helper_advsimd_minh(tcg_res[pass], tcg_op1, tcg_op2, fpst); + break; + default: + g_assert_not_reached(); + } + } + + for (pass = 0; pass < maxpass; pass++) { + write_vec_element_i32(s, tcg_res[pass], rd, pass, MO_16); + tcg_temp_free_i32(tcg_res[pass]); + } + + tcg_temp_free_i32(tcg_op1); + tcg_temp_free_i32(tcg_op2); + + } else { + for (pass = 0; pass < elements; pass++) { + TCGv_i32 tcg_op1 = tcg_temp_new_i32(); + TCGv_i32 tcg_op2 = tcg_temp_new_i32(); + TCGv_i32 tcg_res = tcg_temp_new_i32(); + + read_vec_element_i32(s, tcg_op1, rn, pass, MO_16); + read_vec_element_i32(s, tcg_op2, rm, pass, MO_16); + + switch (fpopcode) { + case 0x0: /* FMAXNM */ + gen_helper_advsimd_maxnumh(tcg_res, tcg_op1, tcg_op2, fpst); + break; + case 0x1: /* FMLA */ + read_vec_element_i32(s, tcg_res, rd, pass, MO_16); + gen_helper_advsimd_muladdh(tcg_res, tcg_op1, tcg_op2, tcg_res, + fpst); + break; + case 0x2: /* FADD */ + gen_helper_advsimd_addh(tcg_res, tcg_op1, tcg_op2, fpst); + break; + case 0x3: /* FMULX */ + gen_helper_advsimd_mulxh(tcg_res, tcg_op1, tcg_op2, fpst); + break; + case 0x4: /* FCMEQ */ + gen_helper_advsimd_ceq_f16(tcg_res, tcg_op1, tcg_op2, fpst); + break; + case 0x6: /* FMAX */ + gen_helper_advsimd_maxh(tcg_res, tcg_op1, tcg_op2, fpst); + break; + case 0x7: /* FRECPS */ + gen_helper_recpsf_f16(tcg_res, tcg_op1, tcg_op2, fpst); + break; + case 0x8: /* FMINNM */ + gen_helper_advsimd_minnumh(tcg_res, tcg_op1, tcg_op2, fpst); + break; + case 0x9: /* FMLS */ + /* As usual for ARM, separate negation for fused multiply-add */ + tcg_gen_xori_i32(tcg_op1, tcg_op1, 0x8000); + read_vec_element_i32(s, tcg_res, rd, pass, MO_16); + gen_helper_advsimd_muladdh(tcg_res, tcg_op1, tcg_op2, tcg_res, + fpst); + break; + case 0xa: /* FSUB */ + gen_helper_advsimd_subh(tcg_res, tcg_op1, tcg_op2, fpst); + break; + case 0xe: /* FMIN */ + gen_helper_advsimd_minh(tcg_res, tcg_op1, tcg_op2, fpst); + break; + case 0xf: /* FRSQRTS */ + gen_helper_rsqrtsf_f16(tcg_res, tcg_op1, tcg_op2, fpst); + break; + case 0x13: /* FMUL */ + gen_helper_advsimd_mulh(tcg_res, tcg_op1, tcg_op2, fpst); + break; + case 0x14: /* FCMGE */ + gen_helper_advsimd_cge_f16(tcg_res, tcg_op1, tcg_op2, fpst); + break; + case 0x15: /* FACGE */ + gen_helper_advsimd_acge_f16(tcg_res, tcg_op1, tcg_op2, fpst); + break; + case 0x17: /* FDIV */ + gen_helper_advsimd_divh(tcg_res, tcg_op1, tcg_op2, fpst); + break; + case 0x1a: /* FABD */ + gen_helper_advsimd_subh(tcg_res, tcg_op1, tcg_op2, fpst); + tcg_gen_andi_i32(tcg_res, tcg_res, 0x7fff); + break; + case 0x1c: /* FCMGT */ + gen_helper_advsimd_cgt_f16(tcg_res, tcg_op1, tcg_op2, fpst); + break; + case 0x1d: /* FACGT */ + gen_helper_advsimd_acgt_f16(tcg_res, tcg_op1, tcg_op2, fpst); + break; + default: + fprintf(stderr, "%s: insn %#04x, fpop %#2x @ %#" PRIx64 "\n", + __func__, insn, fpopcode, s->pc); + g_assert_not_reached(); + } + + write_vec_element_i32(s, tcg_res, rd, pass, MO_16); + tcg_temp_free_i32(tcg_res); + tcg_temp_free_i32(tcg_op1); + tcg_temp_free_i32(tcg_op2); + } + } + + tcg_temp_free_ptr(fpst); + + clear_vec_high(s, is_q, rd); +} + static void handle_2misc_widening(DisasContext *s, int opcode, bool is_q, int size, int rn, int rd) { @@ -10675,14 +11191,14 @@ static void disas_simd_two_reg_misc(DisasContext *s, uint32_t insn) return; } - if (need_fpstatus) { - tcg_fpstatus = get_fpstatus_ptr(); + if (need_fpstatus || need_rmode) { + tcg_fpstatus = get_fpstatus_ptr(false); } else { tcg_fpstatus = NULL; } if (need_rmode) { tcg_rmode = tcg_const_i32(arm_rmode_to_sf(rmode)); - gen_helper_set_rmode(tcg_rmode, tcg_rmode, cpu_env); + gen_helper_set_rmode(tcg_rmode, tcg_rmode, tcg_fpstatus); } else { tcg_rmode = NULL; } @@ -10924,7 +11440,7 @@ static void disas_simd_two_reg_misc(DisasContext *s, uint32_t insn) clear_vec_high(s, is_q, rd); if (need_rmode) { - gen_helper_set_rmode(tcg_rmode, tcg_rmode, cpu_env); + gen_helper_set_rmode(tcg_rmode, tcg_rmode, tcg_fpstatus); tcg_temp_free_i32(tcg_rmode); } if (need_fpstatus) { @@ -10932,6 +11448,307 @@ static void disas_simd_two_reg_misc(DisasContext *s, uint32_t insn) } } +/* AdvSIMD [scalar] two register miscellaneous (FP16) + * + * 31 30 29 28 27 24 23 22 21 17 16 12 11 10 9 5 4 0 + * +---+---+---+---+---------+---+-------------+--------+-----+------+------+ + * | 0 | Q | U | S | 1 1 1 0 | a | 1 1 1 1 0 0 | opcode | 1 0 | Rn | Rd | + * +---+---+---+---+---------+---+-------------+--------+-----+------+------+ + * mask: 1000 1111 0111 1110 0000 1100 0000 0000 0x8f7e 0c00 + * val: 0000 1110 0111 1000 0000 1000 0000 0000 0x0e78 0800 + * + * This actually covers two groups where scalar access is governed by + * bit 28. A bunch of the instructions (float to integral) only exist + * in the vector form and are un-allocated for the scalar decode. Also + * in the scalar decode Q is always 1. + */ +static void disas_simd_two_reg_misc_fp16(DisasContext *s, uint32_t insn) +{ + int fpop, opcode, a, u; + int rn, rd; + bool is_q; + bool is_scalar; + bool only_in_vector = false; + + int pass; + TCGv_i32 tcg_rmode = NULL; + TCGv_ptr tcg_fpstatus = NULL; + bool need_rmode = false; + bool need_fpst = true; + int rmode; + + if (!arm_dc_feature(s, ARM_FEATURE_V8_FP16)) { + unallocated_encoding(s); + return; + } + + rd = extract32(insn, 0, 5); + rn = extract32(insn, 5, 5); + + a = extract32(insn, 23, 1); + u = extract32(insn, 29, 1); + is_scalar = extract32(insn, 28, 1); + is_q = extract32(insn, 30, 1); + + opcode = extract32(insn, 12, 5); + fpop = deposit32(opcode, 5, 1, a); + fpop = deposit32(fpop, 6, 1, u); + + rd = extract32(insn, 0, 5); + rn = extract32(insn, 5, 5); + + switch (fpop) { + case 0x1d: /* SCVTF */ + case 0x5d: /* UCVTF */ + { + int elements; + + if (is_scalar) { + elements = 1; + } else { + elements = (is_q ? 8 : 4); + } + + if (!fp_access_check(s)) { + return; + } + handle_simd_intfp_conv(s, rd, rn, elements, !u, 0, MO_16); + return; + } + break; + case 0x2c: /* FCMGT (zero) */ + case 0x2d: /* FCMEQ (zero) */ + case 0x2e: /* FCMLT (zero) */ + case 0x6c: /* FCMGE (zero) */ + case 0x6d: /* FCMLE (zero) */ + handle_2misc_fcmp_zero(s, fpop, is_scalar, 0, is_q, MO_16, rn, rd); + return; + case 0x3d: /* FRECPE */ + case 0x3f: /* FRECPX */ + break; + case 0x18: /* FRINTN */ + need_rmode = true; + only_in_vector = true; + rmode = FPROUNDING_TIEEVEN; + break; + case 0x19: /* FRINTM */ + need_rmode = true; + only_in_vector = true; + rmode = FPROUNDING_NEGINF; + break; + case 0x38: /* FRINTP */ + need_rmode = true; + only_in_vector = true; + rmode = FPROUNDING_POSINF; + break; + case 0x39: /* FRINTZ */ + need_rmode = true; + only_in_vector = true; + rmode = FPROUNDING_ZERO; + break; + case 0x58: /* FRINTA */ + need_rmode = true; + only_in_vector = true; + rmode = FPROUNDING_TIEAWAY; + break; + case 0x59: /* FRINTX */ + case 0x79: /* FRINTI */ + only_in_vector = true; + /* current rounding mode */ + break; + case 0x1a: /* FCVTNS */ + need_rmode = true; + rmode = FPROUNDING_TIEEVEN; + break; + case 0x1b: /* FCVTMS */ + need_rmode = true; + rmode = FPROUNDING_NEGINF; + break; + case 0x1c: /* FCVTAS */ + need_rmode = true; + rmode = FPROUNDING_TIEAWAY; + break; + case 0x3a: /* FCVTPS */ + need_rmode = true; + rmode = FPROUNDING_POSINF; + break; + case 0x3b: /* FCVTZS */ + need_rmode = true; + rmode = FPROUNDING_ZERO; + break; + case 0x5a: /* FCVTNU */ + need_rmode = true; + rmode = FPROUNDING_TIEEVEN; + break; + case 0x5b: /* FCVTMU */ + need_rmode = true; + rmode = FPROUNDING_NEGINF; + break; + case 0x5c: /* FCVTAU */ + need_rmode = true; + rmode = FPROUNDING_TIEAWAY; + break; + case 0x7a: /* FCVTPU */ + need_rmode = true; + rmode = FPROUNDING_POSINF; + break; + case 0x7b: /* FCVTZU */ + need_rmode = true; + rmode = FPROUNDING_ZERO; + break; + case 0x2f: /* FABS */ + case 0x6f: /* FNEG */ + need_fpst = false; + break; + case 0x7d: /* FRSQRTE */ + case 0x7f: /* FSQRT (vector) */ + break; + default: + fprintf(stderr, "%s: insn %#04x fpop %#2x\n", __func__, insn, fpop); + g_assert_not_reached(); + } + + + /* Check additional constraints for the scalar encoding */ + if (is_scalar) { + if (!is_q) { + unallocated_encoding(s); + return; + } + /* FRINTxx is only in the vector form */ + if (only_in_vector) { + unallocated_encoding(s); + return; + } + } + + if (!fp_access_check(s)) { + return; + } + + if (need_rmode || need_fpst) { + tcg_fpstatus = get_fpstatus_ptr(true); + } + + if (need_rmode) { + tcg_rmode = tcg_const_i32(arm_rmode_to_sf(rmode)); + gen_helper_set_rmode(tcg_rmode, tcg_rmode, tcg_fpstatus); + } + + if (is_scalar) { + TCGv_i32 tcg_op = tcg_temp_new_i32(); + TCGv_i32 tcg_res = tcg_temp_new_i32(); + + read_vec_element_i32(s, tcg_op, rn, 0, MO_16); + + switch (fpop) { + case 0x1a: /* FCVTNS */ + case 0x1b: /* FCVTMS */ + case 0x1c: /* FCVTAS */ + case 0x3a: /* FCVTPS */ + case 0x3b: /* FCVTZS */ + gen_helper_advsimd_f16tosinth(tcg_res, tcg_op, tcg_fpstatus); + break; + case 0x3d: /* FRECPE */ + gen_helper_recpe_f16(tcg_res, tcg_op, tcg_fpstatus); + break; + case 0x3f: /* FRECPX */ + gen_helper_frecpx_f16(tcg_res, tcg_op, tcg_fpstatus); + break; + case 0x5a: /* FCVTNU */ + case 0x5b: /* FCVTMU */ + case 0x5c: /* FCVTAU */ + case 0x7a: /* FCVTPU */ + case 0x7b: /* FCVTZU */ + gen_helper_advsimd_f16touinth(tcg_res, tcg_op, tcg_fpstatus); + break; + case 0x6f: /* FNEG */ + tcg_gen_xori_i32(tcg_res, tcg_op, 0x8000); + break; + case 0x7d: /* FRSQRTE */ + gen_helper_rsqrte_f16(tcg_res, tcg_op, tcg_fpstatus); + break; + default: + g_assert_not_reached(); + } + + /* limit any sign extension going on */ + tcg_gen_andi_i32(tcg_res, tcg_res, 0xffff); + write_fp_sreg(s, rd, tcg_res); + + tcg_temp_free_i32(tcg_res); + tcg_temp_free_i32(tcg_op); + } else { + for (pass = 0; pass < (is_q ? 8 : 4); pass++) { + TCGv_i32 tcg_op = tcg_temp_new_i32(); + TCGv_i32 tcg_res = tcg_temp_new_i32(); + + read_vec_element_i32(s, tcg_op, rn, pass, MO_16); + + switch (fpop) { + case 0x1a: /* FCVTNS */ + case 0x1b: /* FCVTMS */ + case 0x1c: /* FCVTAS */ + case 0x3a: /* FCVTPS */ + case 0x3b: /* FCVTZS */ + gen_helper_advsimd_f16tosinth(tcg_res, tcg_op, tcg_fpstatus); + break; + case 0x3d: /* FRECPE */ + gen_helper_recpe_f16(tcg_res, tcg_op, tcg_fpstatus); + break; + case 0x5a: /* FCVTNU */ + case 0x5b: /* FCVTMU */ + case 0x5c: /* FCVTAU */ + case 0x7a: /* FCVTPU */ + case 0x7b: /* FCVTZU */ + gen_helper_advsimd_f16touinth(tcg_res, tcg_op, tcg_fpstatus); + break; + case 0x18: /* FRINTN */ + case 0x19: /* FRINTM */ + case 0x38: /* FRINTP */ + case 0x39: /* FRINTZ */ + case 0x58: /* FRINTA */ + case 0x79: /* FRINTI */ + gen_helper_advsimd_rinth(tcg_res, tcg_op, tcg_fpstatus); + break; + case 0x59: /* FRINTX */ + gen_helper_advsimd_rinth_exact(tcg_res, tcg_op, tcg_fpstatus); + break; + case 0x2f: /* FABS */ + tcg_gen_andi_i32(tcg_res, tcg_op, 0x7fff); + break; + case 0x6f: /* FNEG */ + tcg_gen_xori_i32(tcg_res, tcg_op, 0x8000); + break; + case 0x7d: /* FRSQRTE */ + gen_helper_rsqrte_f16(tcg_res, tcg_op, tcg_fpstatus); + break; + case 0x7f: /* FSQRT */ + gen_helper_sqrt_f16(tcg_res, tcg_op, tcg_fpstatus); + break; + default: + g_assert_not_reached(); + } + + write_vec_element_i32(s, tcg_res, rd, pass, MO_16); + + tcg_temp_free_i32(tcg_res); + tcg_temp_free_i32(tcg_op); + } + + clear_vec_high(s, is_q, rd); + } + + if (tcg_rmode) { + gen_helper_set_rmode(tcg_rmode, tcg_rmode, tcg_fpstatus); + tcg_temp_free_i32(tcg_rmode); + } + + if (tcg_fpstatus) { + tcg_temp_free_ptr(tcg_fpstatus); + } +} + /* AdvSIMD scalar x indexed element * 31 30 29 28 24 23 22 21 20 19 16 15 12 11 10 9 5 4 0 * +-----+---+-----------+------+---+---+------+-----+---+---+------+------+ @@ -10966,6 +11783,7 @@ static void disas_simd_indexed(DisasContext *s, uint32_t insn) int rd = extract32(insn, 0, 5); bool is_long = false; bool is_fp = false; + bool is_fp16 = false; int index; TCGv_ptr fpst; @@ -11012,7 +11830,7 @@ static void disas_simd_indexed(DisasContext *s, uint32_t insn) } /* fall through */ case 0x9: /* FMUL, FMULX */ - if (!extract32(size, 1, 1)) { + if (size == 1) { unallocated_encoding(s); return; } @@ -11024,18 +11842,34 @@ static void disas_simd_indexed(DisasContext *s, uint32_t insn) } if (is_fp) { - /* low bit of size indicates single/double */ - size = extract32(size, 0, 1) ? 3 : 2; - if (size == 2) { + /* convert insn encoded size to TCGMemOp size */ + switch (size) { + case 2: /* single precision */ + size = MO_32; index = h << 1 | l; - } else { + rm |= (m << 4); + break; + case 3: /* double precision */ + size = MO_64; if (l || !is_q) { unallocated_encoding(s); return; } index = h; + rm |= (m << 4); + break; + case 0: /* half precision */ + size = MO_16; + index = h << 2 | l << 1 | m; + is_fp16 = true; + if (arm_dc_feature(s, ARM_FEATURE_V8_FP16)) { + break; + } + /* fallthru */ + default: /* unallocated */ + unallocated_encoding(s); + return; } - rm |= (m << 4); } else { switch (size) { case 1: @@ -11056,7 +11890,7 @@ static void disas_simd_indexed(DisasContext *s, uint32_t insn) } if (is_fp) { - fpst = get_fpstatus_ptr(); + fpst = get_fpstatus_ptr(is_fp16); } else { fpst = NULL; } @@ -11158,18 +11992,67 @@ static void disas_simd_indexed(DisasContext *s, uint32_t insn) break; } case 0x5: /* FMLS */ - /* As usual for ARM, separate negation for fused multiply-add */ - gen_helper_vfp_negs(tcg_op, tcg_op); - /* fall through */ case 0x1: /* FMLA */ - read_vec_element_i32(s, tcg_res, rd, pass, MO_32); - gen_helper_vfp_muladds(tcg_res, tcg_op, tcg_idx, tcg_res, fpst); + read_vec_element_i32(s, tcg_res, rd, pass, + is_scalar ? size : MO_32); + switch (size) { + case 1: + if (opcode == 0x5) { + /* As usual for ARM, separate negation for fused + * multiply-add */ + tcg_gen_xori_i32(tcg_op, tcg_op, 0x80008000); + } + if (is_scalar) { + gen_helper_advsimd_muladdh(tcg_res, tcg_op, tcg_idx, + tcg_res, fpst); + } else { + gen_helper_advsimd_muladd2h(tcg_res, tcg_op, tcg_idx, + tcg_res, fpst); + } + break; + case 2: + if (opcode == 0x5) { + /* As usual for ARM, separate negation for + * fused multiply-add */ + tcg_gen_xori_i32(tcg_op, tcg_op, 0x80000000); + } + gen_helper_vfp_muladds(tcg_res, tcg_op, tcg_idx, + tcg_res, fpst); + break; + default: + g_assert_not_reached(); + } break; case 0x9: /* FMUL, FMULX */ - if (u) { - gen_helper_vfp_mulxs(tcg_res, tcg_op, tcg_idx, fpst); - } else { - gen_helper_vfp_muls(tcg_res, tcg_op, tcg_idx, fpst); + switch (size) { + case 1: + if (u) { + if (is_scalar) { + gen_helper_advsimd_mulxh(tcg_res, tcg_op, + tcg_idx, fpst); + } else { + gen_helper_advsimd_mulx2h(tcg_res, tcg_op, + tcg_idx, fpst); + } + } else { + if (is_scalar) { + gen_helper_advsimd_mulh(tcg_res, tcg_op, + tcg_idx, fpst); + } else { + gen_helper_advsimd_mul2h(tcg_res, tcg_op, + tcg_idx, fpst); + } + } + break; + case 2: + if (u) { + gen_helper_vfp_mulxs(tcg_res, tcg_op, tcg_idx, fpst); + } else { + gen_helper_vfp_muls(tcg_res, tcg_op, tcg_idx, fpst); + } + break; + default: + g_assert_not_reached(); } break; case 0xc: /* SQDMULH */ @@ -11937,6 +12820,9 @@ static const AArch64DecodeTable data_proc_simd[] = { { 0xce000000, 0xff808000, disas_crypto_four_reg }, { 0xce800000, 0xffe00000, disas_crypto_xar }, { 0xce408000, 0xffe0c000, disas_crypto_three_reg_imm2 }, + { 0x0e400400, 0x9f60c400, disas_simd_three_reg_same_fp16 }, + { 0x0e780800, 0x8f7e0c00, disas_simd_two_reg_misc_fp16 }, + { 0x5e400400, 0xdf60c400, disas_simd_scalar_three_reg_same_fp16 }, { 0x00000000, 0x00000000, NULL } }; diff --git a/target/arm/translate.c b/target/arm/translate.c index 1270022289..aa6dcaa577 100644 --- a/target/arm/translate.c +++ b/target/arm/translate.c @@ -3143,7 +3143,7 @@ static int handle_vrint(uint32_t insn, uint32_t rd, uint32_t rm, uint32_t dp, TCGv_i32 tcg_rmode; tcg_rmode = tcg_const_i32(arm_rmode_to_sf(rounding)); - gen_helper_set_rmode(tcg_rmode, tcg_rmode, cpu_env); + gen_helper_set_rmode(tcg_rmode, tcg_rmode, fpst); if (dp) { TCGv_i64 tcg_op; @@ -3167,7 +3167,7 @@ static int handle_vrint(uint32_t insn, uint32_t rd, uint32_t rm, uint32_t dp, tcg_temp_free_i32(tcg_res); } - gen_helper_set_rmode(tcg_rmode, tcg_rmode, cpu_env); + gen_helper_set_rmode(tcg_rmode, tcg_rmode, fpst); tcg_temp_free_i32(tcg_rmode); tcg_temp_free_ptr(fpst); @@ -3184,7 +3184,7 @@ static int handle_vcvt(uint32_t insn, uint32_t rd, uint32_t rm, uint32_t dp, tcg_shift = tcg_const_i32(0); tcg_rmode = tcg_const_i32(arm_rmode_to_sf(rounding)); - gen_helper_set_rmode(tcg_rmode, tcg_rmode, cpu_env); + gen_helper_set_rmode(tcg_rmode, tcg_rmode, fpst); if (dp) { TCGv_i64 tcg_double, tcg_res; @@ -3222,7 +3222,7 @@ static int handle_vcvt(uint32_t insn, uint32_t rd, uint32_t rm, uint32_t dp, tcg_temp_free_i32(tcg_single); } - gen_helper_set_rmode(tcg_rmode, tcg_rmode, cpu_env); + gen_helper_set_rmode(tcg_rmode, tcg_rmode, fpst); tcg_temp_free_i32(tcg_rmode); tcg_temp_free_i32(tcg_shift); @@ -3892,13 +3892,13 @@ static int disas_vfp_insn(DisasContext *s, uint32_t insn) TCGv_ptr fpst = get_fpstatus_ptr(0); TCGv_i32 tcg_rmode; tcg_rmode = tcg_const_i32(float_round_to_zero); - gen_helper_set_rmode(tcg_rmode, tcg_rmode, cpu_env); + gen_helper_set_rmode(tcg_rmode, tcg_rmode, fpst); if (dp) { gen_helper_rintd(cpu_F0d, cpu_F0d, fpst); } else { gen_helper_rints(cpu_F0s, cpu_F0s, fpst); } - gen_helper_set_rmode(tcg_rmode, tcg_rmode, cpu_env); + gen_helper_set_rmode(tcg_rmode, tcg_rmode, fpst); tcg_temp_free_i32(tcg_rmode); tcg_temp_free_ptr(fpst); break; diff --git a/target/s390x/cpu.c b/target/s390x/cpu.c index a665b9e60e..627002b225 100644 --- a/target/s390x/cpu.c +++ b/target/s390x/cpu.c @@ -35,6 +35,8 @@ #include "qemu/error-report.h" #include "trace.h" #include "qapi/visitor.h" +#include "qapi-visit.h" +#include "sysemu/hw_accel.h" #include "exec/exec-all.h" #include "hw/qdev-properties.h" #ifndef CONFIG_USER_ONLY @@ -59,8 +61,8 @@ static bool s390_cpu_has_work(CPUState *cs) S390CPU *cpu = S390_CPU(cs); /* STOPPED cpus can never wake up */ - if (s390_cpu_get_state(cpu) != CPU_STATE_LOAD && - s390_cpu_get_state(cpu) != CPU_STATE_OPERATING) { + if (s390_cpu_get_state(cpu) != S390_CPU_STATE_LOAD && + s390_cpu_get_state(cpu) != S390_CPU_STATE_OPERATING) { return false; } @@ -78,7 +80,7 @@ static void s390_cpu_load_normal(CPUState *s) S390CPU *cpu = S390_CPU(s); cpu->env.psw.addr = ldl_phys(s->as, 4) & PSW_MASK_ESA_ADDR; cpu->env.psw.mask = PSW_MASK_32 | PSW_MASK_64; - s390_cpu_set_state(CPU_STATE_OPERATING, cpu); + s390_cpu_set_state(S390_CPU_STATE_OPERATING, cpu); } #endif @@ -93,7 +95,7 @@ static void s390_cpu_reset(CPUState *s) env->bpbc = false; scc->parent_reset(s); cpu->env.sigp_order = 0; - s390_cpu_set_state(CPU_STATE_STOPPED, cpu); + s390_cpu_set_state(S390_CPU_STATE_STOPPED, cpu); } /* S390CPUClass::initial_reset() */ @@ -136,7 +138,7 @@ static void s390_cpu_full_reset(CPUState *s) scc->parent_reset(s); cpu->env.sigp_order = 0; - s390_cpu_set_state(CPU_STATE_STOPPED, cpu); + s390_cpu_set_state(S390_CPU_STATE_STOPPED, cpu); memset(env, 0, offsetof(CPUS390XState, end_reset_fields)); @@ -228,6 +230,46 @@ out: error_propagate(errp, err); } +static GuestPanicInformation *s390_cpu_get_crash_info(CPUState *cs) +{ + GuestPanicInformation *panic_info; + S390CPU *cpu = S390_CPU(cs); + + cpu_synchronize_state(cs); + panic_info = g_malloc0(sizeof(GuestPanicInformation)); + + panic_info->type = GUEST_PANIC_INFORMATION_TYPE_S390; +#if !defined(CONFIG_USER_ONLY) + panic_info->u.s390.core = cpu->env.core_id; +#else + panic_info->u.s390.core = 0; /* sane default for non system emulation */ +#endif + panic_info->u.s390.psw_mask = cpu->env.psw.mask; + panic_info->u.s390.psw_addr = cpu->env.psw.addr; + panic_info->u.s390.reason = cpu->env.crash_reason; + + return panic_info; +} + +static void s390_cpu_get_crash_info_qom(Object *obj, Visitor *v, + const char *name, void *opaque, + Error **errp) +{ + CPUState *cs = CPU(obj); + GuestPanicInformation *panic_info; + + if (!cs->crash_occurred) { + error_setg(errp, "No crash occurred"); + return; + } + + panic_info = s390_cpu_get_crash_info(cs); + + visit_type_GuestPanicInformation(v, "crash-information", &panic_info, + errp); + qapi_free_GuestPanicInformation(panic_info); +} + static void s390_cpu_initfn(Object *obj) { CPUState *cs = CPU(obj); @@ -240,6 +282,8 @@ static void s390_cpu_initfn(Object *obj) cs->env_ptr = env; cs->halted = 1; cs->exception_index = EXCP_HLT; + object_property_add(obj, "crash-information", "GuestPanicInformation", + 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); @@ -248,7 +292,7 @@ static void s390_cpu_initfn(Object *obj) env->tod_basetime = 0; 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(CPU_STATE_STOPPED, cpu); + s390_cpu_set_state(S390_CPU_STATE_STOPPED, cpu); #endif } @@ -276,8 +320,8 @@ static unsigned s390_count_running_cpus(void) CPU_FOREACH(cpu) { uint8_t state = S390_CPU(cpu)->env.cpu_state; - if (state == CPU_STATE_OPERATING || - state == CPU_STATE_LOAD) { + if (state == S390_CPU_STATE_OPERATING || + state == S390_CPU_STATE_LOAD) { if (!disabled_wait(cpu)) { nr_running++; } @@ -316,13 +360,13 @@ unsigned int s390_cpu_set_state(uint8_t cpu_state, S390CPU *cpu) trace_cpu_set_state(CPU(cpu)->cpu_index, cpu_state); switch (cpu_state) { - case CPU_STATE_STOPPED: - case CPU_STATE_CHECK_STOP: + case S390_CPU_STATE_STOPPED: + case S390_CPU_STATE_CHECK_STOP: /* halt the cpu for common infrastructure */ s390_cpu_halt(cpu); break; - case CPU_STATE_OPERATING: - case CPU_STATE_LOAD: + case S390_CPU_STATE_OPERATING: + case S390_CPU_STATE_LOAD: /* * Starting a CPU with a PSW WAIT bit set: * KVM: handles this internally and triggers another WAIT exit. @@ -393,15 +437,6 @@ void s390_cmma_reset(void) } } -int s390_get_memslot_count(void) -{ - if (kvm_enabled()) { - return kvm_s390_get_memslot_count(); - } else { - return MAX_AVAIL_SLOTS; - } -} - int s390_assign_subch_ioeventfd(EventNotifier *notifier, uint32_t sch_id, int vq, bool assign) { @@ -473,6 +508,7 @@ static void s390_cpu_class_init(ObjectClass *oc, void *data) cc->do_interrupt = s390_cpu_do_interrupt; #endif cc->dump_state = s390_cpu_dump_state; + cc->get_crash_info = s390_cpu_get_crash_info; cc->set_pc = s390_cpu_set_pc; cc->gdb_read_register = s390_cpu_gdb_read_register; cc->gdb_write_register = s390_cpu_gdb_write_register; diff --git a/target/s390x/cpu.h b/target/s390x/cpu.h index 96df2fe5c9..c5ef930876 100644 --- a/target/s390x/cpu.h +++ b/target/s390x/cpu.h @@ -83,6 +83,8 @@ struct CPUS390XState { PSW psw; + S390CrashReason crash_reason; + uint64_t cc_src; uint64_t cc_dst; uint64_t cc_vr; @@ -139,12 +141,9 @@ struct CPUS390XState { * architectures, there is a difference between a halt and a stop on s390. * If all cpus are either stopped (including check stop) or in the disabled * wait state, the vm can be shut down. + * The acceptable cpu_state values are defined in the CpuInfoS390State + * enum. */ -#define CPU_STATE_UNINITIALIZED 0x00 -#define CPU_STATE_STOPPED 0x01 -#define CPU_STATE_CHECK_STOP 0x02 -#define CPU_STATE_OPERATING 0x03 -#define CPU_STATE_LOAD 0x04 uint8_t cpu_state; /* currently processed sigp order */ @@ -310,11 +309,12 @@ extern const struct VMStateDescription vmstate_s390_cpu; #define FLAG_MASK_PSW_SHIFT 31 #define FLAG_MASK_PER (PSW_MASK_PER >> FLAG_MASK_PSW_SHIFT) +#define FLAG_MASK_DAT (PSW_MASK_DAT >> FLAG_MASK_PSW_SHIFT) #define FLAG_MASK_PSTATE (PSW_MASK_PSTATE >> FLAG_MASK_PSW_SHIFT) #define FLAG_MASK_ASC (PSW_MASK_ASC >> FLAG_MASK_PSW_SHIFT) #define FLAG_MASK_64 (PSW_MASK_64 >> FLAG_MASK_PSW_SHIFT) #define FLAG_MASK_32 (PSW_MASK_32 >> FLAG_MASK_PSW_SHIFT) -#define FLAG_MASK_PSW (FLAG_MASK_PER | FLAG_MASK_PSTATE \ +#define FLAG_MASK_PSW (FLAG_MASK_PER | FLAG_MASK_DAT | FLAG_MASK_PSTATE \ | FLAG_MASK_ASC | FLAG_MASK_64 | FLAG_MASK_32) /* Control register 0 bits */ @@ -338,6 +338,10 @@ extern const struct VMStateDescription vmstate_s390_cpu; static inline int cpu_mmu_index(CPUS390XState *env, bool ifetch) { + if (!(env->psw.mask & PSW_MASK_DAT)) { + return MMU_REAL_IDX; + } + switch (env->psw.mask & PSW_MASK_ASC) { case PSW_ASC_PRIMARY: return MMU_PRIMARY_IDX; @@ -617,10 +621,6 @@ QEMU_BUILD_BUG_ON(sizeof(SysIB) != 4096); /* SIGP order code mask corresponding to bit positions 56-63 */ #define SIGP_ORDER_MASK 0x000000ff -/* from s390-virtio-ccw */ -#define MEM_SECTION_SIZE 0x10000000UL -#define MAX_AVAIL_SLOTS 32 - /* machine check interruption code */ /* subclasses */ @@ -692,7 +692,6 @@ 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_get_memslot_count(void); int s390_set_memory_limit(uint64_t new_limit, uint64_t *hw_limit); void s390_cmma_reset(void); void s390_enable_css_support(S390CPU *cpu); diff --git a/target/s390x/cpu_features.c b/target/s390x/cpu_features.c index a5619f2893..3b9e2745e9 100644 --- a/target/s390x/cpu_features.c +++ b/target/s390x/cpu_features.c @@ -23,6 +23,10 @@ .desc = _desc, \ } +/* S390FeatDef.bit is not applicable as there is no feature block. */ +#define FEAT_INIT_MISC(_name, _desc) \ + FEAT_INIT(_name, S390_FEAT_TYPE_MISC, 0, _desc) + /* indexed by feature number for easy lookup */ static const S390FeatDef s390_features[] = { FEAT_INIT("esan3", S390_FEAT_TYPE_STFL, 0, "Instructions marked as n3"), @@ -123,8 +127,8 @@ static const S390FeatDef s390_features[] = { FEAT_INIT("ib", S390_FEAT_TYPE_SCLP_CPU, 42, "SIE: Intervention bypass facility"), FEAT_INIT("cei", S390_FEAT_TYPE_SCLP_CPU, 43, "SIE: Conditional-external-interception facility"), - FEAT_INIT("dateh2", S390_FEAT_TYPE_MISC, 0, "DAT-enhancement facility 2"), - FEAT_INIT("cmm", S390_FEAT_TYPE_MISC, 0, "Collaborative-memory-management facility"), + FEAT_INIT_MISC("dateh2", "DAT-enhancement facility 2"), + FEAT_INIT_MISC("cmm", "Collaborative-memory-management facility"), FEAT_INIT("plo-cl", S390_FEAT_TYPE_PLO, 0, "PLO Compare and load (32 bit in general registers)"), FEAT_INIT("plo-clg", S390_FEAT_TYPE_PLO, 1, "PLO Compare and load (64 bit in parameter list)"), diff --git a/target/s390x/excp_helper.c b/target/s390x/excp_helper.c index 411051edc3..dfee221111 100644 --- a/target/s390x/excp_helper.c +++ b/target/s390x/excp_helper.c @@ -107,6 +107,10 @@ int s390_cpu_handle_mmu_fault(CPUState *cs, vaddr orig_vaddr, int size, return 1; } } else if (mmu_idx == MMU_REAL_IDX) { + /* 31-Bit mode */ + if (!(env->psw.mask & PSW_MASK_64)) { + vaddr &= 0x7fffffff; + } if (mmu_translate_real(env, vaddr, rw, &raddr, &prot)) { return 1; } diff --git a/target/s390x/helper.c b/target/s390x/helper.c index 84aaef3a53..615fa24ab9 100644 --- a/target/s390x/helper.c +++ b/target/s390x/helper.c @@ -83,12 +83,15 @@ static inline bool is_special_wait_psw(uint64_t psw_addr) void s390_handle_wait(S390CPU *cpu) { + CPUState *cs = CPU(cpu); + if (s390_cpu_halt(cpu) == 0) { #ifndef CONFIG_USER_ONLY if (is_special_wait_psw(cpu->env.psw.addr)) { qemu_system_shutdown_request(SHUTDOWN_CAUSE_GUEST_SHUTDOWN); } else { - qemu_system_guest_panicked(NULL); + cpu->env.crash_reason = S390_CRASH_REASON_DISABLED_WAIT; + qemu_system_guest_panicked(cpu_get_crash_info(cs)); } #endif } diff --git a/target/s390x/insn-data.def b/target/s390x/insn-data.def index 621e10d615..157619403d 100644 --- a/target/s390x/insn-data.def +++ b/target/s390x/insn-data.def @@ -1000,13 +1000,13 @@ /* ??? Not implemented - is it necessary? */ C(0xb204, SCK, S, Z, 0, 0, 0, 0, 0, 0) /* SET CLOCK COMPARATOR */ - C(0xb206, SCKC, S, Z, 0, m2_64, 0, 0, sckc, 0) + C(0xb206, SCKC, S, Z, 0, m2_64a, 0, 0, sckc, 0) /* SET CLOCK PROGRAMMABLE FIELD */ C(0x0107, SCKPF, E, Z, 0, 0, 0, 0, sckpf, 0) /* SET CPU TIMER */ - C(0xb208, SPT, S, Z, 0, m2_64, 0, 0, spt, 0) + C(0xb208, SPT, S, Z, 0, m2_64a, 0, 0, spt, 0) /* SET PREFIX */ - C(0xb210, SPX, S, Z, 0, m2_32u, 0, 0, spx, 0) + C(0xb210, SPX, S, Z, 0, m2_32ua, 0, 0, spx, 0) /* SET PSW KEY FROM ADDRESS */ C(0xb20a, SPKA, S, Z, 0, a2, 0, 0, spka, 0) /* SET STORAGE KEY EXTENDED */ @@ -1021,20 +1021,20 @@ /* STORE CLOCK EXTENDED */ C(0xb278, STCKE, S, Z, 0, a2, 0, 0, stcke, 0) /* STORE CLOCK COMPARATOR */ - C(0xb207, STCKC, S, Z, la2, 0, new, m1_64, stckc, 0) + C(0xb207, STCKC, S, Z, la2, 0, new, m1_64a, stckc, 0) /* STORE CONTROL */ C(0xb600, STCTL, RS_a, Z, 0, a2, 0, 0, stctl, 0) C(0xeb25, STCTG, RSY_a, Z, 0, a2, 0, 0, stctg, 0) /* STORE CPU ADDRESS */ - C(0xb212, STAP, S, Z, la2, 0, new, m1_16, stap, 0) + C(0xb212, STAP, S, Z, la2, 0, new, m1_16a, stap, 0) /* STORE CPU ID */ - C(0xb202, STIDP, S, Z, la2, 0, new, 0, stidp, 0) + C(0xb202, STIDP, S, Z, la2, 0, new, m1_64a, stidp, 0) /* STORE CPU TIMER */ - C(0xb209, STPT, S, Z, la2, 0, new, m1_64, stpt, 0) + C(0xb209, STPT, S, Z, la2, 0, new, m1_64a, stpt, 0) /* STORE FACILITY LIST */ C(0xb2b1, STFL, S, Z, 0, 0, 0, 0, stfl, 0) /* STORE PREFIX */ - C(0xb211, STPX, S, Z, la2, 0, new, m1_32, stpx, 0) + C(0xb211, STPX, S, Z, la2, 0, new, m1_32a, stpx, 0) /* STORE SYSTEM INFORMATION */ C(0xb27d, STSI, S, Z, 0, a2, 0, 0, stsi, 0) /* STORE THEN AND SYSTEM MASK */ diff --git a/target/s390x/kvm-stub.c b/target/s390x/kvm-stub.c index 8cdcf83845..29b10542cc 100644 --- a/target/s390x/kvm-stub.c +++ b/target/s390x/kvm-stub.c @@ -84,11 +84,6 @@ void kvm_s390_cmma_reset(void) { } -int kvm_s390_get_memslot_count(void) -{ - return MAX_AVAIL_SLOTS; -} - void kvm_s390_reset_vcpu(S390CPU *cpu) { } diff --git a/target/s390x/kvm.c b/target/s390x/kvm.c index e13c8907df..656aaea2cd 100644 --- a/target/s390x/kvm.c +++ b/target/s390x/kvm.c @@ -1541,15 +1541,14 @@ static int handle_instruction(S390CPU *cpu, struct kvm_run *run) return r; } -static void unmanageable_intercept(S390CPU *cpu, const char *str, int pswoffset) +static void unmanageable_intercept(S390CPU *cpu, S390CrashReason reason, + int pswoffset) { CPUState *cs = CPU(cpu); - error_report("Unmanageable %s! CPU%i new PSW: 0x%016lx:%016lx", - str, cs->cpu_index, ldq_phys(cs->as, cpu->env.psa + pswoffset), - ldq_phys(cs->as, cpu->env.psa + pswoffset + 8)); s390_cpu_halt(cpu); - qemu_system_guest_panicked(NULL); + cpu->env.crash_reason = reason; + qemu_system_guest_panicked(cpu_get_crash_info(cs)); } /* try to detect pgm check loops */ @@ -1579,7 +1578,7 @@ static int handle_oper_loop(S390CPU *cpu, struct kvm_run *run) !(oldpsw.mask & PSW_MASK_PSTATE) && (newpsw.mask & PSW_MASK_ASC) == (oldpsw.mask & PSW_MASK_ASC) && (newpsw.mask & PSW_MASK_DAT) == (oldpsw.mask & PSW_MASK_DAT)) { - unmanageable_intercept(cpu, "operation exception loop", + unmanageable_intercept(cpu, S390_CRASH_REASON_OPINT_LOOP, offsetof(LowCore, program_new_psw)); return EXCP_HALTED; } @@ -1600,12 +1599,12 @@ static int handle_intercept(S390CPU *cpu) r = handle_instruction(cpu, run); break; case ICPT_PROGRAM: - unmanageable_intercept(cpu, "program interrupt", + unmanageable_intercept(cpu, S390_CRASH_REASON_PGMINT_LOOP, offsetof(LowCore, program_new_psw)); r = EXCP_HALTED; break; case ICPT_EXT_INT: - unmanageable_intercept(cpu, "external interrupt", + unmanageable_intercept(cpu, S390_CRASH_REASON_EXTINT_LOOP, offsetof(LowCore, external_new_psw)); r = EXCP_HALTED; break; @@ -1855,11 +1854,6 @@ int kvm_s390_assign_subch_ioeventfd(EventNotifier *notifier, uint32_t sch, return kvm_vm_ioctl(kvm_state, KVM_IOEVENTFD, &kick); } -int kvm_s390_get_memslot_count(void) -{ - return kvm_check_extension(kvm_state, KVM_CAP_NR_MEMSLOTS); -} - int kvm_s390_get_ri(void) { return cap_ri; @@ -1881,16 +1875,16 @@ int kvm_s390_set_cpu_state(S390CPU *cpu, uint8_t cpu_state) } switch (cpu_state) { - case CPU_STATE_STOPPED: + case S390_CPU_STATE_STOPPED: mp_state.mp_state = KVM_MP_STATE_STOPPED; break; - case CPU_STATE_CHECK_STOP: + case S390_CPU_STATE_CHECK_STOP: mp_state.mp_state = KVM_MP_STATE_CHECK_STOP; break; - case CPU_STATE_OPERATING: + case S390_CPU_STATE_OPERATING: mp_state.mp_state = KVM_MP_STATE_OPERATING; break; - case CPU_STATE_LOAD: + case S390_CPU_STATE_LOAD: mp_state.mp_state = KVM_MP_STATE_LOAD; break; default: diff --git a/target/s390x/kvm_s390x.h b/target/s390x/kvm_s390x.h index 7a3b862eea..c383bf4ee9 100644 --- a/target/s390x/kvm_s390x.h +++ b/target/s390x/kvm_s390x.h @@ -30,7 +30,6 @@ int kvm_s390_set_clock_ext(uint8_t *tod_high, uint64_t *tod_clock); void kvm_s390_enable_css_support(S390CPU *cpu); int kvm_s390_assign_subch_ioeventfd(EventNotifier *notifier, uint32_t sch, int vq, bool assign); -int kvm_s390_get_memslot_count(void); int kvm_s390_cmma_active(void); void kvm_s390_cmma_reset(void); void kvm_s390_reset_vcpu(S390CPU *cpu); diff --git a/target/s390x/mem_helper.c b/target/s390x/mem_helper.c index 427b795a78..d5291b246e 100644 --- a/target/s390x/mem_helper.c +++ b/target/s390x/mem_helper.c @@ -693,6 +693,11 @@ void HELPER(lam)(CPUS390XState *env, uint32_t r1, uint64_t a2, uint32_t r3) uintptr_t ra = GETPC(); int i; + if (a2 & 0x3) { + /* we either came here by lam or lamy, which have different lengths */ + s390_program_interrupt(env, PGM_SPECIFICATION, ILEN_AUTO, ra); + } + for (i = r1;; i = (i + 1) % 16) { env->aregs[i] = cpu_ldl_data_ra(env, a2, ra); a2 += 4; @@ -709,6 +714,10 @@ void HELPER(stam)(CPUS390XState *env, uint32_t r1, uint64_t a2, uint32_t r3) uintptr_t ra = GETPC(); int i; + if (a2 & 0x3) { + s390_program_interrupt(env, PGM_SPECIFICATION, 4, ra); + } + for (i = r1;; i = (i + 1) % 16) { cpu_stl_data_ra(env, a2, env->aregs[i], ra); a2 += 4; @@ -1620,6 +1629,10 @@ void HELPER(lctlg)(CPUS390XState *env, uint32_t r1, uint64_t a2, uint32_t r3) uint64_t src = a2; uint32_t i; + if (src & 0x7) { + s390_program_interrupt(env, PGM_SPECIFICATION, 6, ra); + } + for (i = r1;; i = (i + 1) % 16) { uint64_t val = cpu_ldq_data_ra(env, src, ra); if (env->cregs[i] != val && i >= 9 && i <= 11) { @@ -1650,6 +1663,10 @@ void HELPER(lctl)(CPUS390XState *env, uint32_t r1, uint64_t a2, uint32_t r3) uint64_t src = a2; uint32_t i; + if (src & 0x3) { + s390_program_interrupt(env, PGM_SPECIFICATION, 4, ra); + } + for (i = r1;; i = (i + 1) % 16) { uint32_t val = cpu_ldl_data_ra(env, src, ra); if ((uint32_t)env->cregs[i] != val && i >= 9 && i <= 11) { @@ -1677,6 +1694,10 @@ void HELPER(stctg)(CPUS390XState *env, uint32_t r1, uint64_t a2, uint32_t r3) uint64_t dest = a2; uint32_t i; + if (dest & 0x7) { + s390_program_interrupt(env, PGM_SPECIFICATION, 6, ra); + } + for (i = r1;; i = (i + 1) % 16) { cpu_stq_data_ra(env, dest, env->cregs[i], ra); dest += sizeof(uint64_t); @@ -1693,6 +1714,10 @@ void HELPER(stctl)(CPUS390XState *env, uint32_t r1, uint64_t a2, uint32_t r3) uint64_t dest = a2; uint32_t i; + if (dest & 0x3) { + s390_program_interrupt(env, PGM_SPECIFICATION, 4, ra); + } + for (i = r1;; i = (i + 1) % 16) { cpu_stl_data_ra(env, dest, env->cregs[i], ra); dest += sizeof(uint32_t); diff --git a/target/s390x/mmu_helper.c b/target/s390x/mmu_helper.c index f477cc006a..23fb2e7501 100644 --- a/target/s390x/mmu_helper.c +++ b/target/s390x/mmu_helper.c @@ -544,7 +544,7 @@ int mmu_translate_real(CPUS390XState *env, target_ulong raddr, int rw, { const bool lowprot_enabled = env->cregs[0] & CR0_LOWPROT; - *flags = PAGE_READ | PAGE_WRITE; + *flags = PAGE_READ | PAGE_WRITE | PAGE_EXEC; if (is_low_address(raddr & TARGET_PAGE_MASK) && lowprot_enabled) { /* see comment in mmu_translate() how this works */ *flags |= PAGE_WRITE_INV; diff --git a/target/s390x/sigp.c b/target/s390x/sigp.c index ac3f8e7dc2..5a7a9c4cfb 100644 --- a/target/s390x/sigp.c +++ b/target/s390x/sigp.c @@ -46,13 +46,13 @@ static void sigp_sense(S390CPU *dst_cpu, SigpInfo *si) } /* sensing without locks is racy, but it's the same for real hw */ - if (state != CPU_STATE_STOPPED && !ext_call) { + if (state != S390_CPU_STATE_STOPPED && !ext_call) { si->cc = SIGP_CC_ORDER_CODE_ACCEPTED; } else { if (ext_call) { status |= SIGP_STAT_EXT_CALL_PENDING; } - if (state == CPU_STATE_STOPPED) { + if (state == S390_CPU_STATE_STOPPED) { status |= SIGP_STAT_STOPPED; } set_sigp_status(si, status); @@ -94,12 +94,12 @@ static void sigp_start(CPUState *cs, run_on_cpu_data arg) S390CPU *cpu = S390_CPU(cs); SigpInfo *si = arg.host_ptr; - if (s390_cpu_get_state(cpu) != CPU_STATE_STOPPED) { + if (s390_cpu_get_state(cpu) != S390_CPU_STATE_STOPPED) { si->cc = SIGP_CC_ORDER_CODE_ACCEPTED; return; } - s390_cpu_set_state(CPU_STATE_OPERATING, cpu); + s390_cpu_set_state(S390_CPU_STATE_OPERATING, cpu); si->cc = SIGP_CC_ORDER_CODE_ACCEPTED; } @@ -108,14 +108,14 @@ static void sigp_stop(CPUState *cs, run_on_cpu_data arg) S390CPU *cpu = S390_CPU(cs); SigpInfo *si = arg.host_ptr; - if (s390_cpu_get_state(cpu) != CPU_STATE_OPERATING) { + if (s390_cpu_get_state(cpu) != S390_CPU_STATE_OPERATING) { si->cc = SIGP_CC_ORDER_CODE_ACCEPTED; return; } /* disabled wait - sleeping in user space */ if (cs->halted) { - s390_cpu_set_state(CPU_STATE_STOPPED, cpu); + s390_cpu_set_state(S390_CPU_STATE_STOPPED, cpu); } else { /* execute the stop function */ cpu->env.sigp_order = SIGP_STOP; @@ -130,17 +130,17 @@ static void sigp_stop_and_store_status(CPUState *cs, run_on_cpu_data arg) SigpInfo *si = arg.host_ptr; /* disabled wait - sleeping in user space */ - if (s390_cpu_get_state(cpu) == CPU_STATE_OPERATING && cs->halted) { - s390_cpu_set_state(CPU_STATE_STOPPED, cpu); + if (s390_cpu_get_state(cpu) == S390_CPU_STATE_OPERATING && cs->halted) { + s390_cpu_set_state(S390_CPU_STATE_STOPPED, cpu); } switch (s390_cpu_get_state(cpu)) { - case CPU_STATE_OPERATING: + case S390_CPU_STATE_OPERATING: cpu->env.sigp_order = SIGP_STOP_STORE_STATUS; cpu_inject_stop(cpu); /* store will be performed in do_stop_interrup() */ break; - case CPU_STATE_STOPPED: + case S390_CPU_STATE_STOPPED: /* already stopped, just store the status */ cpu_synchronize_state(cs); s390_store_status(cpu, S390_STORE_STATUS_DEF_ADDR, true); @@ -156,7 +156,7 @@ static void sigp_store_status_at_address(CPUState *cs, run_on_cpu_data arg) uint32_t address = si->param & 0x7ffffe00u; /* cpu has to be stopped */ - if (s390_cpu_get_state(cpu) != CPU_STATE_STOPPED) { + if (s390_cpu_get_state(cpu) != S390_CPU_STATE_STOPPED) { set_sigp_status(si, SIGP_STAT_INCORRECT_STATE); return; } @@ -186,7 +186,7 @@ static void sigp_store_adtl_status(CPUState *cs, run_on_cpu_data arg) } /* cpu has to be stopped */ - if (s390_cpu_get_state(cpu) != CPU_STATE_STOPPED) { + if (s390_cpu_get_state(cpu) != S390_CPU_STATE_STOPPED) { set_sigp_status(si, SIGP_STAT_INCORRECT_STATE); return; } @@ -229,17 +229,17 @@ static void sigp_restart(CPUState *cs, run_on_cpu_data arg) SigpInfo *si = arg.host_ptr; switch (s390_cpu_get_state(cpu)) { - case CPU_STATE_STOPPED: + case S390_CPU_STATE_STOPPED: /* the restart irq has to be delivered prior to any other pending irq */ cpu_synchronize_state(cs); /* * Set OPERATING (and unhalting) before loading the restart PSW. * load_psw() will then properly halt the CPU again if necessary (TCG). */ - s390_cpu_set_state(CPU_STATE_OPERATING, cpu); + s390_cpu_set_state(S390_CPU_STATE_OPERATING, cpu); do_restart_interrupt(&cpu->env); break; - case CPU_STATE_OPERATING: + case S390_CPU_STATE_OPERATING: cpu_inject_restart(cpu); break; } @@ -285,7 +285,7 @@ static void sigp_set_prefix(CPUState *cs, run_on_cpu_data arg) } /* cpu has to be stopped */ - if (s390_cpu_get_state(cpu) != CPU_STATE_STOPPED) { + if (s390_cpu_get_state(cpu) != S390_CPU_STATE_STOPPED) { set_sigp_status(si, SIGP_STAT_INCORRECT_STATE); return; } @@ -318,7 +318,7 @@ static void sigp_cond_emergency(S390CPU *src_cpu, S390CPU *dst_cpu, p_asn = dst_cpu->env.cregs[4] & 0xffff; /* Primary ASN */ s_asn = dst_cpu->env.cregs[3] & 0xffff; /* Secondary ASN */ - if (s390_cpu_get_state(dst_cpu) != CPU_STATE_STOPPED || + if (s390_cpu_get_state(dst_cpu) != S390_CPU_STATE_STOPPED || (psw_mask & psw_int_mask) != psw_int_mask || (idle && psw_addr != 0) || (!idle && (asn == p_asn || asn == s_asn))) { @@ -435,7 +435,7 @@ static int sigp_set_architecture(S390CPU *cpu, uint32_t param, if (cur_cpu == cpu) { continue; } - if (s390_cpu_get_state(cur_cpu) != CPU_STATE_STOPPED) { + if (s390_cpu_get_state(cur_cpu) != S390_CPU_STATE_STOPPED) { all_stopped = false; } } @@ -492,7 +492,7 @@ void do_stop_interrupt(CPUS390XState *env) { S390CPU *cpu = s390_env_get_cpu(env); - if (s390_cpu_set_state(CPU_STATE_STOPPED, cpu) == 0) { + if (s390_cpu_set_state(S390_CPU_STATE_STOPPED, cpu) == 0) { qemu_system_shutdown_request(SHUTDOWN_CAUSE_GUEST_SHUTDOWN); } if (cpu->env.sigp_order == SIGP_STOP_STORE_STATUS) { diff --git a/target/s390x/translate.c b/target/s390x/translate.c index b470d691d3..7d39ab350d 100644 --- a/target/s390x/translate.c +++ b/target/s390x/translate.c @@ -252,13 +252,17 @@ static inline uint64_t ld_code4(CPUS390XState *env, uint64_t pc) static int get_mem_index(DisasContext *s) { + if (!(s->tb->flags & FLAG_MASK_DAT)) { + return MMU_REAL_IDX; + } + switch (s->tb->flags & FLAG_MASK_ASC) { case PSW_ASC_PRIMARY >> FLAG_MASK_PSW_SHIFT: - return 0; + return MMU_PRIMARY_IDX; case PSW_ASC_SECONDARY >> FLAG_MASK_PSW_SHIFT: - return 1; + return MMU_SECONDARY_IDX; case PSW_ASC_HOME >> FLAG_MASK_PSW_SHIFT: - return 2; + return MMU_HOME_IDX; default: tcg_abort(); break; @@ -4058,7 +4062,6 @@ static ExitStatus op_stidp(DisasContext *s, DisasOps *o) { check_privileged(s); tcg_gen_ld_i64(o->out, cpu_env, offsetof(CPUS390XState, cpuid)); - tcg_gen_qemu_st_i64(o->out, o->addr1, get_mem_index(s), MO_TEQ | MO_ALIGN); return NO_EXIT; } @@ -5216,18 +5219,42 @@ static void wout_m1_16(DisasContext *s, DisasFields *f, DisasOps *o) } #define SPEC_wout_m1_16 0 +#ifndef CONFIG_USER_ONLY +static void wout_m1_16a(DisasContext *s, DisasFields *f, DisasOps *o) +{ + tcg_gen_qemu_st_tl(o->out, o->addr1, get_mem_index(s), MO_TEUW | MO_ALIGN); +} +#define SPEC_wout_m1_16a 0 +#endif + static void wout_m1_32(DisasContext *s, DisasFields *f, DisasOps *o) { tcg_gen_qemu_st32(o->out, o->addr1, get_mem_index(s)); } #define SPEC_wout_m1_32 0 +#ifndef CONFIG_USER_ONLY +static void wout_m1_32a(DisasContext *s, DisasFields *f, DisasOps *o) +{ + tcg_gen_qemu_st_tl(o->out, o->addr1, get_mem_index(s), MO_TEUL | MO_ALIGN); +} +#define SPEC_wout_m1_32a 0 +#endif + static void wout_m1_64(DisasContext *s, DisasFields *f, DisasOps *o) { tcg_gen_qemu_st64(o->out, o->addr1, get_mem_index(s)); } #define SPEC_wout_m1_64 0 +#ifndef CONFIG_USER_ONLY +static void wout_m1_64a(DisasContext *s, DisasFields *f, DisasOps *o) +{ + tcg_gen_qemu_st_i64(o->out, o->addr1, get_mem_index(s), MO_TEQ | MO_ALIGN); +} +#define SPEC_wout_m1_64a 0 +#endif + static void wout_m2_32(DisasContext *s, DisasFields *f, DisasOps *o) { tcg_gen_qemu_st32(o->out, o->in2, get_mem_index(s)); @@ -5653,6 +5680,15 @@ static void in2_m2_32u(DisasContext *s, DisasFields *f, DisasOps *o) } #define SPEC_in2_m2_32u 0 +#ifndef CONFIG_USER_ONLY +static void in2_m2_32ua(DisasContext *s, DisasFields *f, DisasOps *o) +{ + in2_a2(s, f, o); + tcg_gen_qemu_ld_tl(o->in2, o->in2, get_mem_index(s), MO_TEUL | MO_ALIGN); +} +#define SPEC_in2_m2_32ua 0 +#endif + static void in2_m2_64(DisasContext *s, DisasFields *f, DisasOps *o) { in2_a2(s, f, o); @@ -5660,6 +5696,15 @@ static void in2_m2_64(DisasContext *s, DisasFields *f, DisasOps *o) } #define SPEC_in2_m2_64 0 +#ifndef CONFIG_USER_ONLY +static void in2_m2_64a(DisasContext *s, DisasFields *f, DisasOps *o) +{ + in2_a2(s, f, o); + tcg_gen_qemu_ld_i64(o->in2, o->in2, get_mem_index(s), MO_TEQ | MO_ALIGN); +} +#define SPEC_in2_m2_64a 0 +#endif + static void in2_mri2_16u(DisasContext *s, DisasFields *f, DisasOps *o) { in2_ri2(s, f, o); @@ -1736,7 +1736,7 @@ void qemu_system_reset(ShutdownCause reason) void qemu_system_guest_panicked(GuestPanicInformation *info) { - qemu_log_mask(LOG_GUEST_ERROR, "Guest crashed\n"); + qemu_log_mask(LOG_GUEST_ERROR, "Guest crashed"); if (current_cpu) { current_cpu->crash_occurred = true; @@ -1752,13 +1752,20 @@ void qemu_system_guest_panicked(GuestPanicInformation *info) if (info) { if (info->type == GUEST_PANIC_INFORMATION_TYPE_HYPER_V) { - qemu_log_mask(LOG_GUEST_ERROR, "HV crash parameters: (%#"PRIx64 + qemu_log_mask(LOG_GUEST_ERROR, "\nHV crash parameters: (%#"PRIx64 " %#"PRIx64" %#"PRIx64" %#"PRIx64" %#"PRIx64")\n", info->u.hyper_v.arg1, info->u.hyper_v.arg2, info->u.hyper_v.arg3, info->u.hyper_v.arg4, info->u.hyper_v.arg5); + } else if (info->type == GUEST_PANIC_INFORMATION_TYPE_S390) { + qemu_log_mask(LOG_GUEST_ERROR, " on cpu %d: %s\n" + "PSW: 0x%016" PRIx64 " 0x%016" PRIx64"\n", + info->u.s390.core, + S390CrashReason_str(info->u.s390.reason), + info->u.s390.psw_mask, + info->u.s390.psw_addr); } qapi_free_GuestPanicInformation(info); } |