diff options
94 files changed, 2581 insertions, 1472 deletions
diff --git a/.gitlab-ci.d/buildtest.yml b/.gitlab-ci.d/buildtest.yml index 01e8470a69..f0cbdf1992 100644 --- a/.gitlab-ci.d/buildtest.yml +++ b/.gitlab-ci.d/buildtest.yml @@ -115,7 +115,7 @@ build-system-fedora: job: amd64-fedora-container variables: IMAGE: fedora - CONFIGURE_ARGS: --disable-gcrypt --enable-nettle --enable-docs + CONFIGURE_ARGS: --disable-gcrypt --enable-nettle --enable-docs --enable-crypto-afalg TARGETS: microblaze-softmmu mips-softmmu xtensa-softmmu m68k-softmmu riscv32-softmmu ppc-softmmu sparc64-softmmu MAKE_CHECK_ARGS: check-build diff --git a/block/block-backend.c b/block/block-backend.c index 7bea43bf72..85bcdedcef 100644 --- a/block/block-backend.c +++ b/block/block-backend.c @@ -1019,16 +1019,14 @@ DeviceState *blk_get_attached_dev(BlockBackend *blk) return blk->dev; } -/* Return the qdev ID, or if no ID is assigned the QOM path, of the block - * device attached to the BlockBackend. */ -char *blk_get_attached_dev_id(BlockBackend *blk) +static char *blk_get_attached_dev_id_or_path(BlockBackend *blk, bool want_id) { DeviceState *dev = blk->dev; IO_CODE(); if (!dev) { return g_strdup(""); - } else if (dev->id) { + } else if (want_id && dev->id) { return g_strdup(dev->id); } @@ -1036,6 +1034,20 @@ char *blk_get_attached_dev_id(BlockBackend *blk) } /* + * Return the qdev ID, or if no ID is assigned the QOM path, of the block + * device attached to the BlockBackend. + */ +char *blk_get_attached_dev_id(BlockBackend *blk) +{ + return blk_get_attached_dev_id_or_path(blk, true); +} + +static char *blk_get_attached_dev_path(BlockBackend *blk) +{ + return blk_get_attached_dev_id_or_path(blk, false); +} + +/* * Return the BlockBackend which has the device model @dev attached if it * exists, else null. * @@ -2125,6 +2137,7 @@ static void send_qmp_error_event(BlockBackend *blk, optype = is_read ? IO_OPERATION_TYPE_READ : IO_OPERATION_TYPE_WRITE; qapi_event_send_block_io_error(blk_name(blk), + blk_get_attached_dev_path(blk), bs ? bdrv_get_node_name(bs) : NULL, optype, action, blk_iostatus_is_enabled(blk), error == ENOSPC, strerror(error)); diff --git a/block/gluster.c b/block/gluster.c index f03d05251e..e9c038042b 100644 --- a/block/gluster.c +++ b/block/gluster.c @@ -514,7 +514,6 @@ static int qemu_gluster_parse_json(BlockdevOptionsGluster *gconf, SocketAddressList **tail; QDict *backing_options = NULL; Error *local_err = NULL; - char *str = NULL; const char *ptr; int i, type, num_servers; @@ -547,7 +546,8 @@ static int qemu_gluster_parse_json(BlockdevOptionsGluster *gconf, tail = &gconf->server; for (i = 0; i < num_servers; i++) { - str = g_strdup_printf(GLUSTER_OPT_SERVER_PATTERN"%d.", i); + g_autofree char *str = g_strdup_printf(GLUSTER_OPT_SERVER_PATTERN"%d.", + i); qdict_extract_subqdict(options, &backing_options, str); /* create opts info from runtime_type_opts list */ @@ -658,8 +658,6 @@ static int qemu_gluster_parse_json(BlockdevOptionsGluster *gconf, qobject_unref(backing_options); backing_options = NULL; - g_free(str); - str = NULL; } return 0; @@ -668,7 +666,6 @@ out: error_propagate(errp, local_err); qapi_free_SocketAddress(gsconf); qemu_opts_del(opts); - g_free(str); qobject_unref(backing_options); errno = EINVAL; return -errno; diff --git a/block/raw-format.c b/block/raw-format.c index ac7e8495f6..e08526e2ec 100644 --- a/block/raw-format.c +++ b/block/raw-format.c @@ -111,7 +111,7 @@ raw_apply_options(BlockDriverState *bs, BDRVRawState *s, uint64_t offset, if (offset > real_size) { error_setg(errp, "Offset (%" PRIu64 ") cannot be greater than " "size of the containing file (%" PRId64 ")", - s->offset, real_size); + offset, real_size); return -EINVAL; } @@ -119,7 +119,7 @@ raw_apply_options(BlockDriverState *bs, BDRVRawState *s, uint64_t offset, error_setg(errp, "The sum of offset (%" PRIu64 ") and size " "(%" PRIu64 ") has to be smaller or equal to the " " actual size of the containing file (%" PRId64 ")", - s->offset, s->size, real_size); + offset, size, real_size); return -EINVAL; } diff --git a/block/ssh.c b/block/ssh.c index 871e1d4753..9f8140bcb6 100644 --- a/block/ssh.c +++ b/block/ssh.c @@ -364,7 +364,7 @@ static unsigned hex2decimal(char ch) return 10 + (ch - 'A'); } - return -1; + return UINT_MAX; } /* Compare the binary fingerprint (hash of host key) with the @@ -376,13 +376,15 @@ static int compare_fingerprint(const unsigned char *fingerprint, size_t len, unsigned c; while (len > 0) { + unsigned c0, c1; while (*host_key_check == ':') host_key_check++; - if (!qemu_isxdigit(host_key_check[0]) || - !qemu_isxdigit(host_key_check[1])) + c0 = hex2decimal(host_key_check[0]); + c1 = hex2decimal(host_key_check[1]); + if (c0 > 0xf || c1 > 0xf) { return 1; - c = hex2decimal(host_key_check[0]) * 16 + - hex2decimal(host_key_check[1]); + } + c = c0 * 16 + c1; if (c - *fingerprint != 0) return c - *fingerprint; fingerprint++; diff --git a/block/vdi.c b/block/vdi.c index 149e15c831..26f7638f1f 100644 --- a/block/vdi.c +++ b/block/vdi.c @@ -87,7 +87,7 @@ /* Command line option for static images. */ #define BLOCK_OPT_STATIC "static" -#define SECTOR_SIZE 512 +#define SECTOR_SIZE 512ULL #define DEFAULT_CLUSTER_SIZE 1048576 /* Note: can't use 1 * MiB, because it's passed to stringify() */ @@ -442,7 +442,7 @@ static int vdi_open(BlockDriverState *bs, QDict *options, int flags, goto fail; } else if (header.sector_size != SECTOR_SIZE) { error_setg(errp, "unsupported VDI image (sector size %" PRIu32 - " is not %u)", header.sector_size, SECTOR_SIZE); + " is not %llu)", header.sector_size, SECTOR_SIZE); ret = -ENOTSUP; goto fail; } else if (header.block_size != DEFAULT_CLUSTER_SIZE) { @@ -2062,7 +2062,8 @@ chmod +x config.status rm -r "$TMPDIR1" if test "$rust" != disabled; then - echo '\nINFO: Rust bindings generation with `bindgen` might fail in some cases where' + echo + echo 'INFO: Rust bindings generation with `bindgen` might fail in some cases where' echo 'the detected `libclang` does not match the expected `clang` version/target. In' echo 'this case you must pass the path to `clang` and `libclang` to your build' echo 'command invocation using the environment variables CLANG_PATH and LIBCLANG_PATH' diff --git a/crypto/der.c b/crypto/der.c index ebbecfc3fe..81367524c3 100644 --- a/crypto/der.c +++ b/crypto/der.c @@ -408,19 +408,6 @@ void qcrypto_der_encode_octet_str(QCryptoEncodeContext *ctx, qcrypto_der_encode_prim(ctx, tag, src, src_len); } -void qcrypto_der_encode_octet_str_begin(QCryptoEncodeContext *ctx) -{ - uint8_t tag = QCRYPTO_DER_TAG(QCRYPTO_DER_TAG_CLASS_UNIV, - QCRYPTO_DER_TAG_ENC_PRIM, - QCRYPTO_DER_TYPE_TAG_OCT_STR); - qcrypto_der_encode_cons_begin(ctx, tag); -} - -void qcrypto_der_encode_octet_str_end(QCryptoEncodeContext *ctx) -{ - qcrypto_der_encode_cons_end(ctx); -} - size_t qcrypto_der_encode_ctx_buffer_len(QCryptoEncodeContext *ctx) { return ctx->root.dlen; diff --git a/crypto/der.h b/crypto/der.h index f4ba6da28a..bcfa4a2495 100644 --- a/crypto/der.h +++ b/crypto/der.h @@ -243,28 +243,6 @@ void qcrypto_der_encode_octet_str(QCryptoEncodeContext *ctx, const uint8_t *src, size_t src_len); /** - * qcrypto_der_encode_octet_str_begin: - * @ctx: the encode context. - * - * Start encoding a octet string, All fields between - * qcrypto_der_encode_octet_str_begin and qcrypto_der_encode_octet_str_end - * are encoded as an octet string. This is useful when we need to encode a - * encoded SEQUENCE as OCTET STRING. - */ -void qcrypto_der_encode_octet_str_begin(QCryptoEncodeContext *ctx); - -/** - * qcrypto_der_encode_octet_str_end: - * @ctx: the encode context. - * - * Finish encoding a octet string, All fields between - * qcrypto_der_encode_octet_str_begin and qcrypto_der_encode_octet_str_end - * are encoded as an octet string. This is useful when we need to encode a - * encoded SEQUENCE as OCTET STRING. - */ -void qcrypto_der_encode_octet_str_end(QCryptoEncodeContext *ctx); - -/** * qcrypto_der_encode_ctx_buffer_len: * @ctx: the encode context. * diff --git a/crypto/hash-afalg.c b/crypto/hash-afalg.c index 06e1e4699c..8c0ce5b520 100644 --- a/crypto/hash-afalg.c +++ b/crypto/hash-afalg.c @@ -142,7 +142,7 @@ QCryptoHash *qcrypto_afalg_hash_new(QCryptoHashAlgo alg, Error **errp) static void qcrypto_afalg_hash_free(QCryptoHash *hash) { - QCryptoAFAlg *ctx = hash->opaque; + QCryptoAFAlgo *ctx = hash->opaque; if (ctx) { qcrypto_afalg_comm_free(ctx); @@ -159,7 +159,7 @@ void qcrypto_afalg_hash_free(QCryptoHash *hash) * be provided to calculate the final hash. */ static -int qcrypto_afalg_send_to_kernel(QCryptoAFAlg *afalg, +int qcrypto_afalg_send_to_kernel(QCryptoAFAlgo *afalg, const struct iovec *iov, size_t niov, bool more_data, @@ -183,7 +183,7 @@ int qcrypto_afalg_send_to_kernel(QCryptoAFAlg *afalg, } static -int qcrypto_afalg_recv_from_kernel(QCryptoAFAlg *afalg, +int qcrypto_afalg_recv_from_kernel(QCryptoAFAlgo *afalg, QCryptoHashAlgo alg, uint8_t **result, size_t *result_len, @@ -222,7 +222,7 @@ int qcrypto_afalg_hash_update(QCryptoHash *hash, size_t niov, Error **errp) { - return qcrypto_afalg_send_to_kernel((QCryptoAFAlg *) hash->opaque, + return qcrypto_afalg_send_to_kernel((QCryptoAFAlgo *) hash->opaque, iov, niov, true, errp); } @@ -232,7 +232,7 @@ int qcrypto_afalg_hash_finalize(QCryptoHash *hash, size_t *result_len, Error **errp) { - return qcrypto_afalg_recv_from_kernel((QCryptoAFAlg *) hash->opaque, + return qcrypto_afalg_recv_from_kernel((QCryptoAFAlgo *) hash->opaque, hash->alg, result, result_len, errp); } diff --git a/crypto/hash-gcrypt.c b/crypto/hash-gcrypt.c index ccc3cce3f8..73533a4949 100644 --- a/crypto/hash-gcrypt.c +++ b/crypto/hash-gcrypt.c @@ -103,16 +103,25 @@ int qcrypto_gcrypt_hash_finalize(QCryptoHash *hash, size_t *result_len, Error **errp) { + int ret; unsigned char *digest; gcry_md_hd_t *ctx = hash->opaque; - *result_len = gcry_md_get_algo_dlen(qcrypto_hash_alg_map[hash->alg]); - if (*result_len == 0) { + ret = gcry_md_get_algo_dlen(qcrypto_hash_alg_map[hash->alg]); + if (ret == 0) { error_setg(errp, "Unable to get hash length"); return -1; } - *result = g_new(uint8_t, *result_len); + if (*result_len == 0) { + *result_len = ret; + *result = g_new(uint8_t, *result_len); + } else if (*result_len != ret) { + error_setg(errp, + "Result buffer size %zu is smaller than hash %d", + *result_len, ret); + return -1; + } /* Digest is freed by gcry_md_close(), copy it */ digest = gcry_md_read(*ctx, 0); diff --git a/crypto/hash-glib.c b/crypto/hash-glib.c index 02a6ec1edf..809cef98ae 100644 --- a/crypto/hash-glib.c +++ b/crypto/hash-glib.c @@ -99,8 +99,15 @@ int qcrypto_glib_hash_finalize(QCryptoHash *hash, return -1; } - *result_len = ret; - *result = g_new(uint8_t, *result_len); + if (*result_len == 0) { + *result_len = ret; + *result = g_new(uint8_t, *result_len); + } else if (*result_len != ret) { + error_setg(errp, + "Result buffer size %zu is smaller than hash %d", + *result_len, ret); + return -1; + } g_checksum_get_digest(ctx, *result, result_len); return 0; diff --git a/crypto/hash-gnutls.c b/crypto/hash-gnutls.c index 34a63994c9..99fbe824ea 100644 --- a/crypto/hash-gnutls.c +++ b/crypto/hash-gnutls.c @@ -115,14 +115,24 @@ int qcrypto_gnutls_hash_finalize(QCryptoHash *hash, Error **errp) { gnutls_hash_hd_t *ctx = hash->opaque; + int ret; - *result_len = gnutls_hash_get_len(qcrypto_hash_alg_map[hash->alg]); - if (*result_len == 0) { + ret = gnutls_hash_get_len(qcrypto_hash_alg_map[hash->alg]); + if (ret == 0) { error_setg(errp, "Unable to get hash length"); return -1; } - *result = g_new(uint8_t, *result_len); + if (*result_len == 0) { + *result_len = ret; + *result = g_new(uint8_t, *result_len); + } else if (*result_len != ret) { + error_setg(errp, + "Result buffer size %zu is smaller than hash %d", + *result_len, ret); + return -1; + } + gnutls_hash_output(*ctx, *result); return 0; } diff --git a/crypto/hash-nettle.c b/crypto/hash-nettle.c index 3b847aa60e..c78624b347 100644 --- a/crypto/hash-nettle.c +++ b/crypto/hash-nettle.c @@ -150,9 +150,17 @@ int qcrypto_nettle_hash_finalize(QCryptoHash *hash, Error **errp) { union qcrypto_hash_ctx *ctx = hash->opaque; - - *result_len = qcrypto_hash_alg_map[hash->alg].len; - *result = g_new(uint8_t, *result_len); + int ret = qcrypto_hash_alg_map[hash->alg].len; + + if (*result_len == 0) { + *result_len = ret; + *result = g_new(uint8_t, *result_len); + } else if (*result_len != ret) { + error_setg(errp, + "Result buffer size %zu is smaller than hash %d", + *result_len, ret); + return -1; + } qcrypto_hash_alg_map[hash->alg].result(ctx, *result_len, *result); diff --git a/disas/riscv.c b/disas/riscv.c index 5965574d87..fc0331b90b 100644 --- a/disas/riscv.c +++ b/disas/riscv.c @@ -4808,7 +4808,7 @@ static void decode_inst_operands(rv_decode *dec, rv_isa isa) break; case rv_codec_vsetivli: dec->rd = operand_rd(inst); - dec->imm = operand_vimm(inst); + dec->imm = extract32(inst, 15, 5); dec->vzimm = operand_vzimm10(inst); break; case rv_codec_zcb_lb: diff --git a/disas/riscv.h b/disas/riscv.h index 16a08e4895..0d1f89ce8a 100644 --- a/disas/riscv.h +++ b/disas/riscv.h @@ -290,7 +290,7 @@ enum { #define rv_fmt_fd_vs2 "O\t3,F" #define rv_fmt_vd_vm "O\tDm" #define rv_fmt_vsetvli "O\t0,1,v" -#define rv_fmt_vsetivli "O\t0,u,v" +#define rv_fmt_vsetivli "O\t0,i,v" #define rv_fmt_rs1_rs2_zce_ldst "O\t2,i(1)" #define rv_fmt_push_rlist "O\tx,-i" #define rv_fmt_pop_rlist "O\tx,i" diff --git a/docs/system/bootindex.rst b/docs/system/bootindex.rst index 8b057f812f..988f7b3beb 100644 --- a/docs/system/bootindex.rst +++ b/docs/system/bootindex.rst @@ -49,10 +49,11 @@ Limitations ----------- Some firmware has limitations on which devices can be considered for -booting. For instance, the PC BIOS boot specification allows only one -disk to be bootable. If boot from disk fails for some reason, the BIOS +booting. For instance, the x86 PC BIOS boot specification allows only one +disk to be bootable. If boot from disk fails for some reason, the x86 BIOS won't retry booting from other disk. It can still try to boot from -floppy or net, though. +floppy or net, though. In the case of s390x BIOS, the BIOS will try up to +8 total devices, any number of which may be disks. Sometimes, firmware cannot map the device path QEMU wants firmware to boot from to a boot method. It doesn't happen for devices the firmware diff --git a/docs/system/s390x/bootdevices.rst b/docs/system/s390x/bootdevices.rst index 1a7a18b43b..1a1a764c1c 100644 --- a/docs/system/s390x/bootdevices.rst +++ b/docs/system/s390x/bootdevices.rst @@ -6,9 +6,7 @@ Booting with bootindex parameter For classical mainframe guests (i.e. LPAR or z/VM installations), you always have to explicitly specify the disk where you want to boot from (or "IPL" from, -in s390x-speak -- IPL means "Initial Program Load"). In particular, there can -also be only one boot device according to the architecture specification, thus -specifying multiple boot devices is not possible (yet). +in s390x-speak -- IPL means "Initial Program Load"). So for booting an s390x guest in QEMU, you should always mark the device where you want to boot from with the ``bootindex`` property, for @@ -17,6 +15,11 @@ example:: qemu-system-s390x -drive if=none,id=dr1,file=guest.qcow2 \ -device virtio-blk,drive=dr1,bootindex=1 +Multiple devices may have a bootindex. The lowest bootindex is assigned to the +device to IPL first. If the IPL fails for the first, the device with the second +lowest bootindex will be tried and so on until IPL is successful or there are no +remaining boot devices to try. + For booting from a CD-ROM ISO image (which needs to include El-Torito boot information in order to be bootable), it is recommended to specify a ``scsi-cd`` device, for example like this:: @@ -82,23 +85,17 @@ Note that ``0`` can be used to boot the default entry. Booting from a network device ----------------------------- -Beside the normal guest firmware (which is loaded from the file ``s390-ccw.img`` -in the data directory of QEMU, or via the ``-bios`` option), QEMU ships with -a small TFTP network bootloader firmware for virtio-net-ccw devices, too. This -firmware is loaded from a file called ``s390-netboot.img`` in the QEMU data -directory. In case you want to load it from a different filename instead, -you can specify it via the ``-global s390-ipl.netboot_fw=filename`` -command line option. - -The ``bootindex`` property is especially important for booting via the network. -If you don't specify the ``bootindex`` property here, the network bootloader -firmware code won't get loaded into the guest memory so that the network boot -will fail. For a successful network boot, try something like this:: +The firmware that ships with QEMU includes a small TFTP network bootloader +for virtio-net-ccw devices. The ``bootindex`` property is especially +important for booting via the network. If you don't specify the ``bootindex`` +property here, the network bootloader won't be taken into consideration and +the network boot will fail. For a successful network boot, try something +like this:: qemu-system-s390x -netdev user,id=n1,tftp=...,bootfile=... \ -device virtio-net-ccw,netdev=n1,bootindex=1 -The network bootloader firmware also has basic support for pxelinux.cfg-style +The network bootloader also has basic support for pxelinux.cfg-style configuration files. See the `PXELINUX Configuration page <https://wiki.syslinux.org/wiki/index.php?title=PXELINUX#Configuration>`__ for details how to set up the configuration file on your TFTP server. diff --git a/host/include/riscv/host/cpuinfo.h b/host/include/riscv/host/cpuinfo.h index 2b00660e36..cdc784e7b6 100644 --- a/host/include/riscv/host/cpuinfo.h +++ b/host/include/riscv/host/cpuinfo.h @@ -10,9 +10,11 @@ #define CPUINFO_ZBA (1u << 1) #define CPUINFO_ZBB (1u << 2) #define CPUINFO_ZICOND (1u << 3) +#define CPUINFO_ZVE64X (1u << 4) /* Initialized with a constructor. */ extern unsigned cpuinfo; +extern unsigned riscv_lg2_vlenb; /* * We cannot rely on constructor ordering, so other constructors must diff --git a/hw/arm/allwinner-a10.c b/hw/arm/allwinner-a10.c index 9eb1aa7366..52327d9210 100644 --- a/hw/arm/allwinner-a10.c +++ b/hw/arm/allwinner-a10.c @@ -17,6 +17,7 @@ #include "qemu/osdep.h" #include "qapi/error.h" +#include "qemu/error-report.h" #include "qemu/module.h" #include "hw/char/serial-mm.h" #include "hw/sysbus.h" @@ -50,9 +51,8 @@ void allwinner_a10_bootrom_setup(AwA10State *s, BlockBackend *blk) g_autofree uint8_t *buffer = g_new0(uint8_t, rom_size); if (blk_pread(blk, 8 * KiB, rom_size, buffer, 0) < 0) { - error_setg(&error_fatal, "%s: failed to read BlockBackend data", - __func__); - return; + error_report("%s: failed to read BlockBackend data", __func__); + exit(1); } rom_add_blob("allwinner-a10.bootrom", buffer, rom_size, diff --git a/hw/arm/allwinner-h3.c b/hw/arm/allwinner-h3.c index 9bc57cd266..fd7638dbe8 100644 --- a/hw/arm/allwinner-h3.c +++ b/hw/arm/allwinner-h3.c @@ -182,9 +182,8 @@ void allwinner_h3_bootrom_setup(AwH3State *s, BlockBackend *blk) g_autofree uint8_t *buffer = g_new0(uint8_t, rom_size); if (blk_pread(blk, 8 * KiB, rom_size, buffer, 0) < 0) { - error_setg(&error_fatal, "%s: failed to read BlockBackend data", - __func__); - return; + error_report("%s: failed to read BlockBackend data", __func__); + exit(1); } rom_add_blob("allwinner-h3.bootrom", buffer, rom_size, diff --git a/hw/arm/allwinner-r40.c b/hw/arm/allwinner-r40.c index ced73009d6..c6f7cab1da 100644 --- a/hw/arm/allwinner-r40.c +++ b/hw/arm/allwinner-r40.c @@ -231,9 +231,8 @@ bool allwinner_r40_bootrom_setup(AwR40State *s, BlockBackend *blk, int unit) struct boot_file_head *head = (struct boot_file_head *)buffer; if (blk_pread(blk, 8 * KiB, rom_size, buffer, 0) < 0) { - error_setg(&error_fatal, "%s: failed to read BlockBackend data", - __func__); - return false; + error_report("%s: failed to read BlockBackend data", __func__); + exit(1); } /* we only check the magic string here. */ diff --git a/hw/arm/xlnx-versal-virt.c b/hw/arm/xlnx-versal-virt.c index 962f98fee2..8b12d3e7cb 100644 --- a/hw/arm/xlnx-versal-virt.c +++ b/hw/arm/xlnx-versal-virt.c @@ -761,9 +761,9 @@ static void versal_virt_init(MachineState *machine) if (!flash_klass || object_class_is_abstract(flash_klass) || !object_class_dynamic_cast(flash_klass, TYPE_M25P80)) { - error_setg(&error_fatal, "'%s' is either abstract or" + error_report("'%s' is either abstract or" " not a subtype of m25p80", s->ospi_model); - return; + exit(1); } } diff --git a/hw/audio/soundhw.c b/hw/audio/soundhw.c index b387b0ef7d..d18fd9fa05 100644 --- a/hw/audio/soundhw.c +++ b/hw/audio/soundhw.c @@ -88,7 +88,8 @@ void select_soundhw(const char *name, const char *audiodev) struct soundhw *c; if (selected) { - error_setg(&error_fatal, "only one -soundhw option is allowed"); + error_report("only one -soundhw option is allowed"); + exit(1); } for (c = soundhw; c->name; ++c) { diff --git a/hw/s390x/ccw-device.c b/hw/s390x/ccw-device.c index 14c24e3890..230cc09e03 100644 --- a/hw/s390x/ccw-device.c +++ b/hw/s390x/ccw-device.c @@ -13,6 +13,10 @@ #include "ccw-device.h" #include "hw/qdev-properties.h" #include "qemu/module.h" +#include "ipl.h" +#include "qapi/visitor.h" +#include "qemu/ctype.h" +#include "qapi/error.h" static void ccw_device_refill_ids(CcwDevice *dev) { @@ -37,10 +41,52 @@ static bool ccw_device_realize(CcwDevice *dev, Error **errp) return true; } +static void ccw_device_get_loadparm(Object *obj, Visitor *v, + const char *name, void *opaque, + Error **errp) +{ + CcwDevice *dev = CCW_DEVICE(obj); + char *str = g_strndup((char *) dev->loadparm, sizeof(dev->loadparm)); + + visit_type_str(v, name, &str, errp); + g_free(str); +} + +static void ccw_device_set_loadparm(Object *obj, Visitor *v, + const char *name, void *opaque, + Error **errp) +{ + CcwDevice *dev = CCW_DEVICE(obj); + char *val; + int index; + + index = object_property_get_int(obj, "bootindex", NULL); + + if (index < 0) { + error_setg(errp, "LOADPARM is only valid for boot devices!"); + } + + if (!visit_type_str(v, name, &val, errp)) { + return; + } + + s390_ipl_fmt_loadparm(dev->loadparm, val, errp); +} + +static const PropertyInfo ccw_loadparm = { + .name = "ccw_loadparm", + .description = "Up to 8 chars in set of [A-Za-z0-9. ] to pass" + " to the guest loader/kernel", + .get = ccw_device_get_loadparm, + .set = ccw_device_set_loadparm, +}; + static Property ccw_device_properties[] = { DEFINE_PROP_CSS_DEV_ID("devno", CcwDevice, devno), DEFINE_PROP_CSS_DEV_ID_RO("dev_id", CcwDevice, dev_id), DEFINE_PROP_CSS_DEV_ID_RO("subch_id", CcwDevice, subch_id), + DEFINE_PROP("loadparm", CcwDevice, loadparm, ccw_loadparm, + typeof(uint8_t[8])), DEFINE_PROP_END_OF_LIST(), }; diff --git a/hw/s390x/ccw-device.h b/hw/s390x/ccw-device.h index 5feeb0ee7a..1e1737c0f3 100644 --- a/hw/s390x/ccw-device.h +++ b/hw/s390x/ccw-device.h @@ -26,6 +26,8 @@ struct CcwDevice { CssDevId dev_id; /* The actual busid of the virtual subchannel. */ CssDevId subch_id; + /* If set, use this loadparm value when device is boot target */ + uint8_t loadparm[8]; }; typedef struct CcwDevice CcwDevice; diff --git a/hw/s390x/ipl.c b/hw/s390x/ipl.c index 5ab7433908..dc02b0fdda 100644 --- a/hw/s390x/ipl.c +++ b/hw/s390x/ipl.c @@ -34,6 +34,7 @@ #include "qemu/config-file.h" #include "qemu/cutils.h" #include "qemu/option.h" +#include "qemu/ctype.h" #include "standard-headers/linux/virtio_ids.h" #define KERN_IMAGE_START 0x010000UL @@ -45,6 +46,7 @@ #define INITRD_PARM_START 0x010408UL #define PARMFILE_START 0x001000UL #define ZIPL_IMAGE_START 0x009000UL +#define BIOS_MAX_SIZE 0x300000UL #define IPL_PSW_MASK (PSW_MASK_32 | PSW_MASK_64) static bool iplb_extended_needed(void *opaque) @@ -54,6 +56,13 @@ static bool iplb_extended_needed(void *opaque) return ipl->iplbext_migration; } +/* Place the IPLB chain immediately before the BIOS in memory */ +static uint64_t find_iplb_chain_addr(uint64_t bios_addr, uint16_t count) +{ + return (bios_addr & TARGET_PAGE_MASK) + - (count * sizeof(IplParameterBlock)); +} + static const VMStateDescription vmstate_iplb_extended = { .name = "ipl/iplb_extended", .version_id = 0, @@ -144,7 +153,14 @@ static void s390_ipl_realize(DeviceState *dev, Error **errp) * even if an external kernel has been defined. */ if (!ipl->kernel || ipl->enforce_bios) { - uint64_t fwbase = (MIN(ms->ram_size, 0x80000000U) - 0x200000) & ~0xffffUL; + uint64_t fwbase; + + if (ms->ram_size < BIOS_MAX_SIZE) { + error_setg(errp, "not enough RAM to load the BIOS file"); + return; + } + + fwbase = (MIN(ms->ram_size, 0x80000000U) - BIOS_MAX_SIZE) & ~0xffffUL; bios_filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, ipl->firmware); if (bios_filename == NULL) { @@ -280,7 +296,6 @@ static Property s390_ipl_properties[] = { DEFINE_PROP_STRING("initrd", S390IPLState, initrd), DEFINE_PROP_STRING("cmdline", S390IPLState, cmdline), DEFINE_PROP_STRING("firmware", S390IPLState, firmware), - DEFINE_PROP_STRING("netboot_fw", S390IPLState, netboot_fw), DEFINE_PROP_BOOL("enforce_bios", S390IPLState, enforce_bios, false), DEFINE_PROP_BOOL("iplbext_migration", S390IPLState, iplbext_migration, true), @@ -390,174 +405,169 @@ static CcwDevice *s390_get_ccw_device(DeviceState *dev_st, int *devtype) return ccw_dev; } -static bool s390_gen_initial_iplb(S390IPLState *ipl) +static uint64_t s390_ipl_map_iplb_chain(IplParameterBlock *iplb_chain) +{ + S390IPLState *ipl = get_ipl_device(); + uint16_t count = be16_to_cpu(ipl->qipl.chain_len); + uint64_t len = sizeof(IplParameterBlock) * count; + uint64_t chain_addr = find_iplb_chain_addr(ipl->bios_start_addr, count); + + cpu_physical_memory_write(chain_addr, iplb_chain, len); + return chain_addr; +} + +void s390_ipl_fmt_loadparm(uint8_t *loadparm, char *str, Error **errp) +{ + int i; + + /* Initialize the loadparm with spaces */ + memset(loadparm, ' ', LOADPARM_LEN); + for (i = 0; i < LOADPARM_LEN && str[i]; i++) { + uint8_t c = qemu_toupper(str[i]); /* mimic HMC */ + + if (qemu_isalnum(c) || c == '.' || c == ' ') { + loadparm[i] = c; + } else { + error_setg(errp, "LOADPARM: invalid character '%c' (ASCII 0x%02x)", + c, c); + return; + } + } +} + +void s390_ipl_convert_loadparm(char *ascii_lp, uint8_t *ebcdic_lp) +{ + int i; + + /* Initialize the loadparm with EBCDIC spaces (0x40) */ + memset(ebcdic_lp, '@', LOADPARM_LEN); + for (i = 0; i < LOADPARM_LEN && ascii_lp[i]; i++) { + ebcdic_lp[i] = ascii2ebcdic[(uint8_t) ascii_lp[i]]; + } +} + +static bool s390_build_iplb(DeviceState *dev_st, IplParameterBlock *iplb) { - DeviceState *dev_st; CcwDevice *ccw_dev = NULL; SCSIDevice *sd; int devtype; - - dev_st = get_boot_device(0); - if (dev_st) { - ccw_dev = s390_get_ccw_device(dev_st, &devtype); - } + uint8_t *lp; /* * Currently allow IPL only from CCW devices. */ + ccw_dev = s390_get_ccw_device(dev_st, &devtype); if (ccw_dev) { + lp = ccw_dev->loadparm; + switch (devtype) { case CCW_DEVTYPE_SCSI: sd = SCSI_DEVICE(dev_st); - ipl->iplb.len = cpu_to_be32(S390_IPLB_MIN_QEMU_SCSI_LEN); - ipl->iplb.blk0_len = + iplb->len = cpu_to_be32(S390_IPLB_MIN_QEMU_SCSI_LEN); + iplb->blk0_len = cpu_to_be32(S390_IPLB_MIN_QEMU_SCSI_LEN - S390_IPLB_HEADER_LEN); - ipl->iplb.pbt = S390_IPL_TYPE_QEMU_SCSI; - ipl->iplb.scsi.lun = cpu_to_be32(sd->lun); - ipl->iplb.scsi.target = cpu_to_be16(sd->id); - ipl->iplb.scsi.channel = cpu_to_be16(sd->channel); - ipl->iplb.scsi.devno = cpu_to_be16(ccw_dev->sch->devno); - ipl->iplb.scsi.ssid = ccw_dev->sch->ssid & 3; + iplb->pbt = S390_IPL_TYPE_QEMU_SCSI; + iplb->scsi.lun = cpu_to_be32(sd->lun); + iplb->scsi.target = cpu_to_be16(sd->id); + iplb->scsi.channel = cpu_to_be16(sd->channel); + iplb->scsi.devno = cpu_to_be16(ccw_dev->sch->devno); + iplb->scsi.ssid = ccw_dev->sch->ssid & 3; break; case CCW_DEVTYPE_VFIO: - ipl->iplb.len = cpu_to_be32(S390_IPLB_MIN_CCW_LEN); - ipl->iplb.pbt = S390_IPL_TYPE_CCW; - ipl->iplb.ccw.devno = cpu_to_be16(ccw_dev->sch->devno); - ipl->iplb.ccw.ssid = ccw_dev->sch->ssid & 3; + iplb->len = cpu_to_be32(S390_IPLB_MIN_CCW_LEN); + iplb->pbt = S390_IPL_TYPE_CCW; + iplb->ccw.devno = cpu_to_be16(ccw_dev->sch->devno); + iplb->ccw.ssid = ccw_dev->sch->ssid & 3; break; case CCW_DEVTYPE_VIRTIO_NET: - ipl->netboot = true; - /* Fall through to CCW_DEVTYPE_VIRTIO case */ case CCW_DEVTYPE_VIRTIO: - ipl->iplb.len = cpu_to_be32(S390_IPLB_MIN_CCW_LEN); - ipl->iplb.blk0_len = + iplb->len = cpu_to_be32(S390_IPLB_MIN_CCW_LEN); + iplb->blk0_len = cpu_to_be32(S390_IPLB_MIN_CCW_LEN - S390_IPLB_HEADER_LEN); - ipl->iplb.pbt = S390_IPL_TYPE_CCW; - ipl->iplb.ccw.devno = cpu_to_be16(ccw_dev->sch->devno); - ipl->iplb.ccw.ssid = ccw_dev->sch->ssid & 3; + iplb->pbt = S390_IPL_TYPE_CCW; + iplb->ccw.devno = cpu_to_be16(ccw_dev->sch->devno); + iplb->ccw.ssid = ccw_dev->sch->ssid & 3; break; } - if (!s390_ipl_set_loadparm(ipl->iplb.loadparm)) { - ipl->iplb.flags |= DIAG308_FLAGS_LP_VALID; + /* If the device loadparm is empty use the global machine loadparm */ + if (memcmp(lp, NO_LOADPARM, 8) == 0) { + lp = S390_CCW_MACHINE(qdev_get_machine())->loadparm; } + s390_ipl_convert_loadparm((char *)lp, iplb->loadparm); + iplb->flags |= DIAG308_FLAGS_LP_VALID; + return true; } return false; } -int s390_ipl_set_loadparm(uint8_t *loadparm) +void s390_rebuild_iplb(uint16_t dev_index, IplParameterBlock *iplb) { - MachineState *machine = MACHINE(qdev_get_machine()); - char *lp = object_property_get_str(OBJECT(machine), "loadparm", NULL); - - if (lp) { - int i; - - /* lp is an uppercase string without leading/embedded spaces */ - for (i = 0; i < 8 && lp[i]; i++) { - loadparm[i] = ascii2ebcdic[(uint8_t) lp[i]]; - } - - if (i < 8) { - memset(loadparm + i, 0x40, 8 - i); /* fill with EBCDIC spaces */ - } - - g_free(lp); - return 0; - } + S390IPLState *ipl = get_ipl_device(); + uint16_t index; + index = ipl->rebuilt_iplb ? ipl->iplb_index : dev_index; - return -1; + ipl->rebuilt_iplb = s390_build_iplb(get_boot_device(index), iplb); + ipl->iplb_index = index; } -static int load_netboot_image(Error **errp) +static bool s390_init_all_iplbs(S390IPLState *ipl) { - MachineState *ms = MACHINE(qdev_get_machine()); - S390IPLState *ipl = get_ipl_device(); - char *netboot_filename; - MemoryRegion *sysmem = get_system_memory(); - MemoryRegion *mr = NULL; - void *ram_ptr = NULL; - int img_size = -1; - - mr = memory_region_find(sysmem, 0, 1).mr; - if (!mr) { - error_setg(errp, "Failed to find memory region at address 0"); - return -1; - } + int iplb_num = 0; + IplParameterBlock iplb_chain[7]; + DeviceState *dev_st = get_boot_device(0); + Object *machine = qdev_get_machine(); - ram_ptr = memory_region_get_ram_ptr(mr); - if (!ram_ptr) { - error_setg(errp, "No RAM found"); - goto unref_mr; + /* + * Parse the boot devices. Generate an IPLB for only the first boot device + * which will later be set with DIAG308. + */ + if (!dev_st) { + ipl->qipl.chain_len = 0; + return false; } - netboot_filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, ipl->netboot_fw); - if (netboot_filename == NULL) { - error_setg(errp, "Could not find network bootloader '%s'", - ipl->netboot_fw); - goto unref_mr; + /* If no machine loadparm was defined fill it with spaces */ + if (memcmp(S390_CCW_MACHINE(machine)->loadparm, NO_LOADPARM, 8) == 0) { + object_property_set_str(machine, "loadparm", " ", NULL); } - img_size = load_elf_ram(netboot_filename, NULL, NULL, NULL, - &ipl->start_addr, - NULL, NULL, NULL, 1, EM_S390, 0, 0, NULL, - false); + iplb_num = 1; + s390_build_iplb(dev_st, &ipl->iplb); - if (img_size < 0) { - img_size = load_image_size(netboot_filename, ram_ptr, ms->ram_size); - ipl->start_addr = KERN_IMAGE_START; + /* Index any fallback boot devices */ + while (get_boot_device(iplb_num)) { + iplb_num++; } - if (img_size < 0) { - error_setg(errp, "Failed to load network bootloader"); - } + if (iplb_num > MAX_BOOT_DEVS) { + warn_report("Excess boot devices defined! %d boot devices found, " + "but only the first %d will be considered.", + iplb_num, MAX_BOOT_DEVS); - g_free(netboot_filename); - -unref_mr: - memory_region_unref(mr); - return img_size; -} - -static bool is_virtio_ccw_device_of_type(IplParameterBlock *iplb, - int virtio_id) -{ - uint8_t cssid; - uint8_t ssid; - uint16_t devno; - uint16_t schid; - SubchDev *sch = NULL; - - if (iplb->pbt != S390_IPL_TYPE_CCW) { - return false; + iplb_num = MAX_BOOT_DEVS; } - devno = be16_to_cpu(iplb->ccw.devno); - ssid = iplb->ccw.ssid & 3; - - for (schid = 0; schid < MAX_SCHID; schid++) { - for (cssid = 0; cssid < MAX_CSSID; cssid++) { - sch = css_find_subch(1, cssid, ssid, schid); + ipl->qipl.chain_len = cpu_to_be16(iplb_num - 1); - if (sch && sch->devno == devno) { - return sch->id.cu_model == virtio_id; - } + /* + * Build fallback IPLBs for any boot devices above index 0, up to a + * maximum amount as defined in ipl.h + */ + if (iplb_num > 1) { + /* Start at 1 because the IPLB for boot index 0 is not chained */ + for (int i = 1; i < iplb_num; i++) { + dev_st = get_boot_device(i); + s390_build_iplb(dev_st, &iplb_chain[i - 1]); } - } - return false; -} -static bool is_virtio_net_device(IplParameterBlock *iplb) -{ - return is_virtio_ccw_device_of_type(iplb, VIRTIO_ID_NET); -} + ipl->qipl.next_iplb = cpu_to_be64(s390_ipl_map_iplb_chain(iplb_chain)); + } -static bool is_virtio_scsi_device(IplParameterBlock *iplb) -{ - return is_virtio_ccw_device_of_type(iplb, VIRTIO_ID_SCSI); + return iplb_num; } static void update_machine_ipl_properties(IplParameterBlock *iplb) @@ -577,7 +587,7 @@ static void update_machine_ipl_properties(IplParameterBlock *iplb) ascii_loadparm[i] = 0; object_property_set_str(machine, "loadparm", ascii_loadparm, &err); } else { - object_property_set_str(machine, "loadparm", "", &err); + object_property_set_str(machine, "loadparm", " ", &err); } if (err) { warn_report_err(err); @@ -599,7 +609,7 @@ void s390_ipl_update_diag308(IplParameterBlock *iplb) ipl->iplb = *iplb; ipl->iplb_valid = true; } - ipl->netboot = is_virtio_net_device(iplb); + update_machine_ipl_properties(iplb); } @@ -626,32 +636,14 @@ IplParameterBlock *s390_ipl_get_iplb(void) void s390_ipl_reset_request(CPUState *cs, enum s390_reset reset_type) { S390IPLState *ipl = get_ipl_device(); - if (reset_type == S390_RESET_EXTERNAL || reset_type == S390_RESET_REIPL) { /* use CPU 0 for full resets */ ipl->reset_cpu_index = 0; } else { ipl->reset_cpu_index = cs->cpu_index; } - ipl->reset_type = reset_type; - - if (reset_type == S390_RESET_REIPL && - ipl->iplb_valid && - !ipl->netboot && - ipl->iplb.pbt == S390_IPL_TYPE_CCW && - is_virtio_scsi_device(&ipl->iplb)) { - CcwDevice *ccw_dev = s390_get_ccw_device(get_boot_device(0), NULL); - if (ccw_dev && - cpu_to_be16(ccw_dev->sch->devno) == ipl->iplb.ccw.devno && - (ccw_dev->sch->ssid & 3) == ipl->iplb.ccw.ssid) { - /* - * this is the original boot device's SCSI - * so restore IPL parameter info from it - */ - ipl->iplb_valid = s390_gen_initial_iplb(ipl); - } - } + ipl->reset_type = reset_type; if (reset_type == S390_RESET_MODIFIED_CLEAR || reset_type == S390_RESET_LOAD_NORMAL || reset_type == S390_RESET_PV) { @@ -743,13 +735,11 @@ void s390_ipl_prepare_cpu(S390CPU *cpu) if (!ipl->kernel || ipl->iplb_valid) { cpu->env.psw.addr = ipl->bios_start_addr; if (!ipl->iplb_valid) { - ipl->iplb_valid = s390_gen_initial_iplb(ipl); + ipl->iplb_valid = s390_init_all_iplbs(ipl); + } else { + ipl->qipl.chain_len = 0; } } - if (ipl->netboot) { - load_netboot_image(&error_fatal); - ipl->qipl.netboot_start_addr = cpu_to_be64(ipl->start_addr); - } s390_ipl_set_boot_menu(ipl); s390_ipl_prepare_qipl(cpu); } diff --git a/hw/s390x/ipl.h b/hw/s390x/ipl.h index 57cd125769..d7d0b7bfd2 100644 --- a/hw/s390x/ipl.h +++ b/hw/s390x/ipl.h @@ -16,96 +16,15 @@ #include "cpu.h" #include "exec/address-spaces.h" #include "hw/qdev-core.h" +#include "hw/s390x/ipl/qipl.h" #include "qom/object.h" -struct IPLBlockPVComp { - uint64_t tweak_pref; - uint64_t addr; - uint64_t size; -} QEMU_PACKED; -typedef struct IPLBlockPVComp IPLBlockPVComp; - -struct IPLBlockPV { - uint8_t reserved18[87]; /* 0x18 */ - uint8_t version; /* 0x6f */ - uint32_t reserved70; /* 0x70 */ - uint32_t num_comp; /* 0x74 */ - uint64_t pv_header_addr; /* 0x78 */ - uint64_t pv_header_len; /* 0x80 */ - struct IPLBlockPVComp components[0]; -} QEMU_PACKED; -typedef struct IPLBlockPV IPLBlockPV; - -struct IplBlockCcw { - uint8_t reserved0[85]; - uint8_t ssid; - uint16_t devno; - uint8_t vm_flags; - uint8_t reserved3[3]; - uint32_t vm_parm_len; - uint8_t nss_name[8]; - uint8_t vm_parm[64]; - uint8_t reserved4[8]; -} QEMU_PACKED; -typedef struct IplBlockCcw IplBlockCcw; - -struct IplBlockFcp { - uint8_t reserved1[305 - 1]; - uint8_t opt; - uint8_t reserved2[3]; - uint16_t reserved3; - uint16_t devno; - uint8_t reserved4[4]; - uint64_t wwpn; - uint64_t lun; - uint32_t bootprog; - uint8_t reserved5[12]; - uint64_t br_lba; - uint32_t scp_data_len; - uint8_t reserved6[260]; - uint8_t scp_data[0]; -} QEMU_PACKED; -typedef struct IplBlockFcp IplBlockFcp; - -struct IplBlockQemuScsi { - uint32_t lun; - uint16_t target; - uint16_t channel; - uint8_t reserved0[77]; - uint8_t ssid; - uint16_t devno; -} QEMU_PACKED; -typedef struct IplBlockQemuScsi IplBlockQemuScsi; - #define DIAG308_FLAGS_LP_VALID 0x80 +#define MAX_BOOT_DEVS 8 /* Max number of devices that may have a bootindex */ -union IplParameterBlock { - struct { - uint32_t len; - uint8_t reserved0[3]; - uint8_t version; - uint32_t blk0_len; - uint8_t pbt; - uint8_t flags; - uint16_t reserved01; - uint8_t loadparm[8]; - union { - IplBlockCcw ccw; - IplBlockFcp fcp; - IPLBlockPV pv; - IplBlockQemuScsi scsi; - }; - } QEMU_PACKED; - struct { - uint8_t reserved1[110]; - uint16_t devno; - uint8_t reserved2[88]; - uint8_t reserved_ext[4096 - 200]; - } QEMU_PACKED; -} QEMU_PACKED; -typedef union IplParameterBlock IplParameterBlock; - -int s390_ipl_set_loadparm(uint8_t *loadparm); +void s390_ipl_convert_loadparm(char *ascii_lp, uint8_t *ebcdic_lp); +void s390_ipl_fmt_loadparm(uint8_t *loadparm, char *str, Error **errp); +void s390_rebuild_iplb(uint16_t index, IplParameterBlock *iplb); void s390_ipl_update_diag308(IplParameterBlock *iplb); int s390_ipl_prepare_pv_header(Error **errp); int s390_ipl_pv_unpack(void); @@ -131,27 +50,6 @@ void s390_ipl_clear_reset_request(void); #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 definition - * 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" OBJECT_DECLARE_SIMPLE_TYPE(S390IPLState, S390_IPL) @@ -168,7 +66,8 @@ struct S390IPLState { bool enforce_bios; bool iplb_valid; bool iplb_valid_pv; - bool netboot; + bool rebuilt_iplb; + uint16_t iplb_index; /* reset related properties don't have to be migrated or reset */ enum s390_reset reset_type; int reset_cpu_index; @@ -178,7 +77,6 @@ struct S390IPLState { char *initrd; char *cmdline; char *firmware; - char *netboot_fw; uint8_t cssid; uint8_t ssid; uint16_t devno; @@ -276,11 +174,14 @@ static inline bool iplb_valid_pv(IplParameterBlock *iplb) static inline bool iplb_valid(IplParameterBlock *iplb) { + uint32_t len = be32_to_cpu(iplb->len); + switch (iplb->pbt) { case S390_IPL_TYPE_FCP: - return be32_to_cpu(iplb->len) >= S390_IPLB_MIN_FCP_LEN; + return len >= S390_IPLB_MIN_FCP_LEN; case S390_IPL_TYPE_CCW: - return be32_to_cpu(iplb->len) >= S390_IPLB_MIN_CCW_LEN; + return len >= S390_IPLB_MIN_CCW_LEN; + case S390_IPL_TYPE_QEMU_SCSI: default: return false; } diff --git a/hw/s390x/s390-virtio-ccw.c b/hw/s390x/s390-virtio-ccw.c index 5aa8d207a3..fe03f716f3 100644 --- a/hw/s390x/s390-virtio-ccw.c +++ b/hw/s390x/s390-virtio-ccw.c @@ -197,11 +197,10 @@ static void s390_memory_init(MemoryRegion *ram) static void s390_init_ipl_dev(const char *kernel_filename, const char *kernel_cmdline, const char *initrd_filename, const char *firmware, - const char *netboot_fw, bool enforce_bios) + bool enforce_bios) { Object *new = object_new(TYPE_S390_IPL); DeviceState *dev = DEVICE(new); - char *netboot_fw_prop; if (kernel_filename) { qdev_prop_set_string(dev, "kernel", kernel_filename); @@ -212,11 +211,6 @@ static void s390_init_ipl_dev(const char *kernel_filename, qdev_prop_set_string(dev, "cmdline", kernel_cmdline); qdev_prop_set_string(dev, "firmware", firmware); qdev_prop_set_bit(dev, "enforce_bios", enforce_bios); - netboot_fw_prop = object_property_get_str(new, "netboot_fw", &error_abort); - if (!strlen(netboot_fw_prop)) { - qdev_prop_set_string(dev, "netboot_fw", netboot_fw); - } - g_free(netboot_fw_prop); object_property_add_child(qdev_get_machine(), TYPE_S390_IPL, new); object_unref(new); @@ -284,7 +278,7 @@ static void ccw_init(MachineState *machine) s390_init_ipl_dev(machine->kernel_filename, machine->kernel_cmdline, machine->initrd_filename, machine->firmware ?: "s390-ccw.img", - "s390-netboot.img", true); + true); dev = qdev_new(TYPE_S390_PCI_HOST_BRIDGE); object_property_add_child(qdev_get_machine(), TYPE_S390_PCI_HOST_BRIDGE, @@ -728,28 +722,12 @@ static void machine_set_loadparm(Object *obj, Visitor *v, { S390CcwMachineState *ms = S390_CCW_MACHINE(obj); char *val; - int i; if (!visit_type_str(v, name, &val, errp)) { return; } - for (i = 0; i < sizeof(ms->loadparm) && val[i]; i++) { - uint8_t c = qemu_toupper(val[i]); /* mimic HMC */ - - if (('A' <= c && c <= 'Z') || ('0' <= c && c <= '9') || (c == '.') || - (c == ' ')) { - ms->loadparm[i] = c; - } else { - error_setg(errp, "LOADPARM: invalid character '%c' (ASCII 0x%02x)", - c, c); - return; - } - } - - for (; i < sizeof(ms->loadparm); i++) { - ms->loadparm[i] = ' '; /* pad right with spaces */ - } + s390_ipl_fmt_loadparm(ms->loadparm, val, errp); } static void ccw_machine_class_init(ObjectClass *oc, void *data) diff --git a/hw/s390x/sclp.c b/hw/s390x/sclp.c index e725dcd5fd..8757626b5c 100644 --- a/hw/s390x/sclp.c +++ b/hw/s390x/sclp.c @@ -110,7 +110,6 @@ static void read_SCP_info(SCLPDevice *sclp, SCCB *sccb) MachineState *machine = MACHINE(qdev_get_machine()); int cpu_count; int rnsize, rnmax; - IplParameterBlock *ipib = s390_ipl_get_iplb(); int required_len = SCCB_REQ_LEN(ReadInfo, machine->possible_cpus->len); int offset_cpu = s390_has_feat(S390_FEAT_EXTENDED_LENGTH_SCCB) ? offsetof(ReadInfo, entries) : @@ -171,12 +170,8 @@ static void read_SCP_info(SCLPDevice *sclp, SCCB *sccb) read_info->rnmax2 = cpu_to_be64(rnmax); } - if (ipib && ipib->flags & DIAG308_FLAGS_LP_VALID) { - memcpy(&read_info->loadparm, &ipib->loadparm, - sizeof(read_info->loadparm)); - } else { - s390_ipl_set_loadparm(read_info->loadparm); - } + s390_ipl_convert_loadparm((char *)S390_CCW_MACHINE(machine)->loadparm, + read_info->loadparm); sccb->h.response_code = cpu_to_be16(SCLP_RC_NORMAL_READ_COMPLETION); } diff --git a/include/crypto/hash.h b/include/crypto/hash.h index b791ca92a4..712cac79ee 100644 --- a/include/crypto/hash.h +++ b/include/crypto/hash.h @@ -73,11 +73,18 @@ size_t qcrypto_hash_digest_len(QCryptoHashAlgo alg); * @errp: pointer to a NULL-initialized error object * * Computes the hash across all the memory regions - * present in @iov. The @result pointer will be - * filled with raw bytes representing the computed - * hash, which will have length @resultlen. The - * memory pointer in @result must be released - * with a call to g_free() when no longer required. + * present in @iov. + * + * If @result_len is set to a non-zero value by the caller, then + * @result must hold a pointer that is @result_len in size, and + * @result_len match the size of the hash output. The digest will + * be written into @result. + * + * If @result_len is set to zero, then this function will allocate + * a buffer to hold the hash output digest, storing a pointer to + * the buffer in @result, and setting @result_len to its size. + * The memory referenced in @result must be released with a call + * to g_free() when no longer required by the caller. * * Returns: 0 on success, -1 on error */ @@ -98,11 +105,18 @@ int qcrypto_hash_bytesv(QCryptoHashAlgo alg, * @errp: pointer to a NULL-initialized error object * * Computes the hash across all the memory region - * @buf of length @len. The @result pointer will be - * filled with raw bytes representing the computed - * hash, which will have length @resultlen. The - * memory pointer in @result must be released - * with a call to g_free() when no longer required. + * @buf of length @len. + * + * If @result_len is set to a non-zero value by the caller, then + * @result must hold a pointer that is @result_len in size, and + * @result_len match the size of the hash output. The digest will + * be written into @result. + * + * If @result_len is set to zero, then this function will allocate + * a buffer to hold the hash output digest, storing a pointer to + * the buffer in @result, and setting @result_len to its size. + * The memory referenced in @result must be released with a call + * to g_free() when no longer required by the caller. * * Returns: 0 on success, -1 on error */ @@ -215,8 +229,17 @@ int qcrypto_hash_finalize_base64(QCryptoHash *hash, * * Computes the hash from the given hash object. Hash object * is expected to have it's data updated from the qcrypto_hash_update function. - * The memory pointer in @result must be released with a call to g_free() - * when no longer required. + * + * If @result_len is set to a non-zero value by the caller, then + * @result must hold a pointer that is @result_len in size, and + * @result_len match the size of the hash output. The digest will + * be written into @result. + * + * If @result_len is set to zero, then this function will allocate + * a buffer to hold the hash output digest, storing a pointer to + * the buffer in @result, and setting @result_len to its size. + * The memory referenced in @result must be released with a call + * to g_free() when no longer required by the caller. * * Returns: 0 on success, -1 on error */ diff --git a/include/crypto/hmac.h b/include/crypto/hmac.h index c69a0dfab3..da8a1e3ceb 100644 --- a/include/crypto/hmac.h +++ b/include/crypto/hmac.h @@ -77,11 +77,18 @@ G_DEFINE_AUTOPTR_CLEANUP_FUNC(QCryptoHmac, qcrypto_hmac_free) * @errp: pointer to a NULL-initialized error object * * Computes the hmac across all the memory regions - * present in @iov. The @result pointer will be - * filled with raw bytes representing the computed - * hmac, which will have length @resultlen. The - * memory pointer in @result must be released - * with a call to g_free() when no longer required. + * present in @iov. + * + * If @result_len is set to a non-zero value by the caller, then + * @result must hold a pointer that is @result_len in size, and + * @result_len match the size of the hash output. The digest will + * be written into @result. + * + * If @result_len is set to zero, then this function will allocate + * a buffer to hold the hash output digest, storing a pointer to + * the buffer in @result, and setting @result_len to its size. + * The memory referenced in @result must be released with a call + * to g_free() when no longer required by the caller. * * Returns: * 0 on success, -1 on error @@ -103,11 +110,18 @@ int qcrypto_hmac_bytesv(QCryptoHmac *hmac, * @errp: pointer to a NULL-initialized error object * * Computes the hmac across all the memory region - * @buf of length @len. The @result pointer will be - * filled with raw bytes representing the computed - * hmac, which will have length @resultlen. The - * memory pointer in @result must be released - * with a call to g_free() when no longer required. + * @buf of length @len. + * + * If @result_len is set to a non-zero value by the caller, then + * @result must hold a pointer that is @result_len in size, and + * @result_len match the size of the hash output. The digest will + * be written into @result. + * + * If @result_len is set to zero, then this function will allocate + * a buffer to hold the hash output digest, storing a pointer to + * the buffer in @result, and setting @result_len to its size. + * The memory referenced in @result must be released with a call + * to g_free() when no longer required by the caller. * * Returns: * 0 on success, -1 on error diff --git a/include/exec/exec-all.h b/include/exec/exec-all.h index 72240ef426..2e4c4cc4b4 100644 --- a/include/exec/exec-all.h +++ b/include/exec/exec-all.h @@ -368,6 +368,13 @@ int probe_access_flags(CPUArchState *env, vaddr addr, int size, * The CPUTLBEntryFull structure returned via @pfull is transient * and must be consumed or copied immediately, before any further * access or changes to TLB @mmu_idx. + * + * This function will not fault if @nonfault is set, but will + * return TLB_INVALID_MASK if the page is not mapped, or is not + * accessible with @access_type. + * + * This function will return TLB_MMIO in order to force the access + * to be handled out-of-line if plugins wish to instrument the access. */ int probe_access_full(CPUArchState *env, vaddr addr, int size, MMUAccessType access_type, int mmu_idx, @@ -375,22 +382,14 @@ int probe_access_full(CPUArchState *env, vaddr addr, int size, CPUTLBEntryFull **pfull, uintptr_t retaddr); /** - * probe_access_mmu() - Like probe_access_full except cannot fault and - * doesn't trigger instrumentation. - * - * @env: CPUArchState - * @vaddr: virtual address to probe - * @size: size of the probe - * @access_type: read, write or execute permission - * @mmu_idx: softmmu index - * @phost: ptr to return value host address or NULL - * @pfull: ptr to return value CPUTLBEntryFull structure or NULL - * - * The CPUTLBEntryFull structure returned via @pfull is transient - * and must be consumed or copied immediately, before any further - * access or changes to TLB @mmu_idx. - * - * Returns: TLB flags as per probe_access_flags() + * probe_access_full_mmu: + * Like probe_access_full, except: + * + * This function is intended to be used for page table accesses by + * the target mmu itself. Since such page walking happens while + * handling another potential mmu fault, this function never raises + * exceptions (akin to @nonfault true for probe_access_full). + * Likewise this function does not trigger plugin instrumentation. */ int probe_access_full_mmu(CPUArchState *env, vaddr addr, int size, MMUAccessType access_type, int mmu_idx, diff --git a/include/hw/s390x/ipl/qipl.h b/include/hw/s390x/ipl/qipl.h new file mode 100644 index 0000000000..6824391111 --- /dev/null +++ b/include/hw/s390x/ipl/qipl.h @@ -0,0 +1,127 @@ +/* + * S/390 boot structures + * + * Copyright 2024 IBM Corp. + * Author(s): Jared Rossi <jrossi@linux.ibm.com> + * + * This work is licensed under the terms of the GNU GPL, version 2 or (at + * your option) any later version. See the COPYING file in the top-level + * directory. + */ + +#ifndef S390X_QIPL_H +#define S390X_QIPL_H + +/* Boot Menu flags */ +#define QIPL_FLAG_BM_OPTS_CMD 0x80 +#define QIPL_FLAG_BM_OPTS_ZIPL 0x40 + +#define QIPL_ADDRESS 0xcc +#define LOADPARM_LEN 8 +#define NO_LOADPARM "\0\0\0\0\0\0\0\0" + +/* + * 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 64-bit data fields in this + * area must account for their alignment needs. + * The total size of the struct must never exceed 28 bytes. + */ +struct QemuIplParameters { + uint8_t qipl_flags; + uint8_t index; + uint8_t reserved1[2]; + uint64_t reserved2; + uint32_t boot_menu_timeout; + uint8_t reserved3[2]; + uint16_t chain_len; + uint64_t next_iplb; +} QEMU_PACKED; +typedef struct QemuIplParameters QemuIplParameters; + +struct IPLBlockPVComp { + uint64_t tweak_pref; + uint64_t addr; + uint64_t size; +} QEMU_PACKED; +typedef struct IPLBlockPVComp IPLBlockPVComp; + +struct IPLBlockPV { + uint8_t reserved18[87]; /* 0x18 */ + uint8_t version; /* 0x6f */ + uint32_t reserved70; /* 0x70 */ + uint32_t num_comp; /* 0x74 */ + uint64_t pv_header_addr; /* 0x78 */ + uint64_t pv_header_len; /* 0x80 */ + struct IPLBlockPVComp components[0]; +} QEMU_PACKED; +typedef struct IPLBlockPV IPLBlockPV; + +struct IplBlockCcw { + uint8_t reserved0[85]; + uint8_t ssid; + uint16_t devno; + uint8_t vm_flags; + uint8_t reserved3[3]; + uint32_t vm_parm_len; + uint8_t nss_name[8]; + uint8_t vm_parm[64]; + uint8_t reserved4[8]; +} QEMU_PACKED; +typedef struct IplBlockCcw IplBlockCcw; + +struct IplBlockFcp { + uint8_t reserved1[305 - 1]; + uint8_t opt; + uint8_t reserved2[3]; + uint16_t reserved3; + uint16_t devno; + uint8_t reserved4[4]; + uint64_t wwpn; + uint64_t lun; + uint32_t bootprog; + uint8_t reserved5[12]; + uint64_t br_lba; + uint32_t scp_data_len; + uint8_t reserved6[260]; + uint8_t scp_data[0]; +} QEMU_PACKED; +typedef struct IplBlockFcp IplBlockFcp; + +struct IplBlockQemuScsi { + uint32_t lun; + uint16_t target; + uint16_t channel; + uint8_t reserved0[77]; + uint8_t ssid; + uint16_t devno; +} QEMU_PACKED; +typedef struct IplBlockQemuScsi IplBlockQemuScsi; + +union IplParameterBlock { + struct { + uint32_t len; + uint8_t reserved0[3]; + uint8_t version; + uint32_t blk0_len; + uint8_t pbt; + uint8_t flags; + uint16_t reserved01; + uint8_t loadparm[LOADPARM_LEN]; + union { + IplBlockCcw ccw; + IplBlockFcp fcp; + IPLBlockPV pv; + IplBlockQemuScsi scsi; + }; + } QEMU_PACKED; + struct { + uint8_t reserved1[110]; + uint16_t devno; + uint8_t reserved2[88]; + uint8_t reserved_ext[4096 - 200]; + } QEMU_PACKED; +} QEMU_PACKED; +typedef union IplParameterBlock IplParameterBlock; + +#endif diff --git a/include/qemu/sockets.h b/include/qemu/sockets.h index d935fd80da..c562690d89 100644 --- a/include/qemu/sockets.h +++ b/include/qemu/sockets.h @@ -61,7 +61,6 @@ int socket_set_fast_reuse(int fd); int inet_ai_family_from_address(InetSocketAddress *addr, Error **errp); int inet_parse(InetSocketAddress *addr, const char *str, Error **errp); -int inet_connect(const char *str, Error **errp); int inet_connect_saddr(InetSocketAddress *saddr, Error **errp); NetworkAddressFamily inet_netfamily(int family); @@ -118,21 +117,6 @@ socket_sockaddr_to_address(struct sockaddr_storage *sa, SocketAddress *socket_local_address(int fd, Error **errp); /** - * socket_remote_address: - * @fd: the socket file handle - * @errp: pointer to uninitialized error object - * - * Get the string representation of the remote socket - * address. A pointer to the allocated address information - * struct will be returned, which the caller is required to - * release with a call qapi_free_SocketAddress() when no - * longer required. - * - * Returns: the socket address struct, or NULL on error - */ -SocketAddress *socket_remote_address(int fd, Error **errp); - -/** * socket_address_flatten: * @addr: the socket address to flatten * diff --git a/include/tcg/tcg.h b/include/tcg/tcg.h index 824fb3560d..a77ed12b9d 100644 --- a/include/tcg/tcg.h +++ b/include/tcg/tcg.h @@ -521,6 +521,12 @@ struct TCGContext { struct qemu_plugin_insn *plugin_insn; #endif + /* For host-specific values. */ +#ifdef __riscv + MemOp riscv_cur_vsew; + TCGType riscv_cur_type; +#endif + GHashTable *const_table[TCG_TYPE_COUNT]; TCGTempSet free_temps[TCG_TYPE_COUNT]; TCGTemp temps[TCG_MAX_TEMPS]; /* globals first, temps after */ diff --git a/linux-user/ppc/signal.c b/linux-user/ppc/signal.c index a1d8c0bccc..24e5a02a78 100644 --- a/linux-user/ppc/signal.c +++ b/linux-user/ppc/signal.c @@ -628,7 +628,7 @@ static int do_setcontext(struct target_ucontext *ucp, CPUPPCState *env, int sig) if (!lock_user_struct(VERIFY_READ, mcp, mcp_addr, 1)) return 1; - target_to_host_sigset_internal(&blocked, &set); + target_to_host_sigset(&blocked, &set); set_sigmask(&blocked); restore_user_regs(env, mcp, sig); diff --git a/linux-user/strace.c b/linux-user/strace.c index c3eb3a2706..b70eadc19e 100644 --- a/linux-user/strace.c +++ b/linux-user/strace.c @@ -161,19 +161,20 @@ static const char * const target_signal_name[] = { }; static void -print_signal(abi_ulong arg, int last) +print_signal_1(abi_ulong arg) { - const char *signal_name = NULL; - if (arg < ARRAY_SIZE(target_signal_name)) { - signal_name = target_signal_name[arg]; + qemu_log("%s", target_signal_name[arg]); + } else { + qemu_log(TARGET_ABI_FMT_lu, arg); } +} - if (signal_name == NULL) { - print_raw_param("%ld", arg, last); - return; - } - qemu_log("%s%s", signal_name, get_comma(last)); +static void +print_signal(abi_ulong arg, int last) +{ + print_signal_1(arg); + qemu_log("%s", get_comma(last)); } static void print_si_code(int arg) @@ -718,6 +719,51 @@ print_ipc(CPUArchState *cpu_env, const struct syscallname *name, } #endif +#ifdef TARGET_NR_rt_sigprocmask +static void print_target_sigset_t_1(target_sigset_t *set, int last) +{ + bool first = true; + int i, sig = 1; + + qemu_log("["); + for (i = 0; i < TARGET_NSIG_WORDS; i++) { + abi_ulong bits = 0; + int j; + + __get_user(bits, &set->sig[i]); + for (j = 0; j < sizeof(bits) * 8; j++) { + if (bits & ((abi_ulong)1 << j)) { + if (first) { + first = false; + } else { + qemu_log(" "); + } + print_signal_1(sig); + } + sig++; + } + } + qemu_log("]%s", get_comma(last)); +} + +static void print_target_sigset_t(abi_ulong addr, abi_ulong size, int last) +{ + if (addr && size == sizeof(target_sigset_t)) { + target_sigset_t *set; + + set = lock_user(VERIFY_READ, addr, sizeof(target_sigset_t), 1); + if (set) { + print_target_sigset_t_1(set, last); + unlock_user(set, addr, 0); + } else { + print_pointer(addr, last); + } + } else { + print_pointer(addr, last); + } +} +#endif + /* * Variants for the return value output function */ @@ -3312,11 +3358,29 @@ print_rt_sigprocmask(CPUArchState *cpu_env, const struct syscallname *name, case TARGET_SIG_SETMASK: how = "SIG_SETMASK"; break; } qemu_log("%s,", how); - print_pointer(arg1, 0); + print_target_sigset_t(arg1, arg3, 0); print_pointer(arg2, 0); print_raw_param("%u", arg3, 1); print_syscall_epilogue(name); } + +static void +print_rt_sigprocmask_ret(CPUArchState *cpu_env, const struct syscallname *name, + abi_long ret, abi_long arg0, abi_long arg1, + abi_long arg2, abi_long arg3, abi_long arg4, + abi_long arg5) +{ + if (!print_syscall_err(ret)) { + qemu_log(TARGET_ABI_FMT_ld, ret); + if (arg2) { + qemu_log(" (oldset="); + print_target_sigset_t(arg2, arg3, 1); + qemu_log(")"); + } + } + + qemu_log("\n"); +} #endif #ifdef TARGET_NR_rt_sigqueueinfo diff --git a/linux-user/strace.list b/linux-user/strace.list index 0d69fb3150..fdf94ef32a 100644 --- a/linux-user/strace.list +++ b/linux-user/strace.list @@ -1189,7 +1189,8 @@ { TARGET_NR_rt_sigpending, "rt_sigpending" , NULL, NULL, NULL }, #endif #ifdef TARGET_NR_rt_sigprocmask -{ TARGET_NR_rt_sigprocmask, "rt_sigprocmask" , NULL, print_rt_sigprocmask, NULL }, +{ TARGET_NR_rt_sigprocmask, "rt_sigprocmask" , NULL, print_rt_sigprocmask, + print_rt_sigprocmask_ret }, #endif #ifdef TARGET_NR_rt_sigqueueinfo { TARGET_NR_rt_sigqueueinfo, "rt_sigqueueinfo" , NULL, print_rt_sigqueueinfo, NULL }, diff --git a/linux-user/syscall.c b/linux-user/syscall.c index 1354e75694..59b2080b98 100644 --- a/linux-user/syscall.c +++ b/linux-user/syscall.c @@ -54,7 +54,6 @@ #include <utime.h> #include <sys/sysinfo.h> #include <sys/signalfd.h> -//#include <sys/user.h> #include <netinet/in.h> #include <netinet/ip.h> #include <netinet/tcp.h> @@ -8151,17 +8150,19 @@ static int open_self_maps_1(CPUArchState *env, int fd, bool smaps) { struct open_self_maps_data d = { .ts = get_task_state(env_cpu(env)), - .host_maps = read_self_maps(), .fd = fd, .smaps = smaps }; + mmap_lock(); + d.host_maps = read_self_maps(); if (d.host_maps) { walk_memory_regions(&d, open_self_maps_2); free_self_maps(d.host_maps); } else { walk_memory_regions(&d, open_self_maps_3); } + mmap_unlock(); return 0; } @@ -8943,7 +8944,7 @@ static int do_getdents64(abi_long dirfd, abi_long arg2, abi_long count) #define RISCV_HWPROBE_EXT_ZFHMIN (1 << 28) #define RISCV_HWPROBE_EXT_ZIHINTNTL (1 << 29) #define RISCV_HWPROBE_EXT_ZVFH (1 << 30) -#define RISCV_HWPROBE_EXT_ZVFHMIN (1 << 31) +#define RISCV_HWPROBE_EXT_ZVFHMIN (1ULL << 31) #define RISCV_HWPROBE_EXT_ZFA (1ULL << 32) #define RISCV_HWPROBE_EXT_ZTSO (1ULL << 33) #define RISCV_HWPROBE_EXT_ZACAS (1ULL << 34) diff --git a/linux-user/syscall_defs.h b/linux-user/syscall_defs.h index 0ade83745e..0e08dfae3e 100644 --- a/linux-user/syscall_defs.h +++ b/linux-user/syscall_defs.h @@ -2750,9 +2750,9 @@ struct target_sched_param { /* from kernel's include/uapi/linux/openat2.h */ struct open_how_ver0 { - __u64 flags; - __u64 mode; - __u64 resolve; + uint64_t flags; + uint64_t mode; + uint64_t resolve; }; struct target_open_how_ver0 { abi_ullong flags; diff --git a/meson.build b/meson.build index d26690ce20..c26c417de1 100644 --- a/meson.build +++ b/meson.build @@ -1399,7 +1399,7 @@ iconv = not_found curses = not_found if have_system and get_option('curses').allowed() curses_test = ''' - #if defined(__APPLE__) || defined(__OpenBSD__) + #ifdef __APPLE__ #define _XOPEN_SOURCE_EXTENDED 1 #endif #include <locale.h> diff --git a/monitor/monitor.c b/monitor/monitor.c index db52a9c7ef..56786c0ccc 100644 --- a/monitor/monitor.c +++ b/monitor/monitor.c @@ -308,6 +308,7 @@ int error_printf_unless_qmp(const char *fmt, ...) static MonitorQAPIEventConf monitor_qapi_event_conf[QAPI_EVENT__MAX] = { /* Limit guest-triggerable events to 1 per second */ [QAPI_EVENT_RTC_CHANGE] = { 1000 * SCALE_MS }, + [QAPI_EVENT_BLOCK_IO_ERROR] = { 1000 * SCALE_MS }, [QAPI_EVENT_WATCHDOG] = { 1000 * SCALE_MS }, [QAPI_EVENT_BALLOON_CHANGE] = { 1000 * SCALE_MS }, [QAPI_EVENT_QUORUM_REPORT_BAD] = { 1000 * SCALE_MS }, @@ -493,7 +494,8 @@ static unsigned int qapi_event_throttle_hash(const void *key) hash += g_str_hash(qdict_get_str(evstate->data, "node-name")); } - if (evstate->event == QAPI_EVENT_MEMORY_DEVICE_SIZE_CHANGE) { + if (evstate->event == QAPI_EVENT_MEMORY_DEVICE_SIZE_CHANGE || + evstate->event == QAPI_EVENT_BLOCK_IO_ERROR) { hash += g_str_hash(qdict_get_str(evstate->data, "qom-path")); } @@ -519,7 +521,8 @@ static gboolean qapi_event_throttle_equal(const void *a, const void *b) qdict_get_str(evb->data, "node-name")); } - if (eva->event == QAPI_EVENT_MEMORY_DEVICE_SIZE_CHANGE) { + if (eva->event == QAPI_EVENT_MEMORY_DEVICE_SIZE_CHANGE || + eva->event == QAPI_EVENT_BLOCK_IO_ERROR) { return !strcmp(qdict_get_str(eva->data, "qom-path"), qdict_get_str(evb->data, "qom-path")); } diff --git a/pc-bios/meson.build b/pc-bios/meson.build index 090379763e..4823dff189 100644 --- a/pc-bios/meson.build +++ b/pc-bios/meson.build @@ -68,7 +68,6 @@ blobs = [ 'kvmvapic.bin', 'pvh.bin', 's390-ccw.img', - 's390-netboot.img', 'slof.bin', 'skiboot.lid', 'palcode-clipper', diff --git a/pc-bios/s390-ccw.img b/pc-bios/s390-ccw.img Binary files differindex f0d9ef6d4d..f9bac25306 100644 --- a/pc-bios/s390-ccw.img +++ b/pc-bios/s390-ccw.img diff --git a/pc-bios/s390-ccw/Makefile b/pc-bios/s390-ccw/Makefile index 6207911b53..dc69dd484f 100644 --- a/pc-bios/s390-ccw/Makefile +++ b/pc-bios/s390-ccw/Makefile @@ -3,7 +3,8 @@ all: build-all @true include config-host.mak -CFLAGS = -O2 -g +CFLAGS = -O2 -g -I $(SRC_PATH)/../../include/hw/s390x/ipl +LDFLAGS ?= MAKEFLAGS += -rR GIT_SUBMODULES = roms/SLOF @@ -32,15 +33,21 @@ QEMU_DGFLAGS = -MMD -MP -MT $@ -MF $(@D)/$(*F).d .PHONY : all clean build-all distclean -OBJECTS = start.o main.o bootmap.o jump2ipl.o sclp.o menu.o \ - virtio.o virtio-scsi.o virtio-blkdev.o libc.o cio.o dasd-ipl.o +OBJECTS = start.o main.o bootmap.o jump2ipl.o sclp.o menu.o netmain.o \ + virtio.o virtio-net.o virtio-scsi.o virtio-blkdev.o cio.o dasd-ipl.o + +SLOF_DIR := $(SRC_PATH)/../../roms/SLOF + +LIBC_INC := -nostdinc -I$(SLOF_DIR)/lib/libc/include +LIBNET_INC := -I$(SLOF_DIR)/lib/libnet EXTRA_CFLAGS += -Wall EXTRA_CFLAGS += -ffreestanding -fno-delete-null-pointer-checks -fno-common -fPIE EXTRA_CFLAGS += -fwrapv -fno-strict-aliasing -fno-asynchronous-unwind-tables EXTRA_CFLAGS += -msoft-float EXTRA_CFLAGS += -std=gnu99 -LDFLAGS += -Wl,-pie -nostdlib -z noexecstack +EXTRA_CFLAGS += $(LIBC_INC) $(LIBNET_INC) +EXTRA_LDFLAGS += -Wl,-pie -nostdlib -z noexecstack -z text cc-test = $(CC) -Werror $1 -c -o /dev/null -xc /dev/null >/dev/null 2>/dev/null cc-option = if $(call cc-test, $1); then \ @@ -55,19 +62,64 @@ config-cc.mak: Makefile $(call cc-option,-march=z900,-march=z10)) 3> config-cc.mak -include config-cc.mak -build-all: s390-ccw.img s390-netboot.img +# libc files: + +LIBC_CFLAGS = $(EXTRA_CFLAGS) $(CFLAGS) $(LIBC_INC) $(LIBNET_INC) \ + -MMD -MP -MT $@ -MF $(@:%.o=%.d) + +CTYPE_OBJS = isdigit.o isxdigit.o toupper.o +%.o : $(SLOF_DIR)/lib/libc/ctype/%.c + $(call quiet-command,$(CC) $(LIBC_CFLAGS) -c -o $@ $<,Compiling) + +STRING_OBJS = strcat.o strchr.o strrchr.o strcpy.o strlen.o strncpy.o \ + strcmp.o strncmp.o strcasecmp.o strncasecmp.o strstr.o \ + memset.o memcpy.o memmove.o memcmp.o +%.o : $(SLOF_DIR)/lib/libc/string/%.c + $(call quiet-command,$(CC) $(LIBC_CFLAGS) -c -o $@ $<,Compiling) + +STDLIB_OBJS = atoi.o atol.o strtoul.o strtol.o rand.o malloc.o free.o +%.o : $(SLOF_DIR)/lib/libc/stdlib/%.c + $(call quiet-command,$(CC) $(LIBC_CFLAGS) -c -o $@ $<,Compiling) + +STDIO_OBJS = sprintf.o snprintf.o vfprintf.o vsnprintf.o vsprintf.o fprintf.o \ + printf.o putc.o puts.o putchar.o stdchnls.o fileno.o +%.o : $(SLOF_DIR)/lib/libc/stdio/%.c + $(call quiet-command,$(CC) $(LIBC_CFLAGS) -c -o $@ $<,Compiling) + +sbrk.o: $(SLOF_DIR)/slof/sbrk.c + $(call quiet-command,$(CC) $(LIBC_CFLAGS) -c -o $@ $<,Compiling) + +LIBCOBJS := $(STRING_OBJS) $(CTYPE_OBJS) $(STDLIB_OBJS) $(STDIO_OBJS) sbrk.o -s390-ccw.elf: $(OBJECTS) - $(call quiet-command,$(CC) $(LDFLAGS) -o $@ $(OBJECTS),Linking) +libc.a: $(LIBCOBJS) + $(call quiet-command,$(AR) -rc $@ $^,Creating static library) + +# libnet files: + +LIBNETOBJS := args.o dhcp.o dns.o icmpv6.o ipv6.o tcp.o udp.o bootp.o \ + dhcpv6.o ethernet.o ipv4.o ndp.o tftp.o pxelinux.o +LIBNETCFLAGS = $(EXTRA_CFLAGS) $(CFLAGS) $(LIBC_INC) $(LIBNET_INC) \ + -DDHCPARCH=0x1F -MMD -MP -MT $@ -MF $(@:%.o=%.d) + +%.o : $(SLOF_DIR)/lib/libnet/%.c + $(call quiet-command,$(CC) $(LIBNETCFLAGS) -c -o $@ $<,Compiling) + +libnet.a: $(LIBNETOBJS) + $(call quiet-command,$(AR) -rc $@ $^,Creating static library) + +# Main targets: + +build-all: s390-ccw.img + +s390-ccw.elf: $(OBJECTS) libnet.a libc.a + $(call quiet-command,$(CC) $(EXTRA_LDFLAGS) $(LDFLAGS) -o $@ $^,Linking) s390-ccw.img: s390-ccw.elf $(call quiet-command,$(STRIP) --strip-unneeded $< -o $@,Stripping $< into) $(OBJECTS): Makefile -include $(SRC_PATH)/netboot.mak - -ALL_OBJS = $(sort $(OBJECTS) $(NETOBJS) $(LIBCOBJS) $(LIBNETOBJS)) +ALL_OBJS = $(sort $(OBJECTS) $(LIBCOBJS) $(LIBNETOBJS)) -include $(ALL_OBJS:%.o=%.d) clean: diff --git a/pc-bios/s390-ccw/bootmap.c b/pc-bios/s390-ccw/bootmap.c index a2137449dc..56f2f75640 100644 --- a/pc-bios/s390-ccw/bootmap.c +++ b/pc-bios/s390-ccw/bootmap.c @@ -8,7 +8,8 @@ * directory. */ -#include "libc.h" +#include <string.h> +#include <stdio.h> #include "s390-ccw.h" #include "s390-arch.h" #include "bootmap.h" @@ -21,7 +22,7 @@ #ifdef DEBUG_FALLBACK #define dputs(txt) \ - do { sclp_print("zipl: " txt); } while (0) + do { printf("zipl: " txt); } while (0) #else #define dputs(fmt, ...) \ do { } while (0) @@ -61,15 +62,34 @@ 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) +static inline int verify_boot_info(BootInfo *bip) { - IPL_assert(magic_match(bip->magic, ZIPL_MAGIC), "No zIPL sig in BootInfo"); - IPL_assert(bip->version == BOOT_INFO_VERSION, "Wrong zIPL version"); - IPL_assert(bip->bp_type == BOOT_INFO_BP_TYPE_IPL, "DASD is not for IPL"); - IPL_assert(bip->dev_type == BOOT_INFO_DEV_TYPE_ECKD, "DASD is not ECKD"); - IPL_assert(bip->flags == BOOT_INFO_FLAGS_ARCH, "Not for this arch"); - IPL_assert(block_size_ok(bip->bp.ipl.bm_ptr.eckd.bptr.size), - "Bad block size in zIPL section of the 1st record."); + if (!magic_match(bip->magic, ZIPL_MAGIC)) { + puts("No zIPL sig in BootInfo"); + return -EINVAL; + } + if (bip->version != BOOT_INFO_VERSION) { + puts("Wrong zIPL version"); + return -EINVAL; + } + if (bip->bp_type != BOOT_INFO_BP_TYPE_IPL) { + puts("DASD is not for IPL"); + return -ENODEV; + } + if (bip->dev_type != BOOT_INFO_DEV_TYPE_ECKD) { + puts("DASD is not ECKD"); + return -ENODEV; + } + if (bip->flags != BOOT_INFO_FLAGS_ARCH) { + puts("Not for this arch"); + return -EINVAL; + } + if (!block_size_ok(bip->bp.ipl.bm_ptr.eckd.bptr.size)) { + puts("Bad block size in zIPL section of 1st record"); + return -EINVAL; + } + + return 0; } static void eckd_format_chs(ExtEckdBlockPtr *ptr, bool ldipl, @@ -144,14 +164,17 @@ static block_number_t load_eckd_segments(block_number_t blk, bool ldipl, bool more_data; memset(_bprs, FREE_SPACE_FILLER, sizeof(_bprs)); - read_block(blk, bprs, "BPRS read failed"); + if (virtio_read(blk, bprs)) { + puts("BPRS read failed"); + return ERROR_BLOCK_NR; + } do { more_data = false; for (j = 0;; j++) { block_nr = gen_eckd_block_num(&bprs[j].xeckd, ldipl); if (is_null_block_number(block_nr)) { /* end of chunk */ - break; + return NULL_BLOCK_NR; } /* we need the updated blockno for the next indirect entry @@ -162,15 +185,20 @@ static block_number_t load_eckd_segments(block_number_t blk, bool ldipl, } /* List directed pointer does not store block size */ - IPL_assert(ldipl || block_size_ok(bprs[j].xeckd.bptr.size), - "bad chunk block size"); + if (!ldipl && !block_size_ok(bprs[j].xeckd.bptr.size)) { + puts("Bad chunk block size"); + return ERROR_BLOCK_NR; + } if (!eckd_valid_address(&bprs[j].xeckd, ldipl)) { /* * If an invalid address is found during LD-IPL then break and - * retry as CCW + * retry as CCW-IPL, otherwise abort on error */ - IPL_assert(ldipl, "bad chunk ECKD addr"); + if (!ldipl) { + puts("Bad chunk ECKD address"); + return ERROR_BLOCK_NR; + } break; } @@ -188,7 +216,10 @@ static block_number_t load_eckd_segments(block_number_t blk, bool ldipl, * I.e. the next ptr must point to the unused memory area */ memset(_bprs, FREE_SPACE_FILLER, sizeof(_bprs)); - read_block(block_nr, bprs, "BPRS continuation read failed"); + if (virtio_read(block_nr, bprs)) { + puts("BPRS continuation read failed"); + return ERROR_BLOCK_NR; + } more_data = true; break; } @@ -197,7 +228,10 @@ static block_number_t load_eckd_segments(block_number_t blk, bool ldipl, * to memory (address). */ rc = virtio_read_many(block_nr, (void *)(*address), count + 1); - IPL_assert(rc == 0, "code chunk read failed"); + if (rc != 0) { + puts("Code chunk read failed"); + return ERROR_BLOCK_NR; + } *address += (count + 1) * virtio_get_block_size(); } @@ -231,7 +265,10 @@ static int eckd_get_boot_menu_index(block_number_t s1b_block_nr) /* Get Stage1b data */ memset(sec, FREE_SPACE_FILLER, sizeof(sec)); - read_block(s1b_block_nr, s1b, "Cannot read stage1b boot loader"); + if (virtio_read(s1b_block_nr, s1b)) { + puts("Cannot read stage1b boot loader"); + return -EIO; + } memset(_s2, FREE_SPACE_FILLER, sizeof(_s2)); @@ -243,7 +280,10 @@ static int eckd_get_boot_menu_index(block_number_t s1b_block_nr) break; } - read_block(cur_block_nr, s2_cur_blk, "Cannot read stage2 boot loader"); + if (virtio_read(cur_block_nr, s2_cur_blk)) { + puts("Cannot read stage2 boot loader"); + return -EIO; + } if (find_zipl_boot_menu_banner(&banner_offset)) { /* @@ -251,8 +291,10 @@ static int eckd_get_boot_menu_index(block_number_t s1b_block_nr) * 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 (virtio_read(prev_block_nr, s2_prev_blk)) { + puts("Cannot read stage2 boot loader"); + return -EIO; + } } if (i + 1 < STAGE2_BLK_CNT_MAX) { @@ -260,8 +302,10 @@ static int eckd_get_boot_menu_index(block_number_t s1b_block_nr) } if (next_block_nr && !is_null_block_number(next_block_nr)) { - read_block(next_block_nr, s2_next_blk, - "Cannot read stage2 boot loader"); + if (virtio_read(next_block_nr, s2_next_blk)) { + puts("Cannot read stage2 boot loader"); + return -EIO; + } } return menu_get_zipl_boot_index(s2_cur_blk + banner_offset); @@ -270,11 +314,11 @@ static int eckd_get_boot_menu_index(block_number_t s1b_block_nr) prev_block_nr = cur_block_nr; } - sclp_print("No zipl boot menu data found. Booting default entry."); + printf("No zipl boot menu data found. Booting default entry."); return 0; } -static void run_eckd_boot_script(block_number_t bmt_block_nr, +static int run_eckd_boot_script(block_number_t bmt_block_nr, block_number_t s1b_block_nr) { int i; @@ -291,17 +335,28 @@ static void run_eckd_boot_script(block_number_t bmt_block_nr, } debug_print_int("loadparm", loadparm); - IPL_assert(loadparm < MAX_BOOT_ENTRIES, "loadparm value greater than" - " maximum number of boot entries allowed"); + if (loadparm >= MAX_BOOT_ENTRIES) { + puts("loadparm value greater than max number of boot entries allowed"); + return -EINVAL; + } memset(sec, FREE_SPACE_FILLER, sizeof(sec)); - read_block(bmt_block_nr, sec, "Cannot read Boot Map Table"); + if (virtio_read(bmt_block_nr, sec)) { + puts("Cannot read Boot Map Table"); + return -EIO; + } block_nr = gen_eckd_block_num(&bmt->entry[loadparm].xeckd, ldipl); - IPL_assert(block_nr != -1, "Cannot find Boot Map Table Entry"); + if (block_nr == NULL_BLOCK_NR) { + puts("Cannot find Boot Map Table Entry"); + return -EIO; + } memset(sec, FREE_SPACE_FILLER, sizeof(sec)); - read_block(block_nr, sec, "Cannot read Boot Map Script"); + if (virtio_read(block_nr, sec)) { + puts("Cannot read Boot Map Script"); + return -EIO; + } for (i = 0; bms->entry[i].type == BOOT_SCRIPT_LOAD || bms->entry[i].type == BOOT_SCRIPT_SIGNATURE; i++) { @@ -316,21 +371,27 @@ static void run_eckd_boot_script(block_number_t bmt_block_nr, do { block_nr = load_eckd_segments(block_nr, ldipl, &address); - } while (block_nr != -1); + if (block_nr == ERROR_BLOCK_NR) { + return ldipl ? 0 : -EIO; + } + } while (block_nr != NULL_BLOCK_NR); } if (ldipl && bms->entry[i].type != BOOT_SCRIPT_EXEC) { /* Abort LD-IPL and retry as CCW-IPL */ - return; + return 0; } - IPL_assert(bms->entry[i].type == BOOT_SCRIPT_EXEC, - "Unknown script entry type"); - write_reset_psw(bms->entry[i].address.load_address); /* no return */ - jump_to_IPL_code(0); /* no return */ + if (bms->entry[i].type != BOOT_SCRIPT_EXEC) { + puts("Unknown script entry type"); + return -EINVAL; + } + write_reset_psw(bms->entry[i].address.load_address); + jump_to_IPL_code(0); + return -1; } -static void ipl_eckd_cdl(void) +static int ipl_eckd_cdl(void) { XEckdMbr *mbr; EckdCdlIpl2 *ipl2 = (void *)sec; @@ -338,23 +399,26 @@ static void ipl_eckd_cdl(void) 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"); + puts("CDL"); memset(sec, FREE_SPACE_FILLER, sizeof(sec)); - read_block(1, ipl2, "Cannot read IPL2 record at block 1"); + if (virtio_read(1, ipl2)) { + puts("Cannot read IPL2 record at block 1"); + return -EIO; + } mbr = &ipl2->mbr; if (!magic_match(mbr, ZIPL_MAGIC)) { - sclp_print("No zIPL section in IPL2 record.\n"); - return; + puts("No zIPL section in IPL2 record."); + return 0; } if (!block_size_ok(mbr->blockptr.xeckd.bptr.size)) { - sclp_print("Bad block size in zIPL section of IPL2 record.\n"); - return; + puts("Bad block size in zIPL section of IPL2 record."); + return 0; } if (mbr->dev_type != DEV_TYPE_ECKD) { - sclp_print("Non-ECKD device type in zIPL section of IPL2 record.\n"); - return; + puts("Non-ECKD device type in zIPL section of IPL2 record."); + return 0; } /* save pointer to Boot Map Table */ @@ -364,19 +428,21 @@ static void ipl_eckd_cdl(void) 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"); + if (virtio_read(2, vlbl)) { + puts("Cannot read Volume Label at block 2"); + return -EIO; + } if (!magic_match(vlbl->key, VOL1_MAGIC)) { - sclp_print("Invalid magic of volume label block.\n"); - return; + puts("Invalid magic of volume label block."); + return 0; } if (!magic_match(vlbl->f.key, VOL1_MAGIC)) { - sclp_print("Invalid magic of volser block.\n"); - return; + puts("Invalid magic of volser block."); + return 0; } print_volser(vlbl->f.volser); - run_eckd_boot_script(bmt_block_nr, s1b_block_nr); - /* no return */ + return run_eckd_boot_script(bmt_block_nr, s1b_block_nr); } static void print_eckd_ldl_msg(ECKD_IPL_mode_t mode) @@ -384,8 +450,8 @@ static void print_eckd_ldl_msg(ECKD_IPL_mode_t mode) LDL_VTOC *vlbl = (void *)sec; /* already read, 3rd block */ char msg[4] = { '?', '.', '\n', '\0' }; - sclp_print((mode == ECKD_CMS) ? "CMS" : "LDL"); - sclp_print(" version "); + printf((mode == ECKD_CMS) ? "CMS" : "LDL"); + printf(" version "); switch (vlbl->LDL_version) { case LDL1_VERSION: msg[0] = '1'; @@ -398,11 +464,11 @@ static void print_eckd_ldl_msg(ECKD_IPL_mode_t mode) msg[1] = '?'; break; } - sclp_print(msg); + printf("%s", msg); print_volser(vlbl->volser); } -static void ipl_eckd_ldl(ECKD_IPL_mode_t mode) +static int ipl_eckd_ldl(ECKD_IPL_mode_t mode) { block_number_t bmt_block_nr, s1b_block_nr; EckdLdlIpl1 *ipl1 = (void *)sec; @@ -414,12 +480,15 @@ static void ipl_eckd_ldl(ECKD_IPL_mode_t mode) /* DO NOT read BootMap pointer (only one, xECKD) at block #2 */ memset(sec, FREE_SPACE_FILLER, sizeof(sec)); - read_block(0, sec, "Cannot read block 0 to grab boot info."); + if (virtio_read(0, sec)) { + puts("Cannot read block 0 to grab boot info."); + return -EIO; + } if (mode == ECKD_LDL_UNLABELED) { if (!magic_match(ipl1->bip.magic, ZIPL_MAGIC)) { - return; /* not applicable layout */ + return 0; /* not applicable layout */ } - sclp_print("unlabeled LDL.\n"); + puts("unlabeled LDL."); } verify_boot_info(&ipl1->bip); @@ -429,8 +498,7 @@ static void ipl_eckd_ldl(ECKD_IPL_mode_t mode) /* 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 */ + return run_eckd_boot_script(bmt_block_nr, s1b_block_nr); } static block_number_t eckd_find_bmt(ExtEckdBlockPtr *ptr) @@ -440,7 +508,10 @@ static block_number_t eckd_find_bmt(ExtEckdBlockPtr *ptr) BootRecord *br; blockno = gen_eckd_block_num(ptr, 0); - read_block(blockno, tmp_sec, "Cannot read boot record"); + if (virtio_read(blockno, tmp_sec)) { + puts("Cannot read boot record"); + return ERROR_BLOCK_NR; + } br = (BootRecord *)tmp_sec; if (!magic_match(br->magic, ZIPL_MAGIC)) { /* If the boot record is invalid, return and try CCW-IPL instead */ @@ -466,10 +537,10 @@ static void print_eckd_msg(void) *p-- = ' '; } } - sclp_print(msg); + printf("%s", msg); } -static void ipl_eckd(void) +static int ipl_eckd(void) { IplVolumeLabel *vlbl = (void *)sec; LDL_VTOC *vtoc = (void *)sec; @@ -479,7 +550,10 @@ static void ipl_eckd(void) /* Block 2 can contain either the CDL VOL1 label or the LDL VTOC */ memset(sec, FREE_SPACE_FILLER, sizeof(sec)); - read_block(2, vlbl, "Cannot read block 2"); + if (virtio_read(2, vlbl)) { + puts("Cannot read block 2"); + return -EIO; + } /* * First check for a list-directed-format pointer which would @@ -487,43 +561,60 @@ static void ipl_eckd(void) */ if (eckd_valid_address((ExtEckdBlockPtr *)&vlbl->f.br, 0)) { ldipl_bmt = eckd_find_bmt((ExtEckdBlockPtr *)&vlbl->f.br); - if (ldipl_bmt) { - sclp_print("List-Directed\n"); - /* LD-IPL does not use the S1B bock, just make it NULL */ - run_eckd_boot_script(ldipl_bmt, NULL_BLOCK_NR); - /* Only return in error, retry as CCW-IPL */ - sclp_print("Retrying IPL "); + switch (ldipl_bmt) { + case ERROR_BLOCK_NR: + return -EIO; + case NULL_BLOCK_NR: + break; /* Invalid BMT but the device may still boot with CCW-IPL */ + default: + puts("List-Directed"); + /* + * LD-IPL does not use the S1B bock, just make it NULL_BLOCK_NR. + * In some failure cases retry IPL before aborting. + */ + if (run_eckd_boot_script(ldipl_bmt, NULL_BLOCK_NR)) { + return -EIO; + } + /* Non-fatal error, retry as CCW-IPL */ + printf("Retrying IPL "); print_eckd_msg(); } memset(sec, FREE_SPACE_FILLER, sizeof(sec)); - read_block(2, vtoc, "Cannot read block 2"); + if (virtio_read(2, vtoc)) { + puts("Cannot read block 2"); + return -EIO; + } } /* Not list-directed */ if (magic_match(vtoc->magic, VOL1_MAGIC)) { - ipl_eckd_cdl(); /* may return in error */ + if (ipl_eckd_cdl()) { + return -1; + } } if (magic_match(vtoc->magic, CMS1_MAGIC)) { - ipl_eckd_ldl(ECKD_CMS); /* no return */ + return ipl_eckd_ldl(ECKD_CMS); } if (magic_match(vtoc->magic, LNX1_MAGIC)) { - ipl_eckd_ldl(ECKD_LDL); /* no return */ + return ipl_eckd_ldl(ECKD_LDL); } - ipl_eckd_ldl(ECKD_LDL_UNLABELED); /* it still may return */ + if (ipl_eckd_ldl(ECKD_LDL_UNLABELED)) { + return -1; + } /* * Ok, it is not a LDL by any means. * It still might be a CDL with zero record keys for IPL1 and IPL2 */ - ipl_eckd_cdl(); + return ipl_eckd_cdl(); } /*********************************************************************** * IPL a SCSI disk */ -static void zipl_load_segment(ComponentEntry *entry) +static int zipl_load_segment(ComponentEntry *entry) { const int max_entries = (MAX_SECTOR_SIZE / sizeof(ScsiBlockPtr)); ScsiBlockPtr *bprs = (void *)sec; @@ -543,7 +634,10 @@ static void zipl_load_segment(ComponentEntry *entry) do { memset(bprs, FREE_SPACE_FILLER, bprs_size); fill_hex_val(blk_no, &blockno, sizeof(blockno)); - read_block(blockno, bprs, err_msg); + if (virtio_read(blockno, bprs)) { + puts(err_msg); + return -EIO; + } for (i = 0;; i++) { uint64_t *cur_desc = (void *)&bprs[i]; @@ -571,23 +665,37 @@ static void zipl_load_segment(ComponentEntry *entry) } address = virtio_load_direct(cur_desc[0], cur_desc[1], 0, (void *)address); - IPL_assert(address != -1, "zIPL load segment failed"); + if (!address) { + puts("zIPL load segment failed"); + return -EIO; + } } } while (blockno); + + return 0; } /* Run a zipl program */ -static void zipl_run(ScsiBlockPtr *pte) +static int zipl_run(ScsiBlockPtr *pte) { ComponentHeader *header; ComponentEntry *entry; uint8_t tmp_sec[MAX_SECTOR_SIZE]; - read_block(pte->blockno, tmp_sec, "Cannot read header"); + if (virtio_read(pte->blockno, tmp_sec)) { + puts("Cannot read header"); + return -EIO; + } header = (ComponentHeader *)tmp_sec; - IPL_assert(magic_match(tmp_sec, ZIPL_MAGIC), "No zIPL magic in header"); - IPL_assert(header->type == ZIPL_COMP_HEADER_IPL, "Bad header type"); + if (!magic_match(tmp_sec, ZIPL_MAGIC)) { + puts("No zIPL magic in header"); + return -EINVAL; + } + if (header->type != ZIPL_COMP_HEADER_IPL) { + puts("Bad header type"); + return -EINVAL; + } dputs("start loading images\n"); @@ -602,22 +710,30 @@ static void zipl_run(ScsiBlockPtr *pte) continue; } - zipl_load_segment(entry); + if (zipl_load_segment(entry)) { + return -1; + } entry++; - IPL_assert((uint8_t *)(&entry[1]) <= (tmp_sec + MAX_SECTOR_SIZE), - "Wrong entry value"); + if ((uint8_t *)(&entry[1]) > (tmp_sec + MAX_SECTOR_SIZE)) { + puts("Wrong entry value"); + return -EINVAL; + } } - IPL_assert(entry->component_type == ZIPL_COMP_ENTRY_EXEC, "No EXEC entry"); + if (entry->component_type != ZIPL_COMP_ENTRY_EXEC) { + puts("No EXEC entry"); + return -EINVAL; + } /* should not return */ write_reset_psw(entry->compdat.load_psw); jump_to_IPL_code(0); + return -1; } -static void ipl_scsi(void) +static int ipl_scsi(void) { ScsiMbr *mbr = (void *)sec; int program_table_entries = 0; @@ -628,22 +744,34 @@ static void ipl_scsi(void) /* Grab the MBR */ memset(sec, FREE_SPACE_FILLER, sizeof(sec)); - read_block(0, mbr, "Cannot read block 0"); + if (virtio_read(0, mbr)) { + puts("Cannot read block 0"); + return -EIO; + } if (!magic_match(mbr->magic, ZIPL_MAGIC)) { - return; + return 0; } - sclp_print("Using SCSI scheme.\n"); + puts("Using SCSI scheme."); 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->pt.blockno); - IPL_assert(mbr->pt.blockno, "No Program Table"); + if (!mbr->pt.blockno) { + puts("No Program Table"); + return -EINVAL; + } /* Parse the program table */ - read_block(mbr->pt.blockno, sec, "Error reading Program Table"); - IPL_assert(magic_match(sec, ZIPL_MAGIC), "No zIPL magic in PT"); + if (virtio_read(mbr->pt.blockno, sec)) { + puts("Error reading Program Table"); + return -EIO; + } + if (!magic_match(sec, ZIPL_MAGIC)) { + puts("No zIPL magic in Program Table"); + return -EINVAL; + } for (i = 0; i < MAX_BOOT_ENTRIES; i++) { if (prog_table->entry[i].scsi.blockno) { @@ -653,17 +781,22 @@ static void ipl_scsi(void) } debug_print_int("program table entries", program_table_entries); - IPL_assert(program_table_entries != 0, "Empty Program Table"); + if (program_table_entries == 0) { + puts("Empty Program Table"); + return -EINVAL; + } if (menu_is_enabled_enum()) { loadparm = menu_get_enum_boot_index(valid_entries); } debug_print_int("loadparm", loadparm); - IPL_assert(loadparm < MAX_BOOT_ENTRIES, "loadparm value greater than" - " maximum number of boot entries allowed"); + if (loadparm >= MAX_BOOT_ENTRIES) { + puts("loadparm value greater than max number of boot entries allowed"); + return -EINVAL; + } - zipl_run(&prog_table->entry[loadparm].scsi); /* no return */ + return zipl_run(&prog_table->entry[loadparm].scsi); } /*********************************************************************** @@ -677,8 +810,10 @@ static bool is_iso_bc_entry_compatible(IsoBcSection *s) if (s->unused || !s->sector_count) { return false; } - read_iso_sector(bswap32(s->load_rba), magic_sec, - "Failed to read image sector 0"); + if (virtio_read(bswap32(s->load_rba), magic_sec)) { + puts("Failed to read image sector 0"); + return false; + } /* Checking bytes 8 - 32 for S390 Linux magic */ return !memcmp(magic_sec + 8, linux_s390_magic, 24); @@ -691,28 +826,35 @@ static uint32_t sec_offset[ISO9660_MAX_DIR_DEPTH]; /* Remained directory space in bytes */ static uint32_t dir_rem[ISO9660_MAX_DIR_DEPTH]; -static inline uint32_t iso_get_file_size(uint32_t load_rba) +static inline long iso_get_file_size(uint32_t load_rba) { IsoVolDesc *vd = (IsoVolDesc *)sec; IsoDirHdr *cur_record = &vd->vd.primary.rootdir; uint8_t *temp = sec + ISO_SECTOR_SIZE; int level = 0; - read_iso_sector(ISO_PRIMARY_VD_SECTOR, sec, - "Failed to read ISO primary descriptor"); + if (virtio_read(ISO_PRIMARY_VD_SECTOR, sec)) { + puts("Failed to read ISO primary descriptor"); + return -EIO; + } + sec_loc[0] = iso_733_to_u32(cur_record->ext_loc); dir_rem[0] = 0; sec_offset[0] = 0; while (level >= 0) { - IPL_assert(sec_offset[level] <= ISO_SECTOR_SIZE, - "Directory tree structure violation"); + if (sec_offset[level] > ISO_SECTOR_SIZE) { + puts("Directory tree structure violation"); + return -EIO; + } cur_record = (IsoDirHdr *)(temp + sec_offset[level]); if (sec_offset[level] == 0) { - read_iso_sector(sec_loc[level], temp, - "Failed to read ISO directory"); + if (virtio_read(sec_loc[level], temp)) { + puts("Failed to read ISO directory"); + return -EIO; + } if (dir_rem[level] == 0) { /* Skip self and parent records */ dir_rem[level] = iso_733_to_u32(cur_record->data_len) - @@ -743,7 +885,7 @@ static inline uint32_t iso_get_file_size(uint32_t load_rba) if (cur_record->file_flags & 0x2) { /* Subdirectory */ if (level == ISO9660_MAX_DIR_DEPTH - 1) { - sclp_print("ISO-9660 directory depth limit exceeded\n"); + puts("ISO-9660 directory depth limit exceeded"); } else { level++; sec_loc[level] = iso_733_to_u32(cur_record->ext_loc); @@ -757,8 +899,10 @@ static inline uint32_t iso_get_file_size(uint32_t load_rba) if (dir_rem[level] == 0) { /* Nothing remaining */ level--; - read_iso_sector(sec_loc[level], temp, - "Failed to read ISO directory"); + if (virtio_read(sec_loc[level], temp)) { + puts("Failed to read ISO directory"); + return -EIO; + } } } @@ -773,19 +917,24 @@ static void load_iso_bc_entry(IsoBcSection *load) * is padded and ISO_SECTOR_SIZE bytes aligned */ uint32_t blks_to_load = bswap16(s.sector_count) >> ET_SECTOR_SHIFT; - uint32_t real_size = iso_get_file_size(bswap32(s.load_rba)); + long real_size = iso_get_file_size(bswap32(s.load_rba)); - if (real_size) { + if (real_size > 0) { /* Round up blocks to load */ blks_to_load = (real_size + ISO_SECTOR_SIZE - 1) / ISO_SECTOR_SIZE; - sclp_print("ISO boot image size verified\n"); + puts("ISO boot image size verified"); } else { - sclp_print("ISO boot image size could not be verified\n"); + puts("ISO boot image size could not be verified"); + if (real_size < 0) { + return; + } } - read_iso_boot_image(bswap32(s.load_rba), + if (read_iso_boot_image(bswap32(s.load_rba), (void *)((uint64_t)bswap16(s.load_segment)), - blks_to_load); + blks_to_load)) { + return; + } jump_to_low_kernel(); } @@ -808,17 +957,18 @@ static uint32_t find_iso_bc(void) return bswap32(et->bc_offset); } } - read_iso_sector(block_num++, sec, - "Failed to read ISO volume descriptor"); + if (virtio_read(block_num++, sec)) { + puts("Failed to read ISO volume descriptor"); + return 0; + } } return 0; } -static IsoBcSection *find_iso_bc_entry(void) +static IsoBcSection *find_iso_bc_entry(uint32_t offset) { IsoBcEntry *e = (IsoBcEntry *)sec; - uint32_t offset = find_iso_bc(); int i; unsigned int loadparm = get_loadparm_index(); @@ -826,11 +976,13 @@ static IsoBcSection *find_iso_bc_entry(void) return NULL; } - read_iso_sector(offset, sec, "Failed to read El Torito boot catalog"); + if (virtio_read(offset, sec)) { + puts("Failed to read El Torito boot catalog"); + return NULL; + } if (!is_iso_bc_valid(e)) { /* The validation entry is mandatory */ - panic("No valid boot catalog found!\n"); return NULL; } @@ -850,19 +1002,25 @@ static IsoBcSection *find_iso_bc_entry(void) } } - panic("No suitable boot entry found on ISO-9660 media!\n"); - return NULL; } -static void ipl_iso_el_torito(void) +static int ipl_iso_el_torito(void) { - IsoBcSection *s = find_iso_bc_entry(); + uint32_t offset = find_iso_bc(); + if (!offset) { + return 0; + } + + IsoBcSection *s = find_iso_bc_entry(offset); if (s) { - load_iso_bc_entry(s); - /* no return */ + load_iso_bc_entry(s); /* only return in error */ + return -1; } + + puts("No suitable boot entry found on ISO-9660 media!"); + return -EIO; } /** @@ -884,7 +1042,7 @@ static bool has_iso_signature(void) * Bus specific IPL sequences */ -static void zipl_load_vblk(void) +static int zipl_load_vblk(void) { int blksize = virtio_get_block_size(); @@ -892,26 +1050,30 @@ static void zipl_load_vblk(void) if (blksize != VIRTIO_ISO_BLOCK_SIZE) { virtio_assume_iso9660(); } - ipl_iso_el_torito(); + if (ipl_iso_el_torito()) { + return 0; + } } if (blksize != VIRTIO_DASD_DEFAULT_BLOCK_SIZE) { - sclp_print("Using guessed DASD geometry.\n"); + puts("Using guessed DASD geometry."); virtio_assume_eckd(); } - ipl_eckd(); + return ipl_eckd(); } -static void zipl_load_vscsi(void) +static int zipl_load_vscsi(void) { if (virtio_get_block_size() == VIRTIO_ISO_BLOCK_SIZE) { /* Is it an ISO image in non-CD drive? */ - ipl_iso_el_torito(); + if (ipl_iso_el_torito()) { + return 0; + } } - sclp_print("Using guessed DASD geometry.\n"); + puts("Using guessed DASD geometry."); virtio_assume_eckd(); - ipl_eckd(); + return ipl_eckd(); } /*********************************************************************** @@ -924,14 +1086,20 @@ void zipl_load(void) if (vdev->is_cdrom) { ipl_iso_el_torito(); - panic("\n! Cannot IPL this ISO image !\n"); + puts("Failed to IPL this ISO image!"); + return; } if (virtio_get_device_type() == VIRTIO_ID_NET) { - jump_to_IPL_code(vdev->netboot_start_addr); + netmain(); + puts("Failed to IPL from this network!"); + return; } - ipl_scsi(); + if (ipl_scsi()) { + puts("Failed to IPL from this SCSI device!"); + return; + } switch (virtio_get_device_type()) { case VIRTIO_ID_BLOCK: @@ -941,8 +1109,9 @@ void zipl_load(void) zipl_load_vscsi(); break; default: - panic("\n! Unknown IPL device type !\n"); + puts("Unknown IPL device type!"); + return; } - sclp_print("zIPL load failed.\n"); + puts("zIPL load failed!"); } diff --git a/pc-bios/s390-ccw/bootmap.h b/pc-bios/s390-ccw/bootmap.h index d4690a88c2..95943441d3 100644 --- a/pc-bios/s390-ccw/bootmap.h +++ b/pc-bios/s390-ccw/bootmap.h @@ -16,6 +16,7 @@ typedef uint64_t block_number_t; #define NULL_BLOCK_NR 0xffffffffffffffffULL +#define ERROR_BLOCK_NR 0xfffffffffffffffeULL #define FREE_SPACE_FILLER '\xAA' @@ -336,9 +337,7 @@ static inline void print_volser(const void *volser) ebcdic_to_ascii((char *)volser, ascii, 6); ascii[6] = '\0'; - sclp_print("VOLSER=["); - sclp_print(ascii); - sclp_print("]\n"); + printf("VOLSER=[%s]\n", ascii); } static inline bool unused_space(const void *p, size_t size) @@ -387,17 +386,14 @@ static inline uint32_t iso_733_to_u32(uint64_t x) #define ISO_PRIMARY_VD_SECTOR 16 -static inline void read_iso_sector(uint32_t block_offset, void *buf, - const char *errmsg) -{ - IPL_assert(virtio_read_many(block_offset, buf, 1) == 0, errmsg); -} - -static inline void read_iso_boot_image(uint32_t block_offset, void *load_addr, +static inline int read_iso_boot_image(uint32_t block_offset, void *load_addr, uint32_t blks_to_load) { - IPL_assert(virtio_read_many(block_offset, load_addr, blks_to_load) == 0, - "Failed to read boot image!"); + if (virtio_read_many(block_offset, load_addr, blks_to_load)) { + puts("Failed to read boot image!"); + return -1; + } + return 0; } #define ISO9660_MAX_DIR_DEPTH 8 diff --git a/pc-bios/s390-ccw/cio.c b/pc-bios/s390-ccw/cio.c index 83ca27ab41..5d543da73f 100644 --- a/pc-bios/s390-ccw/cio.c +++ b/pc-bios/s390-ccw/cio.c @@ -11,7 +11,8 @@ * directory. */ -#include "libc.h" +#include <string.h> +#include <stdio.h> #include "s390-ccw.h" #include "s390-arch.h" #include "helper.h" @@ -58,7 +59,8 @@ uint16_t cu_type(SubChannelId schid) }; if (do_cio(schid, CU_TYPE_UNKNOWN, ptr2u32(&sense_id_ccw), CCW_FMT1)) { - panic("Failed to run SenseID CCw\n"); + puts("Failed to run SenseID CCW"); + return CU_TYPE_UNKNOWN; } return sense_data.cu_type; @@ -90,9 +92,9 @@ static void print_eckd_dasd_sense_data(SenseDataEckdDasd *sd) char msgline[512]; if (sd->config_info & 0x8000) { - sclp_print("Eckd Dasd Sense Data (fmt 24-bytes):\n"); + puts("Eckd Dasd Sense Data (fmt 24-bytes):"); } else { - sclp_print("Eckd Dasd Sense Data (fmt 32-bytes):\n"); + puts("Eckd Dasd Sense Data (fmt 32-bytes):"); } strcat(msgline, " Sense Condition Flags :"); @@ -158,22 +160,21 @@ static void print_eckd_dasd_sense_data(SenseDataEckdDasd *sd) if (sd->status[1] & SNS_STAT2_IMPRECISE_END) { strcat(msgline, " [Imprecise-End]"); } - strcat(msgline, "\n"); - sclp_print(msgline); - - print_int(" Residual Count =", sd->res_count); - print_int(" Phys Drive ID =", sd->phys_drive_id); - print_int(" low cyl address =", sd->low_cyl_addr); - print_int(" head addr & hi cyl =", sd->head_high_cyl_addr); - print_int(" format/message =", sd->fmt_msg); - print_int(" fmt-dependent[0-7] =", sd->fmt_dependent_info[0]); - print_int(" fmt-dependent[8-15]=", sd->fmt_dependent_info[1]); - print_int(" prog action code =", sd->program_action_code); - print_int(" Configuration info =", sd->config_info); - print_int(" mcode / hi-cyl =", sd->mcode_hicyl); - print_int(" cyl & head addr [0]=", sd->cyl_head_addr[0]); - print_int(" cyl & head addr [1]=", sd->cyl_head_addr[1]); - print_int(" cyl & head addr [2]=", sd->cyl_head_addr[2]); + puts(msgline); + + printf(" Residual Count = 0x%X\n", sd->res_count); + printf(" Phys Drive ID = 0x%X\n", sd->phys_drive_id); + printf(" low cyl address = 0x%X\n", sd->low_cyl_addr); + printf(" head addr & hi cyl = 0x%X\n", sd->head_high_cyl_addr); + printf(" format/message = 0x%X\n", sd->fmt_msg); + printf(" fmt-dependent[0-7] = 0x%llX\n", sd->fmt_dependent_info[0]); + printf(" fmt-dependent[8-15]= 0x%llX\n", sd->fmt_dependent_info[1]); + printf(" prog action code = 0x%X\n", sd->program_action_code); + printf(" Configuration info = 0x%X\n", sd->config_info); + printf(" mcode / hi-cyl = 0x%X\n", sd->mcode_hicyl); + printf(" cyl & head addr [0]= 0x%X\n", sd->cyl_head_addr[0]); + printf(" cyl & head addr [1]= 0x%X\n", sd->cyl_head_addr[1]); + printf(" cyl & head addr [2]= 0x%X\n", sd->cyl_head_addr[2]); } static void print_irb_err(Irb *irb) @@ -182,7 +183,7 @@ static void print_irb_err(Irb *irb) uint64_t prev_ccw = *(uint64_t *)u32toptr(irb->scsw.cpa - 8); char msgline[256]; - sclp_print("Interrupt Response Block Data:\n"); + puts("Interrupt Response Block Data:"); strcat(msgline, " Function Ctrl :"); if (irb->scsw.ctrl & SCSW_FCTL_START_FUNC) { @@ -194,8 +195,7 @@ static void print_irb_err(Irb *irb) if (irb->scsw.ctrl & SCSW_FCTL_CLEAR_FUNC) { strcat(msgline, " [Clear]"); } - strcat(msgline, "\n"); - sclp_print(msgline); + puts(msgline); msgline[0] = '\0'; strcat(msgline, " Activity Ctrl :"); @@ -220,8 +220,7 @@ static void print_irb_err(Irb *irb) if (irb->scsw.ctrl & SCSW_ACTL_SUSPENDED) { strcat(msgline, " [Suspended]"); } - strcat(msgline, "\n"); - sclp_print(msgline); + puts(msgline); msgline[0] = '\0'; strcat(msgline, " Status Ctrl :"); @@ -240,9 +239,7 @@ static void print_irb_err(Irb *irb) if (irb->scsw.ctrl & SCSW_SCTL_STATUS_PEND) { strcat(msgline, " [Status-Pending]"); } - - strcat(msgline, "\n"); - sclp_print(msgline); + puts(msgline); msgline[0] = '\0'; strcat(msgline, " Device Status :"); @@ -270,8 +267,7 @@ static void print_irb_err(Irb *irb) if (irb->scsw.dstat & SCSW_DSTAT_UEXCP) { strcat(msgline, " [Unit-Exception]"); } - strcat(msgline, "\n"); - sclp_print(msgline); + puts(msgline); msgline[0] = '\0'; strcat(msgline, " Channel Status :"); @@ -299,12 +295,11 @@ static void print_irb_err(Irb *irb) if (irb->scsw.cstat & SCSW_CSTAT_CHAINCHK) { strcat(msgline, " [Chaining-Check]"); } - strcat(msgline, "\n"); - sclp_print(msgline); + puts(msgline); - print_int(" cpa=", irb->scsw.cpa); - print_int(" prev_ccw=", prev_ccw); - print_int(" this_ccw=", this_ccw); + printf(" cpa= 0x%X\n", irb->scsw.cpa); + printf(" prev_ccw= 0x%llX\n", prev_ccw); + printf(" this_ccw= 0x%llX\n", this_ccw); } /* @@ -341,7 +336,7 @@ static int __do_cio(SubChannelId schid, uint32_t ccw_addr, int fmt, Irb *irb) return -1; } if (rc) { - print_int("ssch failed with cc=", rc); + printf("ssch failed with cc= 0x%x\n", rc); return rc; } @@ -350,7 +345,7 @@ static int __do_cio(SubChannelId schid, uint32_t ccw_addr, int fmt, Irb *irb) /* collect status */ rc = tsch(schid, irb); if (rc) { - print_int("tsch failed with cc=", rc); + printf("tsch failed with cc= 0x%X\n", rc); } return rc; @@ -406,12 +401,12 @@ int do_cio(SubChannelId schid, uint16_t cutype, uint32_t ccw_addr, int fmt) continue; } - sclp_print("cio device error\n"); - print_int(" ssid ", schid.ssid); - print_int(" cssid ", schid.cssid); - print_int(" sch_no", schid.sch_no); - print_int(" ctrl-unit type", cutype); - sclp_print("\n"); + printf("cio device error\n"); + printf(" ssid 0x%X\n", schid.ssid); + printf(" cssid 0x%X\n", schid.cssid); + printf(" sch_no 0x%X\n", schid.sch_no); + printf(" ctrl-unit type 0x%X\n", cutype); + printf("\n"); print_irb_err(&irb); if (cutype == CU_TYPE_DASD_3990 || cutype == CU_TYPE_DASD_2107 || cutype == CU_TYPE_UNKNOWN) { diff --git a/pc-bios/s390-ccw/cio.h b/pc-bios/s390-ccw/cio.h index 8b18153deb..6a5e86ba01 100644 --- a/pc-bios/s390-ccw/cio.h +++ b/pc-bios/s390-ccw/cio.h @@ -361,6 +361,8 @@ typedef struct CcwSearchIdData { uint8_t record; } __attribute__((packed)) CcwSearchIdData; +extern SubChannelId net_schid; + int enable_mss_facility(void); void enable_subchannel(SubChannelId schid); uint16_t cu_type(SubChannelId schid); diff --git a/pc-bios/s390-ccw/dasd-ipl.c b/pc-bios/s390-ccw/dasd-ipl.c index 254bb1a15e..babece95ea 100644 --- a/pc-bios/s390-ccw/dasd-ipl.c +++ b/pc-bios/s390-ccw/dasd-ipl.c @@ -8,7 +8,8 @@ * directory. */ -#include "libc.h" +#include <string.h> +#include <stdio.h> #include "s390-ccw.h" #include "s390-arch.h" #include "dasd-ipl.h" @@ -82,7 +83,7 @@ static int run_dynamic_ccw_program(SubChannelId schid, uint16_t cutype, do { has_next = dynamic_cp_fixup(cpa, &next_cpa); - print_int("executing ccw chain at ", cpa); + printf("executing ccw chain at 0x%X\n", cpa); enable_prefixing(); rc = do_cio(schid, cutype, cpa, CCW_FMT0); disable_prefixing(); @@ -110,38 +111,29 @@ static void make_readipl(void) ccwIplRead->count = 0x18; /* Read 0x18 bytes of data */ } -static void run_readipl(SubChannelId schid, uint16_t cutype) +static int run_readipl(SubChannelId schid, uint16_t cutype) { - if (do_cio(schid, cutype, 0x00, CCW_FMT0)) { - panic("dasd-ipl: Failed to run Read IPL channel program\n"); - } + return do_cio(schid, cutype, 0x00, CCW_FMT0); } /* * The architecture states that IPL1 data should consist of a psw followed by * format-0 READ and TIC CCWs. Let's sanity check. */ -static void check_ipl1(void) +static bool check_ipl1(void) { Ccw0 *ccwread = (Ccw0 *)0x08; Ccw0 *ccwtic = (Ccw0 *)0x10; - if (ccwread->cmd_code != CCW_CMD_DASD_READ || - ccwtic->cmd_code != CCW_CMD_TIC) { - panic("dasd-ipl: IPL1 data invalid. Is this disk really bootable?\n"); - } + return (ccwread->cmd_code == CCW_CMD_DASD_READ && + ccwtic->cmd_code == CCW_CMD_TIC); } -static void check_ipl2(uint32_t ipl2_addr) +static bool check_ipl2(uint32_t ipl2_addr) { Ccw0 *ccw = u32toptr(ipl2_addr); - if (ipl2_addr == 0x00) { - panic("IPL2 address invalid. Is this disk really bootable?\n"); - } - if (ccw->cmd_code == 0x00) { - panic("IPL2 ccw data invalid. Is this disk really bootable?\n"); - } + return (ipl2_addr != 0x00 && ccw->cmd_code != 0x00); } static uint32_t read_ipl2_addr(void) @@ -187,52 +179,67 @@ static void ipl1_fixup(void) ccwSearchTic->cda = ptr2u32(ccwSearchID); } -static void run_ipl1(SubChannelId schid, uint16_t cutype) +static int run_ipl1(SubChannelId schid, uint16_t cutype) { uint32_t startAddr = 0x08; - if (do_cio(schid, cutype, startAddr, CCW_FMT0)) { - panic("dasd-ipl: Failed to run IPL1 channel program\n"); - } + return do_cio(schid, cutype, startAddr, CCW_FMT0); } -static void run_ipl2(SubChannelId schid, uint16_t cutype, uint32_t addr) +static int run_ipl2(SubChannelId schid, uint16_t cutype, uint32_t addr) { - if (run_dynamic_ccw_program(schid, cutype, addr)) { - panic("dasd-ipl: Failed to run IPL2 channel program\n"); - } + return run_dynamic_ccw_program(schid, cutype, addr); } /* * Limitations in vfio-ccw support complicate the IPL process. Details can * be found in docs/devel/s390-dasd-ipl.rst */ -void dasd_ipl(SubChannelId schid, uint16_t cutype) +int dasd_ipl(SubChannelId schid, uint16_t cutype) { PSWLegacy *pswl = (PSWLegacy *) 0x00; uint32_t ipl2_addr; /* Construct Read IPL CCW and run it to read IPL1 from boot disk */ make_readipl(); - run_readipl(schid, cutype); + if (run_readipl(schid, cutype)) { + puts("Failed to run Read IPL channel program"); + return -EIO; + } + ipl2_addr = read_ipl2_addr(); - check_ipl1(); + + if (!check_ipl1()) { + puts("IPL1 invalid for DASD-IPL"); + return -EINVAL; + } /* * Fixup IPL1 channel program to account for vfio-ccw limitations, then run * it to read IPL2 channel program from boot disk. */ ipl1_fixup(); - run_ipl1(schid, cutype); - check_ipl2(ipl2_addr); + if (run_ipl1(schid, cutype)) { + puts("Failed to run IPL1 channel program"); + return -EIO; + } + + if (!check_ipl2(ipl2_addr)) { + puts("IPL2 invalid for DASD-IPL"); + return -EINVAL; + } /* * Run IPL2 channel program to read operating system code from boot disk */ - run_ipl2(schid, cutype, ipl2_addr); + if (run_ipl2(schid, cutype, ipl2_addr)) { + puts("Failed to run IPL2 channel program"); + return -EIO; + } /* Transfer control to the guest operating system */ pswl->mask |= PSW_MASK_EAMODE; /* Force z-mode */ pswl->addr |= PSW_MASK_BAMODE; /* ... */ jump_to_low_kernel(); + return -1; } diff --git a/pc-bios/s390-ccw/dasd-ipl.h b/pc-bios/s390-ccw/dasd-ipl.h index c394828906..eb1898c84a 100644 --- a/pc-bios/s390-ccw/dasd-ipl.h +++ b/pc-bios/s390-ccw/dasd-ipl.h @@ -11,6 +11,6 @@ #ifndef DASD_IPL_H #define DASD_IPL_H -void dasd_ipl(SubChannelId schid, uint16_t cutype); +int dasd_ipl(SubChannelId schid, uint16_t cutype); #endif /* DASD_IPL_H */ diff --git a/pc-bios/s390-ccw/iplb.h b/pc-bios/s390-ccw/iplb.h index cb6ac8a880..08f259ff31 100644 --- a/pc-bios/s390-ccw/iplb.h +++ b/pc-bios/s390-ccw/iplb.h @@ -12,88 +12,16 @@ #ifndef IPLB_H #define IPLB_H -#define LOADPARM_LEN 8 - -struct IplBlockCcw { - uint8_t reserved0[85]; - uint8_t ssid; - uint16_t devno; - uint8_t vm_flags; - uint8_t reserved3[3]; - uint32_t vm_parm_len; - uint8_t nss_name[8]; - uint8_t vm_parm[64]; - uint8_t reserved4[8]; -} __attribute__ ((packed)); -typedef struct IplBlockCcw IplBlockCcw; - -struct IplBlockFcp { - uint8_t reserved1[305 - 1]; - uint8_t opt; - uint8_t reserved2[3]; - uint16_t reserved3; - uint16_t devno; - uint8_t reserved4[4]; - uint64_t wwpn; - uint64_t lun; - uint32_t bootprog; - uint8_t reserved5[12]; - uint64_t br_lba; - uint32_t scp_data_len; - uint8_t reserved6[260]; - uint8_t scp_data[]; -} __attribute__ ((packed)); -typedef struct IplBlockFcp IplBlockFcp; - -struct IplBlockQemuScsi { - uint32_t lun; - uint16_t target; - uint16_t channel; - uint8_t reserved0[77]; - uint8_t ssid; - uint16_t devno; -} __attribute__ ((packed)); -typedef struct IplBlockQemuScsi IplBlockQemuScsi; - -struct IplParameterBlock { - uint32_t len; - uint8_t reserved0[3]; - uint8_t version; - uint32_t blk0_len; - uint8_t pbt; - uint8_t flags; - uint16_t reserved01; - uint8_t loadparm[LOADPARM_LEN]; - union { - IplBlockCcw ccw; - IplBlockFcp fcp; - IplBlockQemuScsi scsi; - }; -} __attribute__ ((packed)); -typedef struct IplParameterBlock IplParameterBlock; +#ifndef QEMU_PACKED +#define QEMU_PACKED __attribute__((packed)) +#endif -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 definition - * 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; +#include <qipl.h> +#include <string.h> extern QemuIplParameters qipl; +extern IplParameterBlock iplb __attribute__((__aligned__(PAGE_SIZE))); +extern bool have_iplb; #define S390_IPL_TYPE_FCP 0x00 #define S390_IPL_TYPE_CCW 0x02 @@ -123,4 +51,26 @@ static inline bool set_iplb(IplParameterBlock *iplb) return manage_iplb(iplb, false); } +/* + * The IPL started on the device, but failed in some way. If the IPLB chain + * still has more devices left to try, use the next device in order. + */ +static inline bool load_next_iplb(void) +{ + IplParameterBlock *next_iplb; + + if (qipl.chain_len < 1) { + return false; + } + + qipl.index++; + next_iplb = (IplParameterBlock *) qipl.next_iplb; + memcpy(&iplb, next_iplb, sizeof(IplParameterBlock)); + + qipl.chain_len--; + qipl.next_iplb = qipl.next_iplb + sizeof(IplParameterBlock); + + return true; +} + #endif /* IPLB_H */ diff --git a/pc-bios/s390-ccw/jump2ipl.c b/pc-bios/s390-ccw/jump2ipl.c index 78f5f46533..86321d0f46 100644 --- a/pc-bios/s390-ccw/jump2ipl.c +++ b/pc-bios/s390-ccw/jump2ipl.c @@ -6,7 +6,8 @@ * directory. */ -#include "libc.h" +#include <string.h> +#include <stdio.h> #include "s390-ccw.h" #include "s390-arch.h" @@ -32,16 +33,22 @@ static void jump_to_IPL_addr(void) /* should not return */ } -void jump_to_IPL_code(uint64_t address) +int jump_to_IPL_code(uint64_t address) { /* store the subsystem information _after_ the bootmap was loaded */ write_subsystem_identification(); write_iplb_location(); - /* prevent unknown IPL types in the guest */ + /* + * The IPLB for QEMU SCSI type devices must be rebuilt during re-ipl. The + * iplb.devno is set to the boot position of the target SCSI device. + */ if (iplb.pbt == S390_IPL_TYPE_QEMU_SCSI) { - iplb.pbt = S390_IPL_TYPE_CCW; - set_iplb(&iplb); + iplb.devno = qipl.index; + } + + if (have_iplb && !set_iplb(&iplb)) { + panic("Failed to set IPLB"); } /* @@ -57,7 +64,7 @@ void jump_to_IPL_code(uint64_t address) debug_print_int("set IPL addr to", address ?: *reset_psw & PSW_MASK_SHORT_ADDR); /* Ensure the guest output starts fresh */ - sclp_print("\n"); + printf("\n"); /* * HACK ALERT. @@ -67,7 +74,8 @@ void jump_to_IPL_code(uint64_t address) asm volatile("lghi %%r1,1\n\t" "diag %%r1,%%r1,0x308\n\t" : : : "1", "memory"); - panic("\n! IPL returns !\n"); + puts("IPL code jump failed"); + return -1; } void jump_to_low_kernel(void) diff --git a/pc-bios/s390-ccw/libc.c b/pc-bios/s390-ccw/libc.c deleted file mode 100644 index 3187923950..0000000000 --- a/pc-bios/s390-ccw/libc.c +++ /dev/null @@ -1,88 +0,0 @@ -/* - * libc-style definitions and functions - * - * Copyright 2018 IBM Corp. - * Author(s): Collin L. Walling <walling@linux.vnet.ibm.com> - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the - * Free Software Foundation; either version 2 of the License, or (at your - * option) any later version. - */ - -#include "libc.h" -#include "s390-ccw.h" - -/** - * atoui: - * @str: the string to be converted. - * - * Given a string @str, convert it to an integer. Leading spaces are - * ignored. Any other non-numerical value will terminate the conversion - * and return 0. This function only handles numbers between 0 and - * UINT64_MAX inclusive. - * - * Returns: an integer converted from the string @str, or the number 0 - * if an error occurred. - */ -uint64_t atoui(const char *str) -{ - int val = 0; - - if (!str || !str[0]) { - return 0; - } - - while (*str == ' ') { - str++; - } - - while (*str) { - if (!isdigit(*(unsigned char *)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) -{ - long 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 deleted file mode 100644 index bcdc45732d..0000000000 --- a/pc-bios/s390-ccw/libc.h +++ /dev/null @@ -1,89 +0,0 @@ -/* - * libc-style definitions and functions - * - * Copyright (c) 2013 Alexander Graf <agraf@suse.de> - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the - * Free Software Foundation; either version 2 of the License, or (at your - * option) any later version. - */ - -#ifndef S390_CCW_LIBC_H -#define S390_CCW_LIBC_H - -typedef unsigned long size_t; -typedef int bool; -typedef unsigned char uint8_t; -typedef unsigned short uint16_t; -typedef unsigned int uint32_t; -typedef unsigned long long uint64_t; - -static inline void *memset(void *s, int c, size_t n) -{ - size_t i; - unsigned char *p = s; - - for (i = 0; i < n; i++) { - p[i] = c; - } - - return s; -} - -static inline void *memcpy(void *s1, const void *s2, size_t n) -{ - uint8_t *dest = s1; - const uint8_t *src = s2; - size_t i; - - for (i = 0; i < n; i++) { - dest[i] = src[i]; - } - - 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 char *strcat(char *dest, const char *src) -{ - int i; - char *dest_end = dest + strlen(dest); - - for (i = 0; i <= strlen(src); i++) { - dest_end[i] = src[i]; - } - return dest; -} - -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 5506798098..a4d1c05aac 100644 --- a/pc-bios/s390-ccw/main.c +++ b/pc-bios/s390-ccw/main.c @@ -8,7 +8,9 @@ * directory. */ -#include "libc.h" +#include <stdlib.h> +#include <string.h> +#include <stdio.h> #include "helper.h" #include "s390-arch.h" #include "s390-ccw.h" @@ -21,7 +23,7 @@ static SubChannelId blk_schid = { .one = 1 }; static char loadparm_str[LOADPARM_LEN + 1]; QemuIplParameters qipl; IplParameterBlock iplb __attribute__((__aligned__(PAGE_SIZE))); -static bool have_iplb; +bool have_iplb; static uint16_t cutype; LowCore *lowcore; /* Yes, this *is* a pointer to address 0 */ @@ -36,8 +38,13 @@ LowCore *lowcore; /* Yes, this *is* a pointer to address 0 */ */ void write_subsystem_identification(void) { - lowcore->subchannel_id = blk_schid.sch_id; - lowcore->subchannel_nr = blk_schid.sch_no; + if (cutype == CU_TYPE_VIRTIO && virtio_get_device_type() == VIRTIO_ID_NET) { + lowcore->subchannel_id = net_schid.sch_id; + lowcore->subchannel_nr = net_schid.sch_no; + } else { + lowcore->subchannel_id = blk_schid.sch_id; + lowcore->subchannel_nr = blk_schid.sch_no; + } lowcore->io_int_parm = 0; } @@ -48,9 +55,15 @@ void write_iplb_location(void) } } +static void copy_qipl(void) +{ + QemuIplParameters *early_qipl = (QemuIplParameters *)QIPL_ADDRESS; + memcpy(&qipl, early_qipl, sizeof(QemuIplParameters)); +} + unsigned int get_loadparm_index(void) { - return atoui(loadparm_str); + return atoi(loadparm_str); } static int is_dev_possibly_bootable(int dev_no, int sch_no) @@ -70,6 +83,9 @@ static int is_dev_possibly_bootable(int dev_no, int sch_no) enable_subchannel(blk_schid); cutype = cu_type(blk_schid); + if (cutype == CU_TYPE_UNKNOWN) { + return -EIO; + } /* * Note: we always have to run virtio_is_supported() here to make @@ -142,6 +158,7 @@ static void menu_setup(void) /* If loadparm was set to any other value, then do not enable menu */ if (memcmp(loadparm_str, LOADPARM_EMPTY, LOADPARM_LEN) != 0) { + menu_set_parms(qipl.qipl_flags & ~BOOT_MENU_FLAG_MASK, 0); return; } @@ -174,26 +191,34 @@ static void boot_setup(void) { char lpmsg[] = "LOADPARM=[________]\n"; - sclp_get_loadparm_ascii(loadparm_str); + if (memcmp(iplb.loadparm, NO_LOADPARM, LOADPARM_LEN) != 0) { + ebcdic_to_ascii((char *) iplb.loadparm, loadparm_str, LOADPARM_LEN); + } else { + sclp_get_loadparm_ascii(loadparm_str); + } + + if (have_iplb) { + menu_setup(); + } + memcpy(lpmsg + 10, loadparm_str, 8); - sclp_print(lpmsg); + puts(lpmsg); /* * Clear out any potential S390EP magic (see jump_to_low_kernel()), * so we don't taint our decision-making process during a reboot. */ memset((char *)S390EP, 0, 6); - - have_iplb = store_iplb(&iplb); } -static void find_boot_device(void) +static bool find_boot_device(void) { VDev *vdev = virtio_get_device(); - bool found; + bool found = false; switch (iplb.pbt) { case S390_IPL_TYPE_CCW: + vdev->scsi_device_selected = false; debug_print_int("device no. ", iplb.ccw.devno); blk_schid.ssid = iplb.ccw.ssid & 0x3; debug_print_int("ssid ", blk_schid.ssid); @@ -208,28 +233,20 @@ static void find_boot_device(void) found = find_subch(iplb.scsi.devno); break; default: - panic("List-directed IPL not supported yet!\n"); + puts("Unsupported IPLB"); } - IPL_assert(found, "Boot device not found\n"); + return found; } static int virtio_setup(void) { VDev *vdev = virtio_get_device(); - QemuIplParameters *early_qipl = (QemuIplParameters *)QIPL_ADDRESS; int ret; - memcpy(&qipl, early_qipl, sizeof(QemuIplParameters)); - - if (have_iplb) { - menu_setup(); - } - switch (vdev->senseid.cu_model) { case VIRTIO_ID_NET: - sclp_print("Network boot device detected\n"); - vdev->netboot_start_addr = qipl.netboot_start_addr; + puts("Network boot device detected"); return 0; case VIRTIO_ID_BLOCK: ret = virtio_blk_setup_device(blk_schid); @@ -238,11 +255,13 @@ static int virtio_setup(void) ret = virtio_scsi_setup_device(blk_schid); break; default: - panic("\n! No IPL device available !\n"); + puts("\n! No IPL device available !\n"); + return -1; } - if (!ret) { - IPL_assert(virtio_ipl_disk_is_valid(), "No valid IPL device detected"); + if (!ret && !virtio_ipl_disk_is_valid()) { + puts("No valid IPL device detected"); + return -ENODEV; } return ret; @@ -253,16 +272,15 @@ static void ipl_boot_device(void) switch (cutype) { case CU_TYPE_DASD_3990: case CU_TYPE_DASD_2107: - dasd_ipl(blk_schid, cutype); /* no return */ + dasd_ipl(blk_schid, cutype); break; case CU_TYPE_VIRTIO: if (virtio_setup() == 0) { - zipl_load(); /* Only returns in case of errors */ + zipl_load(); } break; default: - print_int("Attempting to boot from unexpected device type", cutype); - panic("\nBoot failed.\n"); + printf("Attempting to boot from unexpected device type 0x%X\n", cutype); } } @@ -287,20 +305,27 @@ static void probe_boot_device(void) } } - sclp_print("Could not find a suitable boot device (none specified)\n"); + puts("Could not find a suitable boot device (none specified)"); } void main(void) { + copy_qipl(); sclp_setup(); css_setup(); - boot_setup(); - if (have_iplb) { - find_boot_device(); - ipl_boot_device(); - } else { + have_iplb = store_iplb(&iplb); + if (!have_iplb) { probe_boot_device(); } - panic("Failed to load OS from hard disk\n"); + while (have_iplb) { + boot_setup(); + if (have_iplb && find_boot_device()) { + ipl_boot_device(); + } + have_iplb = load_next_iplb(); + } + + panic("No suitable device for IPL. Halting..."); + } diff --git a/pc-bios/s390-ccw/menu.c b/pc-bios/s390-ccw/menu.c index d601952d3e..84062e94af 100644 --- a/pc-bios/s390-ccw/menu.c +++ b/pc-bios/s390-ccw/menu.c @@ -9,7 +9,10 @@ * directory. */ -#include "libc.h" +#include <ctype.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> #include "s390-ccw.h" #include "sclp.h" #include "s390-time.h" @@ -93,7 +96,7 @@ static int read_prompt(char *buf, size_t len) case KEYCODE_BACKSP: if (idx > 0) { buf[--idx] = 0; - sclp_print("\b \b"); + printf("\b \b"); } continue; case KEYCODE_ENTER: @@ -103,7 +106,7 @@ static int read_prompt(char *buf, size_t len) /* Echo input and add to buffer */ if (idx < len) { buf[idx++] = inp[0]; - sclp_print(inp); + printf("%s", inp); } } } @@ -140,22 +143,19 @@ static int get_index(void) } } - return atoui(buf); + return atoi(buf); } static void boot_menu_prompt(bool retry) { - char tmp[11]; - if (retry) { - sclp_print("\nError: undefined configuration" + printf("\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"); + printf("Please choose (default will boot in %d seconds):\n", + (int)(timeout / 1000)); } else { - sclp_print("Please choose:\n"); + puts("Please choose:"); } } @@ -163,7 +163,6 @@ static int get_boot_index(bool *valid_entries) { int boot_index; bool retry = false; - char tmp[5]; do { boot_menu_prompt(retry); @@ -172,8 +171,7 @@ static int get_boot_index(bool *valid_entries) } while (boot_index < 0 || boot_index >= MAX_BOOT_ENTRIES || !valid_entries[boot_index]); - sclp_print("\nBooting entry #"); - sclp_print(uitoa(boot_index, tmp, sizeof(tmp))); + printf("\nBooting entry #%d", boot_index); return boot_index; } @@ -187,9 +185,9 @@ static int zipl_print_entry(const char *data, size_t len) buf[len] = '\n'; buf[len + 1] = '\0'; - sclp_print(buf); + printf("%s", buf); - return buf[0] == ' ' ? atoui(buf + 1) : atoui(buf); + return buf[0] == ' ' ? atoi(buf + 1) : atoi(buf); } int menu_get_zipl_boot_index(const char *menu_data) @@ -209,7 +207,7 @@ int menu_get_zipl_boot_index(const char *menu_data) } /* Print banner */ - sclp_print("s390-ccw zIPL Boot Menu\n\n"); + puts("s390-ccw zIPL Boot Menu\n"); menu_data += strlen(menu_data) + 1; /* Print entries */ @@ -221,37 +219,34 @@ int menu_get_zipl_boot_index(const char *menu_data) valid_entries[entry] = true; if (entry == 0) { - sclp_print("\n"); + printf("\n"); } } - sclp_print("\n"); + printf("\n"); return get_boot_index(valid_entries); } int menu_get_enum_boot_index(bool *valid_entries) { - char tmp[3]; int i; - sclp_print("s390-ccw Enumerated Boot Menu.\n\n"); + puts("s390-ccw Enumerated Boot Menu.\n"); for (i = 0; i < MAX_BOOT_ENTRIES; i++) { if (valid_entries[i]) { if (i < 10) { - sclp_print(" "); + printf(" "); } - sclp_print("["); - sclp_print(uitoa(i, tmp, sizeof(tmp))); - sclp_print("]"); + printf("[%d]", i); if (i == 0) { - sclp_print(" default\n"); + printf(" default\n"); } - sclp_print("\n"); + printf("\n"); } } - sclp_print("\n"); + printf("\n"); return get_boot_index(valid_entries); } diff --git a/pc-bios/s390-ccw/netboot.mak b/pc-bios/s390-ccw/netboot.mak deleted file mode 100644 index 046aa35587..0000000000 --- a/pc-bios/s390-ccw/netboot.mak +++ /dev/null @@ -1,62 +0,0 @@ - -SLOF_DIR := $(SRC_PATH)/../../roms/SLOF - -NETOBJS := start.o sclp.o cio.o virtio.o virtio-net.o jump2ipl.o netmain.o - -LIBC_INC := -nostdinc -I$(SLOF_DIR)/lib/libc/include -LIBNET_INC := -I$(SLOF_DIR)/lib/libnet - -NETLDFLAGS := $(LDFLAGS) -Wl,-Ttext=0x7800000 - -$(NETOBJS): EXTRA_CFLAGS += $(LIBC_INC) $(LIBNET_INC) - -s390-netboot.elf: $(NETOBJS) libnet.a libc.a - $(call quiet-command,$(CC) $(NETLDFLAGS) -o $@ $^,Linking) - -s390-netboot.img: s390-netboot.elf - $(call quiet-command,$(STRIP) --strip-unneeded $< -o $@,Stripping $< into) - -# libc files: - -LIBC_CFLAGS = $(EXTRA_CFLAGS) $(CFLAGS) $(LIBC_INC) $(LIBNET_INC) \ - -MMD -MP -MT $@ -MF $(@:%.o=%.d) - -CTYPE_OBJS = isdigit.o isxdigit.o toupper.o -%.o : $(SLOF_DIR)/lib/libc/ctype/%.c - $(call quiet-command,$(CC) $(LIBC_CFLAGS) -c -o $@ $<,Compiling) - -STRING_OBJS = strcat.o strchr.o strrchr.o strcpy.o strlen.o strncpy.o \ - strcmp.o strncmp.o strcasecmp.o strncasecmp.o strstr.o \ - memset.o memcpy.o memmove.o memcmp.o -%.o : $(SLOF_DIR)/lib/libc/string/%.c - $(call quiet-command,$(CC) $(LIBC_CFLAGS) -c -o $@ $<,Compiling) - -STDLIB_OBJS = atoi.o atol.o strtoul.o strtol.o rand.o malloc.o free.o -%.o : $(SLOF_DIR)/lib/libc/stdlib/%.c - $(call quiet-command,$(CC) $(LIBC_CFLAGS) -c -o $@ $<,Compiling) - -STDIO_OBJS = sprintf.o snprintf.o vfprintf.o vsnprintf.o vsprintf.o fprintf.o \ - printf.o putc.o puts.o putchar.o stdchnls.o fileno.o -%.o : $(SLOF_DIR)/lib/libc/stdio/%.c - $(call quiet-command,$(CC) $(LIBC_CFLAGS) -c -o $@ $<,Compiling) - -sbrk.o: $(SLOF_DIR)/slof/sbrk.c - $(call quiet-command,$(CC) $(LIBC_CFLAGS) -c -o $@ $<,Compiling) - -LIBCOBJS := $(STRING_OBJS) $(CTYPE_OBJS) $(STDLIB_OBJS) $(STDIO_OBJS) sbrk.o - -libc.a: $(LIBCOBJS) - $(call quiet-command,$(AR) -rc $@ $^,Creating static library) - -# libnet files: - -LIBNETOBJS := args.o dhcp.o dns.o icmpv6.o ipv6.o tcp.o udp.o bootp.o \ - dhcpv6.o ethernet.o ipv4.o ndp.o tftp.o pxelinux.o -LIBNETCFLAGS = $(EXTRA_CFLAGS) $(CFLAGS) $(LIBC_INC) $(LIBNET_INC) \ - -DDHCPARCH=0x1F -MMD -MP -MT $@ -MF $(@:%.o=%.d) - -%.o : $(SLOF_DIR)/lib/libnet/%.c - $(call quiet-command,$(CC) $(LIBNETCFLAGS) -c -o $@ $<,Compiling) - -libnet.a: $(LIBNETOBJS) - $(call quiet-command,$(AR) -rc $@ $^,Creating static library) diff --git a/pc-bios/s390-ccw/netmain.c b/pc-bios/s390-ccw/netmain.c index 5cd619b2d6..e46e470db4 100644 --- a/pc-bios/s390-ccw/netmain.c +++ b/pc-bios/s390-ccw/netmain.c @@ -41,7 +41,6 @@ #define DEFAULT_TFTP_RETRIES 20 extern char _start[]; -void write_iplb_location(void) {} #define KERNEL_ADDR ((void *)0L) #define KERNEL_MAX_SIZE ((long)_start) @@ -50,10 +49,9 @@ void write_iplb_location(void) {} /* STSI 3.2.2 offset of first vmdb + offset of uuid inside vmdb */ #define STSI322_VMDB_UUID_OFFSET ((8 + 12) * 4) -IplParameterBlock iplb __attribute__((aligned(PAGE_SIZE))); static char cfgbuf[2048]; -static SubChannelId net_schid = { .one = 1 }; +SubChannelId net_schid = { .one = 1 }; static uint8_t mac[6]; static uint64_t dest_timer; @@ -293,7 +291,7 @@ static int load_kernel_with_initrd(filename_ip_t *fn_ip, printf("Loading pxelinux.cfg entry '%s'\n", entry->label); if (!entry->kernel) { - printf("Kernel entry is missing!\n"); + puts("Kernel entry is missing!\n"); return -1; } @@ -438,15 +436,6 @@ static int net_try_direct_tftp_load(filename_ip_t *fn_ip) return rc; } -void write_subsystem_identification(void) -{ - SubChannelId *schid = (SubChannelId *) 184; - uint32_t *zeroes = (uint32_t *) 188; - - *schid = net_schid; - *zeroes = 0; -} - static bool find_net_dev(Schib *schib, int dev_no) { int i, r; @@ -475,7 +464,7 @@ static bool find_net_dev(Schib *schib, int dev_no) return false; } -static void virtio_setup(void) +static bool virtio_setup(void) { Schib schib; int ssid; @@ -489,7 +478,7 @@ static void virtio_setup(void) */ enable_mss_facility(); - if (store_iplb(&iplb)) { + if (have_iplb || store_iplb(&iplb)) { IPL_assert(iplb.pbt == S390_IPL_TYPE_CCW, "IPL_TYPE_CCW expected"); dev_no = iplb.ccw.devno; debug_print_int("device no. ", dev_no); @@ -506,22 +495,26 @@ static void virtio_setup(void) } } - IPL_assert(found, "No virtio net device found"); + return found; } -void main(void) +int netmain(void) { filename_ip_t fn_ip; int rc, fnlen; sclp_setup(); - sclp_print("Network boot starting...\n"); + puts("Network boot starting..."); - virtio_setup(); + if (!virtio_setup()) { + puts("No virtio net device found."); + return -1; + } rc = net_init(&fn_ip); if (rc) { - panic("Network initialization failed. Halting.\n"); + puts("Network initialization failed."); + return -1; } fnlen = strlen(fn_ip.filename); @@ -535,9 +528,10 @@ void main(void) net_release(&fn_ip); if (rc > 0) { - sclp_print("Network loading done, starting kernel...\n"); + puts("Network loading done, starting kernel..."); jump_to_low_kernel(); } - panic("Failed to load OS from network\n"); + puts("Failed to load OS from network."); + return -1; } diff --git a/pc-bios/s390-ccw/s390-ccw.h b/pc-bios/s390-ccw/s390-ccw.h index c977a52b50..6cdce3e5e5 100644 --- a/pc-bios/s390-ccw/s390-ccw.h +++ b/pc-bios/s390-ccw/s390-ccw.h @@ -13,6 +13,11 @@ /* #define DEBUG */ +#include <stdbool.h> +#include <stddef.h> +#include <stdint.h> +#include <stdio.h> + typedef unsigned char u8; typedef unsigned short u16; typedef unsigned int u32; @@ -25,10 +30,8 @@ typedef unsigned long long u64; #define EIO 1 #define EBUSY 2 #define ENODEV 3 +#define EINVAL 4 -#ifndef NULL -#define NULL 0 -#endif #ifndef MIN #define MIN(a, b) (((a) < (b)) ? (a) : (b)) #endif @@ -53,6 +56,9 @@ void write_iplb_location(void); unsigned int get_loadparm_index(void); void main(void); +/* netmain.c */ +int netmain(void); + /* sclp.c */ void sclp_print(const char *string); void sclp_set_write_mask(uint32_t receive_mask, uint32_t send_mask); @@ -72,7 +78,7 @@ void zipl_load(void); /* jump2ipl.c */ void write_reset_psw(uint64_t psw); -void jump_to_IPL_code(uint64_t address); +int jump_to_IPL_code(uint64_t address); void jump_to_low_kernel(void); /* menu.c */ @@ -87,7 +93,7 @@ bool menu_is_enabled_enum(void); __attribute__ ((__noreturn__)) static inline void panic(const char *string) { - sclp_print(string); + printf("ERROR: %s\n ", string); disabled_wait(); } @@ -109,20 +115,10 @@ static inline void fill_hex_val(char *out, void *ptr, unsigned size) } } -static inline void print_int(const char *desc, u64 addr) -{ - char out[] = ": 0xffffffffffffffff\n"; - - fill_hex_val(&out[4], &addr, sizeof(addr)); - - sclp_print(desc); - sclp_print(out); -} - static inline void debug_print_int(const char *desc, u64 addr) { #ifdef DEBUG - print_int(desc, addr); + printf("%s 0x%X\n", desc, addr); #endif } @@ -147,18 +143,14 @@ static inline void debug_print_addr(const char *desc, void *p) static inline void IPL_assert(bool term, const char *message) { if (!term) { - sclp_print("\n! "); - sclp_print(message); - panic(" !\n"); /* no return */ + panic(message); /* no return */ } } static inline void IPL_check(bool term, const char *message) { if (!term) { - sclp_print("\n! WARNING: "); - sclp_print(message); - sclp_print(" !\n"); + printf("WARNING: %s\n", message); } } diff --git a/pc-bios/s390-ccw/sclp.c b/pc-bios/s390-ccw/sclp.c index 7251f9af4d..4a07de018d 100644 --- a/pc-bios/s390-ccw/sclp.c +++ b/pc-bios/s390-ccw/sclp.c @@ -8,7 +8,7 @@ * directory. */ -#include "libc.h" +#include <string.h> #include "s390-ccw.h" #include "sclp.h" @@ -101,11 +101,6 @@ long write(int fd, const void *str, size_t len) return len; } -void sclp_print(const char *str) -{ - write(1, str, strlen(str)); -} - void sclp_get_loadparm_ascii(char *loadparm) { diff --git a/pc-bios/s390-ccw/start.S b/pc-bios/s390-ccw/start.S index 061b06591c..b70213e412 100644 --- a/pc-bios/s390-ccw/start.S +++ b/pc-bios/s390-ccw/start.S @@ -112,9 +112,7 @@ io_new_code: lctlg %c6,%c6,0(%r15) br %r14 - .align 8 -bss_start_literal: - .quad __bss_start + .balign 8 disabled_wait_psw: .quad 0x0002000180000000,0x0000000000000000 enabled_wait_psw: @@ -124,8 +122,13 @@ external_new_mask: io_new_mask: .quad 0x0000000180000000 +.data + .balign 8 +bss_start_literal: + .quad __bss_start + .bss - .align 8 + .balign 8 stack: .space STACK_SIZE .size stack,STACK_SIZE diff --git a/pc-bios/s390-ccw/virtio-blkdev.c b/pc-bios/s390-ccw/virtio-blkdev.c index a81207b52e..7b2d1e20f4 100644 --- a/pc-bios/s390-ccw/virtio-blkdev.c +++ b/pc-bios/s390-ccw/virtio-blkdev.c @@ -8,7 +8,7 @@ * directory. */ -#include "libc.h" +#include <stdio.h> #include "s390-ccw.h" #include "virtio.h" #include "virtio-scsi.h" @@ -59,7 +59,7 @@ int virtio_read_many(unsigned long sector, void *load_addr, int sec_num) case VIRTIO_ID_SCSI: return virtio_scsi_read_many(vdev, sector, load_addr, sec_num); } - panic("\n! No readable IPL device !\n"); + return -1; } @@ -73,13 +73,13 @@ unsigned long virtio_load_direct(unsigned long rec_list1, unsigned long rec_list unsigned long addr = (unsigned long)load_addr; if (sec_len != virtio_get_block_size()) { - return -1; + return 0; } - sclp_print("."); + printf("."); status = virtio_read_many(sec, (void *)addr, sec_num); if (status) { - panic("I/O Error"); + return 0; } addr += sec_num * virtio_get_block_size(); @@ -230,7 +230,7 @@ int virtio_blk_setup_device(SubChannelId schid) vdev->schid = schid; virtio_setup_ccw(vdev); - sclp_print("Using virtio-blk.\n"); + puts("Using virtio-blk."); return 0; } diff --git a/pc-bios/s390-ccw/virtio-net.c b/pc-bios/s390-ccw/virtio-net.c index 2fcb0a58c5..f9854a22c3 100644 --- a/pc-bios/s390-ccw/virtio-net.c +++ b/pc-bios/s390-ccw/virtio-net.c @@ -54,8 +54,11 @@ int virtio_net_init(void *mac_addr) vdev->guest_features[0] = VIRTIO_NET_F_MAC_BIT; virtio_setup_ccw(vdev); - IPL_assert(vdev->guest_features[0] & VIRTIO_NET_F_MAC_BIT, - "virtio-net device does not support the MAC address feature"); + if (!(vdev->guest_features[0] & VIRTIO_NET_F_MAC_BIT)) { + puts("virtio-net device does not support the MAC address feature"); + return -1; + } + memcpy(mac_addr, vdev->config.net.mac, ETH_ALEN); for (i = 0; i < 64; i++) { diff --git a/pc-bios/s390-ccw/virtio-scsi.c b/pc-bios/s390-ccw/virtio-scsi.c index d1a84b937c..71db75ce7b 100644 --- a/pc-bios/s390-ccw/virtio-scsi.c +++ b/pc-bios/s390-ccw/virtio-scsi.c @@ -9,7 +9,8 @@ * directory. */ -#include "libc.h" +#include <string.h> +#include <stdio.h> #include "s390-ccw.h" #include "virtio.h" #include "scsi.h" @@ -25,20 +26,22 @@ static uint8_t scsi_inquiry_std_response[256]; static ScsiInquiryEvpdPages scsi_inquiry_evpd_pages_response; static ScsiInquiryEvpdBl scsi_inquiry_evpd_bl_response; -static inline void vs_assert(bool term, const char **msgs) +static inline bool vs_assert(bool term, const char **msgs) { if (!term) { int i = 0; - sclp_print("\n! "); + printf("\n! "); while (msgs[i]) { - sclp_print(msgs[i++]); + printf("%s", msgs[i++]); } - panic(" !\n"); + puts(" !"); } + + return term; } -static void virtio_scsi_verify_response(VirtioScsiCmdResp *resp, +static bool virtio_scsi_verify_response(VirtioScsiCmdResp *resp, const char *title) { const char *mr[] = { @@ -55,8 +58,8 @@ static void virtio_scsi_verify_response(VirtioScsiCmdResp *resp, 0 }; - vs_assert(resp->response == VIRTIO_SCSI_S_OK, mr); - vs_assert(resp->status == CDB_STATUS_GOOD, ms); + return vs_assert(resp->response == VIRTIO_SCSI_S_OK, mr) && + vs_assert(resp->status == CDB_STATUS_GOOD, ms); } static void prepare_request(VDev *vdev, const void *cdb, int cdb_size, @@ -77,24 +80,31 @@ static void prepare_request(VDev *vdev, const void *cdb, int cdb_size, } } -static inline void vs_io_assert(bool term, const char *msg) +static inline bool vs_io_assert(bool term, const char *msg) { - if (!term) { - virtio_scsi_verify_response(&resp, msg); + if (!term && !virtio_scsi_verify_response(&resp, msg)) { + return false; } + + return true; } -static void vs_run(const char *title, VirtioCmd *cmd, VDev *vdev, +static int vs_run(const char *title, VirtioCmd *cmd, VDev *vdev, const void *cdb, int cdb_size, void *data, uint32_t data_size) { prepare_request(vdev, cdb, cdb_size, data, data_size); - vs_io_assert(virtio_run(vdev, VR_REQUEST, cmd) == 0, title); + if (!vs_io_assert(virtio_run(vdev, VR_REQUEST, cmd) == 0, title)) { + puts(title); + return -EIO; + } + + return 0; } /* SCSI protocol implementation routines */ -static bool scsi_inquiry(VDev *vdev, uint8_t evpd, uint8_t page, +static int scsi_inquiry(VDev *vdev, uint8_t evpd, uint8_t page, void *data, uint32_t data_size) { ScsiCdbInquiry cdb = { @@ -109,12 +119,13 @@ static bool scsi_inquiry(VDev *vdev, uint8_t evpd, uint8_t page, { data, data_size, VRING_DESC_F_WRITE }, }; - vs_run("inquiry", inquiry, vdev, &cdb, sizeof(cdb), data, data_size); + int ret = vs_run("inquiry", inquiry, + vdev, &cdb, sizeof(cdb), data, data_size); - return virtio_scsi_response_ok(&resp); + return ret ? ret : virtio_scsi_response_ok(&resp); } -static bool scsi_test_unit_ready(VDev *vdev) +static int scsi_test_unit_ready(VDev *vdev) { ScsiCdbTestUnitReady cdb = { .command = 0x00, @@ -130,7 +141,7 @@ static bool scsi_test_unit_ready(VDev *vdev) return virtio_scsi_response_ok(&resp); } -static bool scsi_report_luns(VDev *vdev, void *data, uint32_t data_size) +static int scsi_report_luns(VDev *vdev, void *data, uint32_t data_size) { ScsiCdbReportLuns cdb = { .command = 0xa0, @@ -143,13 +154,13 @@ static bool scsi_report_luns(VDev *vdev, void *data, uint32_t data_size) { data, data_size, VRING_DESC_F_WRITE }, }; - vs_run("report luns", report_luns, + int ret = vs_run("report luns", report_luns, vdev, &cdb, sizeof(cdb), data, data_size); - return virtio_scsi_response_ok(&resp); + return ret ? ret : virtio_scsi_response_ok(&resp); } -static bool scsi_read_10(VDev *vdev, +static int scsi_read_10(VDev *vdev, unsigned long sector, int sectors, void *data, unsigned int data_size) { @@ -167,12 +178,13 @@ static bool scsi_read_10(VDev *vdev, debug_print_int("read_10 sector", sector); debug_print_int("read_10 sectors", sectors); - vs_run("read(10)", read_10, vdev, &cdb, sizeof(cdb), data, data_size); + int ret = vs_run("read(10)", read_10, + vdev, &cdb, sizeof(cdb), data, data_size); - return virtio_scsi_response_ok(&resp); + return ret ? ret : virtio_scsi_response_ok(&resp); } -static bool scsi_read_capacity(VDev *vdev, +static int scsi_read_capacity(VDev *vdev, void *data, uint32_t data_size) { ScsiCdbReadCapacity16 cdb = { @@ -186,10 +198,10 @@ static bool scsi_read_capacity(VDev *vdev, { data, data_size, VRING_DESC_F_WRITE }, }; - vs_run("read capacity", read_capacity_16, + int ret = vs_run("read capacity", read_capacity_16, vdev, &cdb, sizeof(cdb), data, data_size); - return virtio_scsi_response_ok(&resp); + return ret ? ret : virtio_scsi_response_ok(&resp); } /* virtio-scsi routines */ @@ -206,7 +218,7 @@ static int virtio_scsi_locate_device(VDev *vdev) static uint8_t data[16 + 8 * 63]; ScsiLunReport *r = (void *) data; ScsiDevice *sdev = vdev->scsi_device; - int i, luns; + int i, ret, luns; /* QEMU has hardcoded channel #0 in many places. * If this hardcoded value is ever changed, we'll need to add code for @@ -232,15 +244,23 @@ static int virtio_scsi_locate_device(VDev *vdev) sdev->channel = channel; sdev->target = target; sdev->lun = 0; /* LUN has to be 0 for REPORT LUNS */ - if (!scsi_report_luns(vdev, data, sizeof(data))) { + ret = scsi_report_luns(vdev, data, sizeof(data)); + if (ret < 0) { + return ret; + } + + else if (ret == 0) { if (resp.response == VIRTIO_SCSI_S_BAD_TARGET) { continue; } - print_int("target", target); - virtio_scsi_verify_response(&resp, "SCSI cannot report LUNs"); + printf("target 0x%X\n", target); + if (!virtio_scsi_verify_response(&resp, "SCSI cannot report LUNs")) { + return -EIO; + } } + if (r->lun_list_len == 0) { - print_int("no LUNs for target", target); + printf("no LUNs for target 0x%X\n", target); continue; } luns = r->lun_list_len / 8; @@ -264,7 +284,7 @@ static int virtio_scsi_locate_device(VDev *vdev) } } - sclp_print("Warning: Could not locate a usable virtio-scsi device\n"); + puts("Warning: Could not locate a usable virtio-scsi device"); return -ENODEV; } @@ -282,7 +302,9 @@ int virtio_scsi_read_many(VDev *vdev, data_size = sector_count * virtio_get_block_size() * f; if (!scsi_read_10(vdev, sector * f, sector_count * f, load_addr, data_size)) { - virtio_scsi_verify_response(&resp, "virtio-scsi:read_many"); + if (!virtio_scsi_verify_response(&resp, "virtio-scsi:read_many")) { + return -1; + } } load_addr += data_size; sector += sector_count; @@ -351,11 +373,16 @@ static int virtio_scsi_setup(VDev *vdev) uint8_t code = resp.sense[0] & SCSI_SENSE_CODE_MASK; uint8_t sense_key = resp.sense[2] & SCSI_SENSE_KEY_MASK; - IPL_assert(resp.sense_len != 0, "virtio-scsi:setup: no SENSE data"); + if (resp.sense_len == 0) { + puts("virtio-scsi: setup: no SENSE data"); + return -EINVAL; + } - IPL_assert(retry_test_unit_ready && code == 0x70 && - sense_key == SCSI_SENSE_KEY_UNIT_ATTENTION, - "virtio-scsi:setup: cannot retry"); + if (!retry_test_unit_ready || code != 0x70 || + sense_key != SCSI_SENSE_KEY_UNIT_ATTENTION) { + puts("virtio-scsi:setup: cannot retry"); + return -EIO; + } /* retry on CHECK_CONDITION/UNIT_ATTENTION as it * may not designate a real error, but it may be @@ -366,30 +393,40 @@ static int virtio_scsi_setup(VDev *vdev) continue; } - virtio_scsi_verify_response(&resp, "virtio-scsi:setup"); + if (!virtio_scsi_verify_response(&resp, "virtio-scsi:setup")) { + return -1; + } } /* read and cache SCSI INQUIRY response */ - if (!scsi_inquiry(vdev, + ret = scsi_inquiry(vdev, SCSI_INQUIRY_STANDARD, SCSI_INQUIRY_STANDARD_NONE, scsi_inquiry_std_response, - sizeof(scsi_inquiry_std_response))) { - virtio_scsi_verify_response(&resp, "virtio-scsi:setup:inquiry"); + sizeof(scsi_inquiry_std_response)); + if (ret < 1) { + if (ret != 0 || !virtio_scsi_verify_response(&resp, + "virtio-scsi:setup:inquiry")) { + return -1; + } } if (virtio_scsi_inquiry_response_is_cdrom(scsi_inquiry_std_response)) { - sclp_print("SCSI CD-ROM detected.\n"); + puts("SCSI CD-ROM detected."); vdev->is_cdrom = true; vdev->scsi_block_size = VIRTIO_ISO_BLOCK_SIZE; } - if (!scsi_inquiry(vdev, + ret = scsi_inquiry(vdev, SCSI_INQUIRY_EVPD, SCSI_INQUIRY_EVPD_SUPPORTED_PAGES, evpd, - sizeof(*evpd))) { - virtio_scsi_verify_response(&resp, "virtio-scsi:setup:supported_pages"); + sizeof(*evpd)); + if (ret < 1) { + if (ret != 0 || !virtio_scsi_verify_response(&resp, + "virtio-scsi:setup:supported_pages")) { + return -1; + } } debug_print_int("EVPD length", evpd->page_length); @@ -401,12 +438,16 @@ static int virtio_scsi_setup(VDev *vdev) continue; } - if (!scsi_inquiry(vdev, + ret = scsi_inquiry(vdev, SCSI_INQUIRY_EVPD, SCSI_INQUIRY_EVPD_BLOCK_LIMITS, evpd_bl, - sizeof(*evpd_bl))) { - virtio_scsi_verify_response(&resp, "virtio-scsi:setup:blocklimits"); + sizeof(*evpd_bl)); + if (ret < 1) { + if (ret != 0 || !virtio_scsi_verify_response(&resp, + "virtio-scsi:setup:blocklimits")) { + return -1; + } } debug_print_int("max transfer", evpd_bl->max_transfer); @@ -422,8 +463,12 @@ static int virtio_scsi_setup(VDev *vdev) vdev->max_transfer = MIN_NON_ZERO(VIRTIO_SCSI_MAX_SECTORS, vdev->max_transfer); - if (!scsi_read_capacity(vdev, data, data_size)) { - virtio_scsi_verify_response(&resp, "virtio-scsi:setup:read_capacity"); + ret = scsi_read_capacity(vdev, data, data_size); + if (ret < 1) { + if (ret != 0 || !virtio_scsi_verify_response(&resp, + "virtio-scsi:setup:read_capacity")) { + return -1; + } } scsi_parse_capacity_report(data, &vdev->scsi_last_block, (uint32_t *) &vdev->scsi_block_size); @@ -438,12 +483,17 @@ int virtio_scsi_setup_device(SubChannelId schid) vdev->schid = schid; virtio_setup_ccw(vdev); - IPL_assert(vdev->config.scsi.sense_size == VIRTIO_SCSI_SENSE_SIZE, - "Config: sense size mismatch"); - IPL_assert(vdev->config.scsi.cdb_size == VIRTIO_SCSI_CDB_SIZE, - "Config: CDB size mismatch"); + if (vdev->config.scsi.sense_size != VIRTIO_SCSI_SENSE_SIZE) { + puts("Config: sense size mismatch"); + return -EINVAL; + } + + if (vdev->config.scsi.cdb_size != VIRTIO_SCSI_CDB_SIZE) { + puts("Config: CDB size mismatch"); + return -EINVAL; + } - sclp_print("Using virtio-scsi.\n"); + puts("Using virtio-scsi."); return virtio_scsi_setup(vdev); } diff --git a/pc-bios/s390-ccw/virtio.c b/pc-bios/s390-ccw/virtio.c index 5edd058d88..8b5a370bb3 100644 --- a/pc-bios/s390-ccw/virtio.c +++ b/pc-bios/s390-ccw/virtio.c @@ -8,7 +8,7 @@ * directory. */ -#include "libc.h" +#include <string.h> #include "s390-ccw.h" #include "cio.h" #include "virtio.h" @@ -217,16 +217,19 @@ int virtio_run(VDev *vdev, int vqid, VirtioCmd *cmd) return 0; } -void virtio_setup_ccw(VDev *vdev) +int virtio_setup_ccw(VDev *vdev) { - int i, rc, cfg_size = 0; + int i, cfg_size = 0; uint8_t status; struct VirtioFeatureDesc { uint32_t features; uint8_t index; } __attribute__((packed)) feats; - IPL_assert(virtio_is_supported(vdev->schid), "PE"); + if (!virtio_is_supported(vdev->schid)) { + puts("Virtio unsupported for this device ID"); + return -ENODEV; + } /* device ID has been established now */ vdev->config.blk.blk_size = 0; /* mark "illegal" - setup started... */ @@ -235,8 +238,10 @@ void virtio_setup_ccw(VDev *vdev) run_ccw(vdev, CCW_CMD_VDEV_RESET, NULL, 0, false); status = VIRTIO_CONFIG_S_ACKNOWLEDGE; - rc = run_ccw(vdev, CCW_CMD_WRITE_STATUS, &status, sizeof(status), false); - IPL_assert(rc == 0, "Could not write ACKNOWLEDGE status to host"); + if (run_ccw(vdev, CCW_CMD_WRITE_STATUS, &status, sizeof(status), false)) { + puts("Could not write ACKNOWLEDGE status to host"); + return -EIO; + } switch (vdev->senseid.cu_model) { case VIRTIO_ID_NET: @@ -255,27 +260,37 @@ void virtio_setup_ccw(VDev *vdev) cfg_size = sizeof(vdev->config.scsi); break; default: - panic("Unsupported virtio device\n"); + puts("Unsupported virtio device"); + return -ENODEV; } status |= VIRTIO_CONFIG_S_DRIVER; - rc = run_ccw(vdev, CCW_CMD_WRITE_STATUS, &status, sizeof(status), false); - IPL_assert(rc == 0, "Could not write DRIVER status to host"); + if (run_ccw(vdev, CCW_CMD_WRITE_STATUS, &status, sizeof(status), false)) { + puts("Could not write DRIVER status to host"); + return -EIO; + } /* Feature negotiation */ for (i = 0; i < ARRAY_SIZE(vdev->guest_features); i++) { feats.features = 0; feats.index = i; - rc = run_ccw(vdev, CCW_CMD_READ_FEAT, &feats, sizeof(feats), false); - IPL_assert(rc == 0, "Could not get features bits"); + if (run_ccw(vdev, CCW_CMD_READ_FEAT, &feats, sizeof(feats), false)) { + puts("Could not get features bits"); + return -EIO; + } + vdev->guest_features[i] &= bswap32(feats.features); feats.features = bswap32(vdev->guest_features[i]); - rc = run_ccw(vdev, CCW_CMD_WRITE_FEAT, &feats, sizeof(feats), false); - IPL_assert(rc == 0, "Could not set features bits"); + if (run_ccw(vdev, CCW_CMD_WRITE_FEAT, &feats, sizeof(feats), false)) { + puts("Could not set features bits"); + return -EIO; + } } - rc = run_ccw(vdev, CCW_CMD_READ_CONF, &vdev->config, cfg_size, false); - IPL_assert(rc == 0, "Could not get virtio device configuration"); + if (run_ccw(vdev, CCW_CMD_READ_CONF, &vdev->config, cfg_size, false)) { + puts("Could not get virtio device configuration"); + return -EIO; + } for (i = 0; i < vdev->nr_vqs; i++) { VqInfo info = { @@ -289,19 +304,27 @@ void virtio_setup_ccw(VDev *vdev) .num = 0, }; - rc = run_ccw(vdev, CCW_CMD_READ_VQ_CONF, &config, sizeof(config), false); - IPL_assert(rc == 0, "Could not get virtio device VQ configuration"); + if (run_ccw(vdev, CCW_CMD_READ_VQ_CONF, &config, sizeof(config), + false)) { + puts("Could not get virtio device VQ config"); + return -EIO; + } info.num = config.num; vring_init(&vdev->vrings[i], &info); vdev->vrings[i].schid = vdev->schid; - IPL_assert( - run_ccw(vdev, CCW_CMD_SET_VQ, &info, sizeof(info), false) == 0, - "Cannot set VQ info"); + if (run_ccw(vdev, CCW_CMD_SET_VQ, &info, sizeof(info), false)) { + puts("Cannot set VQ info"); + return -EIO; + } } status |= VIRTIO_CONFIG_S_DRIVER_OK; - rc = run_ccw(vdev, CCW_CMD_WRITE_STATUS, &status, sizeof(status), false); - IPL_assert(rc == 0, "Could not write DRIVER_OK status to host"); + if (run_ccw(vdev, CCW_CMD_WRITE_STATUS, &status, sizeof(status), false)) { + puts("Could not write DRIVER_OK status to host"); + return -EIO; + } + + return 0; } bool virtio_is_supported(SubChannelId schid) diff --git a/pc-bios/s390-ccw/virtio.h b/pc-bios/s390-ccw/virtio.h index 85bd9d1695..9faf3986b1 100644 --- a/pc-bios/s390-ccw/virtio.h +++ b/pc-bios/s390-ccw/virtio.h @@ -253,7 +253,6 @@ struct VDev { uint8_t scsi_dev_heads; bool scsi_device_selected; ScsiDevice selected_scsi_device; - uint64_t netboot_start_addr; uint32_t max_transfer; uint32_t guest_features[2]; }; @@ -275,7 +274,7 @@ void vring_send_buf(VRing *vr, void *p, int len, int flags); int vr_poll(VRing *vr); int vring_wait_reply(void); int virtio_run(VDev *vdev, int vqid, VirtioCmd *cmd); -void virtio_setup_ccw(VDev *vdev); +int virtio_setup_ccw(VDev *vdev); int virtio_net_init(void *mac_addr); diff --git a/pc-bios/s390-netboot.img b/pc-bios/s390-netboot.img Binary files differdeleted file mode 100644 index 6908e49f06..0000000000 --- a/pc-bios/s390-netboot.img +++ /dev/null diff --git a/qapi/block-core.json b/qapi/block-core.json index 2feae8e697..fd3bcc1c17 100644 --- a/qapi/block-core.json +++ b/qapi/block-core.json @@ -5584,6 +5584,8 @@ # # Emitted when a disk I/O error occurs # +# @qom-path: path to the device object in the QOM tree (since 9.2) +# # @device: device name. This is always present for compatibility # reasons, but it can be empty ("") if the image does not have a # device name associated. @@ -5609,12 +5611,15 @@ # .. note:: If action is "stop", a STOP event will eventually follow # the BLOCK_IO_ERROR event. # +# .. note:: This event is rate-limited. +# # Since: 0.13 # # .. qmp-example:: # # <- { "event": "BLOCK_IO_ERROR", -# "data": { "device": "ide0-hd1", +# "data": { "qom-path": "/machine/unattached/device[0]", +# "device": "ide0-hd1", # "node-name": "#block212", # "operation": "write", # "action": "stop", @@ -5622,7 +5627,7 @@ # "timestamp": { "seconds": 1265044230, "microseconds": 450486 } } ## { 'event': 'BLOCK_IO_ERROR', - 'data': { 'device': 'str', '*node-name': 'str', + 'data': { 'qom-path': 'str', 'device': 'str', '*node-name': 'str', 'operation': 'IoOperationType', 'action': 'BlockErrorAction', '*nospace': 'bool', 'reason': 'str' } } diff --git a/system/vl.c b/system/vl.c index e83b3b2608..d217b3d64d 100644 --- a/system/vl.c +++ b/system/vl.c @@ -1841,7 +1841,8 @@ static void object_option_parse(const char *str) type = qemu_opt_get(opts, "qom-type"); if (!type) { - error_setg(&error_fatal, QERR_MISSING_PARAMETER, "qom-type"); + error_report(QERR_MISSING_PARAMETER, "qom-type"); + exit(1); } if (user_creatable_print_help(type, opts)) { exit(0); diff --git a/target/i386/tcg/sysemu/excp_helper.c b/target/i386/tcg/sysemu/excp_helper.c index 8fb05b1f53..da187c8792 100644 --- a/target/i386/tcg/sysemu/excp_helper.c +++ b/target/i386/tcg/sysemu/excp_helper.c @@ -60,14 +60,13 @@ typedef struct PTETranslate { hwaddr gaddr; } PTETranslate; -static bool ptw_translate(PTETranslate *inout, hwaddr addr, uint64_t ra) +static bool ptw_translate(PTETranslate *inout, hwaddr addr) { - CPUTLBEntryFull *full; int flags; inout->gaddr = addr; - flags = probe_access_full(inout->env, addr, 0, MMU_DATA_STORE, - inout->ptw_idx, true, &inout->haddr, &full, ra); + flags = probe_access_full_mmu(inout->env, addr, 0, MMU_DATA_STORE, + inout->ptw_idx, &inout->haddr, NULL); if (unlikely(flags & TLB_INVALID_MASK)) { TranslateFault *err = inout->err; @@ -150,6 +149,7 @@ static bool mmu_translate(CPUX86State *env, const TranslateParams *in, uint32_t pkr; int page_size; int error_code; + int prot; restart_all: rsvd_mask = ~MAKE_64BIT_MASK(0, env_archcpu(env)->phys_bits); @@ -166,7 +166,7 @@ static bool mmu_translate(CPUX86State *env, const TranslateParams *in, * Page table level 5 */ pte_addr = (in->cr3 & ~0xfff) + (((addr >> 48) & 0x1ff) << 3); - if (!ptw_translate(&pte_trans, pte_addr, ra)) { + if (!ptw_translate(&pte_trans, pte_addr)) { return false; } restart_5: @@ -190,7 +190,7 @@ static bool mmu_translate(CPUX86State *env, const TranslateParams *in, * Page table level 4 */ pte_addr = (pte & PG_ADDRESS_MASK) + (((addr >> 39) & 0x1ff) << 3); - if (!ptw_translate(&pte_trans, pte_addr, ra)) { + if (!ptw_translate(&pte_trans, pte_addr)) { return false; } restart_4: @@ -210,7 +210,7 @@ static bool mmu_translate(CPUX86State *env, const TranslateParams *in, * Page table level 3 */ pte_addr = (pte & PG_ADDRESS_MASK) + (((addr >> 30) & 0x1ff) << 3); - if (!ptw_translate(&pte_trans, pte_addr, ra)) { + if (!ptw_translate(&pte_trans, pte_addr)) { return false; } restart_3_lma: @@ -237,7 +237,7 @@ static bool mmu_translate(CPUX86State *env, const TranslateParams *in, * Page table level 3 */ pte_addr = (in->cr3 & 0xffffffe0ULL) + ((addr >> 27) & 0x18); - if (!ptw_translate(&pte_trans, pte_addr, ra)) { + if (!ptw_translate(&pte_trans, pte_addr)) { return false; } rsvd_mask |= PG_HI_USER_MASK; @@ -259,7 +259,7 @@ static bool mmu_translate(CPUX86State *env, const TranslateParams *in, * Page table level 2 */ pte_addr = (pte & PG_ADDRESS_MASK) + (((addr >> 21) & 0x1ff) << 3); - if (!ptw_translate(&pte_trans, pte_addr, ra)) { + if (!ptw_translate(&pte_trans, pte_addr)) { return false; } restart_2_pae: @@ -285,7 +285,7 @@ static bool mmu_translate(CPUX86State *env, const TranslateParams *in, * Page table level 1 */ pte_addr = (pte & PG_ADDRESS_MASK) + (((addr >> 12) & 0x1ff) << 3); - if (!ptw_translate(&pte_trans, pte_addr, ra)) { + if (!ptw_translate(&pte_trans, pte_addr)) { return false; } pte = ptw_ldq(&pte_trans, ra); @@ -298,12 +298,12 @@ static bool mmu_translate(CPUX86State *env, const TranslateParams *in, /* combine pde and pte nx, user and rw protections */ ptep &= pte ^ PG_NX_MASK; page_size = 4096; - } else { + } else if (pg_mode) { /* * Page table level 2 */ pte_addr = (in->cr3 & 0xfffff000ULL) + ((addr >> 20) & 0xffc); - if (!ptw_translate(&pte_trans, pte_addr, ra)) { + if (!ptw_translate(&pte_trans, pte_addr)) { return false; } restart_2_nopae: @@ -332,7 +332,7 @@ static bool mmu_translate(CPUX86State *env, const TranslateParams *in, * Page table level 1 */ pte_addr = (pte & ~0xfffu) + ((addr >> 10) & 0xffc); - if (!ptw_translate(&pte_trans, pte_addr, ra)) { + if (!ptw_translate(&pte_trans, pte_addr)) { return false; } pte = ptw_ldl(&pte_trans, ra); @@ -343,6 +343,15 @@ static bool mmu_translate(CPUX86State *env, const TranslateParams *in, ptep &= pte | PG_NX_MASK; page_size = 4096; rsvd_mask = 0; + } else { + /* + * No paging (real mode), let's tentatively resolve the address as 1:1 + * here, but conditionally still perform an NPT walk on it later. + */ + page_size = 0x40000000; + paddr = in->addr; + prot = PAGE_READ | PAGE_WRITE | PAGE_EXEC; + goto stage2; } do_check_protect: @@ -358,7 +367,7 @@ do_check_protect_pse36: goto do_fault_protect; } - int prot = 0; + prot = 0; if (!is_mmu_index_smap(in->mmu_idx) || !(ptep & PG_USER_MASK)) { prot |= PAGE_READ; if ((ptep & PG_RW_MASK) || !(is_user || (pg_mode & PG_MODE_WP))) { @@ -420,6 +429,7 @@ do_check_protect_pse36: /* merge offset within page */ paddr = (pte & PG_ADDRESS_MASK & ~(page_size - 1)) | (addr & (page_size - 1)); + stage2: /* * Note that NPT is walked (for both paging structures and final guest @@ -429,9 +439,8 @@ do_check_protect_pse36: CPUTLBEntryFull *full; int flags, nested_page_size; - flags = probe_access_full(env, paddr, 0, access_type, - MMU_NESTED_IDX, true, - &pte_trans.haddr, &full, 0); + flags = probe_access_full_mmu(env, paddr, 0, access_type, + MMU_NESTED_IDX, &pte_trans.haddr, &full); if (unlikely(flags & TLB_INVALID_MASK)) { *err = (TranslateFault){ .error_code = env->error_code, @@ -562,7 +571,7 @@ static bool get_physical_address(CPUX86State *env, vaddr addr, addr = (uint32_t)addr; } - if (likely(env->cr[0] & CR0_PG_MASK)) { + if (likely(env->cr[0] & CR0_PG_MASK || use_stage2)) { in.cr3 = env->cr[3]; in.mmu_idx = mmu_idx; in.ptw_idx = use_stage2 ? MMU_NESTED_IDX : MMU_PHYS_IDX; diff --git a/target/s390x/diag.c b/target/s390x/diag.c index 27ffd48576..a1fd54ddac 100644 --- a/target/s390x/diag.c +++ b/target/s390x/diag.c @@ -133,7 +133,14 @@ void handle_diag_308(CPUS390XState *env, uint64_t r1, uint64_t r3, uintptr_t ra) valid = subcode == DIAG308_PV_SET ? iplb_valid_pv(iplb) : iplb_valid(iplb); if (!valid) { - env->regs[r1 + 1] = DIAG_308_RC_INVALID; + if (subcode == DIAG308_SET && iplb->pbt == S390_IPL_TYPE_QEMU_SCSI) { + s390_rebuild_iplb(iplb->devno, iplb); + s390_ipl_update_diag308(iplb); + env->regs[r1 + 1] = DIAG_308_RC_OK; + } else { + env->regs[r1 + 1] = DIAG_308_RC_INVALID; + } + goto out; } diff --git a/tcg/ppc/tcg-target.c.inc b/tcg/ppc/tcg-target.c.inc index 223f079524..9a11c26fd3 100644 --- a/tcg/ppc/tcg-target.c.inc +++ b/tcg/ppc/tcg-target.c.inc @@ -911,7 +911,9 @@ static void tcg_out_rld(TCGContext *s, int op, TCGReg ra, TCGReg rs, static void tcg_out_rlw_rc(TCGContext *s, int op, TCGReg ra, TCGReg rs, int sh, int mb, int me, bool rc) { - tcg_out32(s, op | RA(ra) | RS(rs) | SH(sh) | MB(mb) | ME(me) | rc); + tcg_debug_assert((mb & 0x1f) == mb); + tcg_debug_assert((me & 0x1f) == me); + tcg_out32(s, op | RA(ra) | RS(rs) | SH(sh & 0x1f) | MB(mb) | ME(me) | rc); } static void tcg_out_rlw(TCGContext *s, int op, TCGReg ra, TCGReg rs, diff --git a/tcg/riscv/tcg-target-con-set.h b/tcg/riscv/tcg-target-con-set.h index aac5ceee2b..3c4ef44eb0 100644 --- a/tcg/riscv/tcg-target-con-set.h +++ b/tcg/riscv/tcg-target-con-set.h @@ -21,3 +21,12 @@ C_O1_I2(r, rZ, rZ) C_N1_I2(r, r, rM) C_O1_I4(r, r, rI, rM, rM) C_O2_I4(r, r, rZ, rZ, rM, rM) +C_O0_I2(v, r) +C_O1_I1(v, r) +C_O1_I1(v, v) +C_O1_I2(v, v, r) +C_O1_I2(v, v, v) +C_O1_I2(v, vK, v) +C_O1_I2(v, v, vK) +C_O1_I2(v, v, vL) +C_O1_I4(v, v, vL, vK, vK) diff --git a/tcg/riscv/tcg-target-con-str.h b/tcg/riscv/tcg-target-con-str.h index d5c419dff1..089efe96ca 100644 --- a/tcg/riscv/tcg-target-con-str.h +++ b/tcg/riscv/tcg-target-con-str.h @@ -9,6 +9,7 @@ * REGS(letter, register_mask) */ REGS('r', ALL_GENERAL_REGS) +REGS('v', ALL_VECTOR_REGS) /* * Define constraint letters for constants: @@ -16,6 +17,8 @@ REGS('r', ALL_GENERAL_REGS) */ CONST('I', TCG_CT_CONST_S12) CONST('J', TCG_CT_CONST_J12) +CONST('K', TCG_CT_CONST_S5) +CONST('L', TCG_CT_CONST_CMP_VI) CONST('N', TCG_CT_CONST_N12) CONST('M', TCG_CT_CONST_M12) CONST('Z', TCG_CT_CONST_ZERO) diff --git a/tcg/riscv/tcg-target.c.inc b/tcg/riscv/tcg-target.c.inc index d334857226..f8331e4688 100644 --- a/tcg/riscv/tcg-target.c.inc +++ b/tcg/riscv/tcg-target.c.inc @@ -32,38 +32,14 @@ #ifdef CONFIG_DEBUG_TCG static const char * const tcg_target_reg_names[TCG_TARGET_NB_REGS] = { - "zero", - "ra", - "sp", - "gp", - "tp", - "t0", - "t1", - "t2", - "s0", - "s1", - "a0", - "a1", - "a2", - "a3", - "a4", - "a5", - "a6", - "a7", - "s2", - "s3", - "s4", - "s5", - "s6", - "s7", - "s8", - "s9", - "s10", - "s11", - "t3", - "t4", - "t5", - "t6" + "zero", "ra", "sp", "gp", "tp", "t0", "t1", "t2", + "s0", "s1", "a0", "a1", "a2", "a3", "a4", "a5", + "a6", "a7", "s2", "s3", "s4", "s5", "s6", "s7", + "s8", "s9", "s10", "s11", "t3", "t4", "t5", "t6", + "v0", "v1", "v2", "v3", "v4", "v5", "v6", "v7", + "v8", "v9", "v10", "v11", "v12", "v13", "v14", "v15", + "v16", "v17", "v18", "v19", "v20", "v21", "v22", "v23", + "v24", "v25", "v26", "v27", "v28", "v29", "v30", "v31", }; #endif @@ -100,6 +76,16 @@ static const int tcg_target_reg_alloc_order[] = { TCG_REG_A5, TCG_REG_A6, TCG_REG_A7, + + /* Vector registers and TCG_REG_V0 reserved for mask. */ + TCG_REG_V1, TCG_REG_V2, TCG_REG_V3, TCG_REG_V4, + TCG_REG_V5, TCG_REG_V6, TCG_REG_V7, TCG_REG_V8, + TCG_REG_V9, TCG_REG_V10, TCG_REG_V11, TCG_REG_V12, + TCG_REG_V13, TCG_REG_V14, TCG_REG_V15, TCG_REG_V16, + TCG_REG_V17, TCG_REG_V18, TCG_REG_V19, TCG_REG_V20, + TCG_REG_V21, TCG_REG_V22, TCG_REG_V23, TCG_REG_V24, + TCG_REG_V25, TCG_REG_V26, TCG_REG_V27, TCG_REG_V28, + TCG_REG_V29, TCG_REG_V30, TCG_REG_V31, }; static const int tcg_target_call_iarg_regs[] = { @@ -120,62 +106,50 @@ static TCGReg tcg_target_call_oarg_reg(TCGCallReturnKind kind, int slot) return TCG_REG_A0 + slot; } -#define TCG_CT_CONST_ZERO 0x100 -#define TCG_CT_CONST_S12 0x200 -#define TCG_CT_CONST_N12 0x400 -#define TCG_CT_CONST_M12 0x800 -#define TCG_CT_CONST_J12 0x1000 +#define TCG_CT_CONST_ZERO 0x100 +#define TCG_CT_CONST_S12 0x200 +#define TCG_CT_CONST_N12 0x400 +#define TCG_CT_CONST_M12 0x800 +#define TCG_CT_CONST_J12 0x1000 +#define TCG_CT_CONST_S5 0x2000 +#define TCG_CT_CONST_CMP_VI 0x4000 #define ALL_GENERAL_REGS MAKE_64BIT_MASK(0, 32) +#define ALL_VECTOR_REGS MAKE_64BIT_MASK(32, 32) +#define ALL_DVECTOR_REG_GROUPS 0x5555555500000000 +#define ALL_QVECTOR_REG_GROUPS 0x1111111100000000 #define sextreg sextract64 -/* test if a constant matches the constraint */ -static bool tcg_target_const_match(int64_t val, int ct, - TCGType type, TCGCond cond, int vece) -{ - if (ct & TCG_CT_CONST) { - return 1; - } - if ((ct & TCG_CT_CONST_ZERO) && val == 0) { - return 1; - } - /* - * Sign extended from 12 bits: [-0x800, 0x7ff]. - * Used for most arithmetic, as this is the isa field. - */ - if ((ct & TCG_CT_CONST_S12) && val >= -0x800 && val <= 0x7ff) { - return 1; - } - /* - * Sign extended from 12 bits, negated: [-0x7ff, 0x800]. - * Used for subtraction, where a constant must be handled by ADDI. - */ - if ((ct & TCG_CT_CONST_N12) && val >= -0x7ff && val <= 0x800) { - return 1; - } - /* - * Sign extended from 12 bits, +/- matching: [-0x7ff, 0x7ff]. - * Used by addsub2 and movcond, which may need the negative value, - * and requires the modified constant to be representable. - */ - if ((ct & TCG_CT_CONST_M12) && val >= -0x7ff && val <= 0x7ff) { - return 1; - } - /* - * Inverse of sign extended from 12 bits: ~[-0x800, 0x7ff]. - * Used to map ANDN back to ANDI, etc. - */ - if ((ct & TCG_CT_CONST_J12) && ~val >= -0x800 && ~val <= 0x7ff) { - return 1; - } - return 0; -} - /* * RISC-V Base ISA opcodes (IM) */ +#define V_OPIVV (0x0 << 12) +#define V_OPFVV (0x1 << 12) +#define V_OPMVV (0x2 << 12) +#define V_OPIVI (0x3 << 12) +#define V_OPIVX (0x4 << 12) +#define V_OPFVF (0x5 << 12) +#define V_OPMVX (0x6 << 12) +#define V_OPCFG (0x7 << 12) + +/* NF <= 7 && NF >= 0 */ +#define V_NF(x) (x << 29) +#define V_UNIT_STRIDE (0x0 << 20) +#define V_UNIT_STRIDE_WHOLE_REG (0x8 << 20) + +typedef enum { + VLMUL_M1 = 0, /* LMUL=1 */ + VLMUL_M2, /* LMUL=2 */ + VLMUL_M4, /* LMUL=4 */ + VLMUL_M8, /* LMUL=8 */ + VLMUL_RESERVED, + VLMUL_MF8, /* LMUL=1/8 */ + VLMUL_MF4, /* LMUL=1/4 */ + VLMUL_MF2, /* LMUL=1/2 */ +} RISCVVlmul; + typedef enum { OPC_ADD = 0x33, OPC_ADDI = 0x13, @@ -271,8 +245,199 @@ typedef enum { /* Zicond: integer conditional operations */ OPC_CZERO_EQZ = 0x0e005033, OPC_CZERO_NEZ = 0x0e007033, + + /* V: Vector extension 1.0 */ + OPC_VSETVLI = 0x57 | V_OPCFG, + OPC_VSETIVLI = 0xc0000057 | V_OPCFG, + OPC_VSETVL = 0x80000057 | V_OPCFG, + + OPC_VLE8_V = 0x7 | V_UNIT_STRIDE, + OPC_VLE16_V = 0x5007 | V_UNIT_STRIDE, + OPC_VLE32_V = 0x6007 | V_UNIT_STRIDE, + OPC_VLE64_V = 0x7007 | V_UNIT_STRIDE, + OPC_VSE8_V = 0x27 | V_UNIT_STRIDE, + OPC_VSE16_V = 0x5027 | V_UNIT_STRIDE, + OPC_VSE32_V = 0x6027 | V_UNIT_STRIDE, + OPC_VSE64_V = 0x7027 | V_UNIT_STRIDE, + + OPC_VL1RE64_V = 0x2007007 | V_UNIT_STRIDE_WHOLE_REG | V_NF(0), + OPC_VL2RE64_V = 0x2007007 | V_UNIT_STRIDE_WHOLE_REG | V_NF(1), + OPC_VL4RE64_V = 0x2007007 | V_UNIT_STRIDE_WHOLE_REG | V_NF(3), + OPC_VL8RE64_V = 0x2007007 | V_UNIT_STRIDE_WHOLE_REG | V_NF(7), + + OPC_VS1R_V = 0x2000027 | V_UNIT_STRIDE_WHOLE_REG | V_NF(0), + OPC_VS2R_V = 0x2000027 | V_UNIT_STRIDE_WHOLE_REG | V_NF(1), + OPC_VS4R_V = 0x2000027 | V_UNIT_STRIDE_WHOLE_REG | V_NF(3), + OPC_VS8R_V = 0x2000027 | V_UNIT_STRIDE_WHOLE_REG | V_NF(7), + + OPC_VMERGE_VIM = 0x5c000057 | V_OPIVI, + OPC_VMERGE_VVM = 0x5c000057 | V_OPIVV, + + OPC_VADD_VV = 0x57 | V_OPIVV, + OPC_VADD_VI = 0x57 | V_OPIVI, + OPC_VSUB_VV = 0x8000057 | V_OPIVV, + OPC_VRSUB_VI = 0xc000057 | V_OPIVI, + OPC_VAND_VV = 0x24000057 | V_OPIVV, + OPC_VAND_VI = 0x24000057 | V_OPIVI, + OPC_VOR_VV = 0x28000057 | V_OPIVV, + OPC_VOR_VI = 0x28000057 | V_OPIVI, + OPC_VXOR_VV = 0x2c000057 | V_OPIVV, + OPC_VXOR_VI = 0x2c000057 | V_OPIVI, + + OPC_VMUL_VV = 0x94000057 | V_OPMVV, + OPC_VSADD_VV = 0x84000057 | V_OPIVV, + OPC_VSADD_VI = 0x84000057 | V_OPIVI, + OPC_VSSUB_VV = 0x8c000057 | V_OPIVV, + OPC_VSSUB_VI = 0x8c000057 | V_OPIVI, + OPC_VSADDU_VV = 0x80000057 | V_OPIVV, + OPC_VSADDU_VI = 0x80000057 | V_OPIVI, + OPC_VSSUBU_VV = 0x88000057 | V_OPIVV, + OPC_VSSUBU_VI = 0x88000057 | V_OPIVI, + + OPC_VMAX_VV = 0x1c000057 | V_OPIVV, + OPC_VMAX_VI = 0x1c000057 | V_OPIVI, + OPC_VMAXU_VV = 0x18000057 | V_OPIVV, + OPC_VMAXU_VI = 0x18000057 | V_OPIVI, + OPC_VMIN_VV = 0x14000057 | V_OPIVV, + OPC_VMIN_VI = 0x14000057 | V_OPIVI, + OPC_VMINU_VV = 0x10000057 | V_OPIVV, + OPC_VMINU_VI = 0x10000057 | V_OPIVI, + + OPC_VMSEQ_VV = 0x60000057 | V_OPIVV, + OPC_VMSEQ_VI = 0x60000057 | V_OPIVI, + OPC_VMSEQ_VX = 0x60000057 | V_OPIVX, + OPC_VMSNE_VV = 0x64000057 | V_OPIVV, + OPC_VMSNE_VI = 0x64000057 | V_OPIVI, + OPC_VMSNE_VX = 0x64000057 | V_OPIVX, + + OPC_VMSLTU_VV = 0x68000057 | V_OPIVV, + OPC_VMSLTU_VX = 0x68000057 | V_OPIVX, + OPC_VMSLT_VV = 0x6c000057 | V_OPIVV, + OPC_VMSLT_VX = 0x6c000057 | V_OPIVX, + OPC_VMSLEU_VV = 0x70000057 | V_OPIVV, + OPC_VMSLEU_VX = 0x70000057 | V_OPIVX, + OPC_VMSLE_VV = 0x74000057 | V_OPIVV, + OPC_VMSLE_VX = 0x74000057 | V_OPIVX, + + OPC_VMSLEU_VI = 0x70000057 | V_OPIVI, + OPC_VMSLE_VI = 0x74000057 | V_OPIVI, + OPC_VMSGTU_VI = 0x78000057 | V_OPIVI, + OPC_VMSGTU_VX = 0x78000057 | V_OPIVX, + OPC_VMSGT_VI = 0x7c000057 | V_OPIVI, + OPC_VMSGT_VX = 0x7c000057 | V_OPIVX, + + OPC_VSLL_VV = 0x94000057 | V_OPIVV, + OPC_VSLL_VI = 0x94000057 | V_OPIVI, + OPC_VSLL_VX = 0x94000057 | V_OPIVX, + OPC_VSRL_VV = 0xa0000057 | V_OPIVV, + OPC_VSRL_VI = 0xa0000057 | V_OPIVI, + OPC_VSRL_VX = 0xa0000057 | V_OPIVX, + OPC_VSRA_VV = 0xa4000057 | V_OPIVV, + OPC_VSRA_VI = 0xa4000057 | V_OPIVI, + OPC_VSRA_VX = 0xa4000057 | V_OPIVX, + + OPC_VMV_V_V = 0x5e000057 | V_OPIVV, + OPC_VMV_V_I = 0x5e000057 | V_OPIVI, + OPC_VMV_V_X = 0x5e000057 | V_OPIVX, + + OPC_VMVNR_V = 0x9e000057 | V_OPIVI, } RISCVInsn; +static const struct { + RISCVInsn op; + bool swap; +} tcg_cmpcond_to_rvv_vv[] = { + [TCG_COND_EQ] = { OPC_VMSEQ_VV, false }, + [TCG_COND_NE] = { OPC_VMSNE_VV, false }, + [TCG_COND_LT] = { OPC_VMSLT_VV, false }, + [TCG_COND_GE] = { OPC_VMSLE_VV, true }, + [TCG_COND_GT] = { OPC_VMSLT_VV, true }, + [TCG_COND_LE] = { OPC_VMSLE_VV, false }, + [TCG_COND_LTU] = { OPC_VMSLTU_VV, false }, + [TCG_COND_GEU] = { OPC_VMSLEU_VV, true }, + [TCG_COND_GTU] = { OPC_VMSLTU_VV, true }, + [TCG_COND_LEU] = { OPC_VMSLEU_VV, false } +}; + +static const struct { + RISCVInsn op; + int min; + int max; + bool adjust; +} tcg_cmpcond_to_rvv_vi[] = { + [TCG_COND_EQ] = { OPC_VMSEQ_VI, -16, 15, false }, + [TCG_COND_NE] = { OPC_VMSNE_VI, -16, 15, false }, + [TCG_COND_GT] = { OPC_VMSGT_VI, -16, 15, false }, + [TCG_COND_LE] = { OPC_VMSLE_VI, -16, 15, false }, + [TCG_COND_LT] = { OPC_VMSLE_VI, -15, 16, true }, + [TCG_COND_GE] = { OPC_VMSGT_VI, -15, 16, true }, + [TCG_COND_LEU] = { OPC_VMSLEU_VI, 0, 15, false }, + [TCG_COND_GTU] = { OPC_VMSGTU_VI, 0, 15, false }, + [TCG_COND_LTU] = { OPC_VMSLEU_VI, 1, 16, true }, + [TCG_COND_GEU] = { OPC_VMSGTU_VI, 1, 16, true }, +}; + +/* test if a constant matches the constraint */ +static bool tcg_target_const_match(int64_t val, int ct, + TCGType type, TCGCond cond, int vece) +{ + if (ct & TCG_CT_CONST) { + return 1; + } + if ((ct & TCG_CT_CONST_ZERO) && val == 0) { + return 1; + } + if (type >= TCG_TYPE_V64) { + /* Val is replicated by VECE; extract the highest element. */ + val >>= (-8 << vece) & 63; + } + /* + * Sign extended from 12 bits: [-0x800, 0x7ff]. + * Used for most arithmetic, as this is the isa field. + */ + if ((ct & TCG_CT_CONST_S12) && val >= -0x800 && val <= 0x7ff) { + return 1; + } + /* + * Sign extended from 12 bits, negated: [-0x7ff, 0x800]. + * Used for subtraction, where a constant must be handled by ADDI. + */ + if ((ct & TCG_CT_CONST_N12) && val >= -0x7ff && val <= 0x800) { + return 1; + } + /* + * Sign extended from 12 bits, +/- matching: [-0x7ff, 0x7ff]. + * Used by addsub2 and movcond, which may need the negative value, + * and requires the modified constant to be representable. + */ + if ((ct & TCG_CT_CONST_M12) && val >= -0x7ff && val <= 0x7ff) { + return 1; + } + /* + * Inverse of sign extended from 12 bits: ~[-0x800, 0x7ff]. + * Used to map ANDN back to ANDI, etc. + */ + if ((ct & TCG_CT_CONST_J12) && ~val >= -0x800 && ~val <= 0x7ff) { + return 1; + } + /* + * Sign extended from 5 bits: [-0x10, 0x0f]. + * Used for vector-immediate. + */ + if ((ct & TCG_CT_CONST_S5) && val >= -0x10 && val <= 0x0f) { + return 1; + } + /* + * Used for vector compare OPIVI instructions. + */ + if ((ct & TCG_CT_CONST_CMP_VI) && + val >= tcg_cmpcond_to_rvv_vi[cond].min && + val <= tcg_cmpcond_to_rvv_vi[cond].max) { + return true; + } + return 0; +} + /* * RISC-V immediate and instruction encoders (excludes 16-bit RVC) */ @@ -363,6 +528,45 @@ static int32_t encode_uj(RISCVInsn opc, TCGReg rd, uint32_t imm) return opc | (rd & 0x1f) << 7 | encode_ujimm20(imm); } + +/* Type-OPIVI */ + +static int32_t encode_vi(RISCVInsn opc, TCGReg rd, int32_t imm, + TCGReg vs2, bool vm) +{ + return opc | (rd & 0x1f) << 7 | (imm & 0x1f) << 15 | + (vs2 & 0x1f) << 20 | (vm << 25); +} + +/* Type-OPIVV/OPMVV/OPIVX/OPMVX, Vector load and store */ + +static int32_t encode_v(RISCVInsn opc, TCGReg d, TCGReg s1, + TCGReg s2, bool vm) +{ + return opc | (d & 0x1f) << 7 | (s1 & 0x1f) << 15 | + (s2 & 0x1f) << 20 | (vm << 25); +} + +/* Vector vtype */ + +static uint32_t encode_vtype(bool vta, bool vma, + MemOp vsew, RISCVVlmul vlmul) +{ + return vma << 7 | vta << 6 | vsew << 3 | vlmul; +} + +static int32_t encode_vset(RISCVInsn opc, TCGReg rd, + TCGArg rs1, uint32_t vtype) +{ + return opc | (rd & 0x1f) << 7 | (rs1 & 0x1f) << 15 | (vtype & 0x7ff) << 20; +} + +static int32_t encode_vseti(RISCVInsn opc, TCGReg rd, + uint32_t uimm, uint32_t vtype) +{ + return opc | (rd & 0x1f) << 7 | (uimm & 0x1f) << 15 | (vtype & 0x3ff) << 20; +} + /* * RISC-V instruction emitters */ @@ -476,6 +680,91 @@ static bool patch_reloc(tcg_insn_unit *code_ptr, int type, } /* + * RISC-V vector instruction emitters + */ + +/* + * Vector registers uses the same 5 lower bits as GPR registers, + * and vm=0 (vm = false) means vector masking ENABLED. + * With RVV 1.0, vs2 is the first operand, while rs1/imm is the + * second operand. + */ +static void tcg_out_opc_vv(TCGContext *s, RISCVInsn opc, + TCGReg vd, TCGReg vs2, TCGReg vs1) +{ + tcg_out32(s, encode_v(opc, vd, vs1, vs2, true)); +} + +static void tcg_out_opc_vx(TCGContext *s, RISCVInsn opc, + TCGReg vd, TCGReg vs2, TCGReg rs1) +{ + tcg_out32(s, encode_v(opc, vd, rs1, vs2, true)); +} + +static void tcg_out_opc_vi(TCGContext *s, RISCVInsn opc, + TCGReg vd, TCGReg vs2, int32_t imm) +{ + tcg_out32(s, encode_vi(opc, vd, imm, vs2, true)); +} + +static void tcg_out_opc_vv_vi(TCGContext *s, RISCVInsn o_vv, RISCVInsn o_vi, + TCGReg vd, TCGReg vs2, TCGArg vi1, int c_vi1) +{ + if (c_vi1) { + tcg_out_opc_vi(s, o_vi, vd, vs2, vi1); + } else { + tcg_out_opc_vv(s, o_vv, vd, vs2, vi1); + } +} + +static void tcg_out_opc_vim_mask(TCGContext *s, RISCVInsn opc, TCGReg vd, + TCGReg vs2, int32_t imm) +{ + tcg_out32(s, encode_vi(opc, vd, imm, vs2, false)); +} + +static void tcg_out_opc_vvm_mask(TCGContext *s, RISCVInsn opc, TCGReg vd, + TCGReg vs2, TCGReg vs1) +{ + tcg_out32(s, encode_v(opc, vd, vs1, vs2, false)); +} + +typedef struct VsetCache { + uint32_t movi_insn; + uint32_t vset_insn; +} VsetCache; + +static VsetCache riscv_vset_cache[3][4]; + +static void set_vtype(TCGContext *s, TCGType type, MemOp vsew) +{ + const VsetCache *p = &riscv_vset_cache[type - TCG_TYPE_V64][vsew]; + + s->riscv_cur_type = type; + s->riscv_cur_vsew = vsew; + + if (p->movi_insn) { + tcg_out32(s, p->movi_insn); + } + tcg_out32(s, p->vset_insn); +} + +static MemOp set_vtype_len(TCGContext *s, TCGType type) +{ + if (type != s->riscv_cur_type) { + set_vtype(s, type, MO_64); + } + return s->riscv_cur_vsew; +} + +static void set_vtype_len_sew(TCGContext *s, TCGType type, MemOp vsew) +{ + if (type != s->riscv_cur_type || vsew != s->riscv_cur_vsew) { + set_vtype(s, type, vsew); + } +} + +/* * TCG intrinsics */ @@ -489,6 +778,15 @@ static bool tcg_out_mov(TCGContext *s, TCGType type, TCGReg ret, TCGReg arg) case TCG_TYPE_I64: tcg_out_opc_imm(s, OPC_ADDI, ret, arg, 0); break; + case TCG_TYPE_V64: + case TCG_TYPE_V128: + case TCG_TYPE_V256: + { + int lmul = type - riscv_lg2_vlenb; + int nf = 1 << MAX(lmul, 0); + tcg_out_opc_vi(s, OPC_VMVNR_V, ret, arg, nf - 1); + } + break; default: g_assert_not_reached(); } @@ -681,18 +979,101 @@ static void tcg_out_ldst(TCGContext *s, RISCVInsn opc, TCGReg data, } } +static void tcg_out_vec_ldst(TCGContext *s, RISCVInsn opc, TCGReg data, + TCGReg addr, intptr_t offset) +{ + tcg_debug_assert(data >= TCG_REG_V0); + tcg_debug_assert(addr < TCG_REG_V0); + + if (offset) { + tcg_debug_assert(addr != TCG_REG_ZERO); + if (offset == sextreg(offset, 0, 12)) { + tcg_out_opc_imm(s, OPC_ADDI, TCG_REG_TMP0, addr, offset); + } else { + tcg_out_movi(s, TCG_TYPE_PTR, TCG_REG_TMP0, offset); + tcg_out_opc_reg(s, OPC_ADD, TCG_REG_TMP0, TCG_REG_TMP0, addr); + } + addr = TCG_REG_TMP0; + } + tcg_out32(s, encode_v(opc, data, addr, 0, true)); +} + static void tcg_out_ld(TCGContext *s, TCGType type, TCGReg arg, TCGReg arg1, intptr_t arg2) { - RISCVInsn insn = type == TCG_TYPE_I32 ? OPC_LW : OPC_LD; - tcg_out_ldst(s, insn, arg, arg1, arg2); + RISCVInsn insn; + + switch (type) { + case TCG_TYPE_I32: + tcg_out_ldst(s, OPC_LW, arg, arg1, arg2); + break; + case TCG_TYPE_I64: + tcg_out_ldst(s, OPC_LD, arg, arg1, arg2); + break; + case TCG_TYPE_V64: + case TCG_TYPE_V128: + case TCG_TYPE_V256: + if (type >= riscv_lg2_vlenb) { + static const RISCVInsn whole_reg_ld[] = { + OPC_VL1RE64_V, OPC_VL2RE64_V, OPC_VL4RE64_V, OPC_VL8RE64_V + }; + unsigned idx = type - riscv_lg2_vlenb; + + tcg_debug_assert(idx < ARRAY_SIZE(whole_reg_ld)); + insn = whole_reg_ld[idx]; + } else { + static const RISCVInsn unit_stride_ld[] = { + OPC_VLE8_V, OPC_VLE16_V, OPC_VLE32_V, OPC_VLE64_V + }; + MemOp prev_vsew = set_vtype_len(s, type); + + tcg_debug_assert(prev_vsew < ARRAY_SIZE(unit_stride_ld)); + insn = unit_stride_ld[prev_vsew]; + } + tcg_out_vec_ldst(s, insn, arg, arg1, arg2); + break; + default: + g_assert_not_reached(); + } } static void tcg_out_st(TCGContext *s, TCGType type, TCGReg arg, TCGReg arg1, intptr_t arg2) { - RISCVInsn insn = type == TCG_TYPE_I32 ? OPC_SW : OPC_SD; - tcg_out_ldst(s, insn, arg, arg1, arg2); + RISCVInsn insn; + + switch (type) { + case TCG_TYPE_I32: + tcg_out_ldst(s, OPC_SW, arg, arg1, arg2); + break; + case TCG_TYPE_I64: + tcg_out_ldst(s, OPC_SD, arg, arg1, arg2); + break; + case TCG_TYPE_V64: + case TCG_TYPE_V128: + case TCG_TYPE_V256: + if (type >= riscv_lg2_vlenb) { + static const RISCVInsn whole_reg_st[] = { + OPC_VS1R_V, OPC_VS2R_V, OPC_VS4R_V, OPC_VS8R_V + }; + unsigned idx = type - riscv_lg2_vlenb; + + tcg_debug_assert(idx < ARRAY_SIZE(whole_reg_st)); + insn = whole_reg_st[idx]; + } else { + static const RISCVInsn unit_stride_st[] = { + OPC_VSE8_V, OPC_VSE16_V, OPC_VSE32_V, OPC_VSE64_V + }; + MemOp prev_vsew = set_vtype_len(s, type); + + tcg_debug_assert(prev_vsew < ARRAY_SIZE(unit_stride_st)); + insn = unit_stride_st[prev_vsew]; + } + tcg_out_vec_ldst(s, insn, arg, arg1, arg2); + break; + default: + g_assert_not_reached(); + } } static bool tcg_out_sti(TCGContext *s, TCGType type, TCGArg val, @@ -766,6 +1147,40 @@ static void tcg_out_addsub2(TCGContext *s, } } +static bool tcg_out_dup_vec(TCGContext *s, TCGType type, unsigned vece, + TCGReg dst, TCGReg src) +{ + set_vtype_len_sew(s, type, vece); + tcg_out_opc_vx(s, OPC_VMV_V_X, dst, 0, src); + return true; +} + +static bool tcg_out_dupm_vec(TCGContext *s, TCGType type, unsigned vece, + TCGReg dst, TCGReg base, intptr_t offset) +{ + tcg_out_ld(s, TCG_TYPE_REG, TCG_REG_TMP0, base, offset); + return tcg_out_dup_vec(s, type, vece, dst, TCG_REG_TMP0); +} + +static void tcg_out_dupi_vec(TCGContext *s, TCGType type, unsigned vece, + TCGReg dst, int64_t arg) +{ + /* Arg is replicated by VECE; extract the highest element. */ + arg >>= (-8 << vece) & 63; + + if (arg >= -16 && arg < 16) { + if (arg == 0 || arg == -1) { + set_vtype_len(s, type); + } else { + set_vtype_len_sew(s, type, vece); + } + tcg_out_opc_vi(s, OPC_VMV_V_I, dst, 0, arg); + return; + } + tcg_out_movi(s, TCG_TYPE_I64, TCG_REG_TMP0, arg); + tcg_out_dup_vec(s, type, vece, dst, TCG_REG_TMP0); +} + static const struct { RISCVInsn op; bool swap; @@ -1104,12 +1519,72 @@ static void tcg_out_cltz(TCGContext *s, TCGType type, RISCVInsn insn, } } +static void tcg_out_cmpsel(TCGContext *s, TCGType type, unsigned vece, + TCGCond cond, TCGReg ret, + TCGReg cmp1, TCGReg cmp2, bool c_cmp2, + TCGReg val1, bool c_val1, + TCGReg val2, bool c_val2) +{ + set_vtype_len_sew(s, type, vece); + + /* Use only vmerge_vim if possible, by inverting the test. */ + if (c_val2 && !c_val1) { + TCGArg temp = val1; + cond = tcg_invert_cond(cond); + val1 = val2; + val2 = temp; + c_val1 = true; + c_val2 = false; + } + + /* Perform the comparison into V0 mask. */ + if (c_cmp2) { + tcg_out_opc_vi(s, tcg_cmpcond_to_rvv_vi[cond].op, TCG_REG_V0, cmp1, + cmp2 - tcg_cmpcond_to_rvv_vi[cond].adjust); + } else if (tcg_cmpcond_to_rvv_vv[cond].swap) { + tcg_out_opc_vv(s, tcg_cmpcond_to_rvv_vv[cond].op, + TCG_REG_V0, cmp2, cmp1); + } else { + tcg_out_opc_vv(s, tcg_cmpcond_to_rvv_vv[cond].op, + TCG_REG_V0, cmp1, cmp2); + } + if (c_val1) { + if (c_val2) { + tcg_out_opc_vi(s, OPC_VMV_V_I, ret, 0, val2); + val2 = ret; + } + /* vd[i] == v0.mask[i] ? imm : vs2[i] */ + tcg_out_opc_vim_mask(s, OPC_VMERGE_VIM, ret, val2, val1); + } else { + /* vd[i] == v0.mask[i] ? vs1[i] : vs2[i] */ + tcg_out_opc_vvm_mask(s, OPC_VMERGE_VVM, ret, val2, val1); + } +} + +static void tcg_out_vshifti(TCGContext *s, RISCVInsn opc_vi, RISCVInsn opc_vx, + TCGReg dst, TCGReg src, unsigned imm) +{ + if (imm < 32) { + tcg_out_opc_vi(s, opc_vi, dst, src, imm); + } else { + tcg_out_movi(s, TCG_TYPE_I32, TCG_REG_TMP0, imm); + tcg_out_opc_vx(s, opc_vx, dst, src, TCG_REG_TMP0); + } +} + +static void init_setting_vtype(TCGContext *s) +{ + s->riscv_cur_type = TCG_TYPE_COUNT; +} + static void tcg_out_call_int(TCGContext *s, const tcg_insn_unit *arg, bool tail) { TCGReg link = tail ? TCG_REG_ZERO : TCG_REG_RA; ptrdiff_t offset = tcg_pcrel_diff(s, arg); int ret; + init_setting_vtype(s); + tcg_debug_assert((offset & 1) == 0); if (offset == sextreg(offset, 0, 20)) { /* short jump: -2097150 to 2097152 */ @@ -1247,6 +1722,8 @@ static TCGLabelQemuLdst *prepare_host_addr(TCGContext *s, TCGReg *pbase, ldst->oi = oi; ldst->addrlo_reg = addr_reg; + init_setting_vtype(s); + tcg_out_ld(s, TCG_TYPE_PTR, TCG_REG_TMP0, TCG_AREG0, mask_ofs); tcg_out_ld(s, TCG_TYPE_PTR, TCG_REG_TMP1, TCG_AREG0, table_ofs); @@ -1308,6 +1785,8 @@ static TCGLabelQemuLdst *prepare_host_addr(TCGContext *s, TCGReg *pbase, ldst->oi = oi; ldst->addrlo_reg = addr_reg; + init_setting_vtype(s); + /* We are expecting alignment max 7, so we can always use andi. */ tcg_debug_assert(a_mask == sextreg(a_mask, 0, 12)); tcg_out_opc_imm(s, OPC_ANDI, TCG_REG_TMP1, addr_reg, a_mask); @@ -1881,6 +2360,223 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, } } +static void tcg_out_vec_op(TCGContext *s, TCGOpcode opc, + unsigned vecl, unsigned vece, + const TCGArg args[TCG_MAX_OP_ARGS], + const int const_args[TCG_MAX_OP_ARGS]) +{ + TCGType type = vecl + TCG_TYPE_V64; + TCGArg a0, a1, a2; + int c2; + + a0 = args[0]; + a1 = args[1]; + a2 = args[2]; + c2 = const_args[2]; + + switch (opc) { + case INDEX_op_dupm_vec: + tcg_out_dupm_vec(s, type, vece, a0, a1, a2); + break; + case INDEX_op_ld_vec: + tcg_out_ld(s, type, a0, a1, a2); + break; + case INDEX_op_st_vec: + tcg_out_st(s, type, a0, a1, a2); + break; + case INDEX_op_add_vec: + set_vtype_len_sew(s, type, vece); + tcg_out_opc_vv_vi(s, OPC_VADD_VV, OPC_VADD_VI, a0, a1, a2, c2); + break; + case INDEX_op_sub_vec: + set_vtype_len_sew(s, type, vece); + if (const_args[1]) { + tcg_out_opc_vi(s, OPC_VRSUB_VI, a0, a2, a1); + } else { + tcg_out_opc_vv(s, OPC_VSUB_VV, a0, a1, a2); + } + break; + case INDEX_op_and_vec: + set_vtype_len(s, type); + tcg_out_opc_vv_vi(s, OPC_VAND_VV, OPC_VAND_VI, a0, a1, a2, c2); + break; + case INDEX_op_or_vec: + set_vtype_len(s, type); + tcg_out_opc_vv_vi(s, OPC_VOR_VV, OPC_VOR_VI, a0, a1, a2, c2); + break; + case INDEX_op_xor_vec: + set_vtype_len(s, type); + tcg_out_opc_vv_vi(s, OPC_VXOR_VV, OPC_VXOR_VI, a0, a1, a2, c2); + break; + case INDEX_op_not_vec: + set_vtype_len(s, type); + tcg_out_opc_vi(s, OPC_VXOR_VI, a0, a1, -1); + break; + case INDEX_op_neg_vec: + set_vtype_len_sew(s, type, vece); + tcg_out_opc_vi(s, OPC_VRSUB_VI, a0, a1, 0); + break; + case INDEX_op_mul_vec: + set_vtype_len_sew(s, type, vece); + tcg_out_opc_vv(s, OPC_VMUL_VV, a0, a1, a2); + break; + case INDEX_op_ssadd_vec: + set_vtype_len_sew(s, type, vece); + tcg_out_opc_vv_vi(s, OPC_VSADD_VV, OPC_VSADD_VI, a0, a1, a2, c2); + break; + case INDEX_op_sssub_vec: + set_vtype_len_sew(s, type, vece); + tcg_out_opc_vv_vi(s, OPC_VSSUB_VV, OPC_VSSUB_VI, a0, a1, a2, c2); + break; + case INDEX_op_usadd_vec: + set_vtype_len_sew(s, type, vece); + tcg_out_opc_vv_vi(s, OPC_VSADDU_VV, OPC_VSADDU_VI, a0, a1, a2, c2); + break; + case INDEX_op_ussub_vec: + set_vtype_len_sew(s, type, vece); + tcg_out_opc_vv_vi(s, OPC_VSSUBU_VV, OPC_VSSUBU_VI, a0, a1, a2, c2); + break; + case INDEX_op_smax_vec: + set_vtype_len_sew(s, type, vece); + tcg_out_opc_vv_vi(s, OPC_VMAX_VV, OPC_VMAX_VI, a0, a1, a2, c2); + break; + case INDEX_op_smin_vec: + set_vtype_len_sew(s, type, vece); + tcg_out_opc_vv_vi(s, OPC_VMIN_VV, OPC_VMIN_VI, a0, a1, a2, c2); + break; + case INDEX_op_umax_vec: + set_vtype_len_sew(s, type, vece); + tcg_out_opc_vv_vi(s, OPC_VMAXU_VV, OPC_VMAXU_VI, a0, a1, a2, c2); + break; + case INDEX_op_umin_vec: + set_vtype_len_sew(s, type, vece); + tcg_out_opc_vv_vi(s, OPC_VMINU_VV, OPC_VMINU_VI, a0, a1, a2, c2); + break; + case INDEX_op_shls_vec: + set_vtype_len_sew(s, type, vece); + tcg_out_opc_vx(s, OPC_VSLL_VX, a0, a1, a2); + break; + case INDEX_op_shrs_vec: + set_vtype_len_sew(s, type, vece); + tcg_out_opc_vx(s, OPC_VSRL_VX, a0, a1, a2); + break; + case INDEX_op_sars_vec: + set_vtype_len_sew(s, type, vece); + tcg_out_opc_vx(s, OPC_VSRA_VX, a0, a1, a2); + break; + case INDEX_op_shlv_vec: + set_vtype_len_sew(s, type, vece); + tcg_out_opc_vv(s, OPC_VSLL_VV, a0, a1, a2); + break; + case INDEX_op_shrv_vec: + set_vtype_len_sew(s, type, vece); + tcg_out_opc_vv(s, OPC_VSRL_VV, a0, a1, a2); + break; + case INDEX_op_sarv_vec: + set_vtype_len_sew(s, type, vece); + tcg_out_opc_vv(s, OPC_VSRA_VV, a0, a1, a2); + break; + case INDEX_op_shli_vec: + set_vtype_len_sew(s, type, vece); + tcg_out_vshifti(s, OPC_VSLL_VI, OPC_VSLL_VX, a0, a1, a2); + break; + case INDEX_op_shri_vec: + set_vtype_len_sew(s, type, vece); + tcg_out_vshifti(s, OPC_VSRL_VI, OPC_VSRL_VX, a0, a1, a2); + break; + case INDEX_op_sari_vec: + set_vtype_len_sew(s, type, vece); + tcg_out_vshifti(s, OPC_VSRA_VI, OPC_VSRA_VX, a0, a1, a2); + break; + case INDEX_op_rotli_vec: + set_vtype_len_sew(s, type, vece); + tcg_out_vshifti(s, OPC_VSLL_VI, OPC_VSLL_VX, TCG_REG_V0, a1, a2); + tcg_out_vshifti(s, OPC_VSRL_VI, OPC_VSRL_VX, a0, a1, + -a2 & ((8 << vece) - 1)); + tcg_out_opc_vv(s, OPC_VOR_VV, a0, a0, TCG_REG_V0); + break; + case INDEX_op_rotls_vec: + set_vtype_len_sew(s, type, vece); + tcg_out_opc_vx(s, OPC_VSLL_VX, TCG_REG_V0, a1, a2); + tcg_out_opc_reg(s, OPC_SUBW, TCG_REG_TMP0, TCG_REG_ZERO, a2); + tcg_out_opc_vx(s, OPC_VSRL_VX, a0, a1, TCG_REG_TMP0); + tcg_out_opc_vv(s, OPC_VOR_VV, a0, a0, TCG_REG_V0); + break; + case INDEX_op_rotlv_vec: + set_vtype_len_sew(s, type, vece); + tcg_out_opc_vi(s, OPC_VRSUB_VI, TCG_REG_V0, a2, 0); + tcg_out_opc_vv(s, OPC_VSRL_VV, TCG_REG_V0, a1, TCG_REG_V0); + tcg_out_opc_vv(s, OPC_VSLL_VV, a0, a1, a2); + tcg_out_opc_vv(s, OPC_VOR_VV, a0, a0, TCG_REG_V0); + break; + case INDEX_op_rotrv_vec: + set_vtype_len_sew(s, type, vece); + tcg_out_opc_vi(s, OPC_VRSUB_VI, TCG_REG_V0, a2, 0); + tcg_out_opc_vv(s, OPC_VSLL_VV, TCG_REG_V0, a1, TCG_REG_V0); + tcg_out_opc_vv(s, OPC_VSRL_VV, a0, a1, a2); + tcg_out_opc_vv(s, OPC_VOR_VV, a0, a0, TCG_REG_V0); + break; + case INDEX_op_cmp_vec: + tcg_out_cmpsel(s, type, vece, args[3], a0, a1, a2, c2, + -1, true, 0, true); + break; + case INDEX_op_cmpsel_vec: + tcg_out_cmpsel(s, type, vece, args[5], a0, a1, a2, c2, + args[3], const_args[3], args[4], const_args[4]); + break; + case INDEX_op_mov_vec: /* Always emitted via tcg_out_mov. */ + case INDEX_op_dup_vec: /* Always emitted via tcg_out_dup_vec. */ + default: + g_assert_not_reached(); + } +} + +void tcg_expand_vec_op(TCGOpcode opc, TCGType type, unsigned vece, + TCGArg a0, ...) +{ + g_assert_not_reached(); +} + +int tcg_can_emit_vec_op(TCGOpcode opc, TCGType type, unsigned vece) +{ + switch (opc) { + case INDEX_op_add_vec: + case INDEX_op_sub_vec: + case INDEX_op_and_vec: + case INDEX_op_or_vec: + case INDEX_op_xor_vec: + case INDEX_op_not_vec: + case INDEX_op_neg_vec: + case INDEX_op_mul_vec: + case INDEX_op_ssadd_vec: + case INDEX_op_sssub_vec: + case INDEX_op_usadd_vec: + case INDEX_op_ussub_vec: + case INDEX_op_smax_vec: + case INDEX_op_smin_vec: + case INDEX_op_umax_vec: + case INDEX_op_umin_vec: + case INDEX_op_shls_vec: + case INDEX_op_shrs_vec: + case INDEX_op_sars_vec: + case INDEX_op_shlv_vec: + case INDEX_op_shrv_vec: + case INDEX_op_sarv_vec: + case INDEX_op_shri_vec: + case INDEX_op_shli_vec: + case INDEX_op_sari_vec: + case INDEX_op_rotls_vec: + case INDEX_op_rotlv_vec: + case INDEX_op_rotrv_vec: + case INDEX_op_rotli_vec: + case INDEX_op_cmp_vec: + case INDEX_op_cmpsel_vec: + return 1; + default: + return 0; + } +} + static TCGConstraintSetIndex tcg_target_op_def(TCGOpcode op) { switch (op) { @@ -2020,6 +2716,50 @@ static TCGConstraintSetIndex tcg_target_op_def(TCGOpcode op) case INDEX_op_qemu_st_a64_i64: return C_O0_I2(rZ, r); + case INDEX_op_st_vec: + return C_O0_I2(v, r); + case INDEX_op_dup_vec: + case INDEX_op_dupm_vec: + case INDEX_op_ld_vec: + return C_O1_I1(v, r); + case INDEX_op_neg_vec: + case INDEX_op_not_vec: + case INDEX_op_shli_vec: + case INDEX_op_shri_vec: + case INDEX_op_sari_vec: + case INDEX_op_rotli_vec: + return C_O1_I1(v, v); + case INDEX_op_add_vec: + case INDEX_op_and_vec: + case INDEX_op_or_vec: + case INDEX_op_xor_vec: + case INDEX_op_ssadd_vec: + case INDEX_op_sssub_vec: + case INDEX_op_usadd_vec: + case INDEX_op_ussub_vec: + case INDEX_op_smax_vec: + case INDEX_op_smin_vec: + case INDEX_op_umax_vec: + case INDEX_op_umin_vec: + return C_O1_I2(v, v, vK); + case INDEX_op_sub_vec: + return C_O1_I2(v, vK, v); + case INDEX_op_mul_vec: + case INDEX_op_shlv_vec: + case INDEX_op_shrv_vec: + case INDEX_op_sarv_vec: + case INDEX_op_rotlv_vec: + case INDEX_op_rotrv_vec: + return C_O1_I2(v, v, v); + case INDEX_op_shls_vec: + case INDEX_op_shrs_vec: + case INDEX_op_sars_vec: + case INDEX_op_rotls_vec: + return C_O1_I2(v, v, r); + case INDEX_op_cmp_vec: + return C_O1_I2(v, v, vL); + case INDEX_op_cmpsel_vec: + return C_O1_I4(v, v, vL, vK, vK); default: g_assert_not_reached(); } @@ -2093,7 +2833,65 @@ static void tcg_target_qemu_prologue(TCGContext *s) static void tcg_out_tb_start(TCGContext *s) { - /* nothing to do */ + init_setting_vtype(s); +} + +static bool vtype_check(unsigned vtype) +{ + unsigned long tmp; + + /* vsetvl tmp, zero, vtype */ + asm(".insn r 0x57, 7, 0x40, %0, zero, %1" : "=r"(tmp) : "r"(vtype)); + return tmp != 0; +} + +static void probe_frac_lmul_1(TCGType type, MemOp vsew) +{ + VsetCache *p = &riscv_vset_cache[type - TCG_TYPE_V64][vsew]; + unsigned avl = tcg_type_size(type) >> vsew; + int lmul = type - riscv_lg2_vlenb; + unsigned vtype = encode_vtype(true, true, vsew, lmul & 7); + bool lmul_eq_avl = true; + + /* Guaranteed by Zve64x. */ + assert(lmul < 3); + + /* + * For LMUL < -3, the host vector size is so large that TYPE + * is smaller than the minimum 1/8 fraction. + * + * For other fractional LMUL settings, implementations must + * support SEW settings between SEW_MIN and LMUL * ELEN, inclusive. + * So if ELEN = 64, LMUL = 1/2, then SEW will support e8, e16, e32, + * but e64 may not be supported. In other words, the hardware only + * guarantees SEW_MIN <= SEW <= LMUL * ELEN. Check. + */ + if (lmul < 0 && (lmul < -3 || !vtype_check(vtype))) { + vtype = encode_vtype(true, true, vsew, VLMUL_M1); + lmul_eq_avl = false; + } + + if (avl < 32) { + p->vset_insn = encode_vseti(OPC_VSETIVLI, TCG_REG_ZERO, avl, vtype); + } else if (lmul_eq_avl) { + /* rd != 0 and rs1 == 0 uses vlmax */ + p->vset_insn = encode_vset(OPC_VSETVLI, TCG_REG_TMP0, TCG_REG_ZERO, vtype); + } else { + p->movi_insn = encode_i(OPC_ADDI, TCG_REG_TMP0, TCG_REG_ZERO, avl); + p->vset_insn = encode_vset(OPC_VSETVLI, TCG_REG_ZERO, TCG_REG_TMP0, vtype); + } +} + +static void probe_frac_lmul(void) +{ + /* Match riscv_lg2_vlenb to TCG_TYPE_V64. */ + QEMU_BUILD_BUG_ON(TCG_TYPE_V64 != 3); + + for (TCGType t = TCG_TYPE_V64; t <= TCG_TYPE_V256; t++) { + for (MemOp e = MO_8; e <= MO_64; e++) { + probe_frac_lmul_1(t, e); + } + } } static void tcg_target_init(TCGContext *s) @@ -2101,7 +2899,7 @@ static void tcg_target_init(TCGContext *s) tcg_target_available_regs[TCG_TYPE_I32] = 0xffffffff; tcg_target_available_regs[TCG_TYPE_I64] = 0xffffffff; - tcg_target_call_clobber_regs = -1u; + tcg_target_call_clobber_regs = -1; tcg_regset_reset_reg(tcg_target_call_clobber_regs, TCG_REG_S0); tcg_regset_reset_reg(tcg_target_call_clobber_regs, TCG_REG_S1); tcg_regset_reset_reg(tcg_target_call_clobber_regs, TCG_REG_S2); @@ -2123,6 +2921,32 @@ static void tcg_target_init(TCGContext *s) tcg_regset_set_reg(s->reserved_regs, TCG_REG_SP); tcg_regset_set_reg(s->reserved_regs, TCG_REG_GP); tcg_regset_set_reg(s->reserved_regs, TCG_REG_TP); + + if (cpuinfo & CPUINFO_ZVE64X) { + switch (riscv_lg2_vlenb) { + case TCG_TYPE_V64: + tcg_target_available_regs[TCG_TYPE_V64] = ALL_VECTOR_REGS; + tcg_target_available_regs[TCG_TYPE_V128] = ALL_DVECTOR_REG_GROUPS; + tcg_target_available_regs[TCG_TYPE_V256] = ALL_QVECTOR_REG_GROUPS; + s->reserved_regs |= (~ALL_QVECTOR_REG_GROUPS & ALL_VECTOR_REGS); + break; + case TCG_TYPE_V128: + tcg_target_available_regs[TCG_TYPE_V64] = ALL_VECTOR_REGS; + tcg_target_available_regs[TCG_TYPE_V128] = ALL_VECTOR_REGS; + tcg_target_available_regs[TCG_TYPE_V256] = ALL_DVECTOR_REG_GROUPS; + s->reserved_regs |= (~ALL_DVECTOR_REG_GROUPS & ALL_VECTOR_REGS); + break; + default: + /* Guaranteed by Zve64x. */ + tcg_debug_assert(riscv_lg2_vlenb >= TCG_TYPE_V256); + tcg_target_available_regs[TCG_TYPE_V64] = ALL_VECTOR_REGS; + tcg_target_available_regs[TCG_TYPE_V128] = ALL_VECTOR_REGS; + tcg_target_available_regs[TCG_TYPE_V256] = ALL_VECTOR_REGS; + break; + } + tcg_regset_set_reg(s->reserved_regs, TCG_REG_V0); + probe_frac_lmul(); + } } typedef struct { diff --git a/tcg/riscv/tcg-target.h b/tcg/riscv/tcg-target.h index 1a347eaf6e..334c37cbe6 100644 --- a/tcg/riscv/tcg-target.h +++ b/tcg/riscv/tcg-target.h @@ -28,42 +28,28 @@ #include "host/cpuinfo.h" #define TCG_TARGET_INSN_UNIT_SIZE 4 -#define TCG_TARGET_NB_REGS 32 +#define TCG_TARGET_NB_REGS 64 #define MAX_CODE_GEN_BUFFER_SIZE ((size_t)-1) typedef enum { - TCG_REG_ZERO, - TCG_REG_RA, - TCG_REG_SP, - TCG_REG_GP, - TCG_REG_TP, - TCG_REG_T0, - TCG_REG_T1, - TCG_REG_T2, - TCG_REG_S0, - TCG_REG_S1, - TCG_REG_A0, - TCG_REG_A1, - TCG_REG_A2, - TCG_REG_A3, - TCG_REG_A4, - TCG_REG_A5, - TCG_REG_A6, - TCG_REG_A7, - TCG_REG_S2, - TCG_REG_S3, - TCG_REG_S4, - TCG_REG_S5, - TCG_REG_S6, - TCG_REG_S7, - TCG_REG_S8, - TCG_REG_S9, - TCG_REG_S10, - TCG_REG_S11, - TCG_REG_T3, - TCG_REG_T4, - TCG_REG_T5, - TCG_REG_T6, + TCG_REG_ZERO, TCG_REG_RA, TCG_REG_SP, TCG_REG_GP, + TCG_REG_TP, TCG_REG_T0, TCG_REG_T1, TCG_REG_T2, + TCG_REG_S0, TCG_REG_S1, TCG_REG_A0, TCG_REG_A1, + TCG_REG_A2, TCG_REG_A3, TCG_REG_A4, TCG_REG_A5, + TCG_REG_A6, TCG_REG_A7, TCG_REG_S2, TCG_REG_S3, + TCG_REG_S4, TCG_REG_S5, TCG_REG_S6, TCG_REG_S7, + TCG_REG_S8, TCG_REG_S9, TCG_REG_S10, TCG_REG_S11, + TCG_REG_T3, TCG_REG_T4, TCG_REG_T5, TCG_REG_T6, + + /* RISC-V V Extension registers */ + TCG_REG_V0, TCG_REG_V1, TCG_REG_V2, TCG_REG_V3, + TCG_REG_V4, TCG_REG_V5, TCG_REG_V6, TCG_REG_V7, + TCG_REG_V8, TCG_REG_V9, TCG_REG_V10, TCG_REG_V11, + TCG_REG_V12, TCG_REG_V13, TCG_REG_V14, TCG_REG_V15, + TCG_REG_V16, TCG_REG_V17, TCG_REG_V18, TCG_REG_V19, + TCG_REG_V20, TCG_REG_V21, TCG_REG_V22, TCG_REG_V23, + TCG_REG_V24, TCG_REG_V25, TCG_REG_V26, TCG_REG_V27, + TCG_REG_V28, TCG_REG_V29, TCG_REG_V30, TCG_REG_V31, /* aliases */ TCG_AREG0 = TCG_REG_S0, @@ -156,6 +142,32 @@ typedef enum { #define TCG_TARGET_HAS_tst 0 +/* vector instructions */ +#define TCG_TARGET_HAS_v64 (cpuinfo & CPUINFO_ZVE64X) +#define TCG_TARGET_HAS_v128 (cpuinfo & CPUINFO_ZVE64X) +#define TCG_TARGET_HAS_v256 (cpuinfo & CPUINFO_ZVE64X) +#define TCG_TARGET_HAS_andc_vec 0 +#define TCG_TARGET_HAS_orc_vec 0 +#define TCG_TARGET_HAS_nand_vec 0 +#define TCG_TARGET_HAS_nor_vec 0 +#define TCG_TARGET_HAS_eqv_vec 0 +#define TCG_TARGET_HAS_not_vec 1 +#define TCG_TARGET_HAS_neg_vec 1 +#define TCG_TARGET_HAS_abs_vec 0 +#define TCG_TARGET_HAS_roti_vec 1 +#define TCG_TARGET_HAS_rots_vec 1 +#define TCG_TARGET_HAS_rotv_vec 1 +#define TCG_TARGET_HAS_shi_vec 1 +#define TCG_TARGET_HAS_shs_vec 1 +#define TCG_TARGET_HAS_shv_vec 1 +#define TCG_TARGET_HAS_mul_vec 1 +#define TCG_TARGET_HAS_sat_vec 1 +#define TCG_TARGET_HAS_minmax_vec 1 +#define TCG_TARGET_HAS_bitsel_vec 0 +#define TCG_TARGET_HAS_cmpsel_vec 1 + +#define TCG_TARGET_HAS_tst_vec 0 + #define TCG_TARGET_DEFAULT_MO (0) #define TCG_TARGET_NEED_LDST_LABELS diff --git a/tcg/riscv/tcg-target.opc.h b/tcg/riscv/tcg-target.opc.h new file mode 100644 index 0000000000..b80b39e1e5 --- /dev/null +++ b/tcg/riscv/tcg-target.opc.h @@ -0,0 +1,12 @@ +/* + * Copyright (c) C-SKY Microsystems Co., Ltd. + * + * 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 for details. + * + * Target-specific opcodes for host vector expansion. These will be + * emitted by tcg_expand_vec_op. For those familiar with GCC internals, + * consider these to be UNSPEC with names. + */ @@ -1399,7 +1399,6 @@ TranslationBlock *tcg_tb_alloc(TCGContext *s) goto retry; } qatomic_set(&s->code_gen_ptr, next); - s->data_gen_ptr = NULL; return tb; } @@ -6172,6 +6171,7 @@ int tcg_gen_code(TCGContext *s, TranslationBlock *tb, uint64_t pc_start) */ s->code_buf = tcg_splitwx_to_rw(tb->tc.ptr); s->code_ptr = s->code_buf; + s->data_gen_ptr = NULL; #ifdef TCG_TARGET_NEED_LDST_LABELS QSIMPLEQ_INIT(&s->ldst_labels); diff --git a/tests/qemu-iotests/211.out b/tests/qemu-iotests/211.out index f02c75409c..ff9f9a6913 100644 --- a/tests/qemu-iotests/211.out +++ b/tests/qemu-iotests/211.out @@ -17,7 +17,7 @@ file format: IMGFMT virtual size: 128 MiB (134217728 bytes) cluster_size: 1048576 -[{"data": false, "depth": 0, "length": 134217728, "present": true, "start": 0, "zero": true}] +[{"compressed": false, "data": false, "depth": 0, "length": 134217728, "present": true, "start": 0, "zero": true}] === Successful image creation (explicit defaults) === {"execute": "blockdev-create", "arguments": {"job-id": "job0", "options": {"driver": "file", "filename": "TEST_DIR/PID-t.vdi", "size": 0}}} @@ -35,7 +35,7 @@ file format: IMGFMT virtual size: 64 MiB (67108864 bytes) cluster_size: 1048576 -[{"data": false, "depth": 0, "length": 67108864, "present": true, "start": 0, "zero": true}] +[{"compressed": false, "data": false, "depth": 0, "length": 67108864, "present": true, "start": 0, "zero": true}] === Successful image creation (with non-default options) === {"execute": "blockdev-create", "arguments": {"job-id": "job0", "options": {"driver": "file", "filename": "TEST_DIR/PID-t.vdi", "size": 0}}} @@ -53,7 +53,7 @@ file format: IMGFMT virtual size: 32 MiB (33554432 bytes) cluster_size: 1048576 -[{"data": true, "depth": 0, "length": 3072, "offset": 1024, "present": true, "start": 0, "zero": false}, {"data": true, "depth": 0, "length": 33551360, "offset": 4096, "present": true, "start": 3072, "zero": true}] +[{"compressed": false, "data": true, "depth": 0, "length": 3072, "offset": 1024, "present": true, "start": 0, "zero": false}, {"compressed": false, "data": true, "depth": 0, "length": 33551360, "offset": 4096, "present": true, "start": 3072, "zero": true}] === Invalid BlockdevRef === {"execute": "blockdev-create", "arguments": {"job-id": "job0", "options": {"driver": "vdi", "file": "this doesn't exist", "size": 33554432}}} diff --git a/tests/qemu-iotests/tests/backup-discard-source b/tests/qemu-iotests/tests/backup-discard-source index 2391b12acd..17fef9c6d3 100755 --- a/tests/qemu-iotests/tests/backup-discard-source +++ b/tests/qemu-iotests/tests/backup-discard-source @@ -28,20 +28,14 @@ from iotests import qemu_img_create, qemu_img_map, qemu_io temp_img = os.path.join(iotests.test_dir, 'temp') source_img = os.path.join(iotests.test_dir, 'source') target_img = os.path.join(iotests.test_dir, 'target') -size = '1M' - - -def get_actual_size(vm, node_name): - nodes = vm.cmd('query-named-block-nodes', flat=True) - node = next(n for n in nodes if n['node-name'] == node_name) - return node['image']['actual-size'] +size = 1024 * 1024 class TestBackup(iotests.QMPTestCase): def setUp(self): - qemu_img_create('-f', iotests.imgfmt, source_img, size) - qemu_img_create('-f', iotests.imgfmt, temp_img, size) - qemu_img_create('-f', iotests.imgfmt, target_img, size) + qemu_img_create('-f', iotests.imgfmt, source_img, str(size)) + qemu_img_create('-f', iotests.imgfmt, temp_img, str(size)) + qemu_img_create('-f', iotests.imgfmt, target_img, str(size)) qemu_io('-c', 'write 0 1M', source_img) self.vm = iotests.VM() @@ -84,7 +78,12 @@ class TestBackup(iotests.QMPTestCase): } }) - self.assertLess(get_actual_size(self.vm, 'temp'), 512 * 1024) + self.bitmap = { + 'node': 'temp', + 'name': 'bitmap0' + } + + self.vm.cmd('block-dirty-bitmap-add', self.bitmap) def tearDown(self): # That should fail, because region is discarded @@ -98,7 +97,7 @@ class TestBackup(iotests.QMPTestCase): mapping = qemu_img_map(temp_img) self.assertEqual(len(mapping), 1) self.assertEqual(mapping[0]['start'], 0) - self.assertEqual(mapping[0]['length'], 1024 * 1024) + self.assertEqual(mapping[0]['length'], size) self.assertEqual(mapping[0]['data'], False) os.remove(temp_img) @@ -113,6 +112,13 @@ class TestBackup(iotests.QMPTestCase): self.vm.event_wait(name='BLOCK_JOB_COMPLETED') + def get_bitmap_count(self): + nodes = self.vm.cmd('query-named-block-nodes', flat=True) + temp = next(n for n in nodes if n['node-name'] == 'temp') + bitmap = temp['dirty-bitmaps'][0] + assert bitmap['name'] == self.bitmap['name'] + return bitmap['count'] + def test_discard_written(self): """ 1. Guest writes @@ -125,7 +131,7 @@ class TestBackup(iotests.QMPTestCase): self.assert_qmp(result, 'return', '') # Check that data is written to temporary image - self.assertGreater(get_actual_size(self.vm, 'temp'), 1024 * 1024) + self.assertEqual(self.get_bitmap_count(), size) self.do_backup() @@ -138,13 +144,18 @@ class TestBackup(iotests.QMPTestCase): """ self.do_backup() + # backup job did discard operation and pollute the bitmap, + # we have to clean the bitmap, to check next write + self.assertEqual(self.get_bitmap_count(), size) + self.vm.cmd('block-dirty-bitmap-clear', self.bitmap) + # Try trigger copy-before-write operation result = self.vm.hmp_qemu_io('cbw', 'write 0 1M') self.assert_qmp(result, 'return', '') # Check that data is not written to temporary image, as region # is discarded from copy-before-write process - self.assertLess(get_actual_size(self.vm, 'temp'), 512 * 1024) + self.assertEqual(self.get_bitmap_count(), 0) if __name__ == '__main__': diff --git a/tests/qtest/cdrom-test.c b/tests/qtest/cdrom-test.c index 9d72b24e4b..c86725a511 100644 --- a/tests/qtest/cdrom-test.c +++ b/tests/qtest/cdrom-test.c @@ -213,6 +213,30 @@ static void add_s390x_tests(void) "-drive driver=null-co,read-zeroes=on,if=none,id=d1 " "-device virtio-blk,drive=d2,bootindex=1 " "-drive if=none,id=d2,media=cdrom,file=", test_cdboot); + qtest_add_data_func("cdrom/boot/as-fallback-device", + "-device virtio-serial -device virtio-scsi " + "-device virtio-blk,drive=d1,bootindex=1 " + "-drive driver=null-co,read-zeroes=on,if=none,id=d1 " + "-device virtio-blk,drive=d2,bootindex=2 " + "-drive if=none,id=d2,media=cdrom,file=", test_cdboot); + qtest_add_data_func("cdrom/boot/as-last-option", + "-device virtio-serial -device virtio-scsi " + "-device virtio-blk,drive=d1,bootindex=1 " + "-drive driver=null-co,read-zeroes=on,if=none,id=d1 " + "-device virtio-blk,drive=d2,bootindex=2 " + "-drive driver=null-co,read-zeroes=on,if=none,id=d2 " + "-device virtio-blk,drive=d3,bootindex=3 " + "-drive driver=null-co,read-zeroes=on,if=none,id=d3 " + "-device scsi-hd,drive=d4,bootindex=4 " + "-drive driver=null-co,read-zeroes=on,if=none,id=d4 " + "-device scsi-hd,drive=d5,bootindex=5 " + "-drive driver=null-co,read-zeroes=on,if=none,id=d5 " + "-device virtio-blk,drive=d6,bootindex=6 " + "-drive driver=null-co,read-zeroes=on,if=none,id=d6 " + "-device scsi-hd,drive=d7,bootindex=7 " + "-drive driver=null-co,read-zeroes=on,if=none,id=d7 " + "-device scsi-cd,drive=d8,bootindex=8 " + "-drive if=none,id=d8,media=cdrom,file=", test_cdboot); if (qtest_has_device("x-terminal3270")) { qtest_add_data_func("cdrom/boot/without-bootindex", "-device virtio-scsi -device virtio-serial " diff --git a/tests/tcg/s390x/Makefile.softmmu-target b/tests/tcg/s390x/Makefile.softmmu-target index 3227903348..969bc5728f 100644 --- a/tests/tcg/s390x/Makefile.softmmu-target +++ b/tests/tcg/s390x/Makefile.softmmu-target @@ -3,7 +3,7 @@ VPATH+=$(S390X_SRC) # EXTFLAGS can be passed by the user, e.g. to override the --accel QEMU_OPTS+=-action panic=exit-failure -nographic -serial chardev:output $(EXTFLAGS) -kernel LINK_SCRIPT=$(S390X_SRC)/softmmu.ld -CFLAGS+=-ggdb -O0 +CFLAGS+=-ggdb -O0 -I$(SRC_PATH)/include/hw/s390x/ipl/ LDFLAGS=-nostdlib -static %.o: %.S diff --git a/tests/tcg/s390x/console.c b/tests/tcg/s390x/console.c index d43ce3f44b..6c26f04949 100644 --- a/tests/tcg/s390x/console.c +++ b/tests/tcg/s390x/console.c @@ -4,7 +4,10 @@ * * SPDX-License-Identifier: GPL-2.0-or-later */ + #include "../../../pc-bios/s390-ccw/sclp.c" +#include "../../../roms/SLOF/lib/libc/string/memset.c" +#include "../../../roms/SLOF/lib/libc/string/memcpy.c" void __sys_outc(char c) { diff --git a/tests/unit/test-crypto-hash.c b/tests/unit/test-crypto-hash.c index e5829ca766..76c4699c15 100644 --- a/tests/unit/test-crypto-hash.c +++ b/tests/unit/test-crypto-hash.c @@ -123,7 +123,7 @@ static void test_hash_prealloc(void) size_t i; for (i = 0; i < G_N_ELEMENTS(expected_outputs) ; i++) { - uint8_t *result; + uint8_t *result, *origresult; size_t resultlen; int ret; size_t j; @@ -133,7 +133,7 @@ static void test_hash_prealloc(void) } resultlen = expected_lens[i]; - result = g_new0(uint8_t, resultlen); + origresult = result = g_new0(uint8_t, resultlen); ret = qcrypto_hash_bytes(i, INPUT_TEXT, @@ -142,7 +142,8 @@ static void test_hash_prealloc(void) &resultlen, &error_fatal); g_assert(ret == 0); - + /* Validate that our pre-allocated pointer was not replaced */ + g_assert(result == origresult); g_assert(resultlen == expected_lens[i]); for (j = 0; j < resultlen; j++) { g_assert(expected_outputs[i][j * 2] == hex[(result[j] >> 4) & 0xf]); diff --git a/tests/unit/test-crypto-hmac.c b/tests/unit/test-crypto-hmac.c index 3fa50f24bb..cdb8774443 100644 --- a/tests/unit/test-crypto-hmac.c +++ b/tests/unit/test-crypto-hmac.c @@ -126,7 +126,7 @@ static void test_hmac_prealloc(void) for (i = 0; i < G_N_ELEMENTS(test_data); i++) { QCryptoHmacTestData *data = &test_data[i]; QCryptoHmac *hmac = NULL; - uint8_t *result = NULL; + uint8_t *result = NULL, *origresult = NULL; size_t resultlen = 0; const char *exp_output = NULL; int ret; @@ -139,7 +139,7 @@ static void test_hmac_prealloc(void) exp_output = data->hex_digest; resultlen = strlen(exp_output) / 2; - result = g_new0(uint8_t, resultlen); + origresult = result = g_new0(uint8_t, resultlen); hmac = qcrypto_hmac_new(data->alg, (const uint8_t *)KEY, strlen(KEY), &error_fatal); @@ -149,6 +149,8 @@ static void test_hmac_prealloc(void) strlen(INPUT_TEXT), &result, &resultlen, &error_fatal); g_assert(ret == 0); + /* Validate that our pre-allocated pointer was not replaced */ + g_assert(result == origresult); exp_output = data->hex_digest; for (j = 0; j < resultlen; j++) { diff --git a/ui/console-vc.c b/ui/console-vc.c index 8393d532e7..53fcee88f4 100644 --- a/ui/console-vc.c +++ b/ui/console-vc.c @@ -648,7 +648,7 @@ static void vc_putchar(VCChardev *vc, int ch) QemuTextConsole *s = vc->console; int i; int x, y; - char response[40]; + g_autofree char *response = NULL; switch(vc->state) { case TTY_STATE_NORM: @@ -821,7 +821,7 @@ static void vc_putchar(VCChardev *vc, int ch) break; case 6: /* report cursor position */ - sprintf(response, "\033[%d;%dR", + response = g_strdup_printf("\033[%d;%dR", (s->y_base + s->y) % s->total_height + 1, s->x + 1); vc_respond_str(vc, response); diff --git a/ui/curses.c b/ui/curses.c index ec61615f7c..4d0be9b37d 100644 --- a/ui/curses.c +++ b/ui/curses.c @@ -38,7 +38,7 @@ #include "ui/input.h" #include "sysemu/sysemu.h" -#if defined(__APPLE__) || defined(__OpenBSD__) +#ifdef __APPLE__ #define _XOPEN_SOURCE_EXTENDED 1 #endif diff --git a/ui/vnc-auth-sasl.c b/ui/vnc-auth-sasl.c index 47fdae5b21..3f4cfc471d 100644 --- a/ui/vnc-auth-sasl.c +++ b/ui/vnc-auth-sasl.c @@ -263,8 +263,14 @@ static int protocol_client_auth_sasl_step(VncState *vs, uint8_t *data, size_t le /* NB, distinction of NULL vs "" is *critical* in SASL */ if (datalen) { clientdata = (char*)data; - clientdata[datalen-1] = '\0'; /* Wire includes '\0', but make sure */ - datalen--; /* Don't count NULL byte when passing to _start() */ + if (clientdata[datalen - 1] != '\0') { + trace_vnc_auth_fail(vs, vs->auth, "Malformed SASL client data", + "Missing SASL NUL padding byte"); + sasl_dispose(&vs->sasl.conn); + vs->sasl.conn = NULL; + goto authabort; + } + datalen--; /* Discard the extra NUL padding byte */ } err = sasl_server_step(vs->sasl.conn, @@ -289,9 +295,10 @@ static int protocol_client_auth_sasl_step(VncState *vs, uint8_t *data, size_t le goto authabort; } - if (serveroutlen) { + if (serverout) { vnc_write_u32(vs, serveroutlen + 1); - vnc_write(vs, serverout, serveroutlen + 1); + vnc_write(vs, serverout, serveroutlen); + vnc_write_u8(vs, '\0'); } else { vnc_write_u32(vs, 0); } @@ -384,8 +391,14 @@ static int protocol_client_auth_sasl_start(VncState *vs, uint8_t *data, size_t l /* NB, distinction of NULL vs "" is *critical* in SASL */ if (datalen) { clientdata = (char*)data; - clientdata[datalen-1] = '\0'; /* Should be on wire, but make sure */ - datalen--; /* Don't count NULL byte when passing to _start() */ + if (clientdata[datalen - 1] != '\0') { + trace_vnc_auth_fail(vs, vs->auth, "Malformed SASL client data", + "Missing SASL NUL padding byte"); + sasl_dispose(&vs->sasl.conn); + vs->sasl.conn = NULL; + goto authabort; + } + datalen--; /* Discard the extra NUL padding byte */ } err = sasl_server_start(vs->sasl.conn, @@ -410,9 +423,10 @@ static int protocol_client_auth_sasl_start(VncState *vs, uint8_t *data, size_t l goto authabort; } - if (serveroutlen) { + if (serverout) { vnc_write_u32(vs, serveroutlen + 1); - vnc_write(vs, serverout, serveroutlen + 1); + vnc_write(vs, serverout, serveroutlen); + vnc_write_u8(vs, '\0'); } else { vnc_write_u32(vs, 0); } @@ -524,13 +538,13 @@ static int protocol_client_auth_sasl_mechname_len(VncState *vs, uint8_t *data, s return 0; } -static char * +static int vnc_socket_ip_addr_string(QIOChannelSocket *ioc, bool local, + char **addrstr, Error **errp) { SocketAddress *addr; - char *ret; if (local) { addr = qio_channel_socket_get_local_address(ioc, errp); @@ -538,17 +552,24 @@ vnc_socket_ip_addr_string(QIOChannelSocket *ioc, addr = qio_channel_socket_get_remote_address(ioc, errp); } if (!addr) { - return NULL; + return -1; } if (addr->type != SOCKET_ADDRESS_TYPE_INET) { - error_setg(errp, "Not an inet socket type"); + *addrstr = NULL; qapi_free_SocketAddress(addr); - return NULL; + return 0; } - ret = g_strdup_printf("%s;%s", addr->u.inet.host, addr->u.inet.port); + *addrstr = g_strdup_printf("%s;%s", addr->u.inet.host, addr->u.inet.port); qapi_free_SocketAddress(addr); - return ret; + return 0; +} + +static bool +vnc_socket_is_unix(QIOChannelSocket *ioc) +{ + SocketAddress *addr = qio_channel_socket_get_local_address(ioc, NULL); + return addr && addr->type == SOCKET_ADDRESS_TYPE_UNIX; } void start_auth_sasl(VncState *vs) @@ -561,15 +582,15 @@ void start_auth_sasl(VncState *vs) int mechlistlen; /* Get local & remote client addresses in form IPADDR;PORT */ - localAddr = vnc_socket_ip_addr_string(vs->sioc, true, &local_err); - if (!localAddr) { + if (vnc_socket_ip_addr_string(vs->sioc, true, + &localAddr, &local_err) < 0) { trace_vnc_auth_fail(vs, vs->auth, "Cannot format local IP", error_get_pretty(local_err)); goto authabort; } - remoteAddr = vnc_socket_ip_addr_string(vs->sioc, false, &local_err); - if (!remoteAddr) { + if (vnc_socket_ip_addr_string(vs->sioc, false, + &remoteAddr, &local_err) < 0) { trace_vnc_auth_fail(vs, vs->auth, "Cannot format remote IP", error_get_pretty(local_err)); g_free(localAddr); @@ -621,16 +642,17 @@ void start_auth_sasl(VncState *vs) goto authabort; } } else { - vs->sasl.wantSSF = 1; + vs->sasl.wantSSF = !vnc_socket_is_unix(vs->sioc); } memset (&secprops, 0, sizeof secprops); /* Inform SASL that we've got an external SSF layer from TLS. * - * Disable SSF, if using TLS+x509+SASL only. TLS without x509 - * is not sufficiently strong + * Disable SSF, if using TLS+x509+SASL only, or UNIX sockets. + * TLS without x509 is not sufficiently strong, nor is plain + * TCP */ - if (vs->vd->is_unix || + if (vnc_socket_is_unix(vs->sioc) || (vs->auth == VNC_AUTH_VENCRYPT && vs->subauth == VNC_AUTH_VENCRYPT_X509SASL)) { /* If we've got TLS or UNIX domain sock, we don't care about SSF */ @@ -674,6 +696,13 @@ void start_auth_sasl(VncState *vs) } trace_vnc_auth_sasl_mech_list(vs, mechlist); + if (g_str_equal(mechlist, "")) { + trace_vnc_auth_fail(vs, vs->auth, "no available SASL mechanisms", ""); + sasl_dispose(&vs->sasl.conn); + vs->sasl.conn = NULL; + goto authabort; + } + vs->sasl.mechlist = g_strdup(mechlist); mechlistlen = strlen(mechlist); vnc_write_u32(vs, mechlistlen); @@ -3430,7 +3430,6 @@ static void vnc_display_close(VncDisplay *vd) if (!vd) { return; } - vd->is_unix = false; if (vd->listener) { qio_net_listener_disconnect(vd->listener); @@ -3932,8 +3931,6 @@ static int vnc_display_connect(VncDisplay *vd, error_setg(errp, "Expected a single address in reverse mode"); return -1; } - /* TODO SOCKET_ADDRESS_TYPE_FD when fd has AF_UNIX */ - vd->is_unix = saddr_list->value->type == SOCKET_ADDRESS_TYPE_UNIX; sioc = qio_channel_socket_new(); qio_channel_set_name(QIO_CHANNEL(sioc), "vnc-reverse"); if (qio_channel_socket_connect_sync(sioc, saddr_list->value, errp) < 0) { @@ -168,7 +168,6 @@ struct VncDisplay const char *id; QTAILQ_ENTRY(VncDisplay) next; - bool is_unix; char *password; time_t expires; int auth; diff --git a/util/cpuinfo-riscv.c b/util/cpuinfo-riscv.c index 8cacc67645..971c924012 100644 --- a/util/cpuinfo-riscv.c +++ b/util/cpuinfo-riscv.c @@ -4,6 +4,7 @@ */ #include "qemu/osdep.h" +#include "qemu/host-utils.h" #include "host/cpuinfo.h" #ifdef CONFIG_ASM_HWPROBE_H @@ -13,6 +14,7 @@ #endif unsigned cpuinfo; +unsigned riscv_lg2_vlenb; static volatile sig_atomic_t got_sigill; static void sigill_handler(int signo, siginfo_t *si, void *data) @@ -34,7 +36,7 @@ static void sigill_handler(int signo, siginfo_t *si, void *data) /* Called both as constructor and (possibly) via other constructors. */ unsigned __attribute__((constructor)) cpuinfo_init(void) { - unsigned left = CPUINFO_ZBA | CPUINFO_ZBB | CPUINFO_ZICOND; + unsigned left = CPUINFO_ZBA | CPUINFO_ZBB | CPUINFO_ZICOND | CPUINFO_ZVE64X; unsigned info = cpuinfo; if (info) { @@ -51,6 +53,10 @@ unsigned __attribute__((constructor)) cpuinfo_init(void) #if defined(__riscv_arch_test) && defined(__riscv_zicond) info |= CPUINFO_ZICOND; #endif +#if defined(__riscv_arch_test) && \ + (defined(__riscv_vector) || defined(__riscv_zve64x)) + info |= CPUINFO_ZVE64X; +#endif left &= ~info; #ifdef CONFIG_ASM_HWPROBE_H @@ -70,10 +76,21 @@ unsigned __attribute__((constructor)) cpuinfo_init(void) info |= pair.value & RISCV_HWPROBE_EXT_ZICOND ? CPUINFO_ZICOND : 0; left &= ~CPUINFO_ZICOND; #endif + /* For rv64, V is Zve64d, a superset of Zve64x. */ + info |= pair.value & RISCV_HWPROBE_IMA_V ? CPUINFO_ZVE64X : 0; +#ifdef RISCV_HWPROBE_EXT_ZVE64X + info |= pair.value & RISCV_HWPROBE_EXT_ZVE64X ? CPUINFO_ZVE64X : 0; +#endif } } #endif /* CONFIG_ASM_HWPROBE_H */ + /* + * We only detect support for vectors with hwprobe. All kernels with + * support for vectors in userspace also support the hwprobe syscall. + */ + left &= ~CPUINFO_ZVE64X; + if (left) { struct sigaction sa_old, sa_new; @@ -113,6 +130,21 @@ unsigned __attribute__((constructor)) cpuinfo_init(void) assert(left == 0); } + if (info & CPUINFO_ZVE64X) { + /* + * We are guaranteed by RVV-1.0 that VLEN is a power of 2. + * We are guaranteed by Zve64x that VLEN >= 64, and that + * EEW of {8,16,32,64} are supported. + */ + unsigned long vlenb; + /* csrr %0, vlenb */ + asm volatile(".insn i 0x73, 0x2, %0, zero, -990" : "=r"(vlenb)); + assert(vlenb >= 8); + assert(is_power_of_2(vlenb)); + /* Cache VLEN in a convenient form. */ + riscv_lg2_vlenb = ctz32(vlenb); + } + info |= CPUINFO_ALWAYS; cpuinfo = info; return info; diff --git a/util/qemu-sockets.c b/util/qemu-sockets.c index 60c44b2b56..77477c1cd5 100644 --- a/util/qemu-sockets.c +++ b/util/qemu-sockets.c @@ -367,7 +367,6 @@ static int inet_connect_addr(const InetSocketAddress *saddr, addr->ai_family); return -1; } - socket_set_fast_reuse(sock); /* connect to peer */ do { @@ -707,26 +706,6 @@ int inet_parse(InetSocketAddress *addr, const char *str, Error **errp) } -/** - * Create a blocking socket and connect it to an address. - * - * @str: address string - * @errp: set in case of an error - * - * Returns -1 in case of error, file descriptor on success - **/ -int inet_connect(const char *str, Error **errp) -{ - int sock = -1; - InetSocketAddress *addr = g_new(InetSocketAddress, 1); - - if (!inet_parse(addr, str, errp)) { - sock = inet_connect_saddr(addr, errp); - } - qapi_free_InetSocketAddress(addr); - return sock; -} - #ifdef CONFIG_AF_VSOCK static bool vsock_parse_vaddr_to_sockaddr(const VsockSocketAddress *vaddr, struct sockaddr_vm *svm, @@ -1421,21 +1400,6 @@ SocketAddress *socket_local_address(int fd, Error **errp) } -SocketAddress *socket_remote_address(int fd, Error **errp) -{ - struct sockaddr_storage ss; - socklen_t sslen = sizeof(ss); - - if (getpeername(fd, (struct sockaddr *)&ss, &sslen) < 0) { - error_setg_errno(errp, errno, "%s", - "Unable to query remote socket address"); - return NULL; - } - - return socket_sockaddr_to_address(&ss, sslen, errp); -} - - SocketAddress *socket_address_flatten(SocketAddressLegacy *addr_legacy) { SocketAddress *addr; |