diff options
53 files changed, 3057 insertions, 829 deletions
@@ -275,6 +275,8 @@ acpi-dsdt.aml q35-acpi-dsdt.aml \ ppc_rom.bin openbios-sparc32 openbios-sparc64 openbios-ppc \ pxe-e1000.rom pxe-eepro100.rom pxe-ne2k_pci.rom \ pxe-pcnet.rom pxe-rtl8139.rom pxe-virtio.rom \ +efi-e1000.rom efi-eepro100.rom efi-ne2k_pci.rom \ +efi-pcnet.rom efi-rtl8139.rom efi-virtio.rom \ qemu-icon.bmp \ bamboo.dtb petalogix-s3adsp1800.dtb petalogix-ml605.dtb \ multiboot.bin linuxboot.bin kvmvapic.bin \ @@ -2131,27 +2131,21 @@ static void coroutine_fn bdrv_rw_co_entry(void *opaque) } /* - * Process a synchronous request using coroutines + * Process a vectored synchronous request using coroutines */ -static int bdrv_rw_co(BlockDriverState *bs, int64_t sector_num, uint8_t *buf, - int nb_sectors, bool is_write) +static int bdrv_rwv_co(BlockDriverState *bs, int64_t sector_num, + QEMUIOVector *qiov, bool is_write) { - QEMUIOVector qiov; - struct iovec iov = { - .iov_base = (void *)buf, - .iov_len = nb_sectors * BDRV_SECTOR_SIZE, - }; Coroutine *co; RwCo rwco = { .bs = bs, .sector_num = sector_num, - .nb_sectors = nb_sectors, - .qiov = &qiov, + .nb_sectors = qiov->size >> BDRV_SECTOR_BITS, + .qiov = qiov, .is_write = is_write, .ret = NOT_DONE, }; - - qemu_iovec_init_external(&qiov, &iov, 1); + assert((qiov->size & (BDRV_SECTOR_SIZE - 1)) == 0); /** * In sync call context, when the vcpu is blocked, this throttling timer @@ -2177,6 +2171,22 @@ static int bdrv_rw_co(BlockDriverState *bs, int64_t sector_num, uint8_t *buf, return rwco.ret; } +/* + * Process a synchronous request using coroutines + */ +static int bdrv_rw_co(BlockDriverState *bs, int64_t sector_num, uint8_t *buf, + int nb_sectors, bool is_write) +{ + QEMUIOVector qiov; + struct iovec iov = { + .iov_base = (void *)buf, + .iov_len = nb_sectors * BDRV_SECTOR_SIZE, + }; + + qemu_iovec_init_external(&qiov, &iov, 1); + return bdrv_rwv_co(bs, sector_num, &qiov, is_write); +} + /* return < 0 if error. See bdrv_write() for the return codes */ int bdrv_read(BlockDriverState *bs, int64_t sector_num, uint8_t *buf, int nb_sectors) @@ -2210,6 +2220,11 @@ int bdrv_write(BlockDriverState *bs, int64_t sector_num, return bdrv_rw_co(bs, sector_num, (uint8_t *)buf, nb_sectors, true); } +int bdrv_writev(BlockDriverState *bs, int64_t sector_num, QEMUIOVector *qiov) +{ + return bdrv_rwv_co(bs, sector_num, qiov, true); +} + int bdrv_pread(BlockDriverState *bs, int64_t offset, void *buf, int count1) { @@ -2255,15 +2270,15 @@ int bdrv_pread(BlockDriverState *bs, int64_t offset, return count1; } -int bdrv_pwrite(BlockDriverState *bs, int64_t offset, - const void *buf, int count1) +int bdrv_pwritev(BlockDriverState *bs, int64_t offset, QEMUIOVector *qiov) { uint8_t tmp_buf[BDRV_SECTOR_SIZE]; int len, nb_sectors, count; int64_t sector_num; int ret; - count = count1; + count = qiov->size; + /* first write to align to sector start */ len = (BDRV_SECTOR_SIZE - offset) & (BDRV_SECTOR_SIZE - 1); if (len > count) @@ -2272,24 +2287,32 @@ int bdrv_pwrite(BlockDriverState *bs, int64_t offset, if (len > 0) { if ((ret = bdrv_read(bs, sector_num, tmp_buf, 1)) < 0) return ret; - memcpy(tmp_buf + (offset & (BDRV_SECTOR_SIZE - 1)), buf, len); + qemu_iovec_to_buf(qiov, 0, tmp_buf + (offset & (BDRV_SECTOR_SIZE - 1)), + len); if ((ret = bdrv_write(bs, sector_num, tmp_buf, 1)) < 0) return ret; count -= len; if (count == 0) - return count1; + return qiov->size; sector_num++; - buf += len; } /* write the sectors "in place" */ nb_sectors = count >> BDRV_SECTOR_BITS; if (nb_sectors > 0) { - if ((ret = bdrv_write(bs, sector_num, buf, nb_sectors)) < 0) + QEMUIOVector qiov_inplace; + + qemu_iovec_init(&qiov_inplace, qiov->niov); + qemu_iovec_concat(&qiov_inplace, qiov, len, + nb_sectors << BDRV_SECTOR_BITS); + ret = bdrv_writev(bs, sector_num, &qiov_inplace); + qemu_iovec_destroy(&qiov_inplace); + if (ret < 0) { return ret; + } + sector_num += nb_sectors; len = nb_sectors << BDRV_SECTOR_BITS; - buf += len; count -= len; } @@ -2297,11 +2320,24 @@ int bdrv_pwrite(BlockDriverState *bs, int64_t offset, if (count > 0) { if ((ret = bdrv_read(bs, sector_num, tmp_buf, 1)) < 0) return ret; - memcpy(tmp_buf, buf, count); + qemu_iovec_to_buf(qiov, qiov->size - count, tmp_buf, count); if ((ret = bdrv_write(bs, sector_num, tmp_buf, 1)) < 0) return ret; } - return count1; + return qiov->size; +} + +int bdrv_pwrite(BlockDriverState *bs, int64_t offset, + const void *buf, int count1) +{ + QEMUIOVector qiov; + struct iovec iov = { + .iov_base = (void *) buf, + .iov_len = count1, + }; + + qemu_iovec_init_external(&qiov, &iov, 1); + return bdrv_pwritev(bs, offset, &qiov); } /* @@ -3184,13 +3220,28 @@ int bdrv_get_info(BlockDriverState *bs, BlockDriverInfo *bdi) int bdrv_save_vmstate(BlockDriverState *bs, const uint8_t *buf, int64_t pos, int size) { + QEMUIOVector qiov; + struct iovec iov = { + .iov_base = (void *) buf, + .iov_len = size, + }; + + qemu_iovec_init_external(&qiov, &iov, 1); + return bdrv_writev_vmstate(bs, &qiov, pos); +} + +int bdrv_writev_vmstate(BlockDriverState *bs, QEMUIOVector *qiov, int64_t pos) +{ BlockDriver *drv = bs->drv; - if (!drv) + + if (!drv) { return -ENOMEDIUM; - if (drv->bdrv_save_vmstate) - return drv->bdrv_save_vmstate(bs, buf, pos, size); - if (bs->file) - return bdrv_save_vmstate(bs->file, buf, pos, size); + } else if (drv->bdrv_save_vmstate) { + return drv->bdrv_save_vmstate(bs, qiov, pos); + } else if (bs->file) { + return bdrv_writev_vmstate(bs->file, qiov, pos); + } + return -ENOTSUP; } diff --git a/block/Makefile.objs b/block/Makefile.objs index c067f38..6c4b5bc 100644 --- a/block/Makefile.objs +++ b/block/Makefile.objs @@ -13,6 +13,7 @@ block-obj-$(CONFIG_LIBISCSI) += iscsi.o block-obj-$(CONFIG_CURL) += curl.o block-obj-$(CONFIG_RBD) += rbd.o block-obj-$(CONFIG_GLUSTERFS) += gluster.o +block-obj-$(CONFIG_LIBSSH2) += ssh.o endif common-obj-y += stream.o diff --git a/block/qcow2.c b/block/qcow2.c index 1d18073..e8934de 100644 --- a/block/qcow2.c +++ b/block/qcow2.c @@ -1652,8 +1652,8 @@ static void dump_refcounts(BlockDriverState *bs) } #endif -static int qcow2_save_vmstate(BlockDriverState *bs, const uint8_t *buf, - int64_t pos, int size) +static int qcow2_save_vmstate(BlockDriverState *bs, QEMUIOVector *qiov, + int64_t pos) { BDRVQcowState *s = bs->opaque; int growable = bs->growable; @@ -1661,7 +1661,7 @@ static int qcow2_save_vmstate(BlockDriverState *bs, const uint8_t *buf, BLKDBG_EVENT(bs->file, BLKDBG_VMSTATE_SAVE); bs->growable = 1; - ret = bdrv_pwrite(bs, qcow2_vm_state_offset(s) + pos, buf, size); + ret = bdrv_pwritev(bs, qcow2_vm_state_offset(s) + pos, qiov); bs->growable = growable; return ret; diff --git a/block/rbd.c b/block/rbd.c index 1a8ea6d..141b488 100644 --- a/block/rbd.c +++ b/block/rbd.c @@ -63,7 +63,8 @@ typedef enum { RBD_AIO_READ, RBD_AIO_WRITE, - RBD_AIO_DISCARD + RBD_AIO_DISCARD, + RBD_AIO_FLUSH } RBDAIOCmd; typedef struct RBDAIOCB { @@ -379,8 +380,7 @@ static void qemu_rbd_complete_aio(RADOSCB *rcb) r = rcb->ret; - if (acb->cmd == RBD_AIO_WRITE || - acb->cmd == RBD_AIO_DISCARD) { + if (acb->cmd != RBD_AIO_READ) { if (r < 0) { acb->ret = r; acb->error = 1; @@ -659,6 +659,16 @@ static int rbd_aio_discard_wrapper(rbd_image_t image, #endif } +static int rbd_aio_flush_wrapper(rbd_image_t image, + rbd_completion_t comp) +{ +#ifdef LIBRBD_SUPPORTS_AIO_FLUSH + return rbd_aio_flush(image, comp); +#else + return -ENOTSUP; +#endif +} + static BlockDriverAIOCB *rbd_start_aio(BlockDriverState *bs, int64_t sector_num, QEMUIOVector *qiov, @@ -679,7 +689,7 @@ static BlockDriverAIOCB *rbd_start_aio(BlockDriverState *bs, acb = qemu_aio_get(&rbd_aiocb_info, bs, cb, opaque); acb->cmd = cmd; acb->qiov = qiov; - if (cmd == RBD_AIO_DISCARD) { + if (cmd == RBD_AIO_DISCARD || cmd == RBD_AIO_FLUSH) { acb->bounce = NULL; } else { acb->bounce = qemu_blockalign(bs, qiov->size); @@ -723,6 +733,9 @@ static BlockDriverAIOCB *rbd_start_aio(BlockDriverState *bs, case RBD_AIO_DISCARD: r = rbd_aio_discard_wrapper(s->image, off, size, c); break; + case RBD_AIO_FLUSH: + r = rbd_aio_flush_wrapper(s->image, c); + break; default: r = -EINVAL; } @@ -762,6 +775,16 @@ static BlockDriverAIOCB *qemu_rbd_aio_writev(BlockDriverState *bs, RBD_AIO_WRITE); } +#ifdef LIBRBD_SUPPORTS_AIO_FLUSH +static BlockDriverAIOCB *qemu_rbd_aio_flush(BlockDriverState *bs, + BlockDriverCompletionFunc *cb, + void *opaque) +{ + return rbd_start_aio(bs, 0, NULL, 0, cb, opaque, RBD_AIO_FLUSH); +} + +#else + static int qemu_rbd_co_flush(BlockDriverState *bs) { #if LIBRBD_VERSION_CODE >= LIBRBD_VERSION(0, 1, 1) @@ -772,6 +795,7 @@ static int qemu_rbd_co_flush(BlockDriverState *bs) return 0; #endif } +#endif static int qemu_rbd_getinfo(BlockDriverState *bs, BlockDriverInfo *bdi) { @@ -949,7 +973,12 @@ static BlockDriver bdrv_rbd = { .bdrv_aio_readv = qemu_rbd_aio_readv, .bdrv_aio_writev = qemu_rbd_aio_writev, + +#ifdef LIBRBD_SUPPORTS_AIO_FLUSH + .bdrv_aio_flush = qemu_rbd_aio_flush, +#else .bdrv_co_flush_to_disk = qemu_rbd_co_flush, +#endif #ifdef LIBRBD_SUPPORTS_DISCARD .bdrv_aio_discard = qemu_rbd_aio_discard, diff --git a/block/sheepdog.c b/block/sheepdog.c index 987018e..1c5b532 100644 --- a/block/sheepdog.c +++ b/block/sheepdog.c @@ -2054,12 +2054,19 @@ cleanup: return ret; } -static int sd_save_vmstate(BlockDriverState *bs, const uint8_t *data, - int64_t pos, int size) +static int sd_save_vmstate(BlockDriverState *bs, QEMUIOVector *qiov, + int64_t pos) { BDRVSheepdogState *s = bs->opaque; + void *buf; + int ret; - return do_load_save_vmstate(s, (uint8_t *)data, pos, size, 0); + buf = qemu_blockalign(bs, qiov->size); + qemu_iovec_to_buf(qiov, 0, buf, qiov->size); + ret = do_load_save_vmstate(s, (uint8_t *) buf, pos, qiov->size, 0); + qemu_vfree(buf); + + return ret; } static int sd_load_vmstate(BlockDriverState *bs, uint8_t *data, diff --git a/block/ssh.c b/block/ssh.c new file mode 100644 index 0000000..8f78e2e --- /dev/null +++ b/block/ssh.c @@ -0,0 +1,1063 @@ +/* + * Secure Shell (ssh) backend for QEMU. + * + * Copyright (C) 2013 Red Hat Inc., Richard W.M. Jones <rjones@redhat.com> + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include <stdio.h> +#include <stdlib.h> +#include <stdarg.h> + +#include <libssh2.h> +#include <libssh2_sftp.h> + +#include "block/block_int.h" +#include "qemu/sockets.h" +#include "qemu/uri.h" +#include "qapi/qmp/qint.h" + +/* DEBUG_SSH=1 enables the DPRINTF (debugging printf) statements in + * this block driver code. + * + * TRACE_LIBSSH2=<bitmask> enables tracing in libssh2 itself. Note + * that this requires that libssh2 was specially compiled with the + * `./configure --enable-debug' option, so most likely you will have + * to compile it yourself. The meaning of <bitmask> is described + * here: http://www.libssh2.org/libssh2_trace.html + */ +#define DEBUG_SSH 0 +#define TRACE_LIBSSH2 0 /* or try: LIBSSH2_TRACE_SFTP */ + +#define DPRINTF(fmt, ...) \ + do { \ + if (DEBUG_SSH) { \ + fprintf(stderr, "ssh: %-15s " fmt "\n", \ + __func__, ##__VA_ARGS__); \ + } \ + } while (0) + +typedef struct BDRVSSHState { + /* Coroutine. */ + CoMutex lock; + + /* SSH connection. */ + int sock; /* socket */ + LIBSSH2_SESSION *session; /* ssh session */ + LIBSSH2_SFTP *sftp; /* sftp session */ + LIBSSH2_SFTP_HANDLE *sftp_handle; /* sftp remote file handle */ + + /* See ssh_seek() function below. */ + int64_t offset; + bool offset_op_read; + + /* File attributes at open. We try to keep the .filesize field + * updated if it changes (eg by writing at the end of the file). + */ + LIBSSH2_SFTP_ATTRIBUTES attrs; + + /* Used to warn if 'flush' is not supported. */ + char *hostport; + bool unsafe_flush_warning; +} BDRVSSHState; + +static void ssh_state_init(BDRVSSHState *s) +{ + memset(s, 0, sizeof *s); + s->sock = -1; + s->offset = -1; + qemu_co_mutex_init(&s->lock); +} + +static void ssh_state_free(BDRVSSHState *s) +{ + g_free(s->hostport); + if (s->sftp_handle) { + libssh2_sftp_close(s->sftp_handle); + } + if (s->sftp) { + libssh2_sftp_shutdown(s->sftp); + } + if (s->session) { + libssh2_session_disconnect(s->session, + "from qemu ssh client: " + "user closed the connection"); + libssh2_session_free(s->session); + } + if (s->sock >= 0) { + close(s->sock); + } +} + +/* Wrappers around error_report which make sure to dump as much + * information from libssh2 as possible. + */ +static void +session_error_report(BDRVSSHState *s, const char *fs, ...) +{ + va_list args; + + va_start(args, fs); + error_vprintf(fs, args); + + if ((s)->session) { + char *ssh_err; + int ssh_err_code; + + libssh2_session_last_error((s)->session, &ssh_err, NULL, 0); + /* This is not an errno. See <libssh2.h>. */ + ssh_err_code = libssh2_session_last_errno((s)->session); + + error_printf(": %s (libssh2 error code: %d)", ssh_err, ssh_err_code); + } + + va_end(args); + error_printf("\n"); +} + +static void +sftp_error_report(BDRVSSHState *s, const char *fs, ...) +{ + va_list args; + + va_start(args, fs); + error_vprintf(fs, args); + + if ((s)->sftp) { + char *ssh_err; + int ssh_err_code; + unsigned long sftp_err_code; + + libssh2_session_last_error((s)->session, &ssh_err, NULL, 0); + /* This is not an errno. See <libssh2.h>. */ + ssh_err_code = libssh2_session_last_errno((s)->session); + /* See <libssh2_sftp.h>. */ + sftp_err_code = libssh2_sftp_last_error((s)->sftp); + + error_printf(": %s (libssh2 error code: %d, sftp error code: %lu)", + ssh_err, ssh_err_code, sftp_err_code); + } + + va_end(args); + error_printf("\n"); +} + +static int parse_uri(const char *filename, QDict *options, Error **errp) +{ + URI *uri = NULL; + QueryParams *qp = NULL; + int i; + + uri = uri_parse(filename); + if (!uri) { + return -EINVAL; + } + + if (strcmp(uri->scheme, "ssh") != 0) { + error_setg(errp, "URI scheme must be 'ssh'"); + goto err; + } + + if (!uri->server || strcmp(uri->server, "") == 0) { + error_setg(errp, "missing hostname in URI"); + goto err; + } + + if (!uri->path || strcmp(uri->path, "") == 0) { + error_setg(errp, "missing remote path in URI"); + goto err; + } + + qp = query_params_parse(uri->query); + if (!qp) { + error_setg(errp, "could not parse query parameters"); + goto err; + } + + if(uri->user && strcmp(uri->user, "") != 0) { + qdict_put(options, "user", qstring_from_str(uri->user)); + } + + qdict_put(options, "host", qstring_from_str(uri->server)); + + if (uri->port) { + qdict_put(options, "port", qint_from_int(uri->port)); + } + + qdict_put(options, "path", qstring_from_str(uri->path)); + + /* Pick out any query parameters that we understand, and ignore + * the rest. + */ + for (i = 0; i < qp->n; ++i) { + if (strcmp(qp->p[i].name, "host_key_check") == 0) { + qdict_put(options, "host_key_check", + qstring_from_str(qp->p[i].value)); + } + } + + query_params_free(qp); + uri_free(uri); + return 0; + + err: + if (qp) { + query_params_free(qp); + } + if (uri) { + uri_free(uri); + } + return -EINVAL; +} + +static void ssh_parse_filename(const char *filename, QDict *options, + Error **errp) +{ + if (qdict_haskey(options, "user") || + qdict_haskey(options, "host") || + qdict_haskey(options, "port") || + qdict_haskey(options, "path") || + qdict_haskey(options, "host_key_check")) { + error_setg(errp, "user, host, port, path, host_key_check cannot be used at the same time as a file option"); + return; + } + + parse_uri(filename, options, errp); +} + +static int check_host_key_knownhosts(BDRVSSHState *s, + const char *host, int port) +{ + const char *home; + char *knh_file = NULL; + LIBSSH2_KNOWNHOSTS *knh = NULL; + struct libssh2_knownhost *found; + int ret, r; + const char *hostkey; + size_t len; + int type; + + hostkey = libssh2_session_hostkey(s->session, &len, &type); + if (!hostkey) { + ret = -EINVAL; + session_error_report(s, "failed to read remote host key"); + goto out; + } + + knh = libssh2_knownhost_init(s->session); + if (!knh) { + ret = -EINVAL; + session_error_report(s, "failed to initialize known hosts support"); + goto out; + } + + home = getenv("HOME"); + if (home) { + knh_file = g_strdup_printf("%s/.ssh/known_hosts", home); + } else { + knh_file = g_strdup_printf("/root/.ssh/known_hosts"); + } + + /* Read all known hosts from OpenSSH-style known_hosts file. */ + libssh2_knownhost_readfile(knh, knh_file, LIBSSH2_KNOWNHOST_FILE_OPENSSH); + + r = libssh2_knownhost_checkp(knh, host, port, hostkey, len, + LIBSSH2_KNOWNHOST_TYPE_PLAIN| + LIBSSH2_KNOWNHOST_KEYENC_RAW, + &found); + switch (r) { + case LIBSSH2_KNOWNHOST_CHECK_MATCH: + /* OK */ + DPRINTF("host key OK: %s", found->key); + break; + case LIBSSH2_KNOWNHOST_CHECK_MISMATCH: + ret = -EINVAL; + session_error_report(s, "host key does not match the one in known_hosts (found key %s)", + found->key); + goto out; + case LIBSSH2_KNOWNHOST_CHECK_NOTFOUND: + ret = -EINVAL; + session_error_report(s, "no host key was found in known_hosts"); + goto out; + case LIBSSH2_KNOWNHOST_CHECK_FAILURE: + ret = -EINVAL; + session_error_report(s, "failure matching the host key with known_hosts"); + goto out; + default: + ret = -EINVAL; + session_error_report(s, "unknown error matching the host key with known_hosts (%d)", + r); + goto out; + } + + /* known_hosts checking successful. */ + ret = 0; + + out: + if (knh != NULL) { + libssh2_knownhost_free(knh); + } + g_free(knh_file); + return ret; +} + +static unsigned hex2decimal(char ch) +{ + if (ch >= '0' && ch <= '9') { + return (ch - '0'); + } else if (ch >= 'a' && ch <= 'f') { + return 10 + (ch - 'a'); + } else if (ch >= 'A' && ch <= 'F') { + return 10 + (ch - 'A'); + } + + return -1; +} + +/* Compare the binary fingerprint (hash of host key) with the + * host_key_check parameter. + */ +static int compare_fingerprint(const unsigned char *fingerprint, size_t len, + const char *host_key_check) +{ + unsigned c; + + while (len > 0) { + while (*host_key_check == ':') + host_key_check++; + if (!qemu_isxdigit(host_key_check[0]) || + !qemu_isxdigit(host_key_check[1])) + return 1; + c = hex2decimal(host_key_check[0]) * 16 + + hex2decimal(host_key_check[1]); + if (c - *fingerprint != 0) + return c - *fingerprint; + fingerprint++; + len--; + host_key_check += 2; + } + return *host_key_check - '\0'; +} + +static int +check_host_key_hash(BDRVSSHState *s, const char *hash, + int hash_type, size_t fingerprint_len) +{ + const char *fingerprint; + + fingerprint = libssh2_hostkey_hash(s->session, hash_type); + if (!fingerprint) { + session_error_report(s, "failed to read remote host key"); + return -EINVAL; + } + + if(compare_fingerprint((unsigned char *) fingerprint, fingerprint_len, + hash) != 0) { + error_report("remote host key does not match host_key_check '%s'", + hash); + return -EPERM; + } + + return 0; +} + +static int check_host_key(BDRVSSHState *s, const char *host, int port, + const char *host_key_check) +{ + /* host_key_check=no */ + if (strcmp(host_key_check, "no") == 0) { + return 0; + } + + /* host_key_check=md5:xx:yy:zz:... */ + if (strlen(host_key_check) >= 4 && + strncmp(host_key_check, "md5:", 4) == 0) { + return check_host_key_hash(s, &host_key_check[4], + LIBSSH2_HOSTKEY_HASH_MD5, 16); + } + + /* host_key_check=sha1:xx:yy:zz:... */ + if (strlen(host_key_check) >= 5 && + strncmp(host_key_check, "sha1:", 5) == 0) { + return check_host_key_hash(s, &host_key_check[5], + LIBSSH2_HOSTKEY_HASH_SHA1, 20); + } + + /* host_key_check=yes */ + if (strcmp(host_key_check, "yes") == 0) { + return check_host_key_knownhosts(s, host, port); + } + + error_report("unknown host_key_check setting (%s)", host_key_check); + return -EINVAL; +} + +static int authenticate(BDRVSSHState *s, const char *user) +{ + int r, ret; + const char *userauthlist; + LIBSSH2_AGENT *agent = NULL; + struct libssh2_agent_publickey *identity; + struct libssh2_agent_publickey *prev_identity = NULL; + + userauthlist = libssh2_userauth_list(s->session, user, strlen(user)); + if (strstr(userauthlist, "publickey") == NULL) { + ret = -EPERM; + error_report("remote server does not support \"publickey\" authentication"); + goto out; + } + + /* Connect to ssh-agent and try each identity in turn. */ + agent = libssh2_agent_init(s->session); + if (!agent) { + ret = -EINVAL; + session_error_report(s, "failed to initialize ssh-agent support"); + goto out; + } + if (libssh2_agent_connect(agent)) { + ret = -ECONNREFUSED; + session_error_report(s, "failed to connect to ssh-agent"); + goto out; + } + if (libssh2_agent_list_identities(agent)) { + ret = -EINVAL; + session_error_report(s, "failed requesting identities from ssh-agent"); + goto out; + } + + for(;;) { + r = libssh2_agent_get_identity(agent, &identity, prev_identity); + if (r == 1) { /* end of list */ + break; + } + if (r < 0) { + ret = -EINVAL; + session_error_report(s, "failed to obtain identity from ssh-agent"); + goto out; + } + r = libssh2_agent_userauth(agent, user, identity); + if (r == 0) { + /* Authenticated! */ + ret = 0; + goto out; + } + /* Failed to authenticate with this identity, try the next one. */ + prev_identity = identity; + } + + ret = -EPERM; + error_report("failed to authenticate using publickey authentication " + "and the identities held by your ssh-agent"); + + out: + if (agent != NULL) { + /* Note: libssh2 implementation implicitly calls + * libssh2_agent_disconnect if necessary. + */ + libssh2_agent_free(agent); + } + + return ret; +} + +static int connect_to_ssh(BDRVSSHState *s, QDict *options, + int ssh_flags, int creat_mode) +{ + int r, ret; + Error *err = NULL; + const char *host, *user, *path, *host_key_check; + int port; + + host = qdict_get_str(options, "host"); + + if (qdict_haskey(options, "port")) { + port = qdict_get_int(options, "port"); + } else { + port = 22; + } + + path = qdict_get_str(options, "path"); + + if (qdict_haskey(options, "user")) { + user = qdict_get_str(options, "user"); + } else { + user = g_get_user_name(); + if (!user) { + ret = -errno; + goto err; + } + } + + if (qdict_haskey(options, "host_key_check")) { + host_key_check = qdict_get_str(options, "host_key_check"); + } else { + host_key_check = "yes"; + } + + /* Construct the host:port name for inet_connect. */ + g_free(s->hostport); + s->hostport = g_strdup_printf("%s:%d", host, port); + + /* Open the socket and connect. */ + s->sock = inet_connect(s->hostport, &err); + if (err != NULL) { + ret = -errno; + qerror_report_err(err); + error_free(err); + goto err; + } + + /* Create SSH session. */ + s->session = libssh2_session_init(); + if (!s->session) { + ret = -EINVAL; + session_error_report(s, "failed to initialize libssh2 session"); + goto err; + } + +#if TRACE_LIBSSH2 != 0 + libssh2_trace(s->session, TRACE_LIBSSH2); +#endif + + r = libssh2_session_handshake(s->session, s->sock); + if (r != 0) { + ret = -EINVAL; + session_error_report(s, "failed to establish SSH session"); + goto err; + } + + /* Check the remote host's key against known_hosts. */ + ret = check_host_key(s, host, port, host_key_check); + if (ret < 0) { + goto err; + } + + /* Authenticate. */ + ret = authenticate(s, user); + if (ret < 0) { + goto err; + } + + /* Start SFTP. */ + s->sftp = libssh2_sftp_init(s->session); + if (!s->sftp) { + session_error_report(s, "failed to initialize sftp handle"); + ret = -EINVAL; + goto err; + } + + /* Open the remote file. */ + DPRINTF("opening file %s flags=0x%x creat_mode=0%o", + path, ssh_flags, creat_mode); + s->sftp_handle = libssh2_sftp_open(s->sftp, path, ssh_flags, creat_mode); + if (!s->sftp_handle) { + session_error_report(s, "failed to open remote file '%s'", path); + ret = -EINVAL; + goto err; + } + + r = libssh2_sftp_fstat(s->sftp_handle, &s->attrs); + if (r < 0) { + sftp_error_report(s, "failed to read file attributes"); + return -EINVAL; + } + + /* Delete the options we've used; any not deleted will cause the + * block layer to give an error about unused options. + */ + qdict_del(options, "host"); + qdict_del(options, "port"); + qdict_del(options, "user"); + qdict_del(options, "path"); + qdict_del(options, "host_key_check"); + + return 0; + + err: + if (s->sftp_handle) { + libssh2_sftp_close(s->sftp_handle); + } + s->sftp_handle = NULL; + if (s->sftp) { + libssh2_sftp_shutdown(s->sftp); + } + s->sftp = NULL; + if (s->session) { + libssh2_session_disconnect(s->session, + "from qemu ssh client: " + "error opening connection"); + libssh2_session_free(s->session); + } + s->session = NULL; + + return ret; +} + +static int ssh_file_open(BlockDriverState *bs, const char *filename, + QDict *options, int bdrv_flags) +{ + BDRVSSHState *s = bs->opaque; + int ret; + int ssh_flags; + + ssh_state_init(s); + + ssh_flags = LIBSSH2_FXF_READ; + if (bdrv_flags & BDRV_O_RDWR) { + ssh_flags |= LIBSSH2_FXF_WRITE; + } + + /* Start up SSH. */ + ret = connect_to_ssh(s, options, ssh_flags, 0); + if (ret < 0) { + goto err; + } + + /* Go non-blocking. */ + libssh2_session_set_blocking(s->session, 0); + + return 0; + + err: + if (s->sock >= 0) { + close(s->sock); + } + s->sock = -1; + + return ret; +} + +static QEMUOptionParameter ssh_create_options[] = { + { + .name = BLOCK_OPT_SIZE, + .type = OPT_SIZE, + .help = "Virtual disk size" + }, + { NULL } +}; + +static int ssh_create(const char *filename, QEMUOptionParameter *options) +{ + int r, ret; + Error *local_err = NULL; + int64_t total_size = 0; + QDict *uri_options = NULL; + BDRVSSHState s; + ssize_t r2; + char c[1] = { '\0' }; + + ssh_state_init(&s); + + /* Get desired file size. */ + while (options && options->name) { + if (!strcmp(options->name, BLOCK_OPT_SIZE)) { + total_size = options->value.n; + } + options++; + } + DPRINTF("total_size=%" PRIi64, total_size); + + uri_options = qdict_new(); + r = parse_uri(filename, uri_options, &local_err); + if (r < 0) { + qerror_report_err(local_err); + error_free(local_err); + ret = r; + goto out; + } + + r = connect_to_ssh(&s, uri_options, + LIBSSH2_FXF_READ|LIBSSH2_FXF_WRITE| + LIBSSH2_FXF_CREAT|LIBSSH2_FXF_TRUNC, 0644); + if (r < 0) { + ret = r; + goto out; + } + + if (total_size > 0) { + libssh2_sftp_seek64(s.sftp_handle, total_size-1); + r2 = libssh2_sftp_write(s.sftp_handle, c, 1); + if (r2 < 0) { + sftp_error_report(&s, "truncate failed"); + ret = -EINVAL; + goto out; + } + s.attrs.filesize = total_size; + } + + ret = 0; + + out: + ssh_state_free(&s); + if (uri_options != NULL) { + QDECREF(uri_options); + } + return ret; +} + +static void ssh_close(BlockDriverState *bs) +{ + BDRVSSHState *s = bs->opaque; + + ssh_state_free(s); +} + +static void restart_coroutine(void *opaque) +{ + Coroutine *co = opaque; + + DPRINTF("co=%p", co); + + qemu_coroutine_enter(co, NULL); +} + +/* Always true because when we have called set_fd_handler there is + * always a request being processed. + */ +static int return_true(void *opaque) +{ + return 1; +} + +static coroutine_fn void set_fd_handler(BDRVSSHState *s) +{ + int r; + IOHandler *rd_handler = NULL, *wr_handler = NULL; + Coroutine *co = qemu_coroutine_self(); + + r = libssh2_session_block_directions(s->session); + + if (r & LIBSSH2_SESSION_BLOCK_INBOUND) { + rd_handler = restart_coroutine; + } + if (r & LIBSSH2_SESSION_BLOCK_OUTBOUND) { + wr_handler = restart_coroutine; + } + + DPRINTF("s->sock=%d rd_handler=%p wr_handler=%p", s->sock, + rd_handler, wr_handler); + + qemu_aio_set_fd_handler(s->sock, rd_handler, wr_handler, return_true, co); +} + +static coroutine_fn void clear_fd_handler(BDRVSSHState *s) +{ + DPRINTF("s->sock=%d", s->sock); + qemu_aio_set_fd_handler(s->sock, NULL, NULL, NULL, NULL); +} + +/* A non-blocking call returned EAGAIN, so yield, ensuring the + * handlers are set up so that we'll be rescheduled when there is an + * interesting event on the socket. + */ +static coroutine_fn void co_yield(BDRVSSHState *s) +{ + set_fd_handler(s); + qemu_coroutine_yield(); + clear_fd_handler(s); +} + +/* SFTP has a function `libssh2_sftp_seek64' which seeks to a position + * in the remote file. Notice that it just updates a field in the + * sftp_handle structure, so there is no network traffic and it cannot + * fail. + * + * However, `libssh2_sftp_seek64' does have a catastrophic effect on + * performance since it causes the handle to throw away all in-flight + * reads and buffered readahead data. Therefore this function tries + * to be intelligent about when to call the underlying libssh2 function. + */ +#define SSH_SEEK_WRITE 0 +#define SSH_SEEK_READ 1 +#define SSH_SEEK_FORCE 2 + +static void ssh_seek(BDRVSSHState *s, int64_t offset, int flags) +{ + bool op_read = (flags & SSH_SEEK_READ) != 0; + bool force = (flags & SSH_SEEK_FORCE) != 0; + + if (force || op_read != s->offset_op_read || offset != s->offset) { + DPRINTF("seeking to offset=%" PRIi64, offset); + libssh2_sftp_seek64(s->sftp_handle, offset); + s->offset = offset; + s->offset_op_read = op_read; + } +} + +static coroutine_fn int ssh_read(BDRVSSHState *s, + int64_t offset, size_t size, + QEMUIOVector *qiov) +{ + ssize_t r; + size_t got; + char *buf, *end_of_vec; + struct iovec *i; + + DPRINTF("offset=%" PRIi64 " size=%zu", offset, size); + + ssh_seek(s, offset, SSH_SEEK_READ); + + /* This keeps track of the current iovec element ('i'), where we + * will write to next ('buf'), and the end of the current iovec + * ('end_of_vec'). + */ + i = &qiov->iov[0]; + buf = i->iov_base; + end_of_vec = i->iov_base + i->iov_len; + + /* libssh2 has a hard-coded limit of 2000 bytes per request, + * although it will also do readahead behind our backs. Therefore + * we may have to do repeated reads here until we have read 'size' + * bytes. + */ + for (got = 0; got < size; ) { + again: + DPRINTF("sftp_read buf=%p size=%zu", buf, end_of_vec - buf); + r = libssh2_sftp_read(s->sftp_handle, buf, end_of_vec - buf); + DPRINTF("sftp_read returned %zd", r); + + if (r == LIBSSH2_ERROR_EAGAIN || r == LIBSSH2_ERROR_TIMEOUT) { + co_yield(s); + goto again; + } + if (r < 0) { + sftp_error_report(s, "read failed"); + s->offset = -1; + return -EIO; + } + if (r == 0) { + /* EOF: Short read so pad the buffer with zeroes and return it. */ + qemu_iovec_memset(qiov, got, 0, size - got); + return 0; + } + + got += r; + buf += r; + s->offset += r; + if (buf >= end_of_vec && got < size) { + i++; + buf = i->iov_base; + end_of_vec = i->iov_base + i->iov_len; + } + } + + return 0; +} + +static coroutine_fn int ssh_co_readv(BlockDriverState *bs, + int64_t sector_num, + int nb_sectors, QEMUIOVector *qiov) +{ + BDRVSSHState *s = bs->opaque; + int ret; + + qemu_co_mutex_lock(&s->lock); + ret = ssh_read(s, sector_num * BDRV_SECTOR_SIZE, + nb_sectors * BDRV_SECTOR_SIZE, qiov); + qemu_co_mutex_unlock(&s->lock); + + return ret; +} + +static int ssh_write(BDRVSSHState *s, + int64_t offset, size_t size, + QEMUIOVector *qiov) +{ + ssize_t r; + size_t written; + char *buf, *end_of_vec; + struct iovec *i; + + DPRINTF("offset=%" PRIi64 " size=%zu", offset, size); + + ssh_seek(s, offset, SSH_SEEK_WRITE); + + /* This keeps track of the current iovec element ('i'), where we + * will read from next ('buf'), and the end of the current iovec + * ('end_of_vec'). + */ + i = &qiov->iov[0]; + buf = i->iov_base; + end_of_vec = i->iov_base + i->iov_len; + + for (written = 0; written < size; ) { + again: + DPRINTF("sftp_write buf=%p size=%zu", buf, end_of_vec - buf); + r = libssh2_sftp_write(s->sftp_handle, buf, end_of_vec - buf); + DPRINTF("sftp_write returned %zd", r); + + if (r == LIBSSH2_ERROR_EAGAIN || r == LIBSSH2_ERROR_TIMEOUT) { + co_yield(s); + goto again; + } + if (r < 0) { + sftp_error_report(s, "write failed"); + s->offset = -1; + return -EIO; + } + /* The libssh2 API is very unclear about this. A comment in + * the code says "nothing was acked, and no EAGAIN was + * received!" which apparently means that no data got sent + * out, and the underlying channel didn't return any EAGAIN + * indication. I think this is a bug in either libssh2 or + * OpenSSH (server-side). In any case, forcing a seek (to + * discard libssh2 internal buffers), and then trying again + * works for me. + */ + if (r == 0) { + ssh_seek(s, offset + written, SSH_SEEK_WRITE|SSH_SEEK_FORCE); + co_yield(s); + goto again; + } + + written += r; + buf += r; + s->offset += r; + if (buf >= end_of_vec && written < size) { + i++; + buf = i->iov_base; + end_of_vec = i->iov_base + i->iov_len; + } + + if (offset + written > s->attrs.filesize) + s->attrs.filesize = offset + written; + } + + return 0; +} + +static coroutine_fn int ssh_co_writev(BlockDriverState *bs, + int64_t sector_num, + int nb_sectors, QEMUIOVector *qiov) +{ + BDRVSSHState *s = bs->opaque; + int ret; + + qemu_co_mutex_lock(&s->lock); + ret = ssh_write(s, sector_num * BDRV_SECTOR_SIZE, + nb_sectors * BDRV_SECTOR_SIZE, qiov); + qemu_co_mutex_unlock(&s->lock); + + return ret; +} + +static void unsafe_flush_warning(BDRVSSHState *s, const char *what) +{ + if (!s->unsafe_flush_warning) { + error_report("warning: ssh server %s does not support fsync", + s->hostport); + if (what) { + error_report("to support fsync, you need %s", what); + } + s->unsafe_flush_warning = true; + } +} + +#ifdef HAS_LIBSSH2_SFTP_FSYNC + +static coroutine_fn int ssh_flush(BDRVSSHState *s) +{ + int r; + + DPRINTF("fsync"); + again: + r = libssh2_sftp_fsync(s->sftp_handle); + if (r == LIBSSH2_ERROR_EAGAIN || r == LIBSSH2_ERROR_TIMEOUT) { + co_yield(s); + goto again; + } + if (r == LIBSSH2_ERROR_SFTP_PROTOCOL && + libssh2_sftp_last_error(s->sftp) == LIBSSH2_FX_OP_UNSUPPORTED) { + unsafe_flush_warning(s, "OpenSSH >= 6.3"); + return 0; + } + if (r < 0) { + sftp_error_report(s, "fsync failed"); + return -EIO; + } + + return 0; +} + +static coroutine_fn int ssh_co_flush(BlockDriverState *bs) +{ + BDRVSSHState *s = bs->opaque; + int ret; + + qemu_co_mutex_lock(&s->lock); + ret = ssh_flush(s); + qemu_co_mutex_unlock(&s->lock); + + return ret; +} + +#else /* !HAS_LIBSSH2_SFTP_FSYNC */ + +static coroutine_fn int ssh_co_flush(BlockDriverState *bs) +{ + BDRVSSHState *s = bs->opaque; + + unsafe_flush_warning(s, "libssh2 >= 1.4.4"); + return 0; +} + +#endif /* !HAS_LIBSSH2_SFTP_FSYNC */ + +static int64_t ssh_getlength(BlockDriverState *bs) +{ + BDRVSSHState *s = bs->opaque; + int64_t length; + + /* Note we cannot make a libssh2 call here. */ + length = (int64_t) s->attrs.filesize; + DPRINTF("length=%" PRIi64, length); + + return length; +} + +static BlockDriver bdrv_ssh = { + .format_name = "ssh", + .protocol_name = "ssh", + .instance_size = sizeof(BDRVSSHState), + .bdrv_parse_filename = ssh_parse_filename, + .bdrv_file_open = ssh_file_open, + .bdrv_create = ssh_create, + .bdrv_close = ssh_close, + .bdrv_co_readv = ssh_co_readv, + .bdrv_co_writev = ssh_co_writev, + .bdrv_getlength = ssh_getlength, + .bdrv_co_flush_to_disk = ssh_co_flush, + .create_options = ssh_create_options, +}; + +static void bdrv_ssh_init(void) +{ + int r; + + r = libssh2_init(0); + if (r != 0) { + fprintf(stderr, "libssh2 initialization failed, %d\n", r); + exit(EXIT_FAILURE); + } + + bdrv_register(&bdrv_ssh); +} + +block_init(bdrv_ssh_init); @@ -239,6 +239,7 @@ virtio_blk_data_plane="" gtk="" gtkabi="2.0" tpm="no" +libssh2="" # parse CC options first for opt do @@ -923,6 +924,10 @@ for opt do ;; --enable-tpm) tpm="yes" ;; + --disable-libssh2) libssh2="no" + ;; + --enable-libssh2) libssh2="yes" + ;; *) echo "ERROR: unknown option $opt"; show_help="yes" ;; esac @@ -1183,6 +1188,8 @@ echo " --disable-glusterfs disable GlusterFS backend" echo " --enable-gcov enable test coverage analysis with gcov" echo " --gcov=GCOV use specified gcov [$gcov_tool]" echo " --enable-tpm enable TPM support" +echo " --disable-libssh2 disable ssh block device support" +echo " --enable-libssh2 enable ssh block device support" echo "" echo "NOTE: The object files are built at the place where configure is launched" exit 1 @@ -2315,6 +2322,67 @@ EOF fi ########################################## +# libssh2 probe +if test "$libssh2" != "no" ; then + cat > $TMPC <<EOF +#include <stdio.h> +#include <libssh2.h> +#include <libssh2_sftp.h> +int main(void) { + LIBSSH2_SESSION *session; + session = libssh2_session_init (); + (void) libssh2_sftp_init (session); + return 0; +} +EOF + + if $pkg_config libssh2 --modversion >/dev/null 2>&1; then + libssh2_cflags=`$pkg_config libssh2 --cflags` + libssh2_libs=`$pkg_config libssh2 --libs` + else + libssh2_cflags= + libssh2_libs="-lssh2" + fi + + if compile_prog "$libssh2_cflags" "$libssh2_libs" ; then + libssh2=yes + libs_tools="$libssh2_libs $libs_tools" + libs_softmmu="$libssh2_libs $libs_softmmu" + QEMU_CFLAGS="$QEMU_CFLAGS $libssh2_cflags" + else + if test "$libssh2" = "yes" ; then + feature_not_found "libssh2" + fi + libssh2=no + fi +fi + +########################################## +# libssh2_sftp_fsync probe + +if test "$libssh2" = "yes"; then + cat > $TMPC <<EOF +#include <stdio.h> +#include <libssh2.h> +#include <libssh2_sftp.h> +int main(void) { + LIBSSH2_SESSION *session; + LIBSSH2_SFTP *sftp; + LIBSSH2_SFTP_HANDLE *sftp_handle; + session = libssh2_session_init (); + sftp = libssh2_sftp_init (session); + sftp_handle = libssh2_sftp_open (sftp, "/", 0, 0); + libssh2_sftp_fsync (sftp_handle); + return 0; +} +EOF + # libssh2_cflags/libssh2_libs defined in previous test. + if compile_prog "$libssh2_cflags" "$libssh2_libs" ; then + QEMU_CFLAGS="-DHAS_LIBSSH2_SFTP_FSYNC $QEMU_CFLAGS" + fi +fi + +########################################## # linux-aio probe if test "$linux_aio" != "no" ; then @@ -3250,6 +3318,20 @@ if compile_prog "" "" ; then int128=yes fi +######################################## +# check if getauxval is available. + +getauxval=no +cat > $TMPC << EOF +#include <sys/auxv.h> +int main(void) { + return getauxval(AT_HWCAP) == 0; +} +EOF +if compile_prog "" "" ; then + getauxval=yes +fi + ########################################## # End of CC checks # After here, no more $cc or $ld runs @@ -3445,6 +3527,7 @@ echo "virtio-blk-data-plane $virtio_blk_data_plane" echo "gcov $gcov_tool" echo "gcov enabled $gcov" echo "TPM support $tpm" +echo "libssh2 support $libssh2" echo "TPM passthrough $tpm_passthrough" if test "$sdl_too_old" = "yes"; then @@ -3800,10 +3883,18 @@ if test "$int128" = "yes" ; then echo "CONFIG_INT128=y" >> $config_host_mak fi +if test "$getauxval" = "yes" ; then + echo "CONFIG_GETAUXVAL=y" >> $config_host_mak +fi + if test "$glusterfs" = "yes" ; then echo "CONFIG_GLUSTERFS=y" >> $config_host_mak fi +if test "$libssh2" = "yes" ; then + echo "CONFIG_LIBSSH2=y" >> $config_host_mak +fi + if test "$virtio_blk_data_plane" = "yes" ; then echo 'CONFIG_VIRTIO_BLK_DATA_PLANE=$(CONFIG_VIRTIO)' >> $config_host_mak fi @@ -325,6 +325,7 @@ void disas(FILE *out, void *code, unsigned long size) s.info.mach = bfd_mach_x86_64; print_insn = print_insn_i386; #elif defined(_ARCH_PPC) + s.info.disassembler_options = (char *)"any"; print_insn = print_insn_ppc; #elif defined(__alpha__) print_insn = print_insn_alpha; diff --git a/fpu/softfloat.c b/fpu/softfloat.c index 83ccc4b..7ba51b6 100644 --- a/fpu/softfloat.c +++ b/fpu/softfloat.c @@ -3898,9 +3898,15 @@ float64 float64_muladd(float64 a, float64 b, float64 c, int flags STATUS_PARAM) } zExp -= shiftcount; } else { - shiftcount = countLeadingZeros64(zSig1) - 1; - zSig0 = zSig1 << shiftcount; - zExp -= (shiftcount + 64); + shiftcount = countLeadingZeros64(zSig1); + if (shiftcount == 0) { + zSig0 = (zSig1 >> 1) | (zSig1 & 1); + zExp -= 63; + } else { + shiftcount--; + zSig0 = zSig1 << shiftcount; + zExp -= (shiftcount + 64); + } } return roundAndPackFloat64(zSign, zExp, zSig0 STATUS_VAR); } diff --git a/hmp-commands.hx b/hmp-commands.hx index 3d98604..df44906 100644 --- a/hmp-commands.hx +++ b/hmp-commands.hx @@ -1643,8 +1643,6 @@ show qdev device model list show roms @item info tpm show the TPM device -@item info cpu_max -show the number of CPUs supported by the machine being emulated. @end table ETEXI @@ -750,14 +750,6 @@ void hmp_ringbuf_read(Monitor *mon, const QDict *qdict) g_free(data); } -void hmp_query_cpu_max(Monitor *mon, const QDict *qdict) -{ - int cpu_max; - - cpu_max = qmp_query_cpu_max(NULL); - monitor_printf(mon, "Maximum number of CPUs is %d\n", cpu_max); -} - static void hmp_cont_cb(void *opaque, int err) { if (!err) { @@ -42,7 +42,6 @@ void hmp_stop(Monitor *mon, const QDict *qdict); void hmp_system_reset(Monitor *mon, const QDict *qdict); void hmp_system_powerdown(Monitor *mon, const QDict *qdict); void hmp_cpu(Monitor *mon, const QDict *qdict); -void hmp_query_cpu_max(Monitor *mon, const QDict *qdict); void hmp_memsave(Monitor *mon, const QDict *qdict); void hmp_pmemsave(Monitor *mon, const QDict *qdict); void hmp_ringbuf_write(Monitor *mon, const QDict *qdict); diff --git a/hw/char/virtio-serial-bus.c b/hw/char/virtio-serial-bus.c index 1dba8ab..35c996d 100644 --- a/hw/char/virtio-serial-bus.c +++ b/hw/char/virtio-serial-bus.c @@ -53,7 +53,8 @@ static VirtIOSerialPort *find_port_by_vq(VirtIOSerial *vser, VirtQueue *vq) static bool use_multiport(VirtIOSerial *vser) { - return vser->vdev.guest_features & (1 << VIRTIO_CONSOLE_F_MULTIPORT); + VirtIODevice *vdev = VIRTIO_DEVICE(vser); + return vdev->guest_features & (1 << VIRTIO_CONSOLE_F_MULTIPORT); } static size_t write_to_port(VirtIOSerialPort *port, @@ -83,7 +84,7 @@ static size_t write_to_port(VirtIOSerialPort *port, virtqueue_push(vq, &elem, len); } - virtio_notify(&port->vser->vdev, vq); + virtio_notify(VIRTIO_DEVICE(port->vser), vq); return offset; } @@ -156,7 +157,7 @@ static void flush_queued_data(VirtIOSerialPort *port) if (!virtio_queue_ready(port->ovq)) { return; } - do_flush_queued_data(port, port->ovq, &port->vser->vdev); + do_flush_queued_data(port, port->ovq, VIRTIO_DEVICE(port->vser)); } static size_t send_control_msg(VirtIOSerial *vser, void *buf, size_t len) @@ -175,7 +176,7 @@ static size_t send_control_msg(VirtIOSerial *vser, void *buf, size_t len) memcpy(elem.in_sg[0].iov_base, buf, len); virtqueue_push(vq, &elem, len); - virtio_notify(&vser->vdev, vq); + virtio_notify(VIRTIO_DEVICE(vser), vq); return len; } @@ -214,7 +215,7 @@ int virtio_serial_close(VirtIOSerialPort *port) * consume, reset the throttling flag and discard the data. */ port->throttled = false; - discard_vq_data(port->ovq, &port->vser->vdev); + discard_vq_data(port->ovq, VIRTIO_DEVICE(port->vser)); send_control_event(port->vser, port->id, VIRTIO_CONSOLE_PORT_OPEN, 0); @@ -237,11 +238,12 @@ ssize_t virtio_serial_write(VirtIOSerialPort *port, const uint8_t *buf, */ size_t virtio_serial_guest_ready(VirtIOSerialPort *port) { + VirtIODevice *vdev = VIRTIO_DEVICE(port->vser); VirtQueue *vq = port->ivq; unsigned int bytes; if (!virtio_queue_ready(vq) || - !(port->vser->vdev.status & VIRTIO_CONFIG_S_DRIVER_OK) || + !(vdev->status & VIRTIO_CONFIG_S_DRIVER_OK) || virtio_queue_empty(vq)) { return 0; } @@ -391,7 +393,7 @@ static void control_out(VirtIODevice *vdev, VirtQueue *vq) uint8_t *buf; size_t len; - vser = DO_UPCAST(VirtIOSerial, vdev, vdev); + vser = VIRTIO_SERIAL(vdev); len = 0; buf = NULL; @@ -424,7 +426,7 @@ static void handle_output(VirtIODevice *vdev, VirtQueue *vq) VirtIOSerial *vser; VirtIOSerialPort *port; - vser = DO_UPCAST(VirtIOSerial, vdev, vdev); + vser = VIRTIO_SERIAL(vdev); port = find_port_by_vq(vser, vq); if (!port || !port->host_connected) { @@ -446,7 +448,7 @@ static uint32_t get_features(VirtIODevice *vdev, uint32_t features) { VirtIOSerial *vser; - vser = DO_UPCAST(VirtIOSerial, vdev, vdev); + vser = VIRTIO_SERIAL(vdev); if (vser->bus.max_nr_ports > 1) { features |= (1 << VIRTIO_CONSOLE_F_MULTIPORT); @@ -459,7 +461,7 @@ static void get_config(VirtIODevice *vdev, uint8_t *config_data) { VirtIOSerial *vser; - vser = DO_UPCAST(VirtIOSerial, vdev, vdev); + vser = VIRTIO_SERIAL(vdev); memcpy(config_data, &vser->config, sizeof(struct virtio_console_config)); } @@ -491,7 +493,7 @@ static void set_status(VirtIODevice *vdev, uint8_t status) VirtIOSerial *vser; VirtIOSerialPort *port; - vser = DO_UPCAST(VirtIOSerial, vdev, vdev); + vser = VIRTIO_SERIAL(vdev); port = find_port_by_id(vser, 0); if (port && !use_multiport(port->vser) @@ -513,19 +515,19 @@ static void vser_reset(VirtIODevice *vdev) { VirtIOSerial *vser; - vser = DO_UPCAST(VirtIOSerial, vdev, vdev); + vser = VIRTIO_SERIAL(vdev); guest_reset(vser); } static void virtio_serial_save(QEMUFile *f, void *opaque) { - VirtIOSerial *s = opaque; + VirtIOSerial *s = VIRTIO_SERIAL(opaque); VirtIOSerialPort *port; uint32_t nr_active_ports; unsigned int i, max_nr_ports; /* The virtio device */ - virtio_save(&s->vdev, f); + virtio_save(VIRTIO_DEVICE(s), f); /* The config space */ qemu_put_be16s(f, &s->config.cols); @@ -576,7 +578,7 @@ static void virtio_serial_save(QEMUFile *f, void *opaque) static void virtio_serial_post_load_timer_cb(void *opaque) { uint32_t i; - VirtIOSerial *s = opaque; + VirtIOSerial *s = VIRTIO_SERIAL(opaque); VirtIOSerialPort *port; uint8_t host_connected; VirtIOSerialPortClass *vsc; @@ -664,7 +666,7 @@ static int fetch_active_ports_list(QEMUFile *f, int version_id, static int virtio_serial_load(QEMUFile *f, void *opaque, int version_id) { - VirtIOSerial *s = opaque; + VirtIOSerial *s = VIRTIO_SERIAL(opaque); uint32_t max_nr_ports, nr_active_ports, ports_map; unsigned int i; int ret; @@ -674,7 +676,7 @@ static int virtio_serial_load(QEMUFile *f, void *opaque, int version_id) } /* The virtio device */ - ret = virtio_load(&s->vdev, f); + ret = virtio_load(VIRTIO_DEVICE(s), f); if (ret) { return ret; } @@ -801,7 +803,7 @@ static void remove_port(VirtIOSerial *vser, uint32_t port_id) assert(port); /* Flush out any unconsumed buffers first */ - discard_vq_data(port->ovq, &port->vser->vdev); + discard_vq_data(port->ovq, VIRTIO_DEVICE(port->vser)); send_control_event(vser, port->id, VIRTIO_CONSOLE_PORT_REMOVE, 1); } @@ -865,7 +867,7 @@ static int virtser_port_qdev_init(DeviceState *qdev) add_port(port->vser, port->id); /* Send an update to the guest about this new port added */ - virtio_notify_config(&port->vser->vdev); + virtio_notify_config(VIRTIO_DEVICE(port->vser)); return ret; } @@ -887,38 +889,38 @@ static int virtser_port_qdev_exit(DeviceState *qdev) return 0; } -VirtIODevice *virtio_serial_init(DeviceState *dev, virtio_serial_conf *conf) +static int virtio_serial_device_init(VirtIODevice *vdev) { - VirtIOSerial *vser; - VirtIODevice *vdev; + DeviceState *qdev = DEVICE(vdev); + VirtIOSerial *vser = VIRTIO_SERIAL(vdev); uint32_t i, max_supported_ports; - if (!conf->max_virtserial_ports) - return NULL; + if (!vser->serial.max_virtserial_ports) { + return -1; + } /* Each port takes 2 queues, and one pair is for the control queue */ max_supported_ports = VIRTIO_PCI_QUEUE_MAX / 2 - 1; - if (conf->max_virtserial_ports > max_supported_ports) { + if (vser->serial.max_virtserial_ports > max_supported_ports) { error_report("maximum ports supported: %u", max_supported_ports); - return NULL; + return -1; } - vdev = virtio_common_init("virtio-serial", VIRTIO_ID_CONSOLE, - sizeof(struct virtio_console_config), - sizeof(VirtIOSerial)); - - vser = DO_UPCAST(VirtIOSerial, vdev, vdev); + virtio_init(vdev, "virtio-serial", VIRTIO_ID_CONSOLE, + sizeof(struct virtio_console_config)); /* Spawn a new virtio-serial bus on which the ports will ride as devices */ - qbus_create_inplace(&vser->bus.qbus, TYPE_VIRTIO_SERIAL_BUS, dev, NULL); + qbus_create_inplace(&vser->bus.qbus, TYPE_VIRTIO_SERIAL_BUS, qdev, NULL); vser->bus.qbus.allow_hotplug = 1; vser->bus.vser = vser; QTAILQ_INIT(&vser->ports); - vser->bus.max_nr_ports = conf->max_virtserial_ports; - vser->ivqs = g_malloc(conf->max_virtserial_ports * sizeof(VirtQueue *)); - vser->ovqs = g_malloc(conf->max_virtserial_ports * sizeof(VirtQueue *)); + vser->bus.max_nr_ports = vser->serial.max_virtserial_ports; + vser->ivqs = g_malloc(vser->serial.max_virtserial_ports + * sizeof(VirtQueue *)); + vser->ovqs = g_malloc(vser->serial.max_virtserial_ports + * sizeof(VirtQueue *)); /* Add a queue for host to guest transfers for port 0 (backward compat) */ vser->ivqs[0] = virtio_add_queue(vdev, 128, handle_input); @@ -943,8 +945,8 @@ VirtIODevice *virtio_serial_init(DeviceState *dev, virtio_serial_conf *conf) vser->ovqs[i] = virtio_add_queue(vdev, 128, handle_output); } - vser->config.max_nr_ports = tswap32(conf->max_virtserial_ports); - vser->ports_map = g_malloc0(((conf->max_virtserial_ports + 31) / 32) + vser->config.max_nr_ports = tswap32(vser->serial.max_virtserial_ports); + vser->ports_map = g_malloc0(((vser->serial.max_virtserial_ports + 31) / 32) * sizeof(vser->ports_map[0])); /* * Reserve location 0 for a console port for backward compat @@ -952,13 +954,11 @@ VirtIODevice *virtio_serial_init(DeviceState *dev, virtio_serial_conf *conf) */ mark_port_added(vser, 0); - vser->vdev.get_features = get_features; - vser->vdev.get_config = get_config; - vser->vdev.set_config = set_config; - vser->vdev.set_status = set_status; - vser->vdev.reset = vser_reset; - - vser->qdev = dev; + vdev->get_features = get_features; + vdev->get_config = get_config; + vdev->set_config = set_config; + vdev->set_status = set_status; + vdev->reset = vser_reset; vser->post_load = NULL; @@ -966,28 +966,10 @@ VirtIODevice *virtio_serial_init(DeviceState *dev, virtio_serial_conf *conf) * Register for the savevm section with the virtio-console name * to preserve backward compat */ - register_savevm(dev, "virtio-console", -1, 3, virtio_serial_save, + register_savevm(qdev, "virtio-console", -1, 3, virtio_serial_save, virtio_serial_load, vser); - return vdev; -} - -void virtio_serial_exit(VirtIODevice *vdev) -{ - VirtIOSerial *vser = DO_UPCAST(VirtIOSerial, vdev, vdev); - - unregister_savevm(vser->qdev, "virtio-console", vser); - - g_free(vser->ivqs); - g_free(vser->ovqs); - g_free(vser->ports_map); - if (vser->post_load) { - g_free(vser->post_load->connected); - qemu_del_timer(vser->post_load->timer); - qemu_free_timer(vser->post_load->timer); - g_free(vser->post_load); - } - virtio_cleanup(vdev); + return 0; } static void virtio_serial_port_class_init(ObjectClass *klass, void *data) @@ -1009,10 +991,57 @@ static const TypeInfo virtio_serial_port_type_info = { .class_init = virtio_serial_port_class_init, }; +static int virtio_serial_device_exit(DeviceState *dev) +{ + VirtIOSerial *vser = VIRTIO_SERIAL(dev); + VirtIODevice *vdev = VIRTIO_DEVICE(dev); + + unregister_savevm(dev, "virtio-console", vser); + + g_free(vser->ivqs); + g_free(vser->ovqs); + g_free(vser->ports_map); + if (vser->post_load) { + g_free(vser->post_load->connected); + qemu_del_timer(vser->post_load->timer); + qemu_free_timer(vser->post_load->timer); + g_free(vser->post_load); + } + virtio_common_cleanup(vdev); + return 0; +} + +static Property virtio_serial_properties[] = { + DEFINE_VIRTIO_SERIAL_PROPERTIES(VirtIOSerial, serial), + DEFINE_PROP_END_OF_LIST(), +}; + +static void virtio_serial_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + VirtioDeviceClass *vdc = VIRTIO_DEVICE_CLASS(klass); + dc->exit = virtio_serial_device_exit; + dc->props = virtio_serial_properties; + vdc->init = virtio_serial_device_init; + vdc->get_features = get_features; + vdc->get_config = get_config; + vdc->set_config = set_config; + vdc->set_status = set_status; + vdc->reset = vser_reset; +} + +static const TypeInfo virtio_device_info = { + .name = TYPE_VIRTIO_SERIAL, + .parent = TYPE_VIRTIO_DEVICE, + .instance_size = sizeof(VirtIOSerial), + .class_init = virtio_serial_class_init, +}; + static void virtio_serial_register_types(void) { type_register_static(&virtser_bus_info); type_register_static(&virtio_serial_port_type_info); + type_register_static(&virtio_device_info); } type_init(virtio_serial_register_types) diff --git a/hw/core/qdev.c b/hw/core/qdev.c index e2bb37d..bab4ed7 100644 --- a/hw/core/qdev.c +++ b/hw/core/qdev.c @@ -182,6 +182,19 @@ static void device_realize(DeviceState *dev, Error **err) } } +static void device_unrealize(DeviceState *dev, Error **errp) +{ + DeviceClass *dc = DEVICE_GET_CLASS(dev); + + if (dc->exit) { + int rc = dc->exit(dev); + if (rc < 0) { + error_setg(errp, "Device exit failed."); + return; + } + } +} + void qdev_set_legacy_instance_id(DeviceState *dev, int alias_id, int required_for_version) { @@ -694,6 +707,9 @@ static void device_set_realized(Object *obj, bool value, Error **err) device_reset(dev); } } else if (!value && dev->realized) { + if (qdev_get_vmsd(dev)) { + vmstate_unregister(dev, qdev_get_vmsd(dev), dev); + } if (dc->unrealize) { dc->unrealize(dev, &local_err); } @@ -764,7 +780,6 @@ static void device_class_base_init(ObjectClass *class, void *data) static void device_unparent(Object *obj) { DeviceState *dev = DEVICE(obj); - DeviceClass *dc = DEVICE_GET_CLASS(dev); BusState *bus; QObject *event_data; bool have_realized = dev->realized; @@ -774,12 +789,7 @@ static void device_unparent(Object *obj) qbus_free(bus); } if (dev->realized) { - if (qdev_get_vmsd(dev)) { - vmstate_unregister(dev, qdev_get_vmsd(dev), dev); - } - if (dc->exit) { - dc->exit(dev); - } + object_property_set_bool(obj, false, "realized", NULL); } if (dev->parent_bus) { bus_remove_child(dev->parent_bus, dev); @@ -809,6 +819,7 @@ static void device_class_init(ObjectClass *class, void *data) class->unparent = device_unparent; dc->realize = device_realize; + dc->unrealize = device_unrealize; } void device_reset(DeviceState *dev) diff --git a/hw/ide/core.c b/hw/ide/core.c index 87d67b7..c7a8041 100644 --- a/hw/ide/core.c +++ b/hw/ide/core.c @@ -1262,6 +1262,10 @@ void ide_exec_cmd(IDEBus *bus, uint32_t val) lba48 = 1; /* fall through */ case WIN_READ_NATIVE_MAX: + /* Refuse if no sectors are addressable (e.g. medium not inserted) */ + if (s->nb_sectors == 0) { + goto abort_cmd; + } ide_cmd_lba48_transform(s, lba48); ide_set_sector(s, s->nb_sectors - 1); s->status = READY_STAT | SEEK_STAT; diff --git a/hw/s390x/s390-virtio-bus.c b/hw/s390x/s390-virtio-bus.c index ddf15a2..a71c145 100644 --- a/hw/s390x/s390-virtio-bus.c +++ b/hw/s390x/s390-virtio-bus.c @@ -181,27 +181,36 @@ static void s390_virtio_blk_instance_init(Object *obj) object_property_add_child(obj, "virtio-backend", OBJECT(&dev->vdev), NULL); } -static int s390_virtio_serial_init(VirtIOS390Device *dev) +static int s390_virtio_serial_init(VirtIOS390Device *s390_dev) { + VirtIOSerialS390 *dev = VIRTIO_SERIAL_S390(s390_dev); + DeviceState *vdev = DEVICE(&dev->vdev); + DeviceState *qdev = DEVICE(s390_dev); VirtIOS390Bus *bus; - VirtIODevice *vdev; int r; - bus = DO_UPCAST(VirtIOS390Bus, bus, dev->qdev.parent_bus); + bus = DO_UPCAST(VirtIOS390Bus, bus, qdev->parent_bus); - vdev = virtio_serial_init((DeviceState *)dev, &dev->serial); - if (!vdev) { + qdev_set_parent_bus(vdev, BUS(&s390_dev->bus)); + if (qdev_init(vdev) < 0) { return -1; } - r = s390_virtio_device_init(dev, vdev); + r = s390_virtio_device_init(s390_dev, VIRTIO_DEVICE(vdev)); if (!r) { - bus->console = dev; + bus->console = s390_dev; } return r; } +static void s390_virtio_serial_instance_init(Object *obj) +{ + VirtIOSerialS390 *dev = VIRTIO_SERIAL_S390(obj); + object_initialize(OBJECT(&dev->vdev), TYPE_VIRTIO_SERIAL); + object_property_add_child(obj, "virtio-backend", OBJECT(&dev->vdev), NULL); +} + static int s390_virtio_scsi_init(VirtIOS390Device *s390_dev) { VirtIOSCSIS390 *dev = VIRTIO_SCSI_S390(s390_dev); @@ -465,8 +474,7 @@ static const TypeInfo s390_virtio_blk = { }; static Property s390_virtio_serial_properties[] = { - DEFINE_PROP_UINT32("max_ports", VirtIOS390Device, - serial.max_virtserial_ports, 31), + DEFINE_VIRTIO_SERIAL_PROPERTIES(VirtIOSerialS390, vdev.serial), DEFINE_PROP_END_OF_LIST(), }; @@ -480,9 +488,10 @@ static void s390_virtio_serial_class_init(ObjectClass *klass, void *data) } static const TypeInfo s390_virtio_serial = { - .name = "virtio-serial-s390", + .name = TYPE_VIRTIO_SERIAL_S390, .parent = TYPE_VIRTIO_S390_DEVICE, - .instance_size = sizeof(VirtIOS390Device), + .instance_size = sizeof(VirtIOSerialS390), + .instance_init = s390_virtio_serial_instance_init, .class_init = s390_virtio_serial_class_init, }; diff --git a/hw/s390x/s390-virtio-bus.h b/hw/s390x/s390-virtio-bus.h index c557132..1daf753 100644 --- a/hw/s390x/s390-virtio-bus.h +++ b/hw/s390x/s390-virtio-bus.h @@ -91,7 +91,6 @@ struct VirtIOS390Device { VirtIODevice *vdev; NICConf nic; uint32_t host_features; - virtio_serial_conf serial; virtio_net_conf net; VirtIORNGConf rng; VirtioBusState bus; @@ -141,4 +140,15 @@ typedef struct VirtIOSCSIS390 { VirtIOSCSI vdev; } VirtIOSCSIS390; +/* virtio-serial-s390 */ + +#define TYPE_VIRTIO_SERIAL_S390 "virtio-serial-s390" +#define VIRTIO_SERIAL_S390(obj) \ + OBJECT_CHECK(VirtIOSerialS390, (obj), TYPE_VIRTIO_SERIAL_S390) + +typedef struct VirtIOSerialS390 { + VirtIOS390Device parent_obj; + VirtIOSerial vdev; +} VirtIOSerialS390; + #endif diff --git a/hw/s390x/virtio-ccw.c b/hw/s390x/virtio-ccw.c index 4dec0cd..9f2289d 100644 --- a/hw/s390x/virtio-ccw.c +++ b/hw/s390x/virtio-ccw.c @@ -590,22 +590,25 @@ static void virtio_ccw_blk_instance_init(Object *obj) object_property_add_child(obj, "virtio-backend", OBJECT(&dev->vdev), NULL); } -static int virtio_ccw_serial_init(VirtioCcwDevice *dev) +static int virtio_ccw_serial_init(VirtioCcwDevice *ccw_dev) { - VirtIODevice *vdev; + VirtioSerialCcw *dev = VIRTIO_SERIAL_CCW(ccw_dev); + DeviceState *vdev = DEVICE(&dev->vdev); - vdev = virtio_serial_init((DeviceState *)dev, &dev->serial); - if (!vdev) { + qdev_set_parent_bus(vdev, BUS(&ccw_dev->bus)); + if (qdev_init(vdev) < 0) { return -1; } - return virtio_ccw_device_init(dev, vdev); + return virtio_ccw_device_init(ccw_dev, VIRTIO_DEVICE(vdev)); } -static int virtio_ccw_serial_exit(VirtioCcwDevice *dev) + +static void virtio_ccw_serial_instance_init(Object *obj) { - virtio_serial_exit(dev->vdev); - return virtio_ccw_exit(dev); + VirtioSerialCcw *dev = VIRTIO_SERIAL_CCW(obj); + object_initialize(OBJECT(&dev->vdev), TYPE_VIRTIO_SERIAL); + object_property_add_child(obj, "virtio-backend", OBJECT(&dev->vdev), NULL); } static int virtio_ccw_balloon_init(VirtioCcwDevice *ccw_dev) @@ -786,8 +789,7 @@ static const TypeInfo virtio_ccw_blk = { static Property virtio_ccw_serial_properties[] = { DEFINE_PROP_STRING("devno", VirtioCcwDevice, bus_id), - DEFINE_PROP_UINT32("max_ports", VirtioCcwDevice, - serial.max_virtserial_ports, 31), + DEFINE_VIRTIO_SERIAL_PROPERTIES(VirtioSerialCcw, vdev.serial), DEFINE_VIRTIO_COMMON_FEATURES(VirtioCcwDevice, host_features[0]), DEFINE_PROP_END_OF_LIST(), }; @@ -798,15 +800,16 @@ static void virtio_ccw_serial_class_init(ObjectClass *klass, void *data) VirtIOCCWDeviceClass *k = VIRTIO_CCW_DEVICE_CLASS(klass); k->init = virtio_ccw_serial_init; - k->exit = virtio_ccw_serial_exit; + k->exit = virtio_ccw_exit; dc->reset = virtio_ccw_reset; dc->props = virtio_ccw_serial_properties; } static const TypeInfo virtio_ccw_serial = { - .name = "virtio-serial-ccw", + .name = TYPE_VIRTIO_SERIAL_CCW, .parent = TYPE_VIRTIO_CCW_DEVICE, - .instance_size = sizeof(VirtioCcwDevice), + .instance_size = sizeof(VirtioSerialCcw), + .instance_init = virtio_ccw_serial_instance_init, .class_init = virtio_ccw_serial_class_init, }; diff --git a/hw/s390x/virtio-ccw.h b/hw/s390x/virtio-ccw.h index 46e9a55..2208a11 100644 --- a/hw/s390x/virtio-ccw.h +++ b/hw/s390x/virtio-ccw.h @@ -75,7 +75,6 @@ struct VirtioCcwDevice { char *bus_id; NICConf nic; uint32_t host_features[VIRTIO_CCW_FEATURE_SIZE]; - virtio_serial_conf serial; virtio_net_conf net; VirtIORNGConf rng; VirtioBusState bus; @@ -127,6 +126,17 @@ typedef struct VirtIOBalloonCcw { VirtIOBalloon vdev; } VirtIOBalloonCcw; +/* virtio-serial-ccw */ + +#define TYPE_VIRTIO_SERIAL_CCW "virtio-serial-ccw" +#define VIRTIO_SERIAL_CCW(obj) \ + OBJECT_CHECK(VirtioSerialCcw, (obj), TYPE_VIRTIO_SERIAL_CCW) + +typedef struct VirtioSerialCcw { + VirtioCcwDevice parent_obj; + VirtIOSerial vdev; +} VirtioSerialCcw; + VirtualCssBus *virtual_css_bus_init(void); void virtio_ccw_device_update_status(SubchDev *sch); VirtIODevice *virtio_ccw_get_vdev(SubchDev *sch); diff --git a/hw/virtio/virtio-pci.c b/hw/virtio/virtio-pci.c index 2b22588..c2a0452 100644 --- a/hw/virtio/virtio-pci.c +++ b/hw/virtio/virtio-pci.c @@ -942,40 +942,6 @@ static void virtio_exit_pci(PCIDevice *pci_dev) msix_uninit_exclusive_bar(pci_dev); } -static int virtio_serial_init_pci(PCIDevice *pci_dev) -{ - VirtIOPCIProxy *proxy = DO_UPCAST(VirtIOPCIProxy, pci_dev, pci_dev); - VirtIODevice *vdev; - - if (proxy->class_code != PCI_CLASS_COMMUNICATION_OTHER && - proxy->class_code != PCI_CLASS_DISPLAY_OTHER && /* qemu 0.10 */ - proxy->class_code != PCI_CLASS_OTHERS) /* qemu-kvm */ - proxy->class_code = PCI_CLASS_COMMUNICATION_OTHER; - - vdev = virtio_serial_init(&pci_dev->qdev, &proxy->serial); - if (!vdev) { - return -1; - } - - /* backwards-compatibility with machines that were created with - DEV_NVECTORS_UNSPECIFIED */ - vdev->nvectors = proxy->nvectors == DEV_NVECTORS_UNSPECIFIED - ? proxy->serial.max_virtserial_ports + 1 - : proxy->nvectors; - virtio_init_pci(proxy, vdev); - proxy->nvectors = vdev->nvectors; - return 0; -} - -static void virtio_serial_exit_pci(PCIDevice *pci_dev) -{ - VirtIOPCIProxy *proxy = DO_UPCAST(VirtIOPCIProxy, pci_dev, pci_dev); - - virtio_pci_stop_ioeventfd(proxy); - virtio_serial_exit(proxy->vdev); - virtio_exit_pci(pci_dev); -} - static int virtio_net_init_pci(PCIDevice *pci_dev) { VirtIOPCIProxy *proxy = DO_UPCAST(VirtIOPCIProxy, pci_dev, pci_dev); @@ -1070,37 +1036,6 @@ static const TypeInfo virtio_net_info = { .class_init = virtio_net_class_init, }; -static Property virtio_serial_properties[] = { - DEFINE_PROP_BIT("ioeventfd", VirtIOPCIProxy, flags, VIRTIO_PCI_FLAG_USE_IOEVENTFD_BIT, true), - DEFINE_PROP_UINT32("vectors", VirtIOPCIProxy, nvectors, 2), - DEFINE_PROP_HEX32("class", VirtIOPCIProxy, class_code, 0), - DEFINE_VIRTIO_COMMON_FEATURES(VirtIOPCIProxy, host_features), - DEFINE_PROP_UINT32("max_ports", VirtIOPCIProxy, serial.max_virtserial_ports, 31), - DEFINE_PROP_END_OF_LIST(), -}; - -static void virtio_serial_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); - - k->init = virtio_serial_init_pci; - k->exit = virtio_serial_exit_pci; - k->vendor_id = PCI_VENDOR_ID_REDHAT_QUMRANET; - k->device_id = PCI_DEVICE_ID_VIRTIO_CONSOLE; - k->revision = VIRTIO_PCI_ABI_VERSION; - k->class_id = PCI_CLASS_COMMUNICATION_OTHER; - dc->reset = virtio_pci_reset; - dc->props = virtio_serial_properties; -} - -static const TypeInfo virtio_serial_info = { - .name = "virtio-serial-pci", - .parent = TYPE_PCI_DEVICE, - .instance_size = sizeof(VirtIOPCIProxy), - .class_init = virtio_serial_class_init, -}; - static void virtio_rng_initfn(Object *obj) { PCIDevice *pci_dev = PCI_DEVICE(obj); @@ -1460,6 +1395,70 @@ static const TypeInfo virtio_balloon_pci_info = { .class_init = virtio_balloon_pci_class_init, }; +/* virtio-serial-pci */ + +static int virtio_serial_pci_init(VirtIOPCIProxy *vpci_dev) +{ + VirtIOSerialPCI *dev = VIRTIO_SERIAL_PCI(vpci_dev); + DeviceState *vdev = DEVICE(&dev->vdev); + + if (vpci_dev->class_code != PCI_CLASS_COMMUNICATION_OTHER && + vpci_dev->class_code != PCI_CLASS_DISPLAY_OTHER && /* qemu 0.10 */ + vpci_dev->class_code != PCI_CLASS_OTHERS) { /* qemu-kvm */ + vpci_dev->class_code = PCI_CLASS_COMMUNICATION_OTHER; + } + + /* backwards-compatibility with machines that were created with + DEV_NVECTORS_UNSPECIFIED */ + if (vpci_dev->nvectors == DEV_NVECTORS_UNSPECIFIED) { + vpci_dev->nvectors = dev->vdev.serial.max_virtserial_ports + 1; + } + + qdev_set_parent_bus(vdev, BUS(&vpci_dev->bus)); + if (qdev_init(vdev) < 0) { + return -1; + } + return 0; +} + +static Property virtio_serial_pci_properties[] = { + DEFINE_PROP_BIT("ioeventfd", VirtIOPCIProxy, flags, + VIRTIO_PCI_FLAG_USE_IOEVENTFD_BIT, true), + DEFINE_PROP_UINT32("vectors", VirtIOPCIProxy, nvectors, 2), + DEFINE_PROP_HEX32("class", VirtIOPCIProxy, class_code, 0), + DEFINE_VIRTIO_COMMON_FEATURES(VirtIOPCIProxy, host_features), + DEFINE_VIRTIO_SERIAL_PROPERTIES(VirtIOSerialPCI, vdev.serial), + DEFINE_PROP_END_OF_LIST(), +}; + +static void virtio_serial_pci_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + VirtioPCIClass *k = VIRTIO_PCI_CLASS(klass); + PCIDeviceClass *pcidev_k = PCI_DEVICE_CLASS(klass); + k->init = virtio_serial_pci_init; + dc->props = virtio_serial_pci_properties; + pcidev_k->vendor_id = PCI_VENDOR_ID_REDHAT_QUMRANET; + pcidev_k->device_id = PCI_DEVICE_ID_VIRTIO_CONSOLE; + pcidev_k->revision = VIRTIO_PCI_ABI_VERSION; + pcidev_k->class_id = PCI_CLASS_COMMUNICATION_OTHER; +} + +static void virtio_serial_pci_instance_init(Object *obj) +{ + VirtIOSerialPCI *dev = VIRTIO_SERIAL_PCI(obj); + object_initialize(OBJECT(&dev->vdev), TYPE_VIRTIO_SERIAL); + object_property_add_child(obj, "virtio-backend", OBJECT(&dev->vdev), NULL); +} + +static const TypeInfo virtio_serial_pci_info = { + .name = TYPE_VIRTIO_SERIAL_PCI, + .parent = TYPE_VIRTIO_PCI, + .instance_size = sizeof(VirtIOSerialPCI), + .instance_init = virtio_serial_pci_instance_init, + .class_init = virtio_serial_pci_class_init, +}; + /* virtio-pci-bus */ void virtio_pci_bus_new(VirtioBusState *bus, VirtIOPCIProxy *dev) @@ -1499,7 +1498,6 @@ static const TypeInfo virtio_pci_bus_info = { static void virtio_pci_register_types(void) { type_register_static(&virtio_net_info); - type_register_static(&virtio_serial_info); type_register_static(&virtio_rng_info); type_register_static(&virtio_pci_bus_info); type_register_static(&virtio_pci_info); @@ -1509,6 +1507,7 @@ static void virtio_pci_register_types(void) type_register_static(&virtio_blk_pci_info); type_register_static(&virtio_scsi_pci_info); type_register_static(&virtio_balloon_pci_info); + type_register_static(&virtio_serial_pci_info); } type_init(virtio_pci_register_types) diff --git a/hw/virtio/virtio-pci.h b/hw/virtio/virtio-pci.h index fb83155..879cc83 100644 --- a/hw/virtio/virtio-pci.h +++ b/hw/virtio/virtio-pci.h @@ -29,6 +29,7 @@ typedef struct VirtIOPCIProxy VirtIOPCIProxy; typedef struct VirtIOBlkPCI VirtIOBlkPCI; typedef struct VirtIOSCSIPCI VirtIOSCSIPCI; typedef struct VirtIOBalloonPCI VirtIOBalloonPCI; +typedef struct VirtIOSerialPCI VirtIOSerialPCI; /* virtio-pci-bus */ @@ -82,7 +83,6 @@ struct VirtIOPCIProxy { #ifdef CONFIG_VIRTFS V9fsConf fsconf; #endif - virtio_serial_conf serial; virtio_net_conf net; VirtIORNGConf rng; bool ioeventfd_disabled; @@ -130,6 +130,18 @@ struct VirtIOBalloonPCI { VirtIOBalloon vdev; }; +/* + * virtio-serial-pci: This extends VirtioPCIProxy. + */ +#define TYPE_VIRTIO_SERIAL_PCI "virtio-serial-pci" +#define VIRTIO_SERIAL_PCI(obj) \ + OBJECT_CHECK(VirtIOSerialPCI, (obj), TYPE_VIRTIO_SERIAL_PCI) + +struct VirtIOSerialPCI { + VirtIOPCIProxy parent_obj; + VirtIOSerial vdev; +}; + void virtio_init_pci(VirtIOPCIProxy *proxy, VirtIODevice *vdev); void virtio_pci_bus_new(VirtioBusState *bus, VirtIOPCIProxy *dev); diff --git a/include/block/block.h b/include/block/block.h index 9dc6aad..ebd9512 100644 --- a/include/block/block.h +++ b/include/block/block.h @@ -166,10 +166,12 @@ int bdrv_read_unthrottled(BlockDriverState *bs, int64_t sector_num, uint8_t *buf, int nb_sectors); int bdrv_write(BlockDriverState *bs, int64_t sector_num, const uint8_t *buf, int nb_sectors); +int bdrv_writev(BlockDriverState *bs, int64_t sector_num, QEMUIOVector *qiov); int bdrv_pread(BlockDriverState *bs, int64_t offset, void *buf, int count); int bdrv_pwrite(BlockDriverState *bs, int64_t offset, const void *buf, int count); +int bdrv_pwritev(BlockDriverState *bs, int64_t offset, QEMUIOVector *qiov); int bdrv_pwrite_sync(BlockDriverState *bs, int64_t offset, const void *buf, int count); int coroutine_fn bdrv_co_readv(BlockDriverState *bs, int64_t sector_num, @@ -348,6 +350,7 @@ void path_combine(char *dest, int dest_size, const char *base_path, const char *filename); +int bdrv_writev_vmstate(BlockDriverState *bs, QEMUIOVector *qiov, int64_t pos); int bdrv_save_vmstate(BlockDriverState *bs, const uint8_t *buf, int64_t pos, int size); diff --git a/include/block/block_int.h b/include/block/block_int.h index 9aa98b5..458cde3 100644 --- a/include/block/block_int.h +++ b/include/block/block_int.h @@ -164,8 +164,8 @@ struct BlockDriver { const char *snapshot_name); int (*bdrv_get_info)(BlockDriverState *bs, BlockDriverInfo *bdi); - int (*bdrv_save_vmstate)(BlockDriverState *bs, const uint8_t *buf, - int64_t pos, int size); + int (*bdrv_save_vmstate)(BlockDriverState *bs, QEMUIOVector *qiov, + int64_t pos); int (*bdrv_load_vmstate)(BlockDriverState *bs, uint8_t *buf, int64_t pos, int size); diff --git a/include/hw/qdev-core.h b/include/hw/qdev-core.h index 547fbc7..cf83d54 100644 --- a/include/hw/qdev-core.h +++ b/include/hw/qdev-core.h @@ -96,7 +96,7 @@ typedef struct DeviceClass { /* Private to qdev / bus. */ qdev_initfn init; /* TODO remove, once users are converted to realize */ qdev_event unplug; - qdev_event exit; + qdev_event exit; /* TODO remove, once users are converted to unrealize */ const char *bus_type; } DeviceClass; diff --git a/include/hw/virtio/virtio-balloon.h b/include/hw/virtio/virtio-balloon.h index 3b459bb..f863bfe 100644 --- a/include/hw/virtio/virtio-balloon.h +++ b/include/hw/virtio/virtio-balloon.h @@ -18,7 +18,7 @@ #include "hw/virtio/virtio.h" #include "hw/pci/pci.h" -#define TYPE_VIRTIO_BALLOON "virtio-balloon" +#define TYPE_VIRTIO_BALLOON "virtio-balloon-device" #define VIRTIO_BALLOON(obj) \ OBJECT_CHECK(VirtIOBalloon, (obj), TYPE_VIRTIO_BALLOON) diff --git a/include/hw/virtio/virtio-blk.h b/include/hw/virtio/virtio-blk.h index c10d069..fc71853 100644 --- a/include/hw/virtio/virtio-blk.h +++ b/include/hw/virtio/virtio-blk.h @@ -17,7 +17,7 @@ #include "hw/virtio/virtio.h" #include "hw/block/block.h" -#define TYPE_VIRTIO_BLK "virtio-blk" +#define TYPE_VIRTIO_BLK "virtio-blk-device" #define VIRTIO_BLK(obj) \ OBJECT_CHECK(VirtIOBlock, (obj), TYPE_VIRTIO_BLK) diff --git a/include/hw/virtio/virtio-scsi.h b/include/hw/virtio/virtio-scsi.h index c9d92ca..ccd7b06 100644 --- a/include/hw/virtio/virtio-scsi.h +++ b/include/hw/virtio/virtio-scsi.h @@ -18,7 +18,7 @@ #include "hw/pci/pci.h" #include "hw/scsi/scsi.h" -#define TYPE_VIRTIO_SCSI "virtio-scsi" +#define TYPE_VIRTIO_SCSI "virtio-scsi-device" #define VIRTIO_SCSI(obj) \ OBJECT_CHECK(VirtIOSCSI, (obj), TYPE_VIRTIO_SCSI) diff --git a/include/hw/virtio/virtio-serial.h b/include/hw/virtio/virtio-serial.h index 098deea..7c71304 100644 --- a/include/hw/virtio/virtio-serial.h +++ b/include/hw/virtio/virtio-serial.h @@ -192,7 +192,7 @@ typedef struct VirtIOSerialPostLoad { } VirtIOSerialPostLoad; struct VirtIOSerial { - VirtIODevice vdev; + VirtIODevice parent_obj; VirtQueue *c_ivq, *c_ovq; /* Arrays of ivqs and ovqs: one per port */ @@ -200,8 +200,6 @@ struct VirtIOSerial { VirtIOSerialBus bus; - DeviceState *qdev; - QTAILQ_HEAD(, VirtIOSerialPort) ports; /* bitmap for identifying active ports */ @@ -210,6 +208,8 @@ struct VirtIOSerial { struct virtio_console_config config; struct VirtIOSerialPostLoad *post_load; + + virtio_serial_conf serial; }; /* Interface to the virtio-serial bus */ @@ -244,4 +244,11 @@ size_t virtio_serial_guest_ready(VirtIOSerialPort *port); */ void virtio_serial_throttle_port(VirtIOSerialPort *port, bool throttle); +#define TYPE_VIRTIO_SERIAL "virtio-serial-device" +#define VIRTIO_SERIAL(obj) \ + OBJECT_CHECK(VirtIOSerial, (obj), TYPE_VIRTIO_SERIAL) + +#define DEFINE_VIRTIO_SERIAL_PROPERTIES(_state, _field) \ + DEFINE_PROP_UINT32("max_ports", _state, _field.max_virtserial_ports, 31) + #endif diff --git a/include/migration/qemu-file.h b/include/migration/qemu-file.h index 623c434..7519464 100644 --- a/include/migration/qemu-file.h +++ b/include/migration/qemu-file.h @@ -55,7 +55,7 @@ typedef int (QEMUFileGetFD)(void *opaque); * This function writes an iovec to file. */ typedef ssize_t (QEMUFileWritevBufferFunc)(void *opaque, struct iovec *iov, - int iovcnt); + int iovcnt, int64_t pos); typedef struct QEMUFileOps { QEMUFilePutBufferFunc *put_buffer; @@ -2765,13 +2765,6 @@ static mon_cmd_t info_cmds[] = { .mhandler.cmd = hmp_info_tpm, }, { - .name = "cpu_max", - .args_type = "", - .params = "", - .help = "Get maximum number of VCPUs supported by machine", - .mhandler.cmd = hmp_query_cpu_max, - }, - { .name = NULL, }, }; diff --git a/qapi-schema.json b/qapi-schema.json index db542f6..751d3c2 100644 --- a/qapi-schema.json +++ b/qapi-schema.json @@ -1600,7 +1600,7 @@ ## { 'union': 'BlockdevAction', 'data': { - 'blockdev-snapshot-sync': 'BlockdevSnapshot', + 'blockdev-snapshot-sync': 'BlockdevSnapshot' } } ## @@ -1834,17 +1834,6 @@ { 'command': 'query-migrate-cache-size', 'returns': 'int' } ## -## @query-cpu-max -## -## query maximum number of CPUs supported by machine -## -## Returns: number of CPUs -## -## Since: 1.5 -### -{ 'command': 'query-cpu-max', 'returns': 'int' } - -## # @ObjectPropertyInfo: # # @name: the name of the property @@ -2861,11 +2850,14 @@ # # @default: #optional whether the machine is default # +# @cpu-max: maximum number of CPUs supported by the machine type +# (since 1.5.0) +# # Since: 1.2.0 ## { 'type': 'MachineInfo', 'data': { 'name': 'str', '*alias': 'str', - '*is-default': 'bool' } } + '*is-default': 'bool', 'cpu-max': 'int' } } ## # @query-machines: diff --git a/qemu-char.c b/qemu-char.c index 14707ea..728ed9b 100644 --- a/qemu-char.c +++ b/qemu-char.c @@ -663,6 +663,7 @@ static guint io_add_watch_poll(GIOChannel *channel, gpointer user_data) { IOWatchPoll *iwp; + int tag; iwp = (IOWatchPoll *) g_source_new(&io_watch_poll_funcs, sizeof(IOWatchPoll)); iwp->fd_can_read = fd_can_read; @@ -671,7 +672,9 @@ static guint io_add_watch_poll(GIOChannel *channel, iwp->fd_read = (GSourceFunc) fd_read; iwp->src = NULL; - return g_source_attach(&iwp->parent, NULL); + tag = g_source_attach(&iwp->parent, NULL); + g_source_unref(&iwp->parent); + return tag; } #ifndef _WIN32 diff --git a/qemu-doc.texi b/qemu-doc.texi index af84bef..dfea4d3 100644 --- a/qemu-doc.texi +++ b/qemu-doc.texi @@ -423,6 +423,7 @@ snapshots. * disk_images_sheepdog:: Sheepdog disk images * disk_images_iscsi:: iSCSI LUNs * disk_images_gluster:: GlusterFS disk images +* disk_images_ssh:: Secure Shell (ssh) disk images @end menu @node disk_images_quickstart @@ -1038,6 +1039,59 @@ qemu-system-x86_64 -drive file=gluster+unix:///testvol/dir/a.img?socket=/tmp/glu qemu-system-x86_64 -drive file=gluster+rdma://1.2.3.4:24007/testvol/a.img @end example +@node disk_images_ssh +@subsection Secure Shell (ssh) disk images + +You can access disk images located on a remote ssh server +by using the ssh protocol: + +@example +qemu-system-x86_64 -drive file=ssh://[@var{user}@@]@var{server}[:@var{port}]/@var{path}[?host_key_check=@var{host_key_check}] +@end example + +Alternative syntax using properties: + +@example +qemu-system-x86_64 -drive file.driver=ssh[,file.user=@var{user}],file.host=@var{server}[,file.port=@var{port}],file.path=@var{path}[,file.host_key_check=@var{host_key_check}] +@end example + +@var{ssh} is the protocol. + +@var{user} is the remote user. If not specified, then the local +username is tried. + +@var{server} specifies the remote ssh server. Any ssh server can be +used, but it must implement the sftp-server protocol. Most Unix/Linux +systems should work without requiring any extra configuration. + +@var{port} is the port number on which sshd is listening. By default +the standard ssh port (22) is used. + +@var{path} is the path to the disk image. + +The optional @var{host_key_check} parameter controls how the remote +host's key is checked. The default is @code{yes} which means to use +the local @file{.ssh/known_hosts} file. Setting this to @code{no} +turns off known-hosts checking. Or you can check that the host key +matches a specific fingerprint: +@code{host_key_check=md5:78:45:8e:14:57:4f:d5:45:83:0a:0e:f3:49:82:c9:c8} +(@code{sha1:} can also be used as a prefix, but note that OpenSSH +tools only use MD5 to print fingerprints). + +Currently authentication must be done using ssh-agent. Other +authentication methods may be supported in future. + +Note: Many ssh servers do not support an @code{fsync}-style operation. +The ssh driver cannot guarantee that disk flush requests are +obeyed, and this causes a risk of disk corruption if the remote +server or network goes down during writes. The driver will +print a warning when @code{fsync} is not supported: + +warning: ssh server @code{ssh.example.com:22} does not support fsync + +With sufficiently new versions of libssh2 and OpenSSH, @code{fsync} is +supported. + @node pcsys_network @section Network emulation @@ -306,6 +306,7 @@ static void nbd_accept(void *opaque) int main(int argc, char **argv) { BlockDriverState *bs; + BlockDriver *drv; off_t dev_offset = 0; uint32_t nbdflags = 0; bool disconnect = false; @@ -313,7 +314,7 @@ int main(int argc, char **argv) char *device = NULL; int port = NBD_DEFAULT_PORT; off_t fd_size; - const char *sopt = "hVb:o:p:rsnP:c:dvk:e:t"; + const char *sopt = "hVb:o:p:rsnP:c:dvk:e:f:t"; struct option lopt[] = { { "help", 0, NULL, 'h' }, { "version", 0, NULL, 'V' }, @@ -333,6 +334,7 @@ int main(int argc, char **argv) #endif { "discard", 1, NULL, QEMU_NBD_OPT_DISCARD }, { "shared", 1, NULL, 'e' }, + { "format", 1, NULL, 'f' }, { "persistent", 0, NULL, 't' }, { "verbose", 0, NULL, 'v' }, { NULL, 0, NULL, 0 } @@ -351,6 +353,7 @@ int main(int argc, char **argv) bool seen_aio = false; #endif pthread_t client_thread; + const char *fmt = NULL; /* The client thread uses SIGTERM to interrupt the server. A signal * handler ensures that "qemu-nbd -v -c" exits with a nice status code. @@ -454,6 +457,9 @@ int main(int argc, char **argv) errx(EXIT_FAILURE, "Shared device number must be greater than 0\n"); } break; + case 'f': + fmt = optarg; + break; case 't': persistent = 1; break; @@ -555,9 +561,19 @@ int main(int argc, char **argv) bdrv_init(); atexit(bdrv_close_all); + if (fmt) { + drv = bdrv_find_format(fmt); + if (!drv) { + errx(EXIT_FAILURE, "Unknown file format '%s'", fmt); + } + } else { + drv = NULL; + } + bs = bdrv_new("hda"); srcpath = argv[optind]; - if ((ret = bdrv_open(bs, srcpath, NULL, flags, NULL)) < 0) { + ret = bdrv_open(bs, srcpath, NULL, flags, drv); + if (ret < 0) { errno = -ret; err(EXIT_FAILURE, "Failed to bdrv_open '%s'", argv[optind]); } diff --git a/qemu-nbd.texi b/qemu-nbd.texi index 5f3f3e3..6055ec6 100644 --- a/qemu-nbd.texi +++ b/qemu-nbd.texi @@ -45,6 +45,8 @@ Export QEMU disk image using NBD protocol. disconnect the specified device @item -e, --shared=@var{num} device can be shared by @var{num} clients (default @samp{1}) +@item -f, --format=@var{fmt} + force block driver for format @var{fmt} instead of auto-detecting @item -t, --persistent don't exit on the last connection @item -v, --verbose diff --git a/qemu-options.hx b/qemu-options.hx index 7cd6002..5c115d1 100644 --- a/qemu-options.hx +++ b/qemu-options.hx @@ -2107,6 +2107,18 @@ Example for Unix Domain Sockets qemu-system-i386 --drive file=nbd:unix:/tmp/nbd-socket @end example +@item SSH +QEMU supports SSH (Secure Shell) access to remote disks. + +Examples: +@example +qemu-system-i386 -drive file=ssh://user@@host/path/to/disk.img +qemu-system-i386 -drive file.driver=ssh,file.user=user,file.host=host,file.port=22,file.path=/path/to/disk.img +@end example + +Currently authentication must be done using ssh-agent. Other +authentication methods may be supported in future. + @item Sheepdog Sheepdog is a distributed storage system for QEMU. QEMU supports using either local sheepdog devices or remote networked diff --git a/qmp-commands.hx b/qmp-commands.hx index 1e0e11e..4d65422 100644 --- a/qmp-commands.hx +++ b/qmp-commands.hx @@ -385,28 +385,6 @@ Note: CPUs' indexes are obtained with the 'query-cpus' command. EQMP { - .name = "query-cpu-max", - .args_type = "", - .mhandler.cmd_new = qmp_marshal_input_query_cpu_max, - }, - -SQMP -query-cpu-max -------------- - -Get the maximum CPUs supported by the machine being currently -emulated. - -Returns json-int. - -Example: - --> { "execute": "query-cpu-max" } -<- { "return": 255 } - -EQMP - - { .name = "memsave", .args_type = "val:l,size:i,filename:s,cpu:i?", .mhandler.cmd_new = qmp_marshal_input_memsave, @@ -119,7 +119,6 @@ void qemu_announce_self(void) struct QEMUFile { const QEMUFileOps *ops; void *opaque; - int is_write; int64_t bytes_xfer; int64_t xfer_limit; @@ -176,7 +175,8 @@ static void coroutine_fn yield_until_fd_readable(int fd) qemu_coroutine_yield(); } -static ssize_t socket_writev_buffer(void *opaque, struct iovec *iov, int iovcnt) +static ssize_t socket_writev_buffer(void *opaque, struct iovec *iov, int iovcnt, + int64_t pos) { QEMUFileSocket *s = opaque; ssize_t len; @@ -458,6 +458,21 @@ fail: return NULL; } +static ssize_t block_writev_buffer(void *opaque, struct iovec *iov, int iovcnt, + int64_t pos) +{ + int ret; + QEMUIOVector qiov; + + qemu_iovec_init_external(&qiov, iov, iovcnt); + ret = bdrv_writev_vmstate(opaque, &qiov, pos); + if (ret < 0) { + return ret; + } + + return qiov.size; +} + static int block_put_buffer(void *opaque, const uint8_t *buf, int64_t pos, int size) { @@ -481,8 +496,9 @@ static const QEMUFileOps bdrv_read_ops = { }; static const QEMUFileOps bdrv_write_ops = { - .put_buffer = block_put_buffer, - .close = bdrv_fclose + .put_buffer = block_put_buffer, + .writev_buffer = block_writev_buffer, + .close = bdrv_fclose }; static QEMUFile *qemu_fopen_bdrv(BlockDriverState *bs, int is_writable) @@ -500,7 +516,6 @@ QEMUFile *qemu_fopen_ops(void *opaque, const QEMUFileOps *ops) f->opaque = opaque; f->ops = ops; - f->is_write = 0; return f; } @@ -516,6 +531,11 @@ static void qemu_file_set_error(QEMUFile *f, int ret) } } +static inline bool qemu_file_is_writable(QEMUFile *f) +{ + return f->ops->writev_buffer || f->ops->put_buffer; +} + /** * Flushes QEMUFile buffer * @@ -525,30 +545,25 @@ static void qemu_file_set_error(QEMUFile *f, int ret) static void qemu_fflush(QEMUFile *f) { ssize_t ret = 0; - int i = 0; - if (!f->ops->writev_buffer && !f->ops->put_buffer) { + if (!qemu_file_is_writable(f)) { return; } - if (f->is_write && f->iovcnt > 0) { - if (f->ops->writev_buffer) { - ret = f->ops->writev_buffer(f->opaque, f->iov, f->iovcnt); - if (ret >= 0) { - f->pos += ret; - } - } else { - for (i = 0; i < f->iovcnt && ret >= 0; i++) { - ret = f->ops->put_buffer(f->opaque, f->iov[i].iov_base, f->pos, - f->iov[i].iov_len); - if (ret >= 0) { - f->pos += ret; - } - } + if (f->ops->writev_buffer) { + if (f->iovcnt > 0) { + ret = f->ops->writev_buffer(f->opaque, f->iov, f->iovcnt, f->pos); } - f->buf_index = 0; - f->iovcnt = 0; + } else { + if (f->buf_index > 0) { + ret = f->ops->put_buffer(f->opaque, f->buf, f->pos, f->buf_index); + } + } + if (ret >= 0) { + f->pos += ret; } + f->buf_index = 0; + f->iovcnt = 0; if (ret < 0) { qemu_file_set_error(f, ret); } @@ -559,11 +574,7 @@ static void qemu_fill_buffer(QEMUFile *f) int len; int pending; - if (!f->ops->get_buffer) - return; - - if (f->is_write) - abort(); + assert(!qemu_file_is_writable(f)); pending = f->buf_size - f->buf_index; if (pending > 0) { @@ -631,28 +642,25 @@ static void add_to_iovec(QEMUFile *f, const uint8_t *buf, int size) f->iov[f->iovcnt].iov_base = (uint8_t *)buf; f->iov[f->iovcnt++].iov_len = size; } + + if (f->iovcnt >= MAX_IOV_SIZE) { + qemu_fflush(f); + } } void qemu_put_buffer_async(QEMUFile *f, const uint8_t *buf, int size) { - if (f->last_error) { + if (!f->ops->writev_buffer) { + qemu_put_buffer(f, buf, size); return; } - if (f->is_write == 0 && f->buf_index > 0) { - fprintf(stderr, - "Attempted to write to buffer while read buffer is not empty\n"); - abort(); + if (f->last_error) { + return; } - add_to_iovec(f, buf, size); - - f->is_write = 1; f->bytes_xfer += size; - - if (f->buf_index >= IO_BUF_SIZE || f->iovcnt >= MAX_IOV_SIZE) { - qemu_fflush(f); - } + add_to_iovec(f, buf, size); } void qemu_put_buffer(QEMUFile *f, const uint8_t *buf, int size) @@ -663,20 +671,19 @@ void qemu_put_buffer(QEMUFile *f, const uint8_t *buf, int size) return; } - if (f->is_write == 0 && f->buf_index > 0) { - fprintf(stderr, - "Attempted to write to buffer while read buffer is not empty\n"); - abort(); - } - while (size > 0) { l = IO_BUF_SIZE - f->buf_index; if (l > size) l = size; memcpy(f->buf + f->buf_index, buf, l); - f->is_write = 1; + f->bytes_xfer += size; + if (f->ops->writev_buffer) { + add_to_iovec(f, f->buf + f->buf_index, l); + } f->buf_index += l; - qemu_put_buffer_async(f, f->buf + (f->buf_index - l), l); + if (f->buf_index == IO_BUF_SIZE) { + qemu_fflush(f); + } if (qemu_file_get_error(f)) { break; } @@ -691,19 +698,13 @@ void qemu_put_byte(QEMUFile *f, int v) return; } - if (f->is_write == 0 && f->buf_index > 0) { - fprintf(stderr, - "Attempted to write to buffer while read buffer is not empty\n"); - abort(); - } - - f->buf[f->buf_index++] = v; - f->is_write = 1; + f->buf[f->buf_index] = v; f->bytes_xfer++; - - add_to_iovec(f, f->buf + (f->buf_index - 1), 1); - - if (f->buf_index >= IO_BUF_SIZE || f->iovcnt >= MAX_IOV_SIZE) { + if (f->ops->writev_buffer) { + add_to_iovec(f, f->buf + f->buf_index, 1); + } + f->buf_index++; + if (f->buf_index == IO_BUF_SIZE) { qemu_fflush(f); } } @@ -720,9 +721,7 @@ static int qemu_peek_buffer(QEMUFile *f, uint8_t *buf, int size, size_t offset) int pending; int index; - if (f->is_write) { - abort(); - } + assert(!qemu_file_is_writable(f)); index = f->buf_index + offset; pending = f->buf_size - index; @@ -767,9 +766,7 @@ static int qemu_peek_byte(QEMUFile *f, int offset) { int index = f->buf_index + offset; - if (f->is_write) { - abort(); - } + assert(!qemu_file_is_writable(f)); if (index >= f->buf_size) { qemu_fill_buffer(f); diff --git a/target-mips/dsp_helper.c b/target-mips/dsp_helper.c index c7df595..f975da0 100644 --- a/target-mips/dsp_helper.c +++ b/target-mips/dsp_helper.c @@ -269,18 +269,6 @@ static inline int32_t mipsdsp_sat32_acc_q31(int32_t acc, int32_t a, temp31 = (temp_sum >> 31) & 0x01; result = temp_sum & 0xFFFFFFFF; - /* FIXME - This sat function may wrong, because user manual wrote: - temp127..0 ← temp + ( (signA) || a31..0 - if ( temp32 ≠ temp31 ) then - if ( temp32 = 0 ) then - temp31..0 ← 0x80000000 - else - temp31..0 ← 0x7FFFFFFF - endif - DSPControlouflag:16+acc ← 1 - endif - */ if (temp32 != temp31) { if (temp32 == 0) { result = 0x7FFFFFFF; @@ -578,7 +566,7 @@ static inline int32_t mipsdsp_mul_q15_q15(int32_t ac, uint16_t a, uint16_t b, temp = 0x7FFFFFFF; set_DSPControl_overflow_flag(1, 16 + ac, env); } else { - temp = ((uint32_t)a * (uint32_t)b) << 1; + temp = ((int16_t)a * (int16_t)b) << 1; } return temp; diff --git a/tcg/ppc64/tcg-target.c b/tcg/ppc64/tcg-target.c index 833fe0c..0fcf2b5 100644 --- a/tcg/ppc64/tcg-target.c +++ b/tcg/ppc64/tcg-target.c @@ -22,7 +22,12 @@ * THE SOFTWARE. */ -#define TCG_CT_CONST_U32 0x100 +#define TCG_CT_CONST_S16 0x100 +#define TCG_CT_CONST_U16 0x200 +#define TCG_CT_CONST_S32 0x400 +#define TCG_CT_CONST_U32 0x800 +#define TCG_CT_CONST_ZERO 0x1000 +#define TCG_CT_CONST_MONE 0x2000 static uint8_t *tb_ret_addr; @@ -40,6 +45,16 @@ static uint8_t *tb_ret_addr; #define GUEST_BASE 0 #endif +#ifdef CONFIG_GETAUXVAL +#include <sys/auxv.h> +static bool have_isa_2_06; +#define HAVE_ISA_2_06 have_isa_2_06 +#define HAVE_ISEL have_isa_2_06 +#else +#define HAVE_ISA_2_06 0 +#define HAVE_ISEL 0 +#endif + #ifdef CONFIG_USE_GUEST_BASE #define TCG_GUEST_BASE_REG 30 #else @@ -242,9 +257,24 @@ static int target_parse_constraint (TCGArgConstraint *ct, const char **pct_str) tcg_regset_reset_reg (ct->u.regs, TCG_REG_R6); #endif break; - case 'Z': + case 'I': + ct->ct |= TCG_CT_CONST_S16; + break; + case 'J': + ct->ct |= TCG_CT_CONST_U16; + break; + case 'M': + ct->ct |= TCG_CT_CONST_MONE; + break; + case 'T': + ct->ct |= TCG_CT_CONST_S32; + break; + case 'U': ct->ct |= TCG_CT_CONST_U32; break; + case 'Z': + ct->ct |= TCG_CT_CONST_ZERO; + break; default: return -1; } @@ -257,13 +287,22 @@ static int target_parse_constraint (TCGArgConstraint *ct, const char **pct_str) static int tcg_target_const_match (tcg_target_long val, const TCGArgConstraint *arg_ct) { - int ct; - - ct = arg_ct->ct; - if (ct & TCG_CT_CONST) + int ct = arg_ct->ct; + if (ct & TCG_CT_CONST) { return 1; - else if ((ct & TCG_CT_CONST_U32) && (val == (uint32_t) val)) + } else if ((ct & TCG_CT_CONST_S16) && val == (int16_t)val) { return 1; + } else if ((ct & TCG_CT_CONST_U16) && val == (uint16_t)val) { + return 1; + } else if ((ct & TCG_CT_CONST_S32) && val == (int32_t)val) { + return 1; + } else if ((ct & TCG_CT_CONST_U32) && val == (uint32_t)val) { + return 1; + } else if ((ct & TCG_CT_CONST_ZERO) && val == 0) { + return 1; + } else if ((ct & TCG_CT_CONST_MONE) && val == -1) { + return 1; + } return 0; } @@ -306,15 +345,19 @@ static int tcg_target_const_match (tcg_target_long val, #define MULLI OPCD( 7) #define CMPLI OPCD( 10) #define CMPI OPCD( 11) +#define SUBFIC OPCD( 8) #define LWZU OPCD( 33) #define STWU OPCD( 37) +#define RLWIMI OPCD( 20) #define RLWINM OPCD( 21) +#define RLWNM OPCD( 23) #define RLDICL XO30( 0) #define RLDICR XO30( 1) #define RLDIMI XO30( 3) +#define RLDCL XO30( 8) #define BCLR XO19( 16) #define BCCTR XO19(528) @@ -329,11 +372,15 @@ static int tcg_target_const_match (tcg_target_long val, #define EXTSW XO31(986) #define ADD XO31(266) #define ADDE XO31(138) +#define ADDME XO31(234) +#define ADDZE XO31(202) #define ADDC XO31( 10) #define AND XO31( 28) #define SUBF XO31( 40) #define SUBFC XO31( 8) #define SUBFE XO31(136) +#define SUBFME XO31(232) +#define SUBFZE XO31(200) #define OR XO31(444) #define XOR XO31(316) #define MULLW XO31(235) @@ -344,16 +391,24 @@ static int tcg_target_const_match (tcg_target_long val, #define CMPL XO31( 32) #define LHBRX XO31(790) #define LWBRX XO31(534) +#define LDBRX XO31(532) #define STHBRX XO31(918) #define STWBRX XO31(662) +#define STDBRX XO31(660) #define MFSPR XO31(339) #define MTSPR XO31(467) #define SRAWI XO31(824) #define NEG XO31(104) #define MFCR XO31( 19) +#define MFOCRF (MFCR | (1u << 20)) #define NOR XO31(124) #define CNTLZW XO31( 26) #define CNTLZD XO31( 58) +#define ANDC XO31( 60) +#define ORC XO31(412) +#define EQV XO31(284) +#define NAND XO31(476) +#define ISEL XO31( 15) #define MULLD XO31(233) #define MULHD XO31( 73) @@ -395,17 +450,21 @@ static int tcg_target_const_match (tcg_target_long val, #define ME(e) ((e)<<1) #define BO(o) ((o)<<21) #define MB64(b) ((b)<<5) +#define FXM(b) (1 << (19 - (b))) #define LK 1 -#define TAB(t,a,b) (RT(t) | RA(a) | RB(b)) -#define SAB(s,a,b) (RS(s) | RA(a) | RB(b)) +#define TAB(t, a, b) (RT(t) | RA(a) | RB(b)) +#define SAB(s, a, b) (RS(s) | RA(a) | RB(b)) +#define TAI(s, a, i) (RT(s) | RA(a) | ((i) & 0xffff)) +#define SAI(s, a, i) (RS(s) | RA(a) | ((i) & 0xffff)) #define BF(n) ((n)<<23) #define BI(n, c) (((c)+((n)*4))<<16) #define BT(n, c) (((c)+((n)*4))<<21) #define BA(n, c) (((c)+((n)*4))<<16) #define BB(n, c) (((c)+((n)*4))<<11) +#define BC_(n, c) (((c)+((n)*4))<<6) #define BO_COND_TRUE BO (12) #define BO_COND_FALSE BO ( 4) @@ -431,56 +490,201 @@ static const uint32_t tcg_to_bc[] = { [TCG_COND_GTU] = BC | BI (7, CR_GT) | BO_COND_TRUE, }; -static void tcg_out_mov (TCGContext *s, TCGType type, TCGReg ret, TCGReg arg) +/* The low bit here is set if the RA and RB fields must be inverted. */ +static const uint32_t tcg_to_isel[] = { + [TCG_COND_EQ] = ISEL | BC_(7, CR_EQ), + [TCG_COND_NE] = ISEL | BC_(7, CR_EQ) | 1, + [TCG_COND_LT] = ISEL | BC_(7, CR_LT), + [TCG_COND_GE] = ISEL | BC_(7, CR_LT) | 1, + [TCG_COND_LE] = ISEL | BC_(7, CR_GT) | 1, + [TCG_COND_GT] = ISEL | BC_(7, CR_GT), + [TCG_COND_LTU] = ISEL | BC_(7, CR_LT), + [TCG_COND_GEU] = ISEL | BC_(7, CR_LT) | 1, + [TCG_COND_LEU] = ISEL | BC_(7, CR_GT) | 1, + [TCG_COND_GTU] = ISEL | BC_(7, CR_GT), +}; + +static inline void tcg_out_mov(TCGContext *s, TCGType type, + TCGReg ret, TCGReg arg) { tcg_out32 (s, OR | SAB (arg, ret, arg)); } -static void tcg_out_rld (TCGContext *s, int op, int ra, int rs, int sh, int mb) +static inline void tcg_out_rld(TCGContext *s, int op, TCGReg ra, TCGReg rs, + int sh, int mb) { sh = SH (sh & 0x1f) | (((sh >> 5) & 1) << 1); mb = MB64 ((mb >> 5) | ((mb << 1) & 0x3f)); tcg_out32 (s, op | RA (ra) | RS (rs) | sh | mb); } -static void tcg_out_movi32 (TCGContext *s, int ret, int32_t arg) +static inline void tcg_out_rlw(TCGContext *s, int op, TCGReg ra, TCGReg rs, + int sh, int mb, int me) { - if (arg == (int16_t) arg) - tcg_out32 (s, ADDI | RT (ret) | RA (0) | (arg & 0xffff)); - else { - tcg_out32 (s, ADDIS | RT (ret) | RA (0) | ((arg >> 16) & 0xffff)); - if (arg & 0xffff) - tcg_out32 (s, ORI | RS (ret) | RA (ret) | (arg & 0xffff)); - } + tcg_out32(s, op | RA(ra) | RS(rs) | SH(sh) | MB(mb) | ME(me)); } -static void tcg_out_movi (TCGContext *s, TCGType type, - TCGReg ret, tcg_target_long arg) +static inline void tcg_out_ext32u(TCGContext *s, TCGReg dst, TCGReg src) { - int32_t arg32 = arg; - arg = type == TCG_TYPE_I32 ? arg & 0xffffffff : arg; + tcg_out_rld(s, RLDICL, dst, src, 0, 32); +} - if (arg == arg32) { - tcg_out_movi32 (s, ret, arg32); +static inline void tcg_out_shli64(TCGContext *s, TCGReg dst, TCGReg src, int c) +{ + tcg_out_rld(s, RLDICR, dst, src, c, 63 - c); +} + +static inline void tcg_out_shri64(TCGContext *s, TCGReg dst, TCGReg src, int c) +{ + tcg_out_rld(s, RLDICL, dst, src, 64 - c, c); +} + +static void tcg_out_movi32(TCGContext *s, TCGReg ret, int32_t arg) +{ + if (arg == (int16_t) arg) { + tcg_out32(s, ADDI | TAI(ret, 0, arg)); + } else { + tcg_out32(s, ADDIS | TAI(ret, 0, arg >> 16)); + if (arg & 0xffff) { + tcg_out32(s, ORI | SAI(ret, ret, arg)); + } } - else { - if ((uint64_t) arg >> 32) { - uint16_t h16 = arg >> 16; - uint16_t l16 = arg; - - tcg_out_movi32 (s, ret, arg >> 32); - tcg_out_rld (s, RLDICR, ret, ret, 32, 31); - if (h16) tcg_out32 (s, ORIS | RS (ret) | RA (ret) | h16); - if (l16) tcg_out32 (s, ORI | RS (ret) | RA (ret) | l16); +} + +static void tcg_out_movi(TCGContext *s, TCGType type, TCGReg ret, + tcg_target_long arg) +{ + if (type == TCG_TYPE_I32 || arg == (int32_t)arg) { + tcg_out_movi32(s, ret, arg); + } else if (arg == (uint32_t)arg && !(arg & 0x8000)) { + tcg_out32(s, ADDI | TAI(ret, 0, arg)); + tcg_out32(s, ORIS | SAI(ret, ret, arg >> 16)); + } else { + int32_t high = arg >> 32; + tcg_out_movi32(s, ret, high); + if (high) { + tcg_out_shli64(s, ret, ret, 32); } - else { - tcg_out_movi32 (s, ret, arg32); - if (arg32 < 0) - tcg_out_rld (s, RLDICL, ret, ret, 0, 32); + if (arg & 0xffff0000) { + tcg_out32(s, ORIS | SAI(ret, ret, arg >> 16)); + } + if (arg & 0xffff) { + tcg_out32(s, ORI | SAI(ret, ret, arg)); + } + } +} + +static bool mask_operand(uint32_t c, int *mb, int *me) +{ + uint32_t lsb, test; + + /* Accept a bit pattern like: + 0....01....1 + 1....10....0 + 0..01..10..0 + Keep track of the transitions. */ + if (c == 0 || c == -1) { + return false; + } + test = c; + lsb = test & -test; + test += lsb; + if (test & (test - 1)) { + return false; + } + + *me = clz32(lsb); + *mb = test ? clz32(test & -test) + 1 : 0; + return true; +} + +static bool mask64_operand(uint64_t c, int *mb, int *me) +{ + uint64_t lsb; + + if (c == 0) { + return false; + } + + lsb = c & -c; + /* Accept 1..10..0. */ + if (c == -lsb) { + *mb = 0; + *me = clz64(lsb); + return true; + } + /* Accept 0..01..1. */ + if (lsb == 1 && (c & (c + 1)) == 0) { + *mb = clz64(c + 1) + 1; + *me = 63; + return true; + } + return false; +} + +static void tcg_out_andi32(TCGContext *s, TCGReg dst, TCGReg src, uint32_t c) +{ + int mb, me; + + if ((c & 0xffff) == c) { + tcg_out32(s, ANDI | SAI(src, dst, c)); + return; + } else if ((c & 0xffff0000) == c) { + tcg_out32(s, ANDIS | SAI(src, dst, c >> 16)); + return; + } else if (mask_operand(c, &mb, &me)) { + tcg_out_rlw(s, RLWINM, dst, src, 0, mb, me); + } else { + tcg_out_movi(s, TCG_TYPE_I32, 0, c); + tcg_out32(s, AND | SAB(src, dst, 0)); + } +} + +static void tcg_out_andi64(TCGContext *s, TCGReg dst, TCGReg src, uint64_t c) +{ + int mb, me; + + if ((c & 0xffff) == c) { + tcg_out32(s, ANDI | SAI(src, dst, c)); + return; + } else if ((c & 0xffff0000) == c) { + tcg_out32(s, ANDIS | SAI(src, dst, c >> 16)); + return; + } else if (mask64_operand(c, &mb, &me)) { + if (mb == 0) { + tcg_out_rld(s, RLDICR, dst, src, 0, me); + } else { + tcg_out_rld(s, RLDICL, dst, src, 0, mb); } + } else { + tcg_out_movi(s, TCG_TYPE_I64, 0, c); + tcg_out32(s, AND | SAB(src, dst, 0)); } } +static void tcg_out_zori32(TCGContext *s, TCGReg dst, TCGReg src, uint32_t c, + int op_lo, int op_hi) +{ + if (c >> 16) { + tcg_out32(s, op_hi | SAI(src, dst, c >> 16)); + src = dst; + } + if (c & 0xffff) { + tcg_out32(s, op_lo | SAI(src, dst, c)); + src = dst; + } +} + +static void tcg_out_ori32(TCGContext *s, TCGReg dst, TCGReg src, uint32_t c) +{ + tcg_out_zori32(s, dst, src, c, ORI, ORIS); +} + +static void tcg_out_xori32(TCGContext *s, TCGReg dst, TCGReg src, uint32_t c) +{ + tcg_out_zori32(s, dst, src, c, XORI, XORIS); +} + static void tcg_out_b (TCGContext *s, int mask, tcg_target_long target) { tcg_target_long disp; @@ -522,25 +726,25 @@ static void tcg_out_call (TCGContext *s, tcg_target_long arg, int const_arg) #endif } -static void tcg_out_ldst (TCGContext *s, int ret, int addr, - int offset, int op1, int op2) +static void tcg_out_ldst(TCGContext *s, TCGReg ret, TCGReg addr, + int offset, int op1, int op2) { - if (offset == (int16_t) offset) - tcg_out32 (s, op1 | RT (ret) | RA (addr) | (offset & 0xffff)); - else { - tcg_out_movi (s, TCG_TYPE_I64, 0, offset); - tcg_out32 (s, op2 | RT (ret) | RA (addr) | RB (0)); + if (offset == (int16_t) offset) { + tcg_out32(s, op1 | TAI(ret, addr, offset)); + } else { + tcg_out_movi(s, TCG_TYPE_I64, 0, offset); + tcg_out32(s, op2 | TAB(ret, addr, 0)); } } -static void tcg_out_ldsta (TCGContext *s, int ret, int addr, - int offset, int op1, int op2) +static void tcg_out_ldsta(TCGContext *s, TCGReg ret, TCGReg addr, + int offset, int op1, int op2) { - if (offset == (int16_t) (offset & ~3)) - tcg_out32 (s, op1 | RT (ret) | RA (addr) | (offset & 0xffff)); - else { - tcg_out_movi (s, TCG_TYPE_I64, 0, offset); - tcg_out32 (s, op2 | RT (ret) | RA (addr) | RB (0)); + if (offset == (int16_t) (offset & ~3)) { + tcg_out32(s, op1 | TAI(ret, addr, offset)); + } else { + tcg_out_movi(s, TCG_TYPE_I64, 0, offset); + tcg_out32(s, op2 | TAB(ret, addr, 0)); } } @@ -566,40 +770,28 @@ static const void * const qemu_st_helpers[4] = { helper_stq_mmu, }; -static void tcg_out_tlb_read (TCGContext *s, int r0, int r1, int r2, - int addr_reg, int s_bits, int offset) +static void tcg_out_tlb_read(TCGContext *s, TCGReg r0, TCGReg r1, TCGReg r2, + TCGReg addr_reg, int s_bits, int offset) { #if TARGET_LONG_BITS == 32 - tcg_out_rld (s, RLDICL, addr_reg, addr_reg, 0, 32); - - tcg_out32 (s, (RLWINM - | RA (r0) - | RS (addr_reg) - | SH (32 - (TARGET_PAGE_BITS - CPU_TLB_ENTRY_BITS)) - | MB (32 - (CPU_TLB_BITS + CPU_TLB_ENTRY_BITS)) - | ME (31 - CPU_TLB_ENTRY_BITS) - ) - ); - tcg_out32 (s, ADD | RT (r0) | RA (r0) | RB (TCG_AREG0)); - tcg_out32 (s, (LWZU | RT (r1) | RA (r0) | offset)); - tcg_out32 (s, (RLWINM - | RA (r2) - | RS (addr_reg) - | SH (0) - | MB ((32 - s_bits) & 31) - | ME (31 - TARGET_PAGE_BITS) - ) - ); + tcg_out_ext32u(s, addr_reg, addr_reg); + + tcg_out_rlw(s, RLWINM, r0, addr_reg, + 32 - (TARGET_PAGE_BITS - CPU_TLB_ENTRY_BITS), + 32 - (CPU_TLB_BITS + CPU_TLB_ENTRY_BITS), + 31 - CPU_TLB_ENTRY_BITS); + tcg_out32(s, ADD | TAB(r0, r0, TCG_AREG0)); + tcg_out32(s, LWZU | TAI(r1, r0, offset)); + tcg_out_rlw(s, RLWINM, r2, addr_reg, 0, + (32 - s_bits) & 31, 31 - TARGET_PAGE_BITS); #else tcg_out_rld (s, RLDICL, r0, addr_reg, 64 - TARGET_PAGE_BITS, 64 - CPU_TLB_BITS); - tcg_out_rld (s, RLDICR, r0, r0, - CPU_TLB_ENTRY_BITS, - 63 - CPU_TLB_ENTRY_BITS); + tcg_out_shli64(s, r0, r0, CPU_TLB_ENTRY_BITS); - tcg_out32 (s, ADD | TAB (r0, r0, TCG_AREG0)); - tcg_out32 (s, LD_ADDR | RT (r1) | RA (r0) | offset); + tcg_out32(s, ADD | TAB(r0, r0, TCG_AREG0)); + tcg_out32(s, LD_ADDR | TAI(r1, r0, offset)); if (!s_bits) { tcg_out_rld (s, RLDICR, r2, addr_reg, 0, 63 - TARGET_PAGE_BITS); @@ -614,20 +806,44 @@ static void tcg_out_tlb_read (TCGContext *s, int r0, int r1, int r2, } #endif +static const uint32_t qemu_ldx_opc[8] = { +#ifdef TARGET_WORDS_BIGENDIAN + LBZX, LHZX, LWZX, LDX, + 0, LHAX, LWAX, LDX +#else + LBZX, LHBRX, LWBRX, LDBRX, + 0, 0, 0, LDBRX, +#endif +}; + +static const uint32_t qemu_stx_opc[4] = { +#ifdef TARGET_WORDS_BIGENDIAN + STBX, STHX, STWX, STDX +#else + STBX, STHBRX, STWBRX, STDBRX, +#endif +}; + +static const uint32_t qemu_exts_opc[4] = { + EXTSB, EXTSH, EXTSW, 0 +}; + static void tcg_out_qemu_ld (TCGContext *s, const TCGArg *args, int opc) { - int addr_reg, data_reg, r0, r1, rbase, bswap; + TCGReg addr_reg, data_reg, r0, r1, rbase; + uint32_t insn, s_bits; #ifdef CONFIG_SOFTMMU - int r2, mem_index, s_bits, ir; + TCGReg r2, ir; + int mem_index; void *label1_ptr, *label2_ptr; #endif data_reg = *args++; addr_reg = *args++; + s_bits = opc & 3; #ifdef CONFIG_SOFTMMU mem_index = *args; - s_bits = opc & 3; r0 = 3; r1 = 4; @@ -652,23 +868,11 @@ static void tcg_out_qemu_ld (TCGContext *s, const TCGArg *args, int opc) tcg_out_call (s, (tcg_target_long) qemu_ld_helpers[s_bits], 1); - switch (opc) { - case 0|4: - tcg_out32 (s, EXTSB | RA (data_reg) | RS (3)); - break; - case 1|4: - tcg_out32 (s, EXTSH | RA (data_reg) | RS (3)); - break; - case 2|4: - tcg_out32 (s, EXTSW | RA (data_reg) | RS (3)); - break; - case 0: - case 1: - case 2: - case 3: - if (data_reg != 3) - tcg_out_mov (s, TCG_TYPE_I64, data_reg, 3); - break; + if (opc & 4) { + insn = qemu_exts_opc[s_bits]; + tcg_out32(s, insn | RA(data_reg) | RS(3)); + } else if (data_reg != 3) { + tcg_out_mov(s, TCG_TYPE_I64, data_reg, 3); } label2_ptr = s->code_ptr; tcg_out32 (s, B); @@ -679,84 +883,35 @@ static void tcg_out_qemu_ld (TCGContext *s, const TCGArg *args, int opc) #endif /* r0 now contains &env->tlb_table[mem_index][index].addr_read */ - tcg_out32 (s, (LD - | RT (r0) - | RA (r0) - | (offsetof (CPUTLBEntry, addend) - - offsetof (CPUTLBEntry, addr_read)) - )); + tcg_out32(s, LD | TAI(r0, r0, + offsetof(CPUTLBEntry, addend) + - offsetof(CPUTLBEntry, addr_read))); /* r0 = env->tlb_table[mem_index][index].addend */ - tcg_out32 (s, ADD | RT (r0) | RA (r0) | RB (addr_reg)); + tcg_out32(s, ADD | TAB(r0, r0, addr_reg)); /* r0 = env->tlb_table[mem_index][index].addend + addr */ #else /* !CONFIG_SOFTMMU */ #if TARGET_LONG_BITS == 32 - tcg_out_rld (s, RLDICL, addr_reg, addr_reg, 0, 32); + tcg_out_ext32u(s, addr_reg, addr_reg); #endif r0 = addr_reg; r1 = 3; rbase = GUEST_BASE ? TCG_GUEST_BASE_REG : 0; #endif -#ifdef TARGET_WORDS_BIGENDIAN - bswap = 0; -#else - bswap = 1; -#endif - switch (opc) { - default: - case 0: - tcg_out32 (s, LBZX | TAB (data_reg, rbase, r0)); - break; - case 0|4: - tcg_out32 (s, LBZX | TAB (data_reg, rbase, r0)); - tcg_out32 (s, EXTSB | RA (data_reg) | RS (data_reg)); - break; - case 1: - if (bswap) - tcg_out32 (s, LHBRX | TAB (data_reg, rbase, r0)); - else - tcg_out32 (s, LHZX | TAB (data_reg, rbase, r0)); - break; - case 1|4: - if (bswap) { - tcg_out32 (s, LHBRX | TAB (data_reg, rbase, r0)); - tcg_out32 (s, EXTSH | RA (data_reg) | RS (data_reg)); - } - else tcg_out32 (s, LHAX | TAB (data_reg, rbase, r0)); - break; - case 2: - if (bswap) - tcg_out32 (s, LWBRX | TAB (data_reg, rbase, r0)); - else - tcg_out32 (s, LWZX | TAB (data_reg, rbase, r0)); - break; - case 2|4: - if (bswap) { - tcg_out32 (s, LWBRX | TAB (data_reg, rbase, r0)); - tcg_out32 (s, EXTSW | RA (data_reg) | RS (data_reg)); - } - else tcg_out32 (s, LWAX | TAB (data_reg, rbase, r0)); - break; - case 3: -#ifdef CONFIG_USE_GUEST_BASE - if (bswap) { - tcg_out32 (s, ADDI | RT (r1) | RA (r0) | 4); - tcg_out32 (s, LWBRX | TAB (data_reg, rbase, r0)); - tcg_out32 (s, LWBRX | TAB ( r1, rbase, r1)); - tcg_out_rld (s, RLDIMI, data_reg, r1, 32, 0); - } - else tcg_out32 (s, LDX | TAB (data_reg, rbase, r0)); -#else - if (bswap) { - tcg_out_movi32 (s, 0, 4); - tcg_out32 (s, LWBRX | RT (data_reg) | RB (r0)); - tcg_out32 (s, LWBRX | RT ( r1) | RA (r0)); - tcg_out_rld (s, RLDIMI, data_reg, r1, 32, 0); - } - else tcg_out32 (s, LD | RT (data_reg) | RA (r0)); -#endif - break; + insn = qemu_ldx_opc[opc]; + if (!HAVE_ISA_2_06 && insn == LDBRX) { + tcg_out32(s, ADDI | TAI(r1, r0, 4)); + tcg_out32(s, LWBRX | TAB(data_reg, rbase, r0)); + tcg_out32(s, LWBRX | TAB( r1, rbase, r1)); + tcg_out_rld(s, RLDIMI, data_reg, r1, 32, 0); + } else if (insn) { + tcg_out32(s, insn | TAB(data_reg, rbase, r0)); + } else { + insn = qemu_ldx_opc[s_bits]; + tcg_out32(s, insn | TAB(data_reg, rbase, r0)); + insn = qemu_exts_opc[s_bits]; + tcg_out32 (s, insn | RA(data_reg) | RS(data_reg)); } #ifdef CONFIG_SOFTMMU @@ -766,9 +921,11 @@ static void tcg_out_qemu_ld (TCGContext *s, const TCGArg *args, int opc) static void tcg_out_qemu_st (TCGContext *s, const TCGArg *args, int opc) { - int addr_reg, r0, r1, rbase, data_reg, bswap; + TCGReg addr_reg, r0, r1, rbase, data_reg; + uint32_t insn; #ifdef CONFIG_SOFTMMU - int r2, mem_index, ir; + TCGReg r2, ir; + int mem_index; void *label1_ptr, *label2_ptr; #endif @@ -817,48 +974,26 @@ static void tcg_out_qemu_st (TCGContext *s, const TCGArg *args, int opc) - offsetof (CPUTLBEntry, addr_write)) )); /* r0 = env->tlb_table[mem_index][index].addend */ - tcg_out32 (s, ADD | RT (r0) | RA (r0) | RB (addr_reg)); + tcg_out32(s, ADD | TAB(r0, r0, addr_reg)); /* r0 = env->tlb_table[mem_index][index].addend + addr */ #else /* !CONFIG_SOFTMMU */ #if TARGET_LONG_BITS == 32 - tcg_out_rld (s, RLDICL, addr_reg, addr_reg, 0, 32); + tcg_out_ext32u(s, addr_reg, addr_reg); #endif r1 = 3; r0 = addr_reg; rbase = GUEST_BASE ? TCG_GUEST_BASE_REG : 0; #endif -#ifdef TARGET_WORDS_BIGENDIAN - bswap = 0; -#else - bswap = 1; -#endif - switch (opc) { - case 0: - tcg_out32 (s, STBX | SAB (data_reg, rbase, r0)); - break; - case 1: - if (bswap) - tcg_out32 (s, STHBRX | SAB (data_reg, rbase, r0)); - else - tcg_out32 (s, STHX | SAB (data_reg, rbase, r0)); - break; - case 2: - if (bswap) - tcg_out32 (s, STWBRX | SAB (data_reg, rbase, r0)); - else - tcg_out32 (s, STWX | SAB (data_reg, rbase, r0)); - break; - case 3: - if (bswap) { - tcg_out32 (s, STWBRX | SAB (data_reg, rbase, r0)); - tcg_out32 (s, ADDI | RT (r1) | RA (r0) | 4); - tcg_out_rld (s, RLDICL, 0, data_reg, 32, 0); - tcg_out32 (s, STWBRX | SAB (0, rbase, r1)); - } - else tcg_out32 (s, STDX | SAB (data_reg, rbase, r0)); - break; + insn = qemu_stx_opc[opc]; + if (!HAVE_ISA_2_06 && insn == STDBRX) { + tcg_out32(s, STWBRX | SAB(data_reg, rbase, r0)); + tcg_out32(s, ADDI | TAI(r1, r0, 4)); + tcg_out_shri64(s, 0, data_reg, 32); + tcg_out32(s, STWBRX | SAB(0, rbase, r1)); + } else { + tcg_out32(s, insn | SAB(data_reg, rbase, r0)); } #ifdef CONFIG_SOFTMMU @@ -930,10 +1065,10 @@ static void tcg_target_qemu_prologue (TCGContext *s) | (i * 8 + 48 + TCG_STATIC_CALL_ARGS_SIZE) ) ); - tcg_out32 (s, LD | RT (0) | RA (1) | (frame_size + 16)); - tcg_out32 (s, MTSPR | RS (0) | LR); - tcg_out32 (s, ADDI | RT (1) | RA (1) | frame_size); - tcg_out32 (s, BCLR | BO_ALWAYS); + tcg_out32(s, LD | TAI(0, 1, frame_size + 16)); + tcg_out32(s, MTSPR | RS(0) | LR); + tcg_out32(s, ADDI | TAI(1, 1, frame_size)); + tcg_out32(s, BCLR | BO_ALWAYS); } static void tcg_out_ld (TCGContext *s, TCGType type, TCGReg ret, TCGReg arg1, @@ -954,38 +1089,17 @@ static void tcg_out_st (TCGContext *s, TCGType type, TCGReg arg, TCGReg arg1, tcg_out_ldsta (s, arg, arg1, arg2, STD, STDX); } -static void ppc_addi32 (TCGContext *s, int rt, int ra, tcg_target_long si) -{ - if (!si && rt == ra) - return; - - if (si == (int16_t) si) - tcg_out32 (s, ADDI | RT (rt) | RA (ra) | (si & 0xffff)); - else { - uint16_t h = ((si >> 16) & 0xffff) + ((uint16_t) si >> 15); - tcg_out32 (s, ADDIS | RT (rt) | RA (ra) | h); - tcg_out32 (s, ADDI | RT (rt) | RA (rt) | (si & 0xffff)); - } -} - -static void ppc_addi64 (TCGContext *s, int rt, int ra, tcg_target_long si) -{ - /* XXX: suboptimal */ - if (si == (int16_t) si - || ((((uint64_t) si >> 31) == 0) && (si & 0x8000) == 0)) - ppc_addi32 (s, rt, ra, si); - else { - tcg_out_movi (s, TCG_TYPE_I64, 0, si); - tcg_out32 (s, ADD | RT (rt) | RA (ra)); - } -} - -static void tcg_out_cmp (TCGContext *s, int cond, TCGArg arg1, TCGArg arg2, - int const_arg2, int cr, int arch64) +static void tcg_out_cmp(TCGContext *s, int cond, TCGArg arg1, TCGArg arg2, + int const_arg2, int cr, TCGType type) { int imm; uint32_t op; + /* Simplify the comparisons below wrt CMPI. */ + if (type == TCG_TYPE_I32) { + arg2 = (int32_t)arg2; + } + switch (cond) { case TCG_COND_EQ: case TCG_COND_NE: @@ -1038,96 +1152,132 @@ static void tcg_out_cmp (TCGContext *s, int cond, TCGArg arg1, TCGArg arg2, default: tcg_abort (); } - op |= BF (cr) | (arch64 << 21); + op |= BF(cr) | ((type == TCG_TYPE_I64) << 21); - if (imm) - tcg_out32 (s, op | RA (arg1) | (arg2 & 0xffff)); - else { + if (imm) { + tcg_out32(s, op | RA(arg1) | (arg2 & 0xffff)); + } else { if (const_arg2) { - tcg_out_movi (s, TCG_TYPE_I64, 0, arg2); - tcg_out32 (s, op | RA (arg1) | RB (0)); + tcg_out_movi(s, type, 0, arg2); + arg2 = 0; } - else - tcg_out32 (s, op | RA (arg1) | RB (arg2)); + tcg_out32(s, op | RA(arg1) | RB(arg2)); } +} +static void tcg_out_setcond_eq0(TCGContext *s, TCGType type, + TCGReg dst, TCGReg src) +{ + tcg_out32(s, (type == TCG_TYPE_I64 ? CNTLZD : CNTLZW) | RS(src) | RA(dst)); + tcg_out_shri64(s, dst, dst, type == TCG_TYPE_I64 ? 6 : 5); } -static void tcg_out_setcond (TCGContext *s, TCGType type, TCGCond cond, - TCGArg arg0, TCGArg arg1, TCGArg arg2, - int const_arg2) +static void tcg_out_setcond_ne0(TCGContext *s, TCGReg dst, TCGReg src) { - int crop, sh, arg; + /* X != 0 implies X + -1 generates a carry. Extra addition + trickery means: R = X-1 + ~X + C = X-1 + (-X+1) + C = C. */ + if (dst != src) { + tcg_out32(s, ADDIC | TAI(dst, src, -1)); + tcg_out32(s, SUBFE | TAB(dst, dst, src)); + } else { + tcg_out32(s, ADDIC | TAI(0, src, -1)); + tcg_out32(s, SUBFE | TAB(dst, 0, src)); + } +} - switch (cond) { - case TCG_COND_EQ: - if (const_arg2) { - if (!arg2) { - arg = arg1; - } - else { - arg = 0; - if ((uint16_t) arg2 == arg2) { - tcg_out32 (s, XORI | RS (arg1) | RA (0) | arg2); - } - else { - tcg_out_movi (s, type, 0, arg2); - tcg_out32 (s, XOR | SAB (arg1, 0, 0)); - } - } - } - else { - arg = 0; - tcg_out32 (s, XOR | SAB (arg1, 0, arg2)); +static TCGReg tcg_gen_setcond_xor(TCGContext *s, TCGReg arg1, TCGArg arg2, + bool const_arg2) +{ + if (const_arg2) { + if ((uint32_t)arg2 == arg2) { + tcg_out_xori32(s, TCG_REG_R0, arg1, arg2); + } else { + tcg_out_movi(s, TCG_TYPE_I64, TCG_REG_R0, arg2); + tcg_out32(s, XOR | SAB(arg1, TCG_REG_R0, TCG_REG_R0)); } + } else { + tcg_out32(s, XOR | SAB(arg1, TCG_REG_R0, arg2)); + } + return TCG_REG_R0; +} - if (type == TCG_TYPE_I64) { - tcg_out32 (s, CNTLZD | RS (arg) | RA (0)); - tcg_out_rld (s, RLDICL, arg0, 0, 58, 6); - } - else { - tcg_out32 (s, CNTLZW | RS (arg) | RA (0)); - tcg_out32 (s, (RLWINM - | RA (arg0) - | RS (0) - | SH (27) - | MB (5) - | ME (31) - ) - ); - } - break; +static void tcg_out_setcond(TCGContext *s, TCGType type, TCGCond cond, + TCGArg arg0, TCGArg arg1, TCGArg arg2, + int const_arg2) +{ + int crop, sh; - case TCG_COND_NE: - if (const_arg2) { - if (!arg2) { - arg = arg1; - } - else { - arg = 0; - if ((uint16_t) arg2 == arg2) { - tcg_out32 (s, XORI | RS (arg1) | RA (0) | arg2); - } - else { - tcg_out_movi (s, type, 0, arg2); - tcg_out32 (s, XOR | SAB (arg1, 0, 0)); - } + /* Ignore high bits of a potential constant arg2. */ + if (type == TCG_TYPE_I32) { + arg2 = (uint32_t)arg2; + } + + /* Handle common and trivial cases before handling anything else. */ + if (arg2 == 0) { + switch (cond) { + case TCG_COND_EQ: + tcg_out_setcond_eq0(s, type, arg0, arg1); + return; + case TCG_COND_NE: + if (type == TCG_TYPE_I32) { + tcg_out_ext32u(s, TCG_REG_R0, arg1); + arg1 = TCG_REG_R0; } + tcg_out_setcond_ne0(s, arg0, arg1); + return; + case TCG_COND_GE: + tcg_out32(s, NOR | SAB(arg1, arg0, arg1)); + arg1 = arg0; + /* FALLTHRU */ + case TCG_COND_LT: + /* Extract the sign bit. */ + tcg_out_rld(s, RLDICL, arg0, arg1, + type == TCG_TYPE_I64 ? 1 : 33, 63); + return; + default: + break; } - else { - arg = 0; - tcg_out32 (s, XOR | SAB (arg1, 0, arg2)); - } + } - if (arg == arg1 && arg1 == arg0) { - tcg_out32 (s, ADDIC | RT (0) | RA (arg) | 0xffff); - tcg_out32 (s, SUBFE | TAB (arg0, 0, arg)); + /* If we have ISEL, we can implement everything with 3 or 4 insns. + All other cases below are also at least 3 insns, so speed up the + code generator by not considering them and always using ISEL. */ + if (HAVE_ISEL) { + int isel, tab; + + tcg_out_cmp(s, cond, arg1, arg2, const_arg2, 7, type); + + isel = tcg_to_isel[cond]; + + tcg_out_movi(s, type, arg0, 1); + if (isel & 1) { + /* arg0 = (bc ? 0 : 1) */ + tab = TAB(arg0, 0, arg0); + isel &= ~1; + } else { + /* arg0 = (bc ? 1 : 0) */ + tcg_out_movi(s, type, TCG_REG_R0, 0); + tab = TAB(arg0, arg0, TCG_REG_R0); } - else { - tcg_out32 (s, ADDIC | RT (arg0) | RA (arg) | 0xffff); - tcg_out32 (s, SUBFE | TAB (arg0, arg0, arg)); + tcg_out32(s, isel | tab); + return; + } + + switch (cond) { + case TCG_COND_EQ: + arg1 = tcg_gen_setcond_xor(s, arg1, arg2, const_arg2); + tcg_out_setcond_eq0(s, type, arg0, arg1); + return; + + case TCG_COND_NE: + arg1 = tcg_gen_setcond_xor(s, arg1, arg2, const_arg2); + /* Discard the high bits only once, rather than both inputs. */ + if (type == TCG_TYPE_I32) { + tcg_out_ext32u(s, TCG_REG_R0, arg1); + arg1 = TCG_REG_R0; } - break; + tcg_out_setcond_ne0(s, arg0, arg1); + return; case TCG_COND_GT: case TCG_COND_GTU: @@ -1152,17 +1302,12 @@ static void tcg_out_setcond (TCGContext *s, TCGType type, TCGCond cond, sh = 31; crop = CRNOR | BT (7, CR_EQ) | BA (7, CR_GT) | BB (7, CR_GT); crtest: - tcg_out_cmp (s, cond, arg1, arg2, const_arg2, 7, type == TCG_TYPE_I64); - if (crop) tcg_out32 (s, crop); - tcg_out32 (s, MFCR | RT (0)); - tcg_out32 (s, (RLWINM - | RA (arg0) - | RS (0) - | SH (sh) - | MB (31) - | ME (31) - ) - ); + tcg_out_cmp(s, cond, arg1, arg2, const_arg2, 7, type); + if (crop) { + tcg_out32(s, crop); + } + tcg_out32(s, MFOCRF | RT(TCG_REG_R0) | FXM(7)); + tcg_out_rlw(s, RLWINM, arg0, TCG_REG_R0, sh, 31, 31); break; default: @@ -1185,12 +1330,60 @@ static void tcg_out_bc (TCGContext *s, int bc, int label_index) } } -static void tcg_out_brcond (TCGContext *s, TCGCond cond, - TCGArg arg1, TCGArg arg2, int const_arg2, - int label_index, int arch64) +static void tcg_out_brcond(TCGContext *s, TCGCond cond, + TCGArg arg1, TCGArg arg2, int const_arg2, + int label_index, TCGType type) +{ + tcg_out_cmp(s, cond, arg1, arg2, const_arg2, 7, type); + tcg_out_bc(s, tcg_to_bc[cond], label_index); +} + +static void tcg_out_movcond(TCGContext *s, TCGType type, TCGCond cond, + TCGArg dest, TCGArg c1, TCGArg c2, TCGArg v1, + TCGArg v2, bool const_c2) { - tcg_out_cmp (s, cond, arg1, arg2, const_arg2, 7, arch64); - tcg_out_bc (s, tcg_to_bc[cond], label_index); + /* If for some reason both inputs are zero, don't produce bad code. */ + if (v1 == 0 && v2 == 0) { + tcg_out_movi(s, type, dest, 0); + return; + } + + tcg_out_cmp(s, cond, c1, c2, const_c2, 7, type); + + if (HAVE_ISEL) { + int isel = tcg_to_isel[cond]; + + /* Swap the V operands if the operation indicates inversion. */ + if (isel & 1) { + int t = v1; + v1 = v2; + v2 = t; + isel &= ~1; + } + /* V1 == 0 is handled by isel; V2 == 0 must be handled by hand. */ + if (v2 == 0) { + tcg_out_movi(s, type, 0, 0); + } + tcg_out32(s, isel | TAB(dest, v1, v2)); + } else { + if (dest == v2) { + cond = tcg_invert_cond(cond); + v2 = v1; + } else if (dest != v1) { + if (v1 == 0) { + tcg_out_movi(s, type, dest, 0); + } else { + tcg_out_mov(s, type, dest, v1); + } + } + /* Branch forward over one insn */ + tcg_out32(s, tcg_to_bc[cond] | 8); + if (v2 == 0) { + tcg_out_movi(s, type, dest, 0); + } else { + tcg_out_mov(s, type, dest, v2); + } + } } void ppc_tb_set_jmp_target (unsigned long jmp_addr, unsigned long addr) @@ -1207,6 +1400,7 @@ void ppc_tb_set_jmp_target (unsigned long jmp_addr, unsigned long addr) static void tcg_out_op (TCGContext *s, TCGOpcode opc, const TCGArg *args, const int *const_args) { + TCGArg a0, a1, a2; int c; switch (opc) { @@ -1295,88 +1489,123 @@ static void tcg_out_op (TCGContext *s, TCGOpcode opc, const TCGArg *args, break; case INDEX_op_add_i32: - if (const_args[2]) - ppc_addi32 (s, args[0], args[1], args[2]); - else - tcg_out32 (s, ADD | TAB (args[0], args[1], args[2])); + a0 = args[0], a1 = args[1], a2 = args[2]; + if (const_args[2]) { + int32_t l, h; + do_addi_32: + l = (int16_t)a2; + h = a2 - l; + if (h) { + tcg_out32(s, ADDIS | TAI(a0, a1, h >> 16)); + a1 = a0; + } + if (l || a0 != a1) { + tcg_out32(s, ADDI | TAI(a0, a1, l)); + } + } else { + tcg_out32(s, ADD | TAB(a0, a1, a2)); + } break; case INDEX_op_sub_i32: - if (const_args[2]) - ppc_addi32 (s, args[0], args[1], -args[2]); - else - tcg_out32 (s, SUBF | TAB (args[0], args[2], args[1])); + a0 = args[0], a1 = args[1], a2 = args[2]; + if (const_args[1]) { + if (const_args[2]) { + tcg_out_movi(s, TCG_TYPE_I32, a0, a1 - a2); + } else { + tcg_out32(s, SUBFIC | TAI(a0, a2, a1)); + } + } else if (const_args[2]) { + a2 = -a2; + goto do_addi_32; + } else { + tcg_out32(s, SUBF | TAB(a0, a2, a1)); + } break; - case INDEX_op_and_i64: case INDEX_op_and_i32: + a0 = args[0], a1 = args[1], a2 = args[2]; if (const_args[2]) { - if ((args[2] & 0xffff) == args[2]) - tcg_out32 (s, ANDI | RS (args[1]) | RA (args[0]) | args[2]); - else if ((args[2] & 0xffff0000) == args[2]) - tcg_out32 (s, ANDIS | RS (args[1]) | RA (args[0]) - | ((args[2] >> 16) & 0xffff)); - else { - tcg_out_movi (s, (opc == INDEX_op_and_i32 - ? TCG_TYPE_I32 - : TCG_TYPE_I64), - 0, args[2]); - tcg_out32 (s, AND | SAB (args[1], args[0], 0)); - } + tcg_out_andi32(s, a0, a1, a2); + } else { + tcg_out32(s, AND | SAB(a1, a0, a2)); + } + break; + case INDEX_op_and_i64: + a0 = args[0], a1 = args[1], a2 = args[2]; + if (const_args[2]) { + tcg_out_andi64(s, a0, a1, a2); + } else { + tcg_out32(s, AND | SAB(a1, a0, a2)); } - else - tcg_out32 (s, AND | SAB (args[1], args[0], args[2])); break; case INDEX_op_or_i64: case INDEX_op_or_i32: + a0 = args[0], a1 = args[1], a2 = args[2]; if (const_args[2]) { - if (args[2] & 0xffff) { - tcg_out32 (s, ORI | RS (args[1]) | RA (args[0]) - | (args[2] & 0xffff)); - if (args[2] >> 16) - tcg_out32 (s, ORIS | RS (args[0]) | RA (args[0]) - | ((args[2] >> 16) & 0xffff)); - } - else { - tcg_out32 (s, ORIS | RS (args[1]) | RA (args[0]) - | ((args[2] >> 16) & 0xffff)); - } + tcg_out_ori32(s, a0, a1, a2); + } else { + tcg_out32(s, OR | SAB(a1, a0, a2)); } - else - tcg_out32 (s, OR | SAB (args[1], args[0], args[2])); break; case INDEX_op_xor_i64: case INDEX_op_xor_i32: + a0 = args[0], a1 = args[1], a2 = args[2]; if (const_args[2]) { - if ((args[2] & 0xffff) == args[2]) - tcg_out32 (s, XORI | RS (args[1]) | RA (args[0]) - | (args[2] & 0xffff)); - else if ((args[2] & 0xffff0000) == args[2]) - tcg_out32 (s, XORIS | RS (args[1]) | RA (args[0]) - | ((args[2] >> 16) & 0xffff)); - else { - tcg_out_movi (s, (opc == INDEX_op_and_i32 - ? TCG_TYPE_I32 - : TCG_TYPE_I64), - 0, args[2]); - tcg_out32 (s, XOR | SAB (args[1], args[0], 0)); - } + tcg_out_xori32(s, a0, a1, a2); + } else { + tcg_out32(s, XOR | SAB(a1, a0, a2)); } - else - tcg_out32 (s, XOR | SAB (args[1], args[0], args[2])); + break; + case INDEX_op_andc_i32: + a0 = args[0], a1 = args[1], a2 = args[2]; + if (const_args[2]) { + tcg_out_andi32(s, a0, a1, ~a2); + } else { + tcg_out32(s, ANDC | SAB(a1, a0, a2)); + } + break; + case INDEX_op_andc_i64: + a0 = args[0], a1 = args[1], a2 = args[2]; + if (const_args[2]) { + tcg_out_andi64(s, a0, a1, ~a2); + } else { + tcg_out32(s, ANDC | SAB(a1, a0, a2)); + } + break; + case INDEX_op_orc_i32: + if (const_args[2]) { + tcg_out_ori32(s, args[0], args[1], ~args[2]); + break; + } + /* FALLTHRU */ + case INDEX_op_orc_i64: + tcg_out32(s, ORC | SAB(args[1], args[0], args[2])); + break; + case INDEX_op_eqv_i32: + if (const_args[2]) { + tcg_out_xori32(s, args[0], args[1], ~args[2]); + break; + } + /* FALLTHRU */ + case INDEX_op_eqv_i64: + tcg_out32(s, EQV | SAB(args[1], args[0], args[2])); + break; + case INDEX_op_nand_i32: + case INDEX_op_nand_i64: + tcg_out32(s, NAND | SAB(args[1], args[0], args[2])); + break; + case INDEX_op_nor_i32: + case INDEX_op_nor_i64: + tcg_out32(s, NOR | SAB(args[1], args[0], args[2])); break; case INDEX_op_mul_i32: + a0 = args[0], a1 = args[1], a2 = args[2]; if (const_args[2]) { - if (args[2] == (int16_t) args[2]) - tcg_out32 (s, MULLI | RT (args[0]) | RA (args[1]) - | (args[2] & 0xffff)); - else { - tcg_out_movi (s, TCG_TYPE_I32, 0, args[2]); - tcg_out32 (s, MULLW | TAB (args[0], args[1], 0)); - } + tcg_out32(s, MULLI | TAI(a0, a1, a2)); + } else { + tcg_out32(s, MULLW | TAB(a0, a1, a2)); } - else - tcg_out32 (s, MULLW | TAB (args[0], args[1], args[2])); break; case INDEX_op_div_i32: @@ -1401,31 +1630,17 @@ static void tcg_out_op (TCGContext *s, TCGOpcode opc, const TCGArg *args, case INDEX_op_shl_i32: if (const_args[2]) { - tcg_out32 (s, (RLWINM - | RA (args[0]) - | RS (args[1]) - | SH (args[2]) - | MB (0) - | ME (31 - args[2]) - ) - ); - } - else + tcg_out_rlw(s, RLWINM, args[0], args[1], args[2], 0, 31 - args[2]); + } else { tcg_out32 (s, SLW | SAB (args[1], args[0], args[2])); + } break; case INDEX_op_shr_i32: if (const_args[2]) { - tcg_out32 (s, (RLWINM - | RA (args[0]) - | RS (args[1]) - | SH (32 - args[2]) - | MB (args[2]) - | ME (31) - ) - ); - } - else + tcg_out_rlw(s, RLWINM, args[0], args[1], 32 - args[2], args[2], 31); + } else { tcg_out32 (s, SRW | SAB (args[1], args[0], args[2])); + } break; case INDEX_op_sar_i32: if (const_args[2]) @@ -1433,13 +1648,32 @@ static void tcg_out_op (TCGContext *s, TCGOpcode opc, const TCGArg *args, else tcg_out32 (s, SRAW | SAB (args[1], args[0], args[2])); break; + case INDEX_op_rotl_i32: + if (const_args[2]) { + tcg_out_rlw(s, RLWINM, args[0], args[1], args[2], 0, 31); + } else { + tcg_out32(s, RLWNM | SAB(args[1], args[0], args[2]) + | MB(0) | ME(31)); + } + break; + case INDEX_op_rotr_i32: + if (const_args[2]) { + tcg_out_rlw(s, RLWINM, args[0], args[1], 32 - args[2], 0, 31); + } else { + tcg_out32(s, SUBFIC | TAI(0, args[2], 32)); + tcg_out32(s, RLWNM | SAB(args[1], args[0], args[2]) + | MB(0) | ME(31)); + } + break; case INDEX_op_brcond_i32: - tcg_out_brcond (s, args[2], args[0], args[1], const_args[1], args[3], 0); + tcg_out_brcond(s, args[2], args[0], args[1], const_args[1], + args[3], TCG_TYPE_I32); break; case INDEX_op_brcond_i64: - tcg_out_brcond (s, args[2], args[0], args[1], const_args[1], args[3], 1); + tcg_out_brcond(s, args[2], args[0], args[1], const_args[1], + args[3], TCG_TYPE_I64); break; case INDEX_op_neg_i32: @@ -1453,27 +1687,63 @@ static void tcg_out_op (TCGContext *s, TCGOpcode opc, const TCGArg *args, break; case INDEX_op_add_i64: - if (const_args[2]) - ppc_addi64 (s, args[0], args[1], args[2]); - else - tcg_out32 (s, ADD | TAB (args[0], args[1], args[2])); + a0 = args[0], a1 = args[1], a2 = args[2]; + if (const_args[2]) { + int32_t l0, h1, h2; + do_addi_64: + /* We can always split any 32-bit signed constant into 3 pieces. + Note the positive 0x80000000 coming from the sub_i64 path, + handled with the same code we need for eg 0x7fff8000. */ + assert(a2 == (int32_t)a2 || a2 == 0x80000000); + l0 = (int16_t)a2; + h1 = a2 - l0; + h2 = 0; + if (h1 < 0 && (int64_t)a2 > 0) { + h2 = 0x40000000; + h1 = a2 - h2 - l0; + } + assert((TCGArg)h2 + h1 + l0 == a2); + + if (h2) { + tcg_out32(s, ADDIS | TAI(a0, a1, h2 >> 16)); + a1 = a0; + } + if (h1) { + tcg_out32(s, ADDIS | TAI(a0, a1, h1 >> 16)); + a1 = a0; + } + if (l0 || a0 != a1) { + tcg_out32(s, ADDI | TAI(a0, a1, l0)); + } + } else { + tcg_out32(s, ADD | TAB(a0, a1, a2)); + } break; case INDEX_op_sub_i64: - if (const_args[2]) - ppc_addi64 (s, args[0], args[1], -args[2]); - else - tcg_out32 (s, SUBF | TAB (args[0], args[2], args[1])); + a0 = args[0], a1 = args[1], a2 = args[2]; + if (const_args[1]) { + if (const_args[2]) { + tcg_out_movi(s, TCG_TYPE_I64, a0, a1 - a2); + } else { + tcg_out32(s, SUBFIC | TAI(a0, a2, a1)); + } + } else if (const_args[2]) { + a2 = -a2; + goto do_addi_64; + } else { + tcg_out32(s, SUBF | TAB(a0, a2, a1)); + } break; case INDEX_op_shl_i64: if (const_args[2]) - tcg_out_rld (s, RLDICR, args[0], args[1], args[2], 63 - args[2]); + tcg_out_shli64(s, args[0], args[1], args[2]); else tcg_out32 (s, SLD | SAB (args[1], args[0], args[2])); break; case INDEX_op_shr_i64: if (const_args[2]) - tcg_out_rld (s, RLDICL, args[0], args[1], 64 - args[2], args[2]); + tcg_out_shri64(s, args[0], args[1], args[2]); else tcg_out32 (s, SRD | SAB (args[1], args[0], args[2])); break; @@ -1485,9 +1755,29 @@ static void tcg_out_op (TCGContext *s, TCGOpcode opc, const TCGArg *args, else tcg_out32 (s, SRAD | SAB (args[1], args[0], args[2])); break; + case INDEX_op_rotl_i64: + if (const_args[2]) { + tcg_out_rld(s, RLDICL, args[0], args[1], args[2], 0); + } else { + tcg_out32(s, RLDCL | SAB(args[1], args[0], args[2]) | MB64(0)); + } + break; + case INDEX_op_rotr_i64: + if (const_args[2]) { + tcg_out_rld(s, RLDICL, args[0], args[1], 64 - args[2], 0); + } else { + tcg_out32(s, SUBFIC | TAI(0, args[2], 64)); + tcg_out32(s, RLDCL | SAB(args[1], args[0], 0) | MB64(0)); + } + break; case INDEX_op_mul_i64: - tcg_out32 (s, MULLD | TAB (args[0], args[1], args[2])); + a0 = args[0], a1 = args[1], a2 = args[2]; + if (const_args[2]) { + tcg_out32(s, MULLI | TAI(a0, a1, a2)); + } else { + tcg_out32(s, MULLD | TAB(a0, a1, a2)); + } break; case INDEX_op_div_i64: tcg_out32 (s, DIVD | TAB (args[0], args[1], args[2])); @@ -1556,10 +1846,6 @@ static void tcg_out_op (TCGContext *s, TCGOpcode opc, const TCGArg *args, tcg_out32 (s, c | RS (args[1]) | RA (args[0])); break; - case INDEX_op_ext32u_i64: - tcg_out_rld (s, RLDICL, args[0], args[1], 0, 32); - break; - case INDEX_op_setcond_i32: tcg_out_setcond (s, TCG_TYPE_I32, args[3], args[0], args[1], args[2], const_args[2]); @@ -1569,6 +1855,174 @@ static void tcg_out_op (TCGContext *s, TCGOpcode opc, const TCGArg *args, const_args[2]); break; + case INDEX_op_bswap16_i32: + case INDEX_op_bswap16_i64: + a0 = args[0], a1 = args[1]; + /* a1 = abcd */ + if (a0 != a1) { + /* a0 = (a1 r<< 24) & 0xff # 000c */ + tcg_out_rlw(s, RLWINM, a0, a1, 24, 24, 31); + /* a0 = (a0 & ~0xff00) | (a1 r<< 8) & 0xff00 # 00dc */ + tcg_out_rlw(s, RLWIMI, a0, a1, 8, 16, 23); + } else { + /* r0 = (a1 r<< 8) & 0xff00 # 00d0 */ + tcg_out_rlw(s, RLWINM, TCG_REG_R0, a1, 8, 16, 23); + /* a0 = (a1 r<< 24) & 0xff # 000c */ + tcg_out_rlw(s, RLWINM, a0, a1, 24, 24, 31); + /* a0 = a0 | r0 # 00dc */ + tcg_out32(s, OR | SAB(TCG_REG_R0, a0, a0)); + } + break; + + case INDEX_op_bswap32_i32: + case INDEX_op_bswap32_i64: + /* Stolen from gcc's builtin_bswap32 */ + a1 = args[1]; + a0 = args[0] == a1 ? TCG_REG_R0 : args[0]; + + /* a1 = args[1] # abcd */ + /* a0 = rotate_left (a1, 8) # bcda */ + tcg_out_rlw(s, RLWINM, a0, a1, 8, 0, 31); + /* a0 = (a0 & ~0xff000000) | ((a1 r<< 24) & 0xff000000) # dcda */ + tcg_out_rlw(s, RLWIMI, a0, a1, 24, 0, 7); + /* a0 = (a0 & ~0x0000ff00) | ((a1 r<< 24) & 0x0000ff00) # dcba */ + tcg_out_rlw(s, RLWIMI, a0, a1, 24, 16, 23); + + if (a0 == TCG_REG_R0) { + tcg_out_mov(s, TCG_TYPE_I64, args[0], a0); + } + break; + + case INDEX_op_bswap64_i64: + a0 = args[0], a1 = args[1], a2 = 0; + if (a0 == a1) { + a0 = 0; + a2 = a1; + } + + /* a1 = # abcd efgh */ + /* a0 = rl32(a1, 8) # 0000 fghe */ + tcg_out_rlw(s, RLWINM, a0, a1, 8, 0, 31); + /* a0 = dep(a0, rl32(a1, 24), 0xff000000) # 0000 hghe */ + tcg_out_rlw(s, RLWIMI, a0, a1, 24, 0, 7); + /* a0 = dep(a0, rl32(a1, 24), 0x0000ff00) # 0000 hgfe */ + tcg_out_rlw(s, RLWIMI, a0, a1, 24, 16, 23); + + /* a0 = rl64(a0, 32) # hgfe 0000 */ + /* a2 = rl64(a1, 32) # efgh abcd */ + tcg_out_rld(s, RLDICL, a0, a0, 32, 0); + tcg_out_rld(s, RLDICL, a2, a1, 32, 0); + + /* a0 = dep(a0, rl32(a2, 8), 0xffffffff) # hgfe bcda */ + tcg_out_rlw(s, RLWIMI, a0, a2, 8, 0, 31); + /* a0 = dep(a0, rl32(a2, 24), 0xff000000) # hgfe dcda */ + tcg_out_rlw(s, RLWIMI, a0, a2, 24, 0, 7); + /* a0 = dep(a0, rl32(a2, 24), 0x0000ff00) # hgfe dcba */ + tcg_out_rlw(s, RLWIMI, a0, a2, 24, 16, 23); + + if (a0 == 0) { + tcg_out_mov(s, TCG_TYPE_I64, args[0], a0); + /* Revert the source rotate that we performed above. */ + tcg_out_rld(s, RLDICL, a1, a1, 32, 0); + } + break; + + case INDEX_op_deposit_i32: + if (const_args[2]) { + uint32_t mask = ((2u << (args[4] - 1)) - 1) << args[3]; + tcg_out_andi32(s, args[0], args[0], ~mask); + } else { + tcg_out_rlw(s, RLWIMI, args[0], args[2], args[3], + 32 - args[3] - args[4], 31 - args[3]); + } + break; + case INDEX_op_deposit_i64: + if (const_args[2]) { + uint64_t mask = ((2ull << (args[4] - 1)) - 1) << args[3]; + tcg_out_andi64(s, args[0], args[0], ~mask); + } else { + tcg_out_rld(s, RLDIMI, args[0], args[2], args[3], + 64 - args[3] - args[4]); + } + break; + + case INDEX_op_movcond_i32: + tcg_out_movcond(s, TCG_TYPE_I32, args[5], args[0], args[1], args[2], + args[3], args[4], const_args[2]); + break; + case INDEX_op_movcond_i64: + tcg_out_movcond(s, TCG_TYPE_I64, args[5], args[0], args[1], args[2], + args[3], args[4], const_args[2]); + break; + + case INDEX_op_add2_i64: + /* Note that the CA bit is defined based on the word size of the + environment. So in 64-bit mode it's always carry-out of bit 63. + The fallback code using deposit works just as well for 32-bit. */ + a0 = args[0], a1 = args[1]; + if (a0 == args[4] || (!const_args[5] && a0 == args[5])) { + a0 = TCG_REG_R0; + } + if (const_args[3]) { + tcg_out32(s, ADDIC | TAI(a0, args[2], args[3])); + } else { + tcg_out32(s, ADDC | TAB(a0, args[2], args[3])); + } + if (const_args[5]) { + tcg_out32(s, (args[5] ? ADDME : ADDZE) | RT(a1) | RA(args[4])); + } else { + tcg_out32(s, ADDE | TAB(a1, args[4], args[5])); + } + if (a0 != args[0]) { + tcg_out_mov(s, TCG_TYPE_I64, args[0], a0); + } + break; + + case INDEX_op_sub2_i64: + a0 = args[0], a1 = args[1]; + if (a0 == args[5] || (!const_args[4] && a0 == args[4])) { + a0 = TCG_REG_R0; + } + if (const_args[2]) { + tcg_out32(s, SUBFIC | TAI(a0, args[3], args[2])); + } else { + tcg_out32(s, SUBFC | TAB(a0, args[3], args[2])); + } + if (const_args[4]) { + tcg_out32(s, (args[4] ? SUBFME : SUBFZE) | RT(a1) | RA(args[5])); + } else { + tcg_out32(s, SUBFE | TAB(a1, args[5], args[4])); + } + if (a0 != args[0]) { + tcg_out_mov(s, TCG_TYPE_I64, args[0], a0); + } + break; + + case INDEX_op_mulu2_i64: + case INDEX_op_muls2_i64: + { + int oph = (opc == INDEX_op_mulu2_i64 ? MULHDU : MULHD); + TCGReg outl = args[0], outh = args[1]; + a0 = args[2], a1 = args[3]; + + if (outl == a0 || outl == a1) { + if (outh == a0 || outh == a1) { + outl = TCG_REG_R0; + } else { + tcg_out32(s, oph | TAB(outh, a0, a1)); + oph = 0; + } + } + tcg_out32(s, MULLD | TAB(outl, a0, a1)); + if (oph != 0) { + tcg_out32(s, oph | TAB(outh, a0, a1)); + } + if (outl != args[0]) { + tcg_out_mov(s, TCG_TYPE_I64, args[0], outl); + } + } + break; + default: tcg_dump_ops (s); tcg_abort (); @@ -1608,19 +2062,26 @@ static const TCGTargetOpDef ppc_op_defs[] = { { INDEX_op_ld32s_i64, { "r", "r" } }, { INDEX_op_add_i32, { "r", "r", "ri" } }, - { INDEX_op_mul_i32, { "r", "r", "ri" } }, + { INDEX_op_mul_i32, { "r", "r", "rI" } }, { INDEX_op_div_i32, { "r", "r", "r" } }, { INDEX_op_divu_i32, { "r", "r", "r" } }, { INDEX_op_rem_i32, { "r", "r", "r" } }, { INDEX_op_remu_i32, { "r", "r", "r" } }, - { INDEX_op_sub_i32, { "r", "r", "ri" } }, + { INDEX_op_sub_i32, { "r", "rI", "ri" } }, { INDEX_op_and_i32, { "r", "r", "ri" } }, { INDEX_op_or_i32, { "r", "r", "ri" } }, { INDEX_op_xor_i32, { "r", "r", "ri" } }, + { INDEX_op_andc_i32, { "r", "r", "ri" } }, + { INDEX_op_orc_i32, { "r", "r", "ri" } }, + { INDEX_op_eqv_i32, { "r", "r", "ri" } }, + { INDEX_op_nand_i32, { "r", "r", "r" } }, + { INDEX_op_nor_i32, { "r", "r", "r" } }, { INDEX_op_shl_i32, { "r", "r", "ri" } }, { INDEX_op_shr_i32, { "r", "r", "ri" } }, { INDEX_op_sar_i32, { "r", "r", "ri" } }, + { INDEX_op_rotl_i32, { "r", "r", "ri" } }, + { INDEX_op_rotr_i32, { "r", "r", "ri" } }, { INDEX_op_brcond_i32, { "r", "ri" } }, { INDEX_op_brcond_i64, { "r", "ri" } }, @@ -1628,17 +2089,24 @@ static const TCGTargetOpDef ppc_op_defs[] = { { INDEX_op_neg_i32, { "r", "r" } }, { INDEX_op_not_i32, { "r", "r" } }, - { INDEX_op_add_i64, { "r", "r", "ri" } }, - { INDEX_op_sub_i64, { "r", "r", "ri" } }, - { INDEX_op_and_i64, { "r", "r", "rZ" } }, - { INDEX_op_or_i64, { "r", "r", "rZ" } }, - { INDEX_op_xor_i64, { "r", "r", "rZ" } }, + { INDEX_op_add_i64, { "r", "r", "rT" } }, + { INDEX_op_sub_i64, { "r", "rI", "rT" } }, + { INDEX_op_and_i64, { "r", "r", "ri" } }, + { INDEX_op_or_i64, { "r", "r", "rU" } }, + { INDEX_op_xor_i64, { "r", "r", "rU" } }, + { INDEX_op_andc_i64, { "r", "r", "ri" } }, + { INDEX_op_orc_i64, { "r", "r", "r" } }, + { INDEX_op_eqv_i64, { "r", "r", "r" } }, + { INDEX_op_nand_i64, { "r", "r", "r" } }, + { INDEX_op_nor_i64, { "r", "r", "r" } }, { INDEX_op_shl_i64, { "r", "r", "ri" } }, { INDEX_op_shr_i64, { "r", "r", "ri" } }, { INDEX_op_sar_i64, { "r", "r", "ri" } }, + { INDEX_op_rotl_i64, { "r", "r", "ri" } }, + { INDEX_op_rotr_i64, { "r", "r", "ri" } }, - { INDEX_op_mul_i64, { "r", "r", "r" } }, + { INDEX_op_mul_i64, { "r", "r", "rI" } }, { INDEX_op_div_i64, { "r", "r", "r" } }, { INDEX_op_divu_i64, { "r", "r", "r" } }, { INDEX_op_rem_i64, { "r", "r", "r" } }, @@ -1666,16 +2134,38 @@ static const TCGTargetOpDef ppc_op_defs[] = { { INDEX_op_ext8s_i64, { "r", "r" } }, { INDEX_op_ext16s_i64, { "r", "r" } }, { INDEX_op_ext32s_i64, { "r", "r" } }, - { INDEX_op_ext32u_i64, { "r", "r" } }, { INDEX_op_setcond_i32, { "r", "r", "ri" } }, { INDEX_op_setcond_i64, { "r", "r", "ri" } }, + { INDEX_op_movcond_i32, { "r", "r", "ri", "rZ", "rZ" } }, + { INDEX_op_movcond_i64, { "r", "r", "ri", "rZ", "rZ" } }, + + { INDEX_op_bswap16_i32, { "r", "r" } }, + { INDEX_op_bswap16_i64, { "r", "r" } }, + { INDEX_op_bswap32_i32, { "r", "r" } }, + { INDEX_op_bswap32_i64, { "r", "r" } }, + { INDEX_op_bswap64_i64, { "r", "r" } }, + + { INDEX_op_deposit_i32, { "r", "0", "rZ" } }, + { INDEX_op_deposit_i64, { "r", "0", "rZ" } }, + + { INDEX_op_add2_i64, { "r", "r", "r", "rI", "r", "rZM" } }, + { INDEX_op_sub2_i64, { "r", "r", "rI", "r", "rZM", "r" } }, + { INDEX_op_muls2_i64, { "r", "r", "r", "r" } }, + { INDEX_op_mulu2_i64, { "r", "r", "r", "r" } }, { -1 }, }; static void tcg_target_init (TCGContext *s) { +#ifdef CONFIG_GETAUXVAL + unsigned long hwcap = getauxval(AT_HWCAP); + if (hwcap & PPC_FEATURE_ARCH_2_06) { + have_isa_2_06 = true; + } +#endif + tcg_regset_set32 (tcg_target_available_regs[TCG_TYPE_I32], 0, 0xffffffff); tcg_regset_set32 (tcg_target_available_regs[TCG_TYPE_I64], 0, 0xffffffff); tcg_regset_set32 (tcg_target_call_clobber_regs, 0, diff --git a/tcg/ppc64/tcg-target.h b/tcg/ppc64/tcg-target.h index aa6a0f0..cb77634 100644 --- a/tcg/ppc64/tcg-target.h +++ b/tcg/ppc64/tcg-target.h @@ -67,53 +67,55 @@ typedef enum { #define TCG_TARGET_STACK_ALIGN 16 #define TCG_TARGET_CALL_STACK_OFFSET 48 +/* optional instructions automatically implemented */ +#define TCG_TARGET_HAS_ext8u_i32 0 /* andi */ +#define TCG_TARGET_HAS_ext16u_i32 0 +#define TCG_TARGET_HAS_ext8u_i64 0 +#define TCG_TARGET_HAS_ext16u_i64 0 +#define TCG_TARGET_HAS_ext32u_i64 0 + /* optional instructions */ #define TCG_TARGET_HAS_div_i32 1 -#define TCG_TARGET_HAS_rot_i32 0 +#define TCG_TARGET_HAS_rot_i32 1 #define TCG_TARGET_HAS_ext8s_i32 1 #define TCG_TARGET_HAS_ext16s_i32 1 -#define TCG_TARGET_HAS_ext8u_i32 0 -#define TCG_TARGET_HAS_ext16u_i32 0 -#define TCG_TARGET_HAS_bswap16_i32 0 -#define TCG_TARGET_HAS_bswap32_i32 0 +#define TCG_TARGET_HAS_bswap16_i32 1 +#define TCG_TARGET_HAS_bswap32_i32 1 #define TCG_TARGET_HAS_not_i32 1 #define TCG_TARGET_HAS_neg_i32 1 -#define TCG_TARGET_HAS_andc_i32 0 -#define TCG_TARGET_HAS_orc_i32 0 -#define TCG_TARGET_HAS_eqv_i32 0 -#define TCG_TARGET_HAS_nand_i32 0 -#define TCG_TARGET_HAS_nor_i32 0 -#define TCG_TARGET_HAS_deposit_i32 0 -#define TCG_TARGET_HAS_movcond_i32 0 +#define TCG_TARGET_HAS_andc_i32 1 +#define TCG_TARGET_HAS_orc_i32 1 +#define TCG_TARGET_HAS_eqv_i32 1 +#define TCG_TARGET_HAS_nand_i32 1 +#define TCG_TARGET_HAS_nor_i32 1 +#define TCG_TARGET_HAS_deposit_i32 1 +#define TCG_TARGET_HAS_movcond_i32 1 #define TCG_TARGET_HAS_add2_i32 0 #define TCG_TARGET_HAS_sub2_i32 0 #define TCG_TARGET_HAS_mulu2_i32 0 #define TCG_TARGET_HAS_muls2_i32 0 #define TCG_TARGET_HAS_div_i64 1 -#define TCG_TARGET_HAS_rot_i64 0 +#define TCG_TARGET_HAS_rot_i64 1 #define TCG_TARGET_HAS_ext8s_i64 1 #define TCG_TARGET_HAS_ext16s_i64 1 #define TCG_TARGET_HAS_ext32s_i64 1 -#define TCG_TARGET_HAS_ext8u_i64 0 -#define TCG_TARGET_HAS_ext16u_i64 0 -#define TCG_TARGET_HAS_ext32u_i64 1 -#define TCG_TARGET_HAS_bswap16_i64 0 -#define TCG_TARGET_HAS_bswap32_i64 0 -#define TCG_TARGET_HAS_bswap64_i64 0 +#define TCG_TARGET_HAS_bswap16_i64 1 +#define TCG_TARGET_HAS_bswap32_i64 1 +#define TCG_TARGET_HAS_bswap64_i64 1 #define TCG_TARGET_HAS_not_i64 1 #define TCG_TARGET_HAS_neg_i64 1 -#define TCG_TARGET_HAS_andc_i64 0 -#define TCG_TARGET_HAS_orc_i64 0 -#define TCG_TARGET_HAS_eqv_i64 0 -#define TCG_TARGET_HAS_nand_i64 0 -#define TCG_TARGET_HAS_nor_i64 0 -#define TCG_TARGET_HAS_deposit_i64 0 -#define TCG_TARGET_HAS_movcond_i64 0 -#define TCG_TARGET_HAS_add2_i64 0 -#define TCG_TARGET_HAS_sub2_i64 0 -#define TCG_TARGET_HAS_mulu2_i64 0 -#define TCG_TARGET_HAS_muls2_i64 0 +#define TCG_TARGET_HAS_andc_i64 1 +#define TCG_TARGET_HAS_orc_i64 1 +#define TCG_TARGET_HAS_eqv_i64 1 +#define TCG_TARGET_HAS_nand_i64 1 +#define TCG_TARGET_HAS_nor_i64 1 +#define TCG_TARGET_HAS_deposit_i64 1 +#define TCG_TARGET_HAS_movcond_i64 1 +#define TCG_TARGET_HAS_add2_i64 1 +#define TCG_TARGET_HAS_sub2_i64 1 +#define TCG_TARGET_HAS_mulu2_i64 1 +#define TCG_TARGET_HAS_muls2_i64 1 #define TCG_AREG0 TCG_REG_R27 diff --git a/tests/qemu-iotests/002 b/tests/qemu-iotests/002 index bebed84..51d0a8f 100755 --- a/tests/qemu-iotests/002 +++ b/tests/qemu-iotests/002 @@ -61,10 +61,23 @@ $QEMU_IO -c "read -pP 0xa 0 $size" $TEST_IMG | _filter_qemu_io echo echo "unaligned pwrite" $QEMU_IO -c 'write -pP 0xab 66 42' $TEST_IMG | _filter_qemu_io +$QEMU_IO -c 'write -pP 0xac 512 288' $TEST_IMG | _filter_qemu_io +$QEMU_IO -c 'write -pP 0xad 800 224' $TEST_IMG | _filter_qemu_io +$QEMU_IO -c 'write -pP 0xae 66000 128k' $TEST_IMG | _filter_qemu_io +$QEMU_IO -c 'write -pP 0xaf 256k 42' $TEST_IMG | _filter_qemu_io echo echo "verify pattern" +$QEMU_IO -c 'read -pP 0xa 0 66' $TEST_IMG | _filter_qemu_io $QEMU_IO -c 'read -pP 0xab 66 42' $TEST_IMG | _filter_qemu_io +$QEMU_IO -c 'read -pP 0xa 108 404' $TEST_IMG | _filter_qemu_io +$QEMU_IO -c 'read -pP 0xac 512 288' $TEST_IMG | _filter_qemu_io +$QEMU_IO -c 'read -pP 0xad 800 224' $TEST_IMG | _filter_qemu_io +$QEMU_IO -c 'read -pP 0xa 1k 64976' $TEST_IMG | _filter_qemu_io +$QEMU_IO -c 'read -pP 0xae 66000 128k' $TEST_IMG | _filter_qemu_io +$QEMU_IO -c 'read -pP 0xa 197072 65072' $TEST_IMG | _filter_qemu_io +$QEMU_IO -c 'read -pP 0xaf 256k 42' $TEST_IMG | _filter_qemu_io +$QEMU_IO -c 'read -pP 0xa 262186 470' $TEST_IMG | _filter_qemu_io # success, all done echo "*** done" diff --git a/tests/qemu-iotests/002.out b/tests/qemu-iotests/002.out index 75f5876..cd6aa0f 100644 --- a/tests/qemu-iotests/002.out +++ b/tests/qemu-iotests/002.out @@ -16,8 +16,34 @@ read 134217728/134217728 bytes at offset 0 unaligned pwrite wrote 42/42 bytes at offset 66 42 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +wrote 288/288 bytes at offset 512 +288 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +wrote 224/224 bytes at offset 800 +224 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +wrote 131072/131072 bytes at offset 66000 +128 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +wrote 42/42 bytes at offset 262144 +42 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) verify pattern +read 66/66 bytes at offset 0 +66 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) read 42/42 bytes at offset 66 42 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +read 404/404 bytes at offset 108 +404 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +read 288/288 bytes at offset 512 +288 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +read 224/224 bytes at offset 800 +224 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +read 64976/64976 bytes at offset 1024 +63.453 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +read 131072/131072 bytes at offset 66000 +128 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +read 65072/65072 bytes at offset 197072 +63.547 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +read 42/42 bytes at offset 262144 +42 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +read 470/470 bytes at offset 262186 +470 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) *** done diff --git a/tests/qemu-iotests/051 b/tests/qemu-iotests/051 new file mode 100755 index 0000000..8b51de3 --- /dev/null +++ b/tests/qemu-iotests/051 @@ -0,0 +1,148 @@ +#!/bin/bash +# +# Test command line configuration of block devices and driver-specific options +# +# Copyright (C) 2013 Red Hat, Inc. +# +# This program 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. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. +# + +# creator +owner=kwolf@redhat.com + +seq=`basename $0` +echo "QA output created by $seq" + +here=`pwd` +tmp=/tmp/$$ +status=1 # failure is the default! + +_cleanup() +{ + _cleanup_test_img +} +trap "_cleanup; exit \$status" 0 1 2 3 15 + +# get standard environment, filters and checks +. ./common.rc +. ./common.filter + +_supported_fmt qcow2 +_supported_proto file +_supported_os Linux + +function do_run_qemu() +{ + echo Testing: "$@" + echo quit | $QEMU -nographic -monitor stdio -serial none "$@" + echo +} + +function run_qemu() +{ + do_run_qemu "$@" 2>&1 | _filter_testdir | _filter_qemu +} + +size=128M + +_make_test_img $size + +echo +echo === Unknown option === +echo + +run_qemu -drive file=$TEST_IMG,format=qcow2,unknown_opt= +run_qemu -drive file=$TEST_IMG,format=qcow2,unknown_opt=on +run_qemu -drive file=$TEST_IMG,format=qcow2,unknown_opt=1234 +run_qemu -drive file=$TEST_IMG,format=qcow2,unknown_opt=foo + + +echo +echo === Enable and disable lazy refcounting on the command line, plus some invalid values === +echo + +run_qemu -drive file=$TEST_IMG,format=qcow2,lazy_refcounts=on +run_qemu -drive file=$TEST_IMG,format=qcow2,lazy_refcounts=off +run_qemu -drive file=$TEST_IMG,format=qcow2,lazy_refcounts= +run_qemu -drive file=$TEST_IMG,format=qcow2,lazy_refcounts=42 +run_qemu -drive file=$TEST_IMG,format=qcow2,lazy_refcounts=foo + + +echo +echo === With version 2 images enabling lazy refcounts must fail === +echo + +_make_test_img -ocompat=0.10 $size + +run_qemu -drive file=$TEST_IMG,format=qcow2,lazy_refcounts=on +run_qemu -drive file=$TEST_IMG,format=qcow2,lazy_refcounts=off + +echo +echo === No medium === +echo + +run_qemu -drive if=floppy +run_qemu -drive if=ide,media=cdrom +run_qemu -drive if=scsi,media=cdrom + +run_qemu -drive if=ide +run_qemu -drive if=virtio +run_qemu -drive if=scsi + +run_qemu -drive if=none,id=disk -device ide-cd,drive=disk +run_qemu -drive if=none,id=disk -device lsi53c895a -device scsi-cd,drive=disk + +run_qemu -drive if=none,id=disk -device ide-drive,drive=disk +run_qemu -drive if=none,id=disk -device ide-hd,drive=disk +run_qemu -drive if=none,id=disk -device lsi53c895a -device scsi-disk,drive=disk +run_qemu -drive if=none,id=disk -device lsi53c895a -device scsi-hd,drive=disk + +echo +echo === Read-only === +echo + +run_qemu -drive file=$TEST_IMG,if=floppy,readonly=on +run_qemu -drive file=$TEST_IMG,if=ide,media=cdrom,readonly=on +run_qemu -drive file=$TEST_IMG,if=scsi,media=cdrom,readonly=on + +run_qemu -drive file=$TEST_IMG,if=ide,readonly=on +run_qemu -drive file=$TEST_IMG,if=virtio,readonly=on +run_qemu -drive file=$TEST_IMG,if=scsi,readonly=on + +run_qemu -drive file=$TEST_IMG,if=none,id=disk,readonly=on -device ide-cd,drive=disk +run_qemu -drive file=$TEST_IMG,if=none,id=disk,readonly=on -device lsi53c895a -device scsi-cd,drive=disk + +run_qemu -drive file=$TEST_IMG,if=none,id=disk,readonly=on -device ide-drive,drive=disk +run_qemu -drive file=$TEST_IMG,if=none,id=disk,readonly=on -device ide-hd,drive=disk +run_qemu -drive file=$TEST_IMG,if=none,id=disk,readonly=on -device lsi53c895a -device scsi-disk,drive=disk +run_qemu -drive file=$TEST_IMG,if=none,id=disk,readonly=on -device lsi53c895a -device scsi-hd,drive=disk + +echo +echo === Cache modes === +echo + +# Cannot use the test image because cache=none might not work on the host FS +# Use cdrom so that we won't get errors about missing media + +run_qemu -drive media=cdrom,cache=none +run_qemu -drive media=cdrom,cache=directsync +run_qemu -drive media=cdrom,cache=writeback +run_qemu -drive media=cdrom,cache=writethrough +run_qemu -drive media=cdrom,cache=unsafe +run_qemu -drive media=cdrom,cache=invalid_value + +# success, all done +echo "*** done" +rm -f $seq.full +status=0 diff --git a/tests/qemu-iotests/051.out b/tests/qemu-iotests/051.out new file mode 100644 index 0000000..48456d5 --- /dev/null +++ b/tests/qemu-iotests/051.out @@ -0,0 +1,162 @@ +QA output created by 051 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728 + +=== Unknown option === + +Testing: -drive file=TEST_DIR/t.qcow2,format=qcow2,unknown_opt= +QEMU_PROG: -drive file=TEST_DIR/t.qcow2,format=qcow2,unknown_opt=: Block format 'qcow2' used by device 'ide0-hd0' doesn't support the option 'unknown_opt' +QEMU_PROG: -drive file=TEST_DIR/t.qcow2,format=qcow2,unknown_opt=: could not open disk image TEST_DIR/t.qcow2: Invalid argument + +Testing: -drive file=TEST_DIR/t.qcow2,format=qcow2,unknown_opt=on +QEMU_PROG: -drive file=TEST_DIR/t.qcow2,format=qcow2,unknown_opt=on: Block format 'qcow2' used by device 'ide0-hd0' doesn't support the option 'unknown_opt' +QEMU_PROG: -drive file=TEST_DIR/t.qcow2,format=qcow2,unknown_opt=on: could not open disk image TEST_DIR/t.qcow2: Invalid argument + +Testing: -drive file=TEST_DIR/t.qcow2,format=qcow2,unknown_opt=1234 +QEMU_PROG: -drive file=TEST_DIR/t.qcow2,format=qcow2,unknown_opt=1234: Block format 'qcow2' used by device 'ide0-hd0' doesn't support the option 'unknown_opt' +QEMU_PROG: -drive file=TEST_DIR/t.qcow2,format=qcow2,unknown_opt=1234: could not open disk image TEST_DIR/t.qcow2: Invalid argument + +Testing: -drive file=TEST_DIR/t.qcow2,format=qcow2,unknown_opt=foo +QEMU_PROG: -drive file=TEST_DIR/t.qcow2,format=qcow2,unknown_opt=foo: Block format 'qcow2' used by device 'ide0-hd0' doesn't support the option 'unknown_opt' +QEMU_PROG: -drive file=TEST_DIR/t.qcow2,format=qcow2,unknown_opt=foo: could not open disk image TEST_DIR/t.qcow2: Invalid argument + + +=== Enable and disable lazy refcounting on the command line, plus some invalid values === + +Testing: -drive file=TEST_DIR/t.qcow2,format=qcow2,lazy_refcounts=on +q[K[Dqu[K[D[Dqui[K[D[D[Dquit[K + +Testing: -drive file=TEST_DIR/t.qcow2,format=qcow2,lazy_refcounts=off +q[K[Dqu[K[D[Dqui[K[D[D[Dquit[K + +Testing: -drive file=TEST_DIR/t.qcow2,format=qcow2,lazy_refcounts= +QEMU_PROG: -drive file=TEST_DIR/t.qcow2,format=qcow2,lazy_refcounts=: Parameter 'lazy_refcounts' expects 'on' or 'off' +QEMU_PROG: -drive file=TEST_DIR/t.qcow2,format=qcow2,lazy_refcounts=: could not open disk image TEST_DIR/t.qcow2: Invalid argument + +Testing: -drive file=TEST_DIR/t.qcow2,format=qcow2,lazy_refcounts=42 +QEMU_PROG: -drive file=TEST_DIR/t.qcow2,format=qcow2,lazy_refcounts=42: Parameter 'lazy_refcounts' expects 'on' or 'off' +QEMU_PROG: -drive file=TEST_DIR/t.qcow2,format=qcow2,lazy_refcounts=42: could not open disk image TEST_DIR/t.qcow2: Invalid argument + +Testing: -drive file=TEST_DIR/t.qcow2,format=qcow2,lazy_refcounts=foo +QEMU_PROG: -drive file=TEST_DIR/t.qcow2,format=qcow2,lazy_refcounts=foo: Parameter 'lazy_refcounts' expects 'on' or 'off' +QEMU_PROG: -drive file=TEST_DIR/t.qcow2,format=qcow2,lazy_refcounts=foo: could not open disk image TEST_DIR/t.qcow2: Invalid argument + + +=== With version 2 images enabling lazy refcounts must fail === + +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728 +Testing: -drive file=TEST_DIR/t.qcow2,format=qcow2,lazy_refcounts=on +QEMU_PROG: -drive file=TEST_DIR/t.qcow2,format=qcow2,lazy_refcounts=on: Lazy refcounts require a qcow2 image with at least qemu 1.1 compatibility level +QEMU_PROG: -drive file=TEST_DIR/t.qcow2,format=qcow2,lazy_refcounts=on: could not open disk image TEST_DIR/t.qcow2: Invalid argument + +Testing: -drive file=TEST_DIR/t.qcow2,format=qcow2,lazy_refcounts=off +q[K[Dqu[K[D[Dqui[K[D[D[Dquit[K + + +=== No medium === + +Testing: -drive if=floppy +q[K[Dqu[K[D[Dqui[K[D[D[Dquit[K + +Testing: -drive if=ide,media=cdrom +q[K[Dqu[K[D[Dqui[K[D[D[Dquit[K + +Testing: -drive if=scsi,media=cdrom +q[K[Dqu[K[D[Dqui[K[D[D[Dquit[K + +Testing: -drive if=ide +QEMU_PROG: Device needs media, but drive is empty +QEMU_PROG: Initialization of device ide-hd failed + +Testing: -drive if=virtio +QEMU_PROG: -drive if=virtio: Device needs media, but drive is empty +QEMU_PROG: -drive if=virtio: Device 'virtio-blk-pci' could not be initialized + +Testing: -drive if=scsi +QEMU_PROG: -drive if=scsi: Device needs media, but drive is empty +QEMU_PROG: Initialization of device lsi53c895a failed + +Testing: -drive if=none,id=disk -device ide-cd,drive=disk +q[K[Dqu[K[D[Dqui[K[D[D[Dquit[K + +Testing: -drive if=none,id=disk -device lsi53c895a -device scsi-cd,drive=disk +q[K[Dqu[K[D[Dqui[K[D[D[Dquit[K + +Testing: -drive if=none,id=disk -device ide-drive,drive=disk +QEMU_PROG: -device ide-drive,drive=disk: Device needs media, but drive is empty +QEMU_PROG: -device ide-drive,drive=disk: Device 'ide-drive' could not be initialized + +Testing: -drive if=none,id=disk -device ide-hd,drive=disk +QEMU_PROG: -device ide-hd,drive=disk: Device needs media, but drive is empty +QEMU_PROG: -device ide-hd,drive=disk: Device 'ide-hd' could not be initialized + +Testing: -drive if=none,id=disk -device lsi53c895a -device scsi-disk,drive=disk +QEMU_PROG: -device scsi-disk,drive=disk: Device needs media, but drive is empty +QEMU_PROG: -device scsi-disk,drive=disk: Device 'scsi-disk' could not be initialized + +Testing: -drive if=none,id=disk -device lsi53c895a -device scsi-hd,drive=disk +QEMU_PROG: -device scsi-hd,drive=disk: Device needs media, but drive is empty +QEMU_PROG: -device scsi-hd,drive=disk: Device 'scsi-hd' could not be initialized + + +=== Read-only === + +Testing: -drive file=TEST_DIR/t.qcow2,if=floppy,readonly=on +q[K[Dqu[K[D[Dqui[K[D[D[Dquit[K + +Testing: -drive file=TEST_DIR/t.qcow2,if=ide,media=cdrom,readonly=on +q[K[Dqu[K[D[Dqui[K[D[D[Dquit[K + +Testing: -drive file=TEST_DIR/t.qcow2,if=scsi,media=cdrom,readonly=on +q[K[Dqu[K[D[Dqui[K[D[D[Dquit[K + +Testing: -drive file=TEST_DIR/t.qcow2,if=ide,readonly=on +QEMU_PROG: -drive file=TEST_DIR/t.qcow2,if=ide,readonly=on: readonly not supported by this bus type + +Testing: -drive file=TEST_DIR/t.qcow2,if=virtio,readonly=on +q[K[Dqu[K[D[Dqui[K[D[D[Dquit[K + +Testing: -drive file=TEST_DIR/t.qcow2,if=scsi,readonly=on +q[K[Dqu[K[D[Dqui[K[D[D[Dquit[K + +Testing: -drive file=TEST_DIR/t.qcow2,if=none,id=disk,readonly=on -device ide-cd,drive=disk +q[K[Dqu[K[D[Dqui[K[D[D[Dquit[K + +Testing: -drive file=TEST_DIR/t.qcow2,if=none,id=disk,readonly=on -device lsi53c895a -device scsi-cd,drive=disk +q[K[Dqu[K[D[Dqui[K[D[D[Dquit[K + +Testing: -drive file=TEST_DIR/t.qcow2,if=none,id=disk,readonly=on -device ide-drive,drive=disk +QEMU_PROG: -device ide-drive,drive=disk: Can't use a read-only drive +QEMU_PROG: -device ide-drive,drive=disk: Device 'ide-drive' could not be initialized + +Testing: -drive file=TEST_DIR/t.qcow2,if=none,id=disk,readonly=on -device ide-hd,drive=disk +QEMU_PROG: -device ide-hd,drive=disk: Can't use a read-only drive +QEMU_PROG: -device ide-hd,drive=disk: Device 'ide-hd' could not be initialized + +Testing: -drive file=TEST_DIR/t.qcow2,if=none,id=disk,readonly=on -device lsi53c895a -device scsi-disk,drive=disk +q[K[Dqu[K[D[Dqui[K[D[D[Dquit[K + +Testing: -drive file=TEST_DIR/t.qcow2,if=none,id=disk,readonly=on -device lsi53c895a -device scsi-hd,drive=disk +q[K[Dqu[K[D[Dqui[K[D[D[Dquit[K + + +=== Cache modes === + +Testing: -drive media=cdrom,cache=none +q[K[Dqu[K[D[Dqui[K[D[D[Dquit[K + +Testing: -drive media=cdrom,cache=directsync +q[K[Dqu[K[D[Dqui[K[D[D[Dquit[K + +Testing: -drive media=cdrom,cache=writeback +q[K[Dqu[K[D[Dqui[K[D[D[Dquit[K + +Testing: -drive media=cdrom,cache=writethrough +q[K[Dqu[K[D[Dqui[K[D[D[Dquit[K + +Testing: -drive media=cdrom,cache=unsafe +q[K[Dqu[K[D[Dqui[K[D[D[Dquit[K + +Testing: -drive media=cdrom,cache=invalid_value +QEMU_PROG: -drive media=cdrom,cache=invalid_value: invalid cache option + +*** done diff --git a/tests/qemu-iotests/common b/tests/qemu-iotests/common index b3aad89..6826ea7 100644 --- a/tests/qemu-iotests/common +++ b/tests/qemu-iotests/common @@ -137,6 +137,7 @@ check options -rbd test rbd -sheepdog test sheepdog -nbd test nbd + -ssh test ssh -xdiff graphical mode diff -nocache use O_DIRECT on backing file -misalign misalign memory allocations @@ -206,6 +207,10 @@ testlist options IMGPROTO=nbd xpand=false ;; + -ssh) + IMGPROTO=ssh + xpand=false + ;; -nocache) QEMU_IO_OPTIONS="$QEMU_IO_OPTIONS --nocache" xpand=false diff --git a/tests/qemu-iotests/common.filter b/tests/qemu-iotests/common.filter index fa26b62..bc5f250 100644 --- a/tests/qemu-iotests/common.filter +++ b/tests/qemu-iotests/common.filter @@ -152,5 +152,11 @@ _filter_qemu_io() _filter_win32 | sed -e "s/[0-9]* ops\; [0-9/:. sec]* ([0-9/.inf]* [EPTGMKiBbytes]*\/sec and [0-9/.inf]* ops\/sec)/X ops\; XX:XX:XX.X (XXX YYY\/sec and XXX ops\/sec)/" } +# replace occurrences of QEMU_PROG with "qemu" +_filter_qemu() +{ + sed -e "s#$(basename $QEMU_PROG)#QEMU_PROG#g" +} + # make sure this script returns success /bin/true diff --git a/tests/qemu-iotests/common.rc b/tests/qemu-iotests/common.rc index e522d61..a536bf7 100644 --- a/tests/qemu-iotests/common.rc +++ b/tests/qemu-iotests/common.rc @@ -52,6 +52,9 @@ if [ "$IMGPROTO" = "file" ]; then elif [ "$IMGPROTO" = "nbd" ]; then TEST_IMG_FILE=$TEST_DIR/t.$IMGFMT TEST_IMG="nbd:127.0.0.1:10810" +elif [ "$IMGPROTO" = "ssh" ]; then + TEST_IMG_FILE=$TEST_DIR/t.$IMGFMT + TEST_IMG="ssh://127.0.0.1$TEST_IMG_FILE" else TEST_IMG=$IMGPROTO:$TEST_DIR/t.$IMGFMT fi diff --git a/tests/qemu-iotests/group b/tests/qemu-iotests/group index 73c5966..324bacb 100644 --- a/tests/qemu-iotests/group +++ b/tests/qemu-iotests/group @@ -57,4 +57,5 @@ 048 img auto quick 049 rw auto 050 rw auto backing quick +051 rw auto 052 rw auto backing diff --git a/tests/tcg/mips/mips32-dsp/maq_sa_w_phl.c b/tests/tcg/mips/mips32-dsp/maq_sa_w_phl.c index a756991..d83dce6 100644 --- a/tests/tcg/mips/mips32-dsp/maq_sa_w_phl.c +++ b/tests/tcg/mips/mips32-dsp/maq_sa_w_phl.c @@ -10,12 +10,12 @@ int main() int resulth, resultl; int resdsp; - achi = 0x05; - acli = 0xB4CB; + achi = 0x00000000; + acli = 0x0000B4CB; rs = 0xFF060000; rt = 0xCB000000; - resulth = 0x00; - resultl = 0x7FFFFFFF; + resulth = 0x00000000; + resultl = 0x006838CB; __asm ("mthi %2, $ac1\n\t" @@ -29,8 +29,8 @@ int main() assert(resulth == acho); assert(resultl == aclo); - achi = 0x06; - acli = 0xB4CB; + achi = 0x00000000; + acli = 0x0000B4CB; rs = 0x80000000; rt = 0x80000000; resulth = 0x00; diff --git a/tests/tcg/mips/mips32-dsp/maq_sa_w_phr.c b/tests/tcg/mips/mips32-dsp/maq_sa_w_phr.c index d6498f8..d233111 100644 --- a/tests/tcg/mips/mips32-dsp/maq_sa_w_phr.c +++ b/tests/tcg/mips/mips32-dsp/maq_sa_w_phr.c @@ -10,12 +10,12 @@ int main() int resulth, resultl; int resdsp; - achi = 0x05; - acli = 0xB4CB; - rs = 0xFF06; - rt = 0xCB00; - resulth = 0x00; - resultl = 0x7FFFFFFF; + achi = 0x00000000; + acli = 0x0000B4CB; + rs = 0x0000FF06; + rt = 0x0000CB00; + resulth = 0x00000000; + resultl = 0x006838CB; __asm ("mthi %2, $ac1\n\t" @@ -29,12 +29,12 @@ int main() assert(resulth == acho); assert(resultl == aclo); - achi = 0x06; - acli = 0xB4CB; - rs = 0x8000; - rt = 0x8000; - resulth = 0x00; - resultl = 0x7fffffff; + achi = 0x00000000; + acli = 0x0000B4CB; + rs = 0x00008000; + rt = 0x00008000; + resulth = 0x00000000; + resultl = 0x7FFFFFFF; resdsp = 0x01; __asm @@ -662,11 +662,6 @@ StatusInfo *qmp_query_status(Error **errp) return info; } -int64_t qmp_query_cpu_max(Error **errp) -{ - return current_machine->max_cpus; -} - /***********************************************************/ /* real time host monotonic timer */ @@ -1617,6 +1612,7 @@ MachineInfoList *qmp_query_machines(Error **errp) } info->name = g_strdup(m->name); + info->cpu_max = !m->max_cpus ? 1 : m->max_cpus; entry = g_malloc0(sizeof(*entry)); entry->value = info; @@ -4131,6 +4127,10 @@ int main(int argc, char **argv, char **envp) configure_accelerator(); + if (!qtest_enabled() && qtest_chrdev) { + qtest_init(); + } + machine_opts = qemu_opts_find(qemu_find_opts("machine"), 0); if (machine_opts) { kernel_filename = qemu_opt_get(machine_opts, "kernel"); |