diff --git a/cpus.c b/cpus.c index f298b659f4..af678264f6 100644 --- a/cpus.c +++ b/cpus.c @@ -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/hmp.c b/hmp.c index 7870d6a300..ae86bfbade 100644 --- a/hmp.c +++ b/hmp.c @@ -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/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/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/monitor.c b/monitor.c index 373bb8d1c3..308a3d9b78 100644 --- a/monitor.c +++ b/monitor.c @@ -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 index 97155d2638..fbd76bb55e 100644 Binary files a/pc-bios/s390-ccw.img and b/pc-bios/s390-ccw.img differ 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; +/* + * Structs for IPL + */ +#define STAGE2_BLK_CNT_MAX 24 /* Stage 1b can load up to 24 blocks */ -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; +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 + * + * 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 + * * 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 + * + * 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 old mode 100755 new mode 100644 index 24f40feae6..9f5926b534 Binary files a/pc-bios/s390-netboot.img and b/pc-bios/s390-netboot.img differ 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' } } ## @@ -521,11 +524,40 @@ ## { '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,9 +587,88 @@ # ] # } # +# 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: # 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/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); diff --git a/vl.c b/vl.c index 9e7235df6d..3018433706 100644 --- a/vl.c +++ b/vl.c @@ -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); }