Merge tag 's390x-20260812' of https://gitlab.com/efarman/qemu into staging First batch of s390x updates for 11.2: - compat machines for 11.2 - allow guest backing with 2G hugepages - fixes to TCG (and a couple tests on them) - hardening fixes in boot/sclp code - regenerate s390-ccw.img # -----BEGIN PGP SIGNATURE----- # # iIsEABYKADMWIQQB3Dhbwk4ZE3uUN6KmTx4R4Fx3tAUCanygMxUcZmFybWFuQGxp # bnV4LmlibS5jb20ACgkQpk8eEeBcd7RMqwEAzBhcnvUDHowDalVgBJ4QTWcbUx2D # dZgoMvFpFYB2aEsA/32H81TzvrwX4xAkJlqdGIrQbPGmtdvAOKfjketAjGMC # =lFhB # -----END PGP SIGNATURE----- # gpg: Signature made Wed 12 Aug 2026 09:32:51 AM PDT # gpg: using EDDSA key 01DC385BC24E19137B9437A2A64F1E11E05C77B4 # gpg: issuer "farman@linux.ibm.com" # gpg: Good signature from "Eric Farman <farman@linux.ibm.com>" [unknown] # gpg: WARNING: The key's User ID is not certified with a trusted signature! # gpg: There is no indication that the signature belongs to the owner. # Primary key fingerprint: D2C6 0504 C9E8 F568 CFE5 87B5 3827 B212 71BF 9562 # Subkey fingerprint: 01DC 385B C24E 1913 7B94 37A2 A64F 1E11 E05C 77B4 * tag 's390x-20260812' of https://gitlab.com/efarman/qemu: pc-bios/s390-ccw.img: update s390x bios hw: add compat machines for 11.2 target/s390x: Allow 2G hugepages guest backing tests/tcg/s390x: Test STCKF condition code on a faulting store target/s390x/tcg: Set STCK/STCKF condition code after the store pc-bios/s390-ccw: Fix off-by-one errors with loadparm and boot entries pc-bios/s390-ccw: bound zipl menu strlen and replace VLA in zipl_print_entry pc-bios/s390-ccw: bounds-check zipl menu entry index before array write pc-bios/s390-ccw: fix out-of-bounds read in iso_get_file_size() s390x/ipl: validate num_comp against iplb length before iterating hw/char/sclpconsole-lm: avoid guest triggerable assert tests/tcg/s390x: Test DR overflow (INT64_MIN / -1) target/s390x: Fix DR/D INT64_MIN / -1 host crash tests/tcg/s390x: Test PRNO TRNG interruptibility target/s390x: Make PRNO TRNG interruptible Signed-off-by: Richard Henderson <richard.henderson@linaro.org>
diff --git a/hw/arm/virt.c b/hw/arm/virt.c index 219597c..e7a56e3 100644 --- a/hw/arm/virt.c +++ b/hw/arm/virt.c
@@ -4441,10 +4441,17 @@ } type_init(machvirt_machine_init); -static void virt_machine_11_1_options(MachineClass *mc) +static void virt_machine_11_2_options(MachineClass *mc) { } -DEFINE_VIRT_MACHINE_AS_LATEST(11, 1) +DEFINE_VIRT_MACHINE_AS_LATEST(11, 2) + +static void virt_machine_11_1_options(MachineClass *mc) +{ + virt_machine_11_2_options(mc); + compat_props_add(mc->compat_props, hw_compat_11_1, hw_compat_11_1_len); +} +DEFINE_VIRT_MACHINE(11, 1) static void virt_machine_11_0_options(MachineClass *mc) {
diff --git a/hw/char/sclpconsole-lm.c b/hw/char/sclpconsole-lm.c index 9a16896..f6ed282 100644 --- a/hw/char/sclpconsole-lm.c +++ b/hw/char/sclpconsole-lm.c
@@ -243,7 +243,8 @@ SCLPConsoleLM *scon = SCLPLM_CONSOLE(event); len = be16_to_cpu(data->mdb.header.length); - if (len < sizeof(data->mdb.header)) { + if (len < sizeof(data->mdb.header) || + len > be16_to_cpu(data->header.length) - sizeof(EventBufferHeader)) { return SCLP_RC_INCONSISTENT_LENGTHS; } len -= sizeof(data->mdb.header);
diff --git a/hw/core/machine.c b/hw/core/machine.c index eb5e380..4e55119 100644 --- a/hw/core/machine.c +++ b/hw/core/machine.c
@@ -40,6 +40,9 @@ #include "qemu/audio.h" #include "hw/arm/smmuv3.h" +GlobalProperty hw_compat_11_1[] = {}; +const size_t hw_compat_11_1_len = G_N_ELEMENTS(hw_compat_11_1); + GlobalProperty hw_compat_11_0[] = { { "virtio-mmio", VIRTIO_QUEUE_SIZE_OVERRIDE, "1024" }, { "chardev-vc", "encoding", "cp437" },
diff --git a/hw/i386/pc.c b/hw/i386/pc.c index f064aa2..e9e4fc2 100644 --- a/hw/i386/pc.c +++ b/hw/i386/pc.c
@@ -74,6 +74,9 @@ #include "hw/xen/xen-bus.h" #endif +GlobalProperty pc_compat_11_1[] = {}; +const size_t pc_compat_11_1_len = G_N_ELEMENTS(pc_compat_11_1); + GlobalProperty pc_compat_11_0[] = {}; const size_t pc_compat_11_0_len = G_N_ELEMENTS(pc_compat_11_0);
diff --git a/hw/i386/pc_piix.c b/hw/i386/pc_piix.c index 82457bd..223a19c 100644 --- a/hw/i386/pc_piix.c +++ b/hw/i386/pc_piix.c
@@ -428,12 +428,21 @@ pc_piix_compat_defaults, pc_piix_compat_defaults_len); } -static void pc_i440fx_machine_11_1_options(MachineClass *m) +static void pc_i440fx_machine_11_2_options(MachineClass *m) { pc_i440fx_machine_options(m); } -DEFINE_I440FX_MACHINE_AS_LATEST(11, 1); +DEFINE_I440FX_MACHINE_AS_LATEST(11, 2); + +static void pc_i440fx_machine_11_1_options(MachineClass *m) +{ + pc_i440fx_machine_11_2_options(m); + compat_props_add(m->compat_props, hw_compat_11_1, hw_compat_11_1_len); + compat_props_add(m->compat_props, pc_compat_11_1, pc_compat_11_1_len); +} + +DEFINE_I440FX_MACHINE(11, 1); static void pc_i440fx_machine_11_0_options(MachineClass *m) {
diff --git a/hw/i386/pc_q35.c b/hw/i386/pc_q35.c index 6c1e4ef..94cd711 100644 --- a/hw/i386/pc_q35.c +++ b/hw/i386/pc_q35.c
@@ -383,12 +383,21 @@ pc_q35_compat_defaults, pc_q35_compat_defaults_len); } -static void pc_q35_machine_11_1_options(MachineClass *m) +static void pc_q35_machine_11_2_options(MachineClass *m) { pc_q35_machine_options(m); } -DEFINE_Q35_MACHINE_AS_LATEST(11, 1); +DEFINE_Q35_MACHINE_AS_LATEST(11, 2); + +static void pc_q35_machine_11_1_options(MachineClass *m) +{ + pc_q35_machine_11_2_options(m); + compat_props_add(m->compat_props, hw_compat_11_1, hw_compat_11_1_len); + compat_props_add(m->compat_props, pc_compat_11_1, pc_compat_11_1_len); +} + +DEFINE_Q35_MACHINE(11, 1); static void pc_q35_machine_11_0_options(MachineClass *m) {
diff --git a/hw/loongarch/virt.c b/hw/loongarch/virt.c index 6693dea..9cc7992 100644 --- a/hw/loongarch/virt.c +++ b/hw/loongarch/virt.c
@@ -1465,7 +1465,6 @@ mc->default_ram_id = "loongarch.ram"; mc->desc = "QEMU LoongArch Virtual Machine"; mc->max_cpus = LOONGARCH_MAX_CPUS; - mc->is_default = 1; mc->default_kernel_irqchip_split = false; mc->block_default_type = IF_VIRTIO; mc->default_boot_order = "c"; @@ -1546,6 +1545,7 @@ MACHINE_VER_DEPRECATION(__VA_ARGS__); \ if (latest) { \ mc->alias = "virt"; \ + mc->is_default = true; \ } \ } \ static const TypeInfo MACHINE_VER_SYM(info, virt, __VA_ARGS__) = \ @@ -1600,7 +1600,14 @@ type_init(machvirt_machine_init); -static void virt_machine_11_1_options(MachineClass *mc) +static void virt_machine_11_2_options(MachineClass *mc) { } -DEFINE_VIRT_MACHINE_AS_LATEST(11, 1) +DEFINE_VIRT_MACHINE_AS_LATEST(11, 2) + +static void virt_machine_11_1_options(MachineClass *mc) +{ + virt_machine_11_2_options(mc); + compat_props_add(mc->compat_props, hw_compat_11_1, hw_compat_11_1_len); +} +DEFINE_VIRT_MACHINE(11, 1)
diff --git a/hw/m68k/virt.c b/hw/m68k/virt.c index 51158ce..2749709 100644 --- a/hw/m68k/virt.c +++ b/hw/m68k/virt.c
@@ -367,10 +367,17 @@ #define DEFINE_VIRT_MACHINE(major, minor) \ DEFINE_VIRT_MACHINE_IMPL(false, major, minor) -static void virt_machine_11_1_options(MachineClass *mc) +static void virt_machine_11_2_options(MachineClass *mc) { } -DEFINE_VIRT_MACHINE_AS_LATEST(11, 1) +DEFINE_VIRT_MACHINE_AS_LATEST(11, 2) + +static void virt_machine_11_1_options(MachineClass *mc) +{ + virt_machine_11_2_options(mc); + compat_props_add(mc->compat_props, hw_compat_11_1, hw_compat_11_1_len); +} +DEFINE_VIRT_MACHINE(11, 1) static void virt_machine_11_0_options(MachineClass *mc) {
diff --git a/hw/ppc/spapr.c b/hw/ppc/spapr.c index b79828b..7e284ed 100644 --- a/hw/ppc/spapr.c +++ b/hw/ppc/spapr.c
@@ -4764,14 +4764,25 @@ DEFINE_SPAPR_MACHINE_IMPL(false, major, minor) /* - * pseries-11.1 + * pseries-11.2 */ -static void spapr_machine_11_1_class_options(MachineClass *mc) +static void spapr_machine_11_2_class_options(MachineClass *mc) { /* Defaults for the latest behaviour inherited from the base class */ } -DEFINE_SPAPR_MACHINE_AS_LATEST(11, 1); +DEFINE_SPAPR_MACHINE_AS_LATEST(11, 2); + +/* + * pseries-11.1 + */ +static void spapr_machine_11_1_class_options(MachineClass *mc) +{ + spapr_machine_11_2_class_options(mc); + compat_props_add(mc->compat_props, hw_compat_11_1, hw_compat_11_1_len); +} + +DEFINE_SPAPR_MACHINE(11, 1); /* * pseries-11.0
diff --git a/hw/s390x/ipl.h b/hw/s390x/ipl.h index fac3076..ef9c063 100644 --- a/hw/s390x/ipl.h +++ b/hw/s390x/ipl.h
@@ -124,6 +124,12 @@ return false; } + if (offsetof(IplParameterBlock, pv.components) + + ipib_pv->num_comp * sizeof(IPLBlockPVComp) > + be32_to_cpu(iplb->len)) { + return false; + } + for (i = 0; i < ipib_pv->num_comp; i++) { /* Addr must be 4k aligned */ if (ipib_pv->components[i].addr & ~TARGET_PAGE_MASK) {
diff --git a/hw/s390x/s390-virtio-ccw.c b/hw/s390x/s390-virtio-ccw.c index 25a9fa4..55131c1 100644 --- a/hw/s390x/s390-virtio-ccw.c +++ b/hw/s390x/s390-virtio-ccw.c
@@ -925,14 +925,26 @@ DEFINE_CCW_MACHINE_IMPL(false, major, minor) +static void ccw_machine_11_2_instance_options(MachineState *machine) +{ +} + +static void ccw_machine_11_2_class_options(MachineClass *mc) +{ +} +DEFINE_CCW_MACHINE_AS_LATEST(11, 2); + static void ccw_machine_11_1_instance_options(MachineState *machine) { + ccw_machine_11_2_instance_options(machine); } static void ccw_machine_11_1_class_options(MachineClass *mc) { + ccw_machine_11_2_class_options(mc); + compat_props_add(mc->compat_props, hw_compat_11_1, hw_compat_11_1_len); } -DEFINE_CCW_MACHINE_AS_LATEST(11, 1); +DEFINE_CCW_MACHINE(11, 1); static void ccw_machine_11_0_instance_options(MachineState *machine) {
diff --git a/include/hw/core/boards.h b/include/hw/core/boards.h index 29c6893..a436d48 100644 --- a/include/hw/core/boards.h +++ b/include/hw/core/boards.h
@@ -815,6 +815,9 @@ } } +extern GlobalProperty hw_compat_11_1[]; +extern const size_t hw_compat_11_1_len; + extern GlobalProperty hw_compat_11_0[]; extern const size_t hw_compat_11_0_len;
diff --git a/include/hw/i386/pc.h b/include/hw/i386/pc.h index d4b6d3e..ac03da9 100644 --- a/include/hw/i386/pc.h +++ b/include/hw/i386/pc.h
@@ -209,6 +209,9 @@ /* sgx.c */ void pc_machine_init_sgx_epc(PCMachineState *pcms); +extern GlobalProperty pc_compat_11_1[]; +extern const size_t pc_compat_11_1_len; + extern GlobalProperty pc_compat_11_0[]; extern const size_t pc_compat_11_0_len;
diff --git a/include/hw/s390x/ipl/qipl.h b/include/hw/s390x/ipl/qipl.h index 8d3c83a..b390f2f 100644 --- a/include/hw/s390x/ipl/qipl.h +++ b/include/hw/s390x/ipl/qipl.h
@@ -20,6 +20,8 @@ #define LOADPARM_LEN 8 #define NO_LOADPARM "\0\0\0\0\0\0\0\0" +#define MAX_BOOT_ENTRIES 32 + enum S390IplType { S390_IPL_TYPE_FCP = 0x00, S390_IPL_TYPE_CCW = 0x02,
diff --git a/pc-bios/s390-ccw.img b/pc-bios/s390-ccw.img index e24a93e1..291cd77 100644 --- a/pc-bios/s390-ccw.img +++ b/pc-bios/s390-ccw.img Binary files differ
diff --git a/pc-bios/s390-ccw/bootmap.c b/pc-bios/s390-ccw/bootmap.c index 420ee32..8151226 100644 --- a/pc-bios/s390-ccw/bootmap.c +++ b/pc-bios/s390-ccw/bootmap.c
@@ -61,6 +61,7 @@ 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 void *s2_end = _s2 + sizeof(_s2); static inline int verify_boot_info(BootInfo *bip) { @@ -308,7 +309,8 @@ } } - return menu_get_zipl_boot_index(s2_cur_blk + banner_offset); + return menu_get_zipl_boot_index(s2_cur_blk + banner_offset, + s2_end); } prev_block_nr = cur_block_nr; @@ -902,7 +904,7 @@ if (dir_rem[level] == 0) { /* Nothing remaining */ level--; - if (virtio_read(sec_loc[level], temp)) { + if (level >= 0 && virtio_read(sec_loc[level], temp)) { puts("Failed to read ISO directory"); return -EIO; }
diff --git a/pc-bios/s390-ccw/helper.h b/pc-bios/s390-ccw/helper.h index 8e3dfcb..d9b7da4 100644 --- a/pc-bios/s390-ccw/helper.h +++ b/pc-bios/s390-ccw/helper.h
@@ -45,4 +45,14 @@ } } +static inline size_t strnlen(const char *s, size_t maxlen) +{ + size_t len = 0; + + while (len < maxlen && s[len]) { + len++; + } + return len; +} + #endif
diff --git a/pc-bios/s390-ccw/menu.c b/pc-bios/s390-ccw/menu.c index eeaff78..9b81154 100644 --- a/pc-bios/s390-ccw/menu.c +++ b/pc-bios/s390-ccw/menu.c
@@ -16,6 +16,7 @@ #include "s390-ccw.h" #include "sclp.h" #include "s390-time.h" +#include "helper.h" #define KEYCODE_NO_INP '\0' #define KEYCODE_ESCAPE '\033' @@ -26,6 +27,9 @@ #define ZIPL_TIMEOUT_OFFSET 138 #define ZIPL_FLAG_OFFSET 140 +/* Max printable chars for a zipl boot menu entry */ +#define ZIPL_ENTRY_MAX 80 + #define TOD_CLOCK_MILLISECOND 0x3e8000 #define LOW_CORE_EXTERNAL_INT_ADDR 0x86 @@ -176,21 +180,31 @@ return boot_index; } -/* Returns the entry number that was printed */ +/* Returns the entry number that was printed, or -1 on invalid entry */ static int zipl_print_entry(const char *data, size_t len) { - char buf[len + 2]; + char buf[ZIPL_ENTRY_MAX + 2]; + const char *p; + + if (len > ZIPL_ENTRY_MAX) { + len = ZIPL_ENTRY_MAX; + } ebcdic_to_ascii(data, buf, len); buf[len] = '\n'; buf[len + 1] = '\0'; + p = (buf[0] == ' ') ? buf + 1 : buf; + if (!isdigit((unsigned char)*p)) { + return -1; + } + printf("%s", buf); - return buf[0] == ' ' ? atoi(buf + 1) : atoi(buf); + return atoi(p); } -int menu_get_zipl_boot_index(const char *menu_data) +int menu_get_zipl_boot_index(const char *menu_data, const char *menu_data_end) { size_t len; int entry; @@ -206,16 +220,28 @@ timeout = zipl_timeout * 1000; } - /* Print banner */ + if (menu_data >= menu_data_end) { + return 0; /* Boot default */ + } + + /* Skip banner */ + len = strnlen(menu_data, menu_data_end - menu_data); + menu_data += len + 1; + if (menu_data >= menu_data_end || !(*menu_data)) { + return 0; /* No entries, boot default */ + } + puts("s390-ccw zIPL Boot Menu\n"); - menu_data += strlen(menu_data) + 1; /* Print entries */ - while (*menu_data) { - len = strlen(menu_data); + while (menu_data < menu_data_end && *menu_data) { + len = strnlen(menu_data, menu_data_end - menu_data); entry = zipl_print_entry(menu_data, len); menu_data += len + 1; + if (entry < 0 || entry >= MAX_BOOT_ENTRIES) { + continue; + } valid_entries[entry] = true; if (entry == 0) {
diff --git a/pc-bios/s390-ccw/netmain.c b/pc-bios/s390-ccw/netmain.c index 651cedf..791854f 100644 --- a/pc-bios/s390-ccw/netmain.c +++ b/pc-bios/s390-ccw/netmain.c
@@ -40,6 +40,9 @@ #define DEFAULT_BOOT_RETRIES 10 #define DEFAULT_TFTP_RETRIES 20 +/* Index 0 is reserved for default alias, start PXE cfg indices at 1 */ +#define PXECFG_MAX (MAX_BOOT_ENTRIES - 1) + extern char _start[]; #define KERNEL_ADDR ((void *)0L) @@ -381,13 +384,13 @@ static int net_try_pxelinux_cfg(filename_ip_t *fn_ip) { - struct pl_cfg_entry entries[MAX_BOOT_ENTRIES]; + struct pl_cfg_entry entries[PXECFG_MAX]; int num_ent, def_ent = 0; num_ent = pxelinux_load_parse_cfg(fn_ip, mac, get_uuid(), DEFAULT_TFTP_RETRIES, cfgbuf, sizeof(cfgbuf), - entries, MAX_BOOT_ENTRIES, &def_ent); + entries, PXECFG_MAX, &def_ent); return net_select_and_load_kernel(fn_ip, num_ent, def_ent, entries); } @@ -470,11 +473,11 @@ * a magic comment string. */ if (!strncasecmp("# pxelinux", cfgbuf, 10)) { - struct pl_cfg_entry entries[MAX_BOOT_ENTRIES]; + struct pl_cfg_entry entries[PXECFG_MAX]; int num_ent, def_ent = 0; num_ent = pxelinux_parse_cfg(cfgbuf, sizeof(cfgbuf), entries, - MAX_BOOT_ENTRIES, &def_ent); + PXECFG_MAX, &def_ent); return net_select_and_load_kernel(fn_ip, num_ent, def_ent, entries); }
diff --git a/pc-bios/s390-ccw/s390-ccw.h b/pc-bios/s390-ccw/s390-ccw.h index 1e1f717..25aac91 100644 --- a/pc-bios/s390-ccw/s390-ccw.h +++ b/pc-bios/s390-ccw/s390-ccw.h
@@ -76,14 +76,12 @@ /* 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); +int menu_get_zipl_boot_index(const char *menu_data, const char *menu_data_end); bool menu_is_enabled_zipl(void); int menu_get_enum_boot_index(bool *valid_entries); bool menu_is_enabled_enum(void); int menu_get_boot_index(bool *valid_entries); -#define MAX_BOOT_ENTRIES 31 - __attribute__ ((__noreturn__)) static inline void panic(const char *string) {
diff --git a/target/s390x/diag.c b/target/s390x/diag.c index 80f0958..46d191b 100644 --- a/target/s390x/diag.c +++ b/target/s390x/diag.c
@@ -185,7 +185,7 @@ return false; } - if (kvm_enabled() && kvm_s390_get_hpage_1m()) { + if (kvm_enabled() && kvm_s390_get_hpage()) { error_report("Protected VMs can currently not be backed with " "huge pages"); env->regs[r1 + 1] = DIAG_308_RC_INVAL_FOR_PV;
diff --git a/target/s390x/kvm/kvm.c b/target/s390x/kvm/kvm.c index 72031a5..803b878 100644 --- a/target/s390x/kvm/kvm.c +++ b/target/s390x/kvm/kvm.c
@@ -145,7 +145,7 @@ static int cap_mem_op_extension; static int cap_s390_irq; static int cap_ri; -static int cap_hpage_1m; +static int cap_hpage; static int cap_vcpu_resets; static int cap_protected; static int cap_zpci_op; @@ -231,7 +231,7 @@ .attr = KVM_S390_VM_MEM_ENABLE_CMMA, }; - if (cap_hpage_1m) { + if (cap_hpage) { warn_report("CMM will not be enabled because it is not " "compatible with huge memory backings."); return; @@ -292,30 +292,28 @@ } } -void kvm_s390_set_max_pagesize(uint64_t pagesize, Error **errp) +static bool kvm_s390_pgsize_cap(uint32_t capa, const char *s, Error **errp) { - if (pagesize == 4 * KiB) { - return; + if (kvm_vm_enable_cap(kvm_state, capa, 0)) { + error_setg(errp, "Memory backing with %s pages was specified, " + "but KVM does not support this memory backing", s); + return false; } - - if (pagesize != 1 * MiB) { - error_setg(errp, "Memory backing with 2G pages was specified, " - "but KVM does not support this memory backing"); - return; - } - - if (kvm_vm_enable_cap(kvm_state, KVM_CAP_S390_HPAGE_1M, 0)) { - error_setg(errp, "Memory backing with 1M pages was specified, " - "but KVM does not support this memory backing"); - return; - } - - cap_hpage_1m = 1; + return true; } -int kvm_s390_get_hpage_1m(void) +void kvm_s390_set_max_pagesize(uint64_t pagesize, Error **errp) { - return cap_hpage_1m; + if (pagesize == MiB) { + cap_hpage = kvm_s390_pgsize_cap(KVM_CAP_S390_HPAGE_1M, "1M", errp); + } else if (pagesize != 4 * KiB) { + cap_hpage = 2 * kvm_s390_pgsize_cap(KVM_CAP_S390_HPAGE_2G, "2G", errp); + } +} + +int kvm_s390_get_hpage(void) +{ + return cap_hpage; } static void ccw_machine_class_foreach(ObjectClass *oc, void *opaque)
diff --git a/target/s390x/kvm/kvm_s390x.h b/target/s390x/kvm/kvm_s390x.h index 7b1cce3..3c4fa04 100644 --- a/target/s390x/kvm/kvm_s390x.h +++ b/target/s390x/kvm/kvm_s390x.h
@@ -25,7 +25,7 @@ int kvm_s390_set_cpu_state(S390CPU *cpu, uint8_t cpu_state); void kvm_s390_vcpu_interrupt_pre_save(S390CPU *cpu); int kvm_s390_vcpu_interrupt_post_load(S390CPU *cpu); -int kvm_s390_get_hpage_1m(void); +int kvm_s390_get_hpage(void); int kvm_s390_get_protected_dump(void); int kvm_s390_get_ri(void); int kvm_s390_get_zpci_op(void);
diff --git a/target/s390x/kvm/stubs.c b/target/s390x/kvm/stubs.c index 196127b..ebf3c83 100644 --- a/target/s390x/kvm/stubs.c +++ b/target/s390x/kvm/stubs.c
@@ -143,7 +143,7 @@ g_assert_not_reached(); } -int kvm_s390_get_hpage_1m(void) +int kvm_s390_get_hpage(void) { g_assert_not_reached(); }
diff --git a/target/s390x/tcg/crypto_helper.c b/target/s390x/tcg/crypto_helper.c index 8fe0a22..6a5dbe1 100644 --- a/target/s390x/tcg/crypto_helper.c +++ b/target/s390x/tcg/crypto_helper.c
@@ -16,6 +16,7 @@ #include "qemu/guest-random.h" #include "s390x-internal.h" #include "tcg_s390x.h" +#include "exec/cpu-common.h" #include "exec/helper-proto.h" #include "accel/tcg/cpu-ldst-common.h" #include "accel/tcg/cpu-mmu-index.h" @@ -242,8 +243,8 @@ return !len ? 0 : 3; } -static void fill_buf_random(CPUS390XState *env, const int mmu_idx, uintptr_t ra, - uint64_t *buf_reg, uint64_t *len_reg) +static int fill_buf_random(CPUS390XState *env, const int mmu_idx, uintptr_t ra, + uint64_t *buf_reg, uint64_t *len_reg) { const MemOpIdx oi = make_memop_idx(MO_8, mmu_idx); uint8_t tmp[256]; @@ -265,7 +266,13 @@ --*len_reg; } len -= block; + + if (cpu_loop_exit_requested(env_cpu(env))) { + break; + } } + + return len == 0 ? 0 : 3; } uint32_t HELPER(msa)(CPUS390XState *env, uint32_t r1, uint32_t r2, uint32_t r3, @@ -278,6 +285,7 @@ uint8_t subfunc[16] = { 0 }; uint64_t param_addr; MemOpIdx oi; + int cc; switch (type) { case S390_FEAT_TYPE_KMAC: @@ -308,9 +316,13 @@ return cpacf_sha512(env, mmu_idx, ra, env->regs[1], &env->regs[r2], &env->regs[r2 + 1], type); case 114: /* CPACF_PRNO_TRNG */ - fill_buf_random(env, mmu_idx, ra, &env->regs[r1], &env->regs[r1 + 1]); - fill_buf_random(env, mmu_idx, ra, &env->regs[r2], &env->regs[r2 + 1]); - break; + cc = fill_buf_random(env, mmu_idx, ra, + &env->regs[r1], &env->regs[r1 + 1]); + if (cc == 0) { + cc = fill_buf_random(env, mmu_idx, ra, + &env->regs[r2], &env->regs[r2 + 1]); + } + return cc; default: /* we don't implement any other subfunction yet */ g_assert_not_reached();
diff --git a/target/s390x/tcg/insn-data.h.inc b/target/s390x/tcg/insn-data.h.inc index 0d5392e..1ea7224 100644 --- a/target/s390x/tcg/insn-data.h.inc +++ b/target/s390x/tcg/insn-data.h.inc
@@ -887,8 +887,8 @@ C(0xe32f, STRVG, RXY_a, Z, la2, r1_o, new, m1_64, rev64, 0) /* STORE CLOCK */ - F(0xb205, STCK, S, Z, la2, 0, new, m1_64, stck, 0, IF_IO) - F(0xb27c, STCKF, S, SCF, la2, 0, new, m1_64, stck, 0, IF_IO) + F(0xb205, STCK, S, Z, la2, 0, new, 0, stck, 0, IF_IO) + F(0xb27c, STCKF, S, SCF, la2, 0, new, 0, stck, 0, IF_IO) /* STORE CLOCK EXTENDED */ F(0xb278, STCKE, S, Z, 0, a2, 0, 0, stcke, 0, IF_IO)
diff --git a/target/s390x/tcg/int_helper.c b/target/s390x/tcg/int_helper.c index fbda396..5aedd14 100644 --- a/target/s390x/tcg/int_helper.c +++ b/target/s390x/tcg/int_helper.c
@@ -39,7 +39,8 @@ int32_t b = b64; int64_t q, r; - if (b == 0) { + /* Catch divide by zero, and non-representable quotient (MIN / -1). */ + if (b == 0 || (b == -1 && a == (1ll << 63))) { tcg_s390_program_interrupt(env, PGM_FIXPT_DIVIDE, GETPC()); }
diff --git a/target/s390x/tcg/translate.c b/target/s390x/tcg/translate.c index 82165ac..1b60231 100644 --- a/target/s390x/tcg/translate.c +++ b/target/s390x/tcg/translate.c
@@ -4108,7 +4108,9 @@ static DisasJumpType op_stck(DisasContext *s, DisasOps *o) { gen_helper_stck(o->out, tcg_env); + tcg_gen_qemu_st_i64(o->out, o->addr1, get_mem_index(s), MO_BEUQ); /* ??? We don't implement clock states. */ + /* Set the CC after the store; a suppressed store must preserve it. */ gen_op_movi_cc(s, 0); return DISAS_NEXT; }
diff --git a/tests/tcg/s390x/Makefile.target b/tests/tcg/s390x/Makefile.target index 0ca030d..97c0f02 100644 --- a/tests/tcg/s390x/Makefile.target +++ b/tests/tcg/s390x/Makefile.target
@@ -50,6 +50,7 @@ TESTS+=ts TESTS+=ex-smc TESTS+=divide-to-integer +TESTS+=stckf cdsg: CFLAGS+=-pthread cdsg: LDFLAGS+=-pthread @@ -75,6 +76,7 @@ Z13_TESTS+=vstl Z13_TESTS+=vrep Z13_TESTS+=precise-smc-user +Z13_TESTS+=prno-trng $(Z13_TESTS): CFLAGS+=-march=z13 -O2 TESTS+=$(Z13_TESTS)
diff --git a/tests/tcg/s390x/div.c b/tests/tcg/s390x/div.c index 6ad9900..124c9ec 100644 --- a/tests/tcg/s390x/div.c +++ b/tests/tcg/s390x/div.c
@@ -1,6 +1,15 @@ #include <assert.h> +#include <signal.h> #include <stdint.h> +/* Set asynchronously by the signal handler. */ +static volatile int signum; + +static void signal_handler(int n) +{ + signum = n; +} + static void test_dr(void) { register int32_t r0 asm("r0") = -1; @@ -65,11 +74,39 @@ assert(r == 1); } +/* + * The most negative dividend divided by -1 yields a quotient that does not + * fit into 32 bits, so DR must raise a fixed-point-divide exception. + */ +static void test_dr_overflow(void) +{ + struct sigaction act = { .sa_handler = signal_handler }; + register int32_t r0 asm("r0"); + register int32_t r1 asm("r1"); + int32_t b = -1; + int err; + + err = sigaction(SIGFPE, &act, NULL); + assert(err == 0); + signum = -1; + + r0 = 0x80000000; + r1 = 0; + asm volatile("dr %[r0],%[b]" + : [r0] "+r" (r0), [r1] "+r" (r1) + : [b] "r" (b) + : "cc"); + assert(signum == SIGFPE); + + signal(SIGFPE, SIG_DFL); +} + int main(void) { test_dr(); test_dlr(); test_dsgr(); test_dlgr(); + test_dr_overflow(); return 0; }
diff --git a/tests/tcg/s390x/prno-trng.c b/tests/tcg/s390x/prno-trng.c new file mode 100644 index 0000000..43eea5d --- /dev/null +++ b/tests/tcg/s390x/prno-trng.c
@@ -0,0 +1,67 @@ +/* + * Test that PERFORM RANDOM NUMBER OPERATION TRNG is interruptible. + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ +#include <assert.h> +#include <signal.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/time.h> +#include <asm/ucontext.h> + +static unsigned char buf1[16 * 1024 * 1024]; +static unsigned char buf2[16 * 1024 * 1024]; + +static volatile sig_atomic_t interrupted; + +static void sigprof_handler(int sig, siginfo_t *info, void *ucontext) +{ + struct ucontext *uc = ucontext; + unsigned long addr = uc->uc_mcontext.regs.psw.addr; + + if (*(unsigned short *)(addr - 4) == 0xb93c) { + interrupted++; + } +} + +static void prno_trng(void *b1, unsigned long l1, void *b2, unsigned long l2) +{ + register unsigned long r0 asm("r0") = 114; /* TRNG */ + register unsigned long r2 asm("r2") = (unsigned long)b1; + register unsigned long r3 asm("r3") = l1; + register unsigned long r4 asm("r4") = (unsigned long)b2; + register unsigned long r5 asm("r5") = l2; + + asm volatile("0: ppno %[r2],%[r4]\n" /* prno alias for old toolchains */ + " jo 0b" + : [r2] "+r" (r2), [r3] "+r" (r3) + , [r4] "+r" (r4), [r5] "+r" (r5) + : "r" (r0) + : "cc", "memory"); +} + +int main(void) +{ + struct itimerval it = { + .it_interval = { .tv_usec = 10000 }, /* 0.01s */ + .it_value = { .tv_usec = 10000 }, + }; + struct sigaction act = { + .sa_sigaction = sigprof_handler, + .sa_flags = SA_SIGINFO, + }; + int err; + + err = sigaction(SIGPROF, &act, NULL); + assert(err == 0); + err = setitimer(ITIMER_PROF, &it, NULL); + assert(err == 0); + + prno_trng(buf1, sizeof(buf1), buf2, sizeof(buf2)); + printf("interrupted %d times\n", interrupted); + assert(interrupted >= 3); + + return EXIT_SUCCESS; +}
diff --git a/tests/tcg/s390x/stckf.c b/tests/tcg/s390x/stckf.c new file mode 100644 index 0000000..51c8c9d --- /dev/null +++ b/tests/tcg/s390x/stckf.c
@@ -0,0 +1,44 @@ +/* + * Test that a faulting STORE CLOCK FAST does not clobber the condition code. + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ +#include <assert.h> +#include <signal.h> +#include <stdlib.h> +#include <unistd.h> + +static void handle_sigsegv(int sig, siginfo_t *info, void *ucontext) +{ + mcontext_t *mcontext = &((ucontext_t *)ucontext)->uc_mcontext; + + /* The condition code must be the one set by SLGR, not garbage. */ + _exit(((mcontext->psw.mask >> 44) & 3) == 3 ? EXIT_SUCCESS : EXIT_FAILURE); +} + +int main(void) +{ + struct sigaction act = { + .sa_sigaction = handle_sigsegv, + .sa_flags = SA_SIGINFO, + }; + int err; + + err = sigaction(SIGSEGV, &act, NULL); + assert(err == 0); + + asm volatile( + "lghi %%r1,100\n" + "lghi %%r2,0\n" + "clgr %%r1,%%r2\n" /* CC_OP_LTUGTU_64 */ + /* cc_src=100 is not valid for CC_OP_SUBU */ + "ipm %%r0\n" /* force cc_src to env */ + "lghi %%r3,5\n" + "lghi %%r4,3\n" + "slgr %%r3,%%r4\n" /* CC_OP_SUBU, cc=3 */ + "lghi %%r5,0\n" + "stckf 0(%%r5)\n" /* faults; cc must stay 3 */ + : : : "r0", "r1", "r2", "r3", "r4", "r5", "cc", "memory"); + + return EXIT_FAILURE; +}