diff options
42 files changed, 875 insertions, 499 deletions
diff --git a/block/iscsi.c b/block/iscsi.c index 6f87605e72..84aa22a62e 100644 --- a/block/iscsi.c +++ b/block/iscsi.c @@ -26,6 +26,7 @@ #include "config-host.h" #include <poll.h> +#include <math.h> #include <arpa/inet.h> #include "qemu-common.h" #include "qemu/config-file.h" @@ -64,6 +65,7 @@ typedef struct IscsiLun { unsigned char *zeroblock; unsigned long *allocationmap; int cluster_sectors; + bool use_16_for_rw; } IscsiLun; typedef struct IscsiTask { @@ -75,6 +77,7 @@ typedef struct IscsiTask { Coroutine *co; QEMUBH *bh; IscsiLun *iscsilun; + QEMUTimer retry_timer; } IscsiTask; typedef struct IscsiAIOCB { @@ -86,7 +89,6 @@ typedef struct IscsiAIOCB { uint8_t *buf; int status; int canceled; - int retries; int64_t sector_num; int nb_sectors; #ifdef __linux__ @@ -96,7 +98,8 @@ typedef struct IscsiAIOCB { #define NOP_INTERVAL 5000 #define MAX_NOP_FAILURES 3 -#define ISCSI_CMD_RETRIES 5 +#define ISCSI_CMD_RETRIES ARRAY_SIZE(iscsi_retry_times) +static const unsigned iscsi_retry_times[] = {8, 32, 128, 512, 2048}; /* this threshhold is a trade-off knob to choose between * the potential additional overhead of an extra GET_LBA_STATUS request @@ -142,10 +145,25 @@ iscsi_schedule_bh(IscsiAIOCB *acb) static void iscsi_co_generic_bh_cb(void *opaque) { struct IscsiTask *iTask = opaque; + iTask->complete = 1; qemu_bh_delete(iTask->bh); qemu_coroutine_enter(iTask->co, NULL); } +static void iscsi_retry_timer_expired(void *opaque) +{ + struct IscsiTask *iTask = opaque; + iTask->complete = 1; + if (iTask->co) { + qemu_coroutine_enter(iTask->co, NULL); + } +} + +static inline unsigned exp_random(double mean) +{ + return -mean * log((double)rand() / RAND_MAX); +} + static void iscsi_co_generic_cb(struct iscsi_context *iscsi, int status, void *command_data, void *opaque) @@ -153,19 +171,34 @@ iscsi_co_generic_cb(struct iscsi_context *iscsi, int status, struct IscsiTask *iTask = opaque; struct scsi_task *task = command_data; - iTask->complete = 1; iTask->status = status; iTask->do_retry = 0; iTask->task = task; - if (iTask->retries-- > 0 && status == SCSI_STATUS_CHECK_CONDITION - && task->sense.key == SCSI_SENSE_UNIT_ATTENTION) { - error_report("iSCSI CheckCondition: %s", iscsi_get_error(iscsi)); - iTask->do_retry = 1; - goto out; - } - if (status != SCSI_STATUS_GOOD) { + if (iTask->retries++ < ISCSI_CMD_RETRIES) { + if (status == SCSI_STATUS_CHECK_CONDITION + && task->sense.key == SCSI_SENSE_UNIT_ATTENTION) { + error_report("iSCSI CheckCondition: %s", + iscsi_get_error(iscsi)); + iTask->do_retry = 1; + goto out; + } + if (status == SCSI_STATUS_BUSY) { + unsigned retry_time = + exp_random(iscsi_retry_times[iTask->retries - 1]); + error_report("iSCSI Busy (retry #%u in %u ms): %s", + iTask->retries, retry_time, + iscsi_get_error(iscsi)); + aio_timer_init(iTask->iscsilun->aio_context, + &iTask->retry_timer, QEMU_CLOCK_REALTIME, + SCALE_MS, iscsi_retry_timer_expired, iTask); + timer_mod(&iTask->retry_timer, + qemu_clock_get_ms(QEMU_CLOCK_REALTIME) + retry_time); + iTask->do_retry = 1; + return; + } + } error_report("iSCSI Failure: %s", iscsi_get_error(iscsi)); } @@ -174,15 +207,16 @@ out: iTask->bh = aio_bh_new(iTask->iscsilun->aio_context, iscsi_co_generic_bh_cb, iTask); qemu_bh_schedule(iTask->bh); + } else { + iTask->complete = 1; } } static void iscsi_co_init_iscsitask(IscsiLun *iscsilun, struct IscsiTask *iTask) { *iTask = (struct IscsiTask) { - .co = qemu_coroutine_self(), - .retries = ISCSI_CMD_RETRIES, - .iscsilun = iscsilun, + .co = qemu_coroutine_self(), + .iscsilun = iscsilun, }; } @@ -325,8 +359,6 @@ static int coroutine_fn iscsi_co_writev(BlockDriverState *bs, struct IscsiTask iTask; uint64_t lba; uint32_t num_sectors; - uint8_t *data = NULL; - uint8_t *buf = NULL; if (!is_request_lun_aligned(sector_num, nb_sectors, iscsilun)) { return -EINVAL; @@ -334,31 +366,24 @@ static int coroutine_fn iscsi_co_writev(BlockDriverState *bs, lba = sector_qemu2lun(sector_num, iscsilun); num_sectors = sector_qemu2lun(nb_sectors, iscsilun); -#if !defined(LIBISCSI_FEATURE_IOVECTOR) - /* if the iovec only contains one buffer we can pass it directly */ - if (iov->niov == 1) { - data = iov->iov[0].iov_base; - } else { - size_t size = MIN(nb_sectors * BDRV_SECTOR_SIZE, iov->size); - buf = g_malloc(size); - qemu_iovec_to_buf(iov, 0, buf, size); - data = buf; - } -#endif iscsi_co_init_iscsitask(iscsilun, &iTask); retry: - iTask.task = iscsi_write16_task(iscsilun->iscsi, iscsilun->lun, lba, - data, num_sectors * iscsilun->block_size, - iscsilun->block_size, 0, 0, 0, 0, 0, - iscsi_co_generic_cb, &iTask); + if (iscsilun->use_16_for_rw) { + iTask.task = iscsi_write16_task(iscsilun->iscsi, iscsilun->lun, lba, + NULL, num_sectors * iscsilun->block_size, + iscsilun->block_size, 0, 0, 0, 0, 0, + iscsi_co_generic_cb, &iTask); + } else { + iTask.task = iscsi_write10_task(iscsilun->iscsi, iscsilun->lun, lba, + NULL, num_sectors * iscsilun->block_size, + iscsilun->block_size, 0, 0, 0, 0, 0, + iscsi_co_generic_cb, &iTask); + } if (iTask.task == NULL) { - g_free(buf); return -ENOMEM; } -#if defined(LIBISCSI_FEATURE_IOVECTOR) scsi_task_set_iov_out(iTask.task, (struct scsi_iovec *) iov->iov, iov->niov); -#endif while (!iTask.complete) { iscsi_set_events(iscsilun); qemu_coroutine_yield(); @@ -374,8 +399,6 @@ retry: goto retry; } - g_free(buf); - if (iTask.status != SCSI_STATUS_GOOD) { return -EIO; } @@ -386,7 +409,6 @@ retry: } -#if defined(LIBISCSI_FEATURE_IOVECTOR) static bool iscsi_allocationmap_is_allocated(IscsiLun *iscsilun, int64_t sector_num, int nb_sectors) { @@ -496,9 +518,6 @@ out: return ret; } -#endif /* LIBISCSI_FEATURE_IOVECTOR */ - - static int coroutine_fn iscsi_co_readv(BlockDriverState *bs, int64_t sector_num, int nb_sectors, QEMUIOVector *iov) @@ -507,15 +526,11 @@ static int coroutine_fn iscsi_co_readv(BlockDriverState *bs, struct IscsiTask iTask; uint64_t lba; uint32_t num_sectors; -#if !defined(LIBISCSI_FEATURE_IOVECTOR) - int i; -#endif if (!is_request_lun_aligned(sector_num, nb_sectors, iscsilun)) { return -EINVAL; } -#if defined(LIBISCSI_FEATURE_IOVECTOR) if (iscsilun->lbprz && nb_sectors >= ISCSI_CHECKALLOC_THRES && !iscsi_allocationmap_is_allocated(iscsilun, sector_num, nb_sectors)) { int64_t ret; @@ -529,42 +544,28 @@ static int coroutine_fn iscsi_co_readv(BlockDriverState *bs, return 0; } } -#endif lba = sector_qemu2lun(sector_num, iscsilun); num_sectors = sector_qemu2lun(nb_sectors, iscsilun); iscsi_co_init_iscsitask(iscsilun, &iTask); retry: - switch (iscsilun->type) { - case TYPE_DISK: + if (iscsilun->use_16_for_rw) { iTask.task = iscsi_read16_task(iscsilun->iscsi, iscsilun->lun, lba, num_sectors * iscsilun->block_size, iscsilun->block_size, 0, 0, 0, 0, 0, iscsi_co_generic_cb, &iTask); - break; - default: + } else { iTask.task = iscsi_read10_task(iscsilun->iscsi, iscsilun->lun, lba, num_sectors * iscsilun->block_size, iscsilun->block_size, -#if !defined(CONFIG_LIBISCSI_1_4) /* API change from 1.4.0 to 1.5.0 */ 0, 0, 0, 0, 0, -#endif iscsi_co_generic_cb, &iTask); - break; } if (iTask.task == NULL) { return -ENOMEM; } -#if defined(LIBISCSI_FEATURE_IOVECTOR) scsi_task_set_iov_in(iTask.task, (struct scsi_iovec *) iov->iov, iov->niov); -#else - for (i = 0; i < iov->niov; i++) { - scsi_task_add_data_in_buffer(iTask.task, - iov->iov[i].iov_len, - iov->iov[i].iov_base); - } -#endif while (!iTask.complete) { iscsi_set_events(iscsilun); @@ -719,18 +720,9 @@ static BlockDriverAIOCB *iscsi_aio_ioctl(BlockDriverState *bs, data.data = acb->ioh->dxferp; data.size = acb->ioh->dxfer_len; } else { -#if defined(LIBISCSI_FEATURE_IOVECTOR) scsi_task_set_iov_out(acb->task, (struct scsi_iovec *) acb->ioh->dxferp, acb->ioh->iovec_count); -#else - struct iovec *iov = (struct iovec *)acb->ioh->dxferp; - - acb->buf = g_malloc(acb->ioh->dxfer_len); - data.data = acb->buf; - data.size = iov_to_buf(iov, acb->ioh->iovec_count, 0, - acb->buf, acb->ioh->dxfer_len); -#endif } } @@ -750,20 +742,9 @@ static BlockDriverAIOCB *iscsi_aio_ioctl(BlockDriverState *bs, acb->ioh->dxfer_len, acb->ioh->dxferp); } else { -#if defined(LIBISCSI_FEATURE_IOVECTOR) scsi_task_set_iov_in(acb->task, (struct scsi_iovec *) acb->ioh->dxferp, acb->ioh->iovec_count); -#else - int i; - for (i = 0; i < acb->ioh->iovec_count; i++) { - struct iovec *iov = (struct iovec *)acb->ioh->dxferp; - - scsi_task_add_data_in_buffer(acb->task, - iov[i].iov_len, - iov[i].iov_base); - } -#endif } } @@ -772,7 +753,6 @@ static BlockDriverAIOCB *iscsi_aio_ioctl(BlockDriverState *bs, return &acb->common; } - static void ioctl_cb(void *opaque, int status) { int *p_status = opaque; @@ -877,8 +857,6 @@ retry: return 0; } -#if defined(SCSI_SENSE_ASCQ_CAPACITY_DATA_HAS_CHANGED) - static int coroutine_fn iscsi_co_write_zeroes(BlockDriverState *bs, int64_t sector_num, int nb_sectors, BdrvRequestFlags flags) @@ -887,19 +865,27 @@ coroutine_fn iscsi_co_write_zeroes(BlockDriverState *bs, int64_t sector_num, struct IscsiTask iTask; uint64_t lba; uint32_t nb_blocks; + bool use_16_for_ws = iscsilun->use_16_for_rw; if (!is_request_lun_aligned(sector_num, nb_sectors, iscsilun)) { return -EINVAL; } - if ((flags & BDRV_REQ_MAY_UNMAP) && !iscsilun->lbp.lbpws) { - /* WRITE SAME with UNMAP is not supported by the target, - * fall back and try WRITE SAME without UNMAP */ - flags &= ~BDRV_REQ_MAY_UNMAP; + if (flags & BDRV_REQ_MAY_UNMAP) { + if (!use_16_for_ws && !iscsilun->lbp.lbpws10) { + /* WRITESAME10 with UNMAP is unsupported try WRITESAME16 */ + use_16_for_ws = true; + } + if (use_16_for_ws && !iscsilun->lbp.lbpws) { + /* WRITESAME16 with UNMAP is not supported by the target, + * fall back and try WRITESAME10/16 without UNMAP */ + flags &= ~BDRV_REQ_MAY_UNMAP; + use_16_for_ws = iscsilun->use_16_for_rw; + } } if (!(flags & BDRV_REQ_MAY_UNMAP) && !iscsilun->has_write_same) { - /* WRITE SAME without UNMAP is not supported by the target */ + /* WRITESAME without UNMAP is not supported by the target */ return -ENOTSUP; } @@ -912,10 +898,18 @@ coroutine_fn iscsi_co_write_zeroes(BlockDriverState *bs, int64_t sector_num, iscsi_co_init_iscsitask(iscsilun, &iTask); retry: - if (iscsi_writesame16_task(iscsilun->iscsi, iscsilun->lun, lba, - iscsilun->zeroblock, iscsilun->block_size, - nb_blocks, 0, !!(flags & BDRV_REQ_MAY_UNMAP), - 0, 0, iscsi_co_generic_cb, &iTask) == NULL) { + if (use_16_for_ws) { + iTask.task = iscsi_writesame16_task(iscsilun->iscsi, iscsilun->lun, lba, + iscsilun->zeroblock, iscsilun->block_size, + nb_blocks, 0, !!(flags & BDRV_REQ_MAY_UNMAP), + 0, 0, iscsi_co_generic_cb, &iTask); + } else { + iTask.task = iscsi_writesame10_task(iscsilun->iscsi, iscsilun->lun, lba, + iscsilun->zeroblock, iscsilun->block_size, + nb_blocks, 0, !!(flags & BDRV_REQ_MAY_UNMAP), + 0, 0, iscsi_co_generic_cb, &iTask); + } + if (iTask.task == NULL) { return -ENOMEM; } @@ -957,8 +951,6 @@ retry: return 0; } -#endif /* SCSI_SENSE_ASCQ_CAPACITY_DATA_HAS_CHANGED */ - static void parse_chap(struct iscsi_context *iscsi, const char *target, Error **errp) { @@ -1068,7 +1060,6 @@ static char *parse_initiator_name(const char *target) return iscsi_name; } -#if defined(LIBISCSI_FEATURE_NOP_COUNTER) static void iscsi_nop_timed_event(void *opaque) { IscsiLun *iscsilun = opaque; @@ -1086,7 +1077,6 @@ static void iscsi_nop_timed_event(void *opaque) timer_mod(iscsilun->nop_timer, qemu_clock_get_ms(QEMU_CLOCK_REALTIME) + NOP_INTERVAL); iscsi_set_events(iscsilun); } -#endif static void iscsi_readcapacity_sync(IscsiLun *iscsilun, Error **errp) { @@ -1113,6 +1103,7 @@ static void iscsi_readcapacity_sync(IscsiLun *iscsilun, Error **errp) iscsilun->num_blocks = rc16->returned_lba + 1; iscsilun->lbpme = rc16->lbpme; iscsilun->lbprz = rc16->lbprz; + iscsilun->use_16_for_rw = (rc16->returned_lba > 0xffffffff); } } break; @@ -1224,14 +1215,12 @@ static void iscsi_attach_aio_context(BlockDriverState *bs, iscsilun->aio_context = new_context; iscsi_set_events(iscsilun); -#if defined(LIBISCSI_FEATURE_NOP_COUNTER) /* Set up a timer for sending out iSCSI NOPs */ iscsilun->nop_timer = aio_timer_new(iscsilun->aio_context, QEMU_CLOCK_REALTIME, SCALE_MS, iscsi_nop_timed_event, iscsilun); timer_mod(iscsilun->nop_timer, qemu_clock_get_ms(QEMU_CLOCK_REALTIME) + NOP_INTERVAL); -#endif } /* @@ -1423,13 +1412,11 @@ static int iscsi_open(BlockDriverState *bs, QDict *options, int flags, iscsilun->bl.opt_unmap_gran * iscsilun->block_size <= 16 * 1024 * 1024) { iscsilun->cluster_sectors = (iscsilun->bl.opt_unmap_gran * iscsilun->block_size) >> BDRV_SECTOR_BITS; -#if defined(LIBISCSI_FEATURE_IOVECTOR) if (iscsilun->lbprz && !(bs->open_flags & BDRV_O_NOCACHE)) { iscsilun->allocationmap = bitmap_new(DIV_ROUND_UP(bs->total_sectors, iscsilun->cluster_sectors)); } -#endif } out: @@ -1614,13 +1601,9 @@ static BlockDriver bdrv_iscsi = { .bdrv_truncate = iscsi_truncate, .bdrv_refresh_limits = iscsi_refresh_limits, -#if defined(LIBISCSI_FEATURE_IOVECTOR) .bdrv_co_get_block_status = iscsi_co_get_block_status, -#endif .bdrv_co_discard = iscsi_co_discard, -#if defined(SCSI_SENSE_ASCQ_CAPACITY_DATA_HAS_CHANGED) .bdrv_co_write_zeroes = iscsi_co_write_zeroes, -#endif .bdrv_co_readv = iscsi_co_readv, .bdrv_co_writev = iscsi_co_writev, .bdrv_co_flush_to_disk = iscsi_co_flush, @@ -3432,46 +3432,20 @@ if compile_prog "" "" ; then fi ########################################## -# Do we have libiscsi -# We check for iscsi_write16_sync() to make sure we have a -# at least version 1.4.0 of libiscsi. +# Do we have libiscsi >= 1.9.0 if test "$libiscsi" != "no" ; then - cat > $TMPC << EOF -#include <stdio.h> -#include <iscsi/iscsi.h> -int main(void) { iscsi_write16_sync(NULL,0,0,NULL,0,0,0,0,0,0,0); return 0; } -EOF - if $pkg_config --atleast-version=1.7.0 libiscsi; then + if $pkg_config --atleast-version=1.9.0 libiscsi; then libiscsi="yes" libiscsi_cflags=$($pkg_config --cflags libiscsi) libiscsi_libs=$($pkg_config --libs libiscsi) - elif compile_prog "" "-liscsi" ; then - libiscsi="yes" - libiscsi_libs="-liscsi" else if test "$libiscsi" = "yes" ; then - feature_not_found "libiscsi" "Install libiscsi devel" + feature_not_found "libiscsi" "Install libiscsi >= 1.9.0" fi libiscsi="no" fi fi -# We also need to know the API version because there was an -# API change from 1.4.0 to 1.5.0. -if test "$libiscsi" = "yes"; then - cat >$TMPC <<EOF -#include <iscsi/iscsi.h> -int main(void) -{ - iscsi_read10_task(0, 0, 0, 0, 0, 0, 0); - return 0; -} -EOF - if compile_prog "" "-liscsi"; then - libiscsi_version="1.4.0" - fi -fi - ########################################## # Do we need libm cat > $TMPC << EOF @@ -4245,11 +4219,7 @@ echo "nss used $smartcard_nss" echo "libusb $libusb" echo "usb net redir $usb_redir" echo "GLX support $glx" -if test "$libiscsi_version" = "1.4.0"; then -echo "libiscsi support $libiscsi (1.4.0)" -else echo "libiscsi support $libiscsi" -fi echo "libnfs support $libnfs" echo "build guest agent $guest_agent" echo "QGA VSS support $guest_agent_with_vss" @@ -4610,9 +4580,6 @@ fi if test "$libiscsi" = "yes" ; then echo "CONFIG_LIBISCSI=m" >> $config_host_mak - if test "$libiscsi_version" = "1.4.0"; then - echo "CONFIG_LIBISCSI_1_4=y" >> $config_host_mak - fi echo "LIBISCSI_CFLAGS=$libiscsi_cflags" >> $config_host_mak echo "LIBISCSI_LIBS=$libiscsi_libs" >> $config_host_mak fi diff --git a/hw/arm/spitz.c b/hw/arm/spitz.c index 5455dbf326..45e75081e8 100644 --- a/hw/arm/spitz.c +++ b/hw/arm/spitz.c @@ -285,9 +285,9 @@ static void spitz_keyboard_keydown(SpitzKeyboardState *s, int keycode) spitz_keyboard_sense_update(s); } -#define MOD_SHIFT (1 << 7) -#define MOD_CTRL (1 << 8) -#define MOD_FN (1 << 9) +#define SPITZ_MOD_SHIFT (1 << 7) +#define SPITZ_MOD_CTRL (1 << 8) +#define SPITZ_MOD_FN (1 << 9) #define QUEUE_KEY(c) s->fifo[(s->fifopos + s->fifolen ++) & 0xf] = c @@ -324,21 +324,26 @@ static void spitz_keyboard_handler(void *opaque, int keycode) } code = s->pre_map[mapcode = ((s->modifiers & 3) ? - (keycode | MOD_SHIFT) : - (keycode & ~MOD_SHIFT))]; + (keycode | SPITZ_MOD_SHIFT) : + (keycode & ~SPITZ_MOD_SHIFT))]; if (code != mapcode) { #if 0 - if ((code & MOD_SHIFT) && !(s->modifiers & 1)) + if ((code & SPITZ_MOD_SHIFT) && !(s->modifiers & 1)) { QUEUE_KEY(0x2a | (keycode & 0x80)); - if ((code & MOD_CTRL ) && !(s->modifiers & 4)) + } + if ((code & SPITZ_MOD_CTRL) && !(s->modifiers & 4)) { QUEUE_KEY(0x1d | (keycode & 0x80)); - if ((code & MOD_FN ) && !(s->modifiers & 8)) + } + if ((code & SPITZ_MOD_FN) && !(s->modifiers & 8)) { QUEUE_KEY(0x38 | (keycode & 0x80)); - if ((code & MOD_FN ) && (s->modifiers & 1)) + } + if ((code & SPITZ_MOD_FN) && (s->modifiers & 1)) { QUEUE_KEY(0x2a | (~keycode & 0x80)); - if ((code & MOD_FN ) && (s->modifiers & 2)) + } + if ((code & SPITZ_MOD_FN) && (s->modifiers & 2)) { QUEUE_KEY(0x36 | (~keycode & 0x80)); + } #else if (keycode & 0x80) { if ((s->imodifiers & 1 ) && !(s->modifiers & 1)) @@ -353,24 +358,27 @@ static void spitz_keyboard_handler(void *opaque, int keycode) QUEUE_KEY(0x36); s->imodifiers = 0; } else { - if ((code & MOD_SHIFT) && !((s->modifiers | s->imodifiers) & 1)) { + if ((code & SPITZ_MOD_SHIFT) && + !((s->modifiers | s->imodifiers) & 1)) { QUEUE_KEY(0x2a); s->imodifiers |= 1; } - if ((code & MOD_CTRL ) && !((s->modifiers | s->imodifiers) & 4)) { + if ((code & SPITZ_MOD_CTRL) && + !((s->modifiers | s->imodifiers) & 4)) { QUEUE_KEY(0x1d); s->imodifiers |= 4; } - if ((code & MOD_FN ) && !((s->modifiers | s->imodifiers) & 8)) { + if ((code & SPITZ_MOD_FN) && + !((s->modifiers | s->imodifiers) & 8)) { QUEUE_KEY(0x38); s->imodifiers |= 8; } - if ((code & MOD_FN ) && (s->modifiers & 1) && + if ((code & SPITZ_MOD_FN) && (s->modifiers & 1) && !(s->imodifiers & 0x10)) { QUEUE_KEY(0x2a | 0x80); s->imodifiers |= 0x10; } - if ((code & MOD_FN ) && (s->modifiers & 2) && + if ((code & SPITZ_MOD_FN) && (s->modifiers & 2) && !(s->imodifiers & 0x20)) { QUEUE_KEY(0x36 | 0x80); s->imodifiers |= 0x20; @@ -402,38 +410,38 @@ static void spitz_keyboard_pre_map(SpitzKeyboardState *s) int i; for (i = 0; i < 0x100; i ++) s->pre_map[i] = i; - s->pre_map[0x02 | MOD_SHIFT ] = 0x02 | MOD_SHIFT; /* exclam */ - s->pre_map[0x28 | MOD_SHIFT ] = 0x03 | MOD_SHIFT; /* quotedbl */ - s->pre_map[0x04 | MOD_SHIFT ] = 0x04 | MOD_SHIFT; /* numbersign */ - s->pre_map[0x05 | MOD_SHIFT ] = 0x05 | MOD_SHIFT; /* dollar */ - s->pre_map[0x06 | MOD_SHIFT ] = 0x06 | MOD_SHIFT; /* percent */ - s->pre_map[0x08 | MOD_SHIFT ] = 0x07 | MOD_SHIFT; /* ampersand */ - s->pre_map[0x28 ] = 0x08 | MOD_SHIFT; /* apostrophe */ - s->pre_map[0x0a | MOD_SHIFT ] = 0x09 | MOD_SHIFT; /* parenleft */ - s->pre_map[0x0b | MOD_SHIFT ] = 0x0a | MOD_SHIFT; /* parenright */ - s->pre_map[0x29 | MOD_SHIFT ] = 0x0b | MOD_SHIFT; /* asciitilde */ - s->pre_map[0x03 | MOD_SHIFT ] = 0x0c | MOD_SHIFT; /* at */ - s->pre_map[0xd3 ] = 0x0e | MOD_FN; /* Delete */ - s->pre_map[0x3a ] = 0x0f | MOD_FN; /* Caps_Lock */ - s->pre_map[0x07 | MOD_SHIFT ] = 0x11 | MOD_FN; /* asciicircum */ - s->pre_map[0x0d ] = 0x12 | MOD_FN; /* equal */ - s->pre_map[0x0d | MOD_SHIFT ] = 0x13 | MOD_FN; /* plus */ - s->pre_map[0x1a ] = 0x14 | MOD_FN; /* bracketleft */ - s->pre_map[0x1b ] = 0x15 | MOD_FN; /* bracketright */ - s->pre_map[0x1a | MOD_SHIFT ] = 0x16 | MOD_FN; /* braceleft */ - s->pre_map[0x1b | MOD_SHIFT ] = 0x17 | MOD_FN; /* braceright */ - s->pre_map[0x27 ] = 0x22 | MOD_FN; /* semicolon */ - s->pre_map[0x27 | MOD_SHIFT ] = 0x23 | MOD_FN; /* colon */ - s->pre_map[0x09 | MOD_SHIFT ] = 0x24 | MOD_FN; /* asterisk */ - s->pre_map[0x2b ] = 0x25 | MOD_FN; /* backslash */ - s->pre_map[0x2b | MOD_SHIFT ] = 0x26 | MOD_FN; /* bar */ - s->pre_map[0x0c | MOD_SHIFT ] = 0x30 | MOD_FN; /* underscore */ - s->pre_map[0x33 | MOD_SHIFT ] = 0x33 | MOD_FN; /* less */ - s->pre_map[0x35 ] = 0x33 | MOD_SHIFT; /* slash */ - s->pre_map[0x34 | MOD_SHIFT ] = 0x34 | MOD_FN; /* greater */ - s->pre_map[0x35 | MOD_SHIFT ] = 0x34 | MOD_SHIFT; /* question */ - s->pre_map[0x49 ] = 0x48 | MOD_FN; /* Page_Up */ - s->pre_map[0x51 ] = 0x50 | MOD_FN; /* Page_Down */ + s->pre_map[0x02 | SPITZ_MOD_SHIFT] = 0x02 | SPITZ_MOD_SHIFT; /* exclam */ + s->pre_map[0x28 | SPITZ_MOD_SHIFT] = 0x03 | SPITZ_MOD_SHIFT; /* quotedbl */ + s->pre_map[0x04 | SPITZ_MOD_SHIFT] = 0x04 | SPITZ_MOD_SHIFT; /* # */ + s->pre_map[0x05 | SPITZ_MOD_SHIFT] = 0x05 | SPITZ_MOD_SHIFT; /* dollar */ + s->pre_map[0x06 | SPITZ_MOD_SHIFT] = 0x06 | SPITZ_MOD_SHIFT; /* percent */ + s->pre_map[0x08 | SPITZ_MOD_SHIFT] = 0x07 | SPITZ_MOD_SHIFT; /* ampersand */ + s->pre_map[0x28] = 0x08 | SPITZ_MOD_SHIFT; /* ' */ + s->pre_map[0x0a | SPITZ_MOD_SHIFT] = 0x09 | SPITZ_MOD_SHIFT; /* ( */ + s->pre_map[0x0b | SPITZ_MOD_SHIFT] = 0x0a | SPITZ_MOD_SHIFT; /* ) */ + s->pre_map[0x29 | SPITZ_MOD_SHIFT] = 0x0b | SPITZ_MOD_SHIFT; /* tilde */ + s->pre_map[0x03 | SPITZ_MOD_SHIFT] = 0x0c | SPITZ_MOD_SHIFT; /* at */ + s->pre_map[0xd3] = 0x0e | SPITZ_MOD_FN; /* Delete */ + s->pre_map[0x3a] = 0x0f | SPITZ_MOD_FN; /* Caps_Lock */ + s->pre_map[0x07 | SPITZ_MOD_SHIFT] = 0x11 | SPITZ_MOD_FN; /* ^ */ + s->pre_map[0x0d] = 0x12 | SPITZ_MOD_FN; /* equal */ + s->pre_map[0x0d | SPITZ_MOD_SHIFT] = 0x13 | SPITZ_MOD_FN; /* plus */ + s->pre_map[0x1a] = 0x14 | SPITZ_MOD_FN; /* [ */ + s->pre_map[0x1b] = 0x15 | SPITZ_MOD_FN; /* ] */ + s->pre_map[0x1a | SPITZ_MOD_SHIFT] = 0x16 | SPITZ_MOD_FN; /* { */ + s->pre_map[0x1b | SPITZ_MOD_SHIFT] = 0x17 | SPITZ_MOD_FN; /* } */ + s->pre_map[0x27] = 0x22 | SPITZ_MOD_FN; /* semicolon */ + s->pre_map[0x27 | SPITZ_MOD_SHIFT] = 0x23 | SPITZ_MOD_FN; /* colon */ + s->pre_map[0x09 | SPITZ_MOD_SHIFT] = 0x24 | SPITZ_MOD_FN; /* asterisk */ + s->pre_map[0x2b] = 0x25 | SPITZ_MOD_FN; /* backslash */ + s->pre_map[0x2b | SPITZ_MOD_SHIFT] = 0x26 | SPITZ_MOD_FN; /* bar */ + s->pre_map[0x0c | SPITZ_MOD_SHIFT] = 0x30 | SPITZ_MOD_FN; /* _ */ + s->pre_map[0x33 | SPITZ_MOD_SHIFT] = 0x33 | SPITZ_MOD_FN; /* less */ + s->pre_map[0x35] = 0x33 | SPITZ_MOD_SHIFT; /* slash */ + s->pre_map[0x34 | SPITZ_MOD_SHIFT] = 0x34 | SPITZ_MOD_FN; /* greater */ + s->pre_map[0x35 | SPITZ_MOD_SHIFT] = 0x34 | SPITZ_MOD_SHIFT; /* question */ + s->pre_map[0x49] = 0x48 | SPITZ_MOD_FN; /* Page_Up */ + s->pre_map[0x51] = 0x50 | SPITZ_MOD_FN; /* Page_Down */ s->modifiers = 0; s->imodifiers = 0; @@ -441,9 +449,9 @@ static void spitz_keyboard_pre_map(SpitzKeyboardState *s) s->fifolen = 0; } -#undef MOD_SHIFT -#undef MOD_CTRL -#undef MOD_FN +#undef SPITZ_MOD_SHIFT +#undef SPITZ_MOD_CTRL +#undef SPITZ_MOD_FN static int spitz_keyboard_post_load(void *opaque, int version_id) { diff --git a/hw/arm/vexpress.c b/hw/arm/vexpress.c index f311595d67..3d83e6c98d 100644 --- a/hw/arm/vexpress.c +++ b/hw/arm/vexpress.c @@ -533,7 +533,15 @@ static void vexpress_common_init(VEDBoardInfo *daughterboard, * If a bios file was provided, attempt to map it into memory */ if (bios_name) { - const char *fn = qemu_find_file(QEMU_FILE_TYPE_BIOS, bios_name); + const char *fn; + + if (drive_get(IF_PFLASH, 0, 0)) { + error_report("The contents of the first flash device may be " + "specified with -bios or with -drive if=pflash... " + "but you cannot use both options at once"); + exit(1); + } + fn = qemu_find_file(QEMU_FILE_TYPE_BIOS, bios_name); if (!fn || load_image_targphys(fn, map[VE_NORFLASH0], VEXPRESS_FLASH_SIZE) < 0) { error_report("Could not load ROM image '%s'", bios_name); diff --git a/hw/arm/virt.c b/hw/arm/virt.c index 3b55a4bf7d..72fe030e93 100644 --- a/hw/arm/virt.c +++ b/hw/arm/virt.c @@ -180,10 +180,23 @@ static void create_fdt(VirtBoardInfo *vbi) "clk24mhz"); qemu_fdt_setprop_cell(fdt, "/apb-pclk", "phandle", vbi->clock_phandle); +} + +static void fdt_add_psci_node(const VirtBoardInfo *vbi) +{ + void *fdt = vbi->fdt; + ARMCPU *armcpu = ARM_CPU(qemu_get_cpu(0)); + /* No PSCI for TCG yet */ if (kvm_enabled()) { qemu_fdt_add_subnode(fdt, "/psci"); - qemu_fdt_setprop_string(fdt, "/psci", "compatible", "arm,psci"); + if (armcpu->psci_version == 2) { + const char comp[] = "arm,psci-0.2\0arm,psci"; + qemu_fdt_setprop(fdt, "/psci", "compatible", comp, sizeof(comp)); + } else { + qemu_fdt_setprop_string(fdt, "/psci", "compatible", "arm,psci"); + } + qemu_fdt_setprop_string(fdt, "/psci", "method", "hvc"); qemu_fdt_setprop_cell(fdt, "/psci", "cpu_suspend", PSCI_FN_CPU_SUSPEND); @@ -446,6 +459,7 @@ static void machvirt_init(MachineState *machine) object_property_set_bool(cpuobj, true, "realized", NULL); } fdt_add_cpu_nodes(vbi); + fdt_add_psci_node(vbi); memory_region_init_ram(ram, NULL, "mach-virt.ram", machine->ram_size); vmstate_register_ram_global(ram); diff --git a/hw/block/pflash_cfi01.c b/hw/block/pflash_cfi01.c index 0c95d53dca..f9507b4e5a 100644 --- a/hw/block/pflash_cfi01.c +++ b/hw/block/pflash_cfi01.c @@ -748,9 +748,18 @@ static void pflash_cfi01_realize(DeviceState *dev, Error **errp) pflash_t *pfl = CFI_PFLASH01(dev); uint64_t total_len; int ret; + uint64_t blocks_per_device, device_len; + int num_devices; total_len = pfl->sector_len * pfl->nb_blocs; + /* These are only used to expose the parameters of each device + * in the cfi_table[]. + */ + num_devices = pfl->device_width ? (pfl->bank_width / pfl->device_width) : 1; + blocks_per_device = pfl->nb_blocs / num_devices; + device_len = pfl->sector_len * blocks_per_device; + /* XXX: to be fixed */ #if 0 if (total_len != (8 * 1024 * 1024) && total_len != (16 * 1024 * 1024) && @@ -838,7 +847,7 @@ static void pflash_cfi01_realize(DeviceState *dev, Error **errp) /* Max timeout for chip erase */ pfl->cfi_table[0x26] = 0x00; /* Device size */ - pfl->cfi_table[0x27] = ctz32(total_len); // + 1; + pfl->cfi_table[0x27] = ctz32(device_len); /* + 1; */ /* Flash device interface (8 & 16 bits) */ pfl->cfi_table[0x28] = 0x02; pfl->cfi_table[0x29] = 0x00; @@ -854,8 +863,8 @@ static void pflash_cfi01_realize(DeviceState *dev, Error **errp) /* Number of erase block regions (uniform) */ pfl->cfi_table[0x2C] = 0x01; /* Erase block region 1 */ - pfl->cfi_table[0x2D] = pfl->nb_blocs - 1; - pfl->cfi_table[0x2E] = (pfl->nb_blocs - 1) >> 8; + pfl->cfi_table[0x2D] = blocks_per_device - 1; + pfl->cfi_table[0x2E] = (blocks_per_device - 1) >> 8; pfl->cfi_table[0x2F] = pfl->sector_len >> 8; pfl->cfi_table[0x30] = pfl->sector_len >> 16; @@ -882,6 +891,11 @@ static void pflash_cfi01_realize(DeviceState *dev, Error **errp) static Property pflash_cfi01_properties[] = { DEFINE_PROP_DRIVE("drive", struct pflash_t, bs), + /* num-blocks is the number of blocks actually visible to the guest, + * ie the total size of the device divided by the sector length. + * If we're emulating flash devices wired in parallel the actual + * number of blocks per indvidual device will differ. + */ DEFINE_PROP_UINT32("num-blocks", struct pflash_t, nb_blocs, 0), DEFINE_PROP_UINT64("sector-length", struct pflash_t, sector_len, 0), /* width here is the overall width of this QEMU device in bytes. diff --git a/hw/display/qxl.c b/hw/display/qxl.c index 736fd3c4e2..d43aa49eb8 100644 --- a/hw/display/qxl.c +++ b/hw/display/qxl.c @@ -710,7 +710,7 @@ static void interface_release_resource(QXLInstance *sin, if (ext.group_id == MEMSLOT_GROUP_HOST) { /* host group -> vga mode update request */ - QXLCommandExt *cmdext = (void *)(ext.info->id); + QXLCommandExt *cmdext = (void *)(intptr_t)(ext.info->id); SimpleSpiceUpdate *update; g_assert(cmdext->cmd.type == QXL_CMD_DRAW); update = container_of(cmdext, SimpleSpiceUpdate, ext); diff --git a/hw/intc/armv7m_nvic.c b/hw/intc/armv7m_nvic.c index 75d9c6e41e..1a7af450a7 100644 --- a/hw/intc/armv7m_nvic.c +++ b/hw/intc/armv7m_nvic.c @@ -211,7 +211,7 @@ static uint32_t nvic_readl(nvic_state *s, uint32_t offset) cpu = ARM_CPU(current_cpu); return cpu->env.v7m.vecbase; case 0xd0c: /* Application Interrupt/Reset Control. */ - return 0xfa05000; + return 0xfa050000; case 0xd10: /* System Control. */ /* TODO: Implement SLEEPONEXIT. */ return 0; @@ -346,6 +346,9 @@ static void nvic_writel(nvic_state *s, uint32_t offset, uint32_t value) if (value & 5) { qemu_log_mask(LOG_UNIMP, "AIRCR system reset unimplemented\n"); } + if (value & 0x700) { + qemu_log_mask(LOG_UNIMP, "PRIGROUP unimplemented\n"); + } } break; case 0xd10: /* System Control. */ diff --git a/hw/scsi/megasas.c b/hw/scsi/megasas.c index b05c47abdc..c68a873f18 100644 --- a/hw/scsi/megasas.c +++ b/hw/scsi/megasas.c @@ -294,6 +294,7 @@ static void megasas_unmap_sgl(MegasasCmd *cmd) static int megasas_build_sense(MegasasCmd *cmd, uint8_t *sense_ptr, uint8_t sense_len) { + PCIDevice *pcid = PCI_DEVICE(cmd->state); uint32_t pa_hi = 0, pa_lo; hwaddr pa; @@ -306,7 +307,7 @@ static int megasas_build_sense(MegasasCmd *cmd, uint8_t *sense_ptr, pa_hi = le32_to_cpu(cmd->frame->pass.sense_addr_hi); } pa = ((uint64_t) pa_hi << 32) | pa_lo; - cpu_physical_memory_write(pa, sense_ptr, sense_len); + pci_dma_write(pcid, pa, sense_ptr, sense_len); cmd->frame->header.sense_len = sense_len; } return sense_len; @@ -472,6 +473,7 @@ static MegasasCmd *megasas_next_frame(MegasasState *s, static MegasasCmd *megasas_enqueue_frame(MegasasState *s, hwaddr frame, uint64_t context, int count) { + PCIDevice *pcid = PCI_DEVICE(s); MegasasCmd *cmd = NULL; int frame_size = MFI_FRAME_SIZE * 16; hwaddr frame_size_p = frame_size; @@ -484,11 +486,11 @@ static MegasasCmd *megasas_enqueue_frame(MegasasState *s, if (!cmd->pa) { cmd->pa = frame; /* Map all possible frames */ - cmd->frame = cpu_physical_memory_map(frame, &frame_size_p, 0); + cmd->frame = pci_dma_map(pcid, frame, &frame_size_p, 0); if (frame_size_p != frame_size) { trace_megasas_qf_map_failed(cmd->index, (unsigned long)frame); if (cmd->frame) { - cpu_physical_memory_unmap(cmd->frame, frame_size_p, 0, 0); + pci_dma_unmap(pcid, cmd->frame, frame_size_p, 0, 0); cmd->frame = NULL; cmd->pa = 0; } @@ -561,13 +563,14 @@ static void megasas_complete_frame(MegasasState *s, uint64_t context) static void megasas_reset_frames(MegasasState *s) { + PCIDevice *pcid = PCI_DEVICE(s); int i; MegasasCmd *cmd; for (i = 0; i < s->fw_cmds; i++) { cmd = &s->frames[i]; if (cmd->pa) { - cpu_physical_memory_unmap(cmd->frame, cmd->pa_size, 0, 0); + pci_dma_unmap(pcid, cmd->frame, cmd->pa_size, 0, 0); cmd->frame = NULL; cmd->pa = 0; } @@ -584,6 +587,7 @@ static void megasas_abort_command(MegasasCmd *cmd) static int megasas_init_firmware(MegasasState *s, MegasasCmd *cmd) { + PCIDevice *pcid = PCI_DEVICE(s); uint32_t pa_hi, pa_lo; hwaddr iq_pa, initq_size; struct mfi_init_qinfo *initq; @@ -595,7 +599,7 @@ static int megasas_init_firmware(MegasasState *s, MegasasCmd *cmd) iq_pa = (((uint64_t) pa_hi << 32) | pa_lo); trace_megasas_init_firmware((uint64_t)iq_pa); initq_size = sizeof(*initq); - initq = cpu_physical_memory_map(iq_pa, &initq_size, 0); + initq = pci_dma_map(pcid, iq_pa, &initq_size, 0); if (!initq || initq_size != sizeof(*initq)) { trace_megasas_initq_map_failed(cmd->index); s->event_count++; @@ -631,7 +635,7 @@ static int megasas_init_firmware(MegasasState *s, MegasasCmd *cmd) s->fw_state = MFI_FWSTATE_OPERATIONAL; out: if (initq) { - cpu_physical_memory_unmap(initq, initq_size, 0, 0); + pci_dma_unmap(pcid, initq, initq_size, 0, 0); } return ret; } diff --git a/hw/scsi/scsi-bus.c b/hw/scsi/scsi-bus.c index 003d2843df..ea1ac09c8a 100644 --- a/hw/scsi/scsi-bus.c +++ b/hw/scsi/scsi-bus.c @@ -1429,7 +1429,7 @@ int scsi_build_sense(uint8_t *in_buf, int in_len, } } -static const char *scsi_command_name(uint8_t cmd) +const char *scsi_command_name(uint8_t cmd) { static const char *names[] = { [ TEST_UNIT_READY ] = "TEST_UNIT_READY", @@ -1545,6 +1545,8 @@ static const char *scsi_command_name(uint8_t cmd) [ SET_READ_AHEAD ] = "SET_READ_AHEAD", [ ALLOW_OVERWRITE ] = "ALLOW_OVERWRITE", [ MECHANISM_STATUS ] = "MECHANISM_STATUS", + [ GET_EVENT_STATUS_NOTIFICATION ] = "GET_EVENT_STATUS_NOTIFICATION", + [ READ_DISC_INFORMATION ] = "READ_DISC_INFORMATION", }; if (cmd >= ARRAY_SIZE(names) || names[cmd] == NULL) diff --git a/hw/scsi/scsi-disk.c b/hw/scsi/scsi-disk.c index fc6e32ada5..a529ad24c7 100644 --- a/hw/scsi/scsi-disk.c +++ b/hw/scsi/scsi-disk.c @@ -2015,7 +2015,7 @@ static int32_t scsi_disk_emulate_command(SCSIRequest *req, uint8_t *buf) case VERIFY_10: case VERIFY_12: case VERIFY_16: - DPRINTF("Verify (bytchk %lu)\n", (r->req.buf[1] >> 1) & 3); + DPRINTF("Verify (bytchk %d)\n", (req->cmd.buf[1] >> 1) & 3); if (req->cmd.buf[1] & 6) { goto illegal_request; } @@ -2027,7 +2027,8 @@ static int32_t scsi_disk_emulate_command(SCSIRequest *req, uint8_t *buf) (long)r->req.cmd.xfer); break; default: - DPRINTF("Unknown SCSI command (%2.2x)\n", buf[0]); + DPRINTF("Unknown SCSI command (%2.2x=%s)\n", buf[0], + scsi_command_name(buf[0])); scsi_check_condition(r, SENSE_CODE(INVALID_OPCODE)); return 0; } @@ -2526,7 +2527,7 @@ static SCSIRequest *scsi_block_new_request(SCSIDevice *d, uint32_t tag, * ones (such as WRITE SAME or EXTENDED COPY, etc.). So, without * O_DIRECT everything must go through SG_IO. */ - if (bdrv_get_flags(s->qdev.conf.bs) & BDRV_O_NOCACHE) { + if (!(bdrv_get_flags(s->qdev.conf.bs) & BDRV_O_NOCACHE)) { break; } diff --git a/hw/scsi/spapr_vscsi.c b/hw/scsi/spapr_vscsi.c index f96b7af791..048cfc7b05 100644 --- a/hw/scsi/spapr_vscsi.c +++ b/hw/scsi/spapr_vscsi.c @@ -799,8 +799,9 @@ static int vscsi_queue_cmd(VSCSIState *s, vscsi_req *req) req->sreq = scsi_req_new(sdev, req->qtag, lun, srp->cmd.cdb, req); n = scsi_req_enqueue(req->sreq); - DPRINTF("VSCSI: Queued command tag 0x%x CMD 0x%x LUN %d ret: %d\n", - req->qtag, srp->cmd.cdb[0], lun, n); + DPRINTF("VSCSI: Queued command tag 0x%x CMD 0x%x=%s LUN %d ret: %d\n", + req->qtag, srp->cmd.cdb[0], scsi_command_name(srp->cmd.cdb[0]), + lun, n); if (n) { /* Transfer direction must be set before preprocessing the diff --git a/hw/scsi/vhost-scsi.c b/hw/scsi/vhost-scsi.c index 9eba5adcfe..ddfe76aed0 100644 --- a/hw/scsi/vhost-scsi.c +++ b/hw/scsi/vhost-scsi.c @@ -195,6 +195,10 @@ static void vhost_scsi_set_status(VirtIODevice *vdev, uint8_t val) } } +static void vhost_dummy_handle_output(VirtIODevice *vdev, VirtQueue *vq) +{ +} + static void vhost_scsi_realize(DeviceState *dev, Error **errp) { VirtIOSCSICommon *vs = VIRTIO_SCSI_COMMON(dev); @@ -223,7 +227,9 @@ static void vhost_scsi_realize(DeviceState *dev, Error **errp) } } - virtio_scsi_common_realize(dev, &err); + virtio_scsi_common_realize(dev, &err, vhost_dummy_handle_output, + vhost_dummy_handle_output, + vhost_dummy_handle_output); if (err != NULL) { error_propagate(errp, err); return; diff --git a/hw/scsi/virtio-scsi.c b/hw/scsi/virtio-scsi.c index b39880a9cd..8c8c9d1f61 100644 --- a/hw/scsi/virtio-scsi.c +++ b/hw/scsi/virtio-scsi.c @@ -15,6 +15,7 @@ #include "hw/virtio/virtio-scsi.h" #include "qemu/error-report.h" +#include "qemu/iov.h" #include <hw/scsi/scsi.h> #include <block/scsi.h> #include <hw/virtio/virtio-bus.h> @@ -25,21 +26,28 @@ typedef struct VirtIOSCSIReq { VirtQueueElement elem; QEMUSGList qsgl; SCSIRequest *sreq; + size_t resp_size; + enum SCSIXferMode mode; + QEMUIOVector resp_iov; union { - char *buf; - VirtIOSCSICmdReq *cmd; - VirtIOSCSICtrlTMFReq *tmf; - VirtIOSCSICtrlANReq *an; - } req; - union { - char *buf; - VirtIOSCSICmdResp *cmd; - VirtIOSCSICtrlTMFResp *tmf; - VirtIOSCSICtrlANResp *an; - VirtIOSCSIEvent *event; + VirtIOSCSICmdResp cmd; + VirtIOSCSICtrlTMFResp tmf; + VirtIOSCSICtrlANResp an; + VirtIOSCSIEvent event; } resp; + union { + struct { + VirtIOSCSICmdReq cmd; + uint8_t cdb[]; + } QEMU_PACKED; + VirtIOSCSICtrlTMFReq tmf; + VirtIOSCSICtrlANReq an; + } req; } VirtIOSCSIReq; +QEMU_BUILD_BUG_ON(offsetof(VirtIOSCSIReq, req.cdb) != + offsetof(VirtIOSCSIReq, req.cmd) + sizeof(VirtIOSCSICmdReq)); + static inline int virtio_scsi_get_lun(uint8_t *lun) { return ((lun[2] << 8) | lun[3]) & 0x3FFF; @@ -56,18 +64,41 @@ static inline SCSIDevice *virtio_scsi_device_find(VirtIOSCSI *s, uint8_t *lun) return scsi_device_find(&s->bus, 0, lun[1], virtio_scsi_get_lun(lun)); } +static VirtIOSCSIReq *virtio_scsi_init_req(VirtIOSCSI *s, VirtQueue *vq) +{ + VirtIOSCSIReq *req; + VirtIOSCSICommon *vs = VIRTIO_SCSI_COMMON(s); + + req = g_malloc0(sizeof(*req) + vs->cdb_size); + + req->vq = vq; + req->dev = s; + req->sreq = NULL; + qemu_sglist_init(&req->qsgl, DEVICE(s), 8, &address_space_memory); + qemu_iovec_init(&req->resp_iov, 1); + return req; +} + +static void virtio_scsi_free_req(VirtIOSCSIReq *req) +{ + qemu_iovec_destroy(&req->resp_iov); + qemu_sglist_destroy(&req->qsgl); + g_free(req); +} + static void virtio_scsi_complete_req(VirtIOSCSIReq *req) { VirtIOSCSI *s = req->dev; VirtQueue *vq = req->vq; VirtIODevice *vdev = VIRTIO_DEVICE(s); - virtqueue_push(vq, &req->elem, req->qsgl.size + req->elem.in_sg[0].iov_len); - qemu_sglist_destroy(&req->qsgl); + + qemu_iovec_from_buf(&req->resp_iov, 0, &req->resp, req->resp_size); + virtqueue_push(vq, &req->elem, req->qsgl.size + req->resp_iov.size); if (req->sreq) { req->sreq->hba_private = NULL; scsi_req_unref(req->sreq); } - g_free(req); + virtio_scsi_free_req(req); virtio_notify(vdev, vq); } @@ -77,50 +108,73 @@ static void virtio_scsi_bad_req(void) exit(1); } -static void qemu_sgl_init_external(VirtIOSCSIReq *req, struct iovec *sg, - hwaddr *addr, int num) +static size_t qemu_sgl_concat(VirtIOSCSIReq *req, struct iovec *iov, + hwaddr *addr, int num, size_t skip) { QEMUSGList *qsgl = &req->qsgl; - - qemu_sglist_init(qsgl, DEVICE(req->dev), num, &address_space_memory); - while (num--) { - qemu_sglist_add(qsgl, *(addr++), (sg++)->iov_len); + size_t copied = 0; + + while (num) { + if (skip >= iov->iov_len) { + skip -= iov->iov_len; + } else { + qemu_sglist_add(qsgl, *addr + skip, iov->iov_len - skip); + copied += iov->iov_len - skip; + skip = 0; + } + iov++; + addr++; + num--; } + + assert(skip == 0); + return copied; } -static void virtio_scsi_parse_req(VirtIOSCSI *s, VirtQueue *vq, - VirtIOSCSIReq *req) +static int virtio_scsi_parse_req(VirtIOSCSIReq *req, + unsigned req_size, unsigned resp_size) { - assert(req->elem.in_num); - req->vq = vq; - req->dev = s; - req->sreq = NULL; - if (req->elem.out_num) { - req->req.buf = req->elem.out_sg[0].iov_base; + size_t in_size, out_size; + + if (iov_to_buf(req->elem.out_sg, req->elem.out_num, 0, + &req->req, req_size) < req_size) { + return -EINVAL; } - req->resp.buf = req->elem.in_sg[0].iov_base; - if (req->elem.out_num > 1) { - qemu_sgl_init_external(req, &req->elem.out_sg[1], - &req->elem.out_addr[1], - req->elem.out_num - 1); - } else { - qemu_sgl_init_external(req, &req->elem.in_sg[1], - &req->elem.in_addr[1], - req->elem.in_num - 1); + if (qemu_iovec_concat_iov(&req->resp_iov, + req->elem.in_sg, req->elem.in_num, 0, + resp_size) < resp_size) { + return -EINVAL; + } + req->resp_size = resp_size; + + out_size = qemu_sgl_concat(req, req->elem.out_sg, + &req->elem.out_addr[0], req->elem.out_num, + req_size); + in_size = qemu_sgl_concat(req, req->elem.in_sg, + &req->elem.in_addr[0], req->elem.in_num, + resp_size); + + if (out_size && in_size) { + return -ENOTSUP; + } + + if (out_size) { + req->mode = SCSI_XFER_TO_DEV; + } else if (in_size) { + req->mode = SCSI_XFER_FROM_DEV; } + + return 0; } static VirtIOSCSIReq *virtio_scsi_pop_req(VirtIOSCSI *s, VirtQueue *vq) { - VirtIOSCSIReq *req; - req = g_malloc(sizeof(*req)); + VirtIOSCSIReq *req = virtio_scsi_init_req(s, vq); if (!virtqueue_pop(vq, &req->elem)) { - g_free(req); + virtio_scsi_free_req(req); return NULL; } - - virtio_scsi_parse_req(s, vq, req); return req; } @@ -143,9 +197,9 @@ static void *virtio_scsi_load_request(QEMUFile *f, SCSIRequest *sreq) VirtIOSCSIReq *req; uint32_t n; - req = g_malloc(sizeof(*req)); qemu_get_be32s(f, &n); assert(n < vs->conf.num_queues); + req = virtio_scsi_init_req(s, vs->cmd_vqs[n]); qemu_get_buffer(f, (unsigned char *)&req->elem, sizeof(req->elem)); /* TODO: add a way for SCSIBusInfo's load_request to fail, * and fail migration instead of asserting here. @@ -156,41 +210,44 @@ static void *virtio_scsi_load_request(QEMUFile *f, SCSIRequest *sreq) #endif assert(req->elem.in_num <= ARRAY_SIZE(req->elem.in_sg)); assert(req->elem.out_num <= ARRAY_SIZE(req->elem.out_sg)); - virtio_scsi_parse_req(s, vs->cmd_vqs[n], req); + + if (virtio_scsi_parse_req(req, sizeof(VirtIOSCSICmdReq) + vs->cdb_size, + sizeof(VirtIOSCSICmdResp) + vs->sense_size) < 0) { + error_report("invalid SCSI request migration data"); + exit(1); + } scsi_req_ref(sreq); req->sreq = sreq; if (req->sreq->cmd.mode != SCSI_XFER_NONE) { - int req_mode = - (req->elem.in_num > 1 ? SCSI_XFER_FROM_DEV : SCSI_XFER_TO_DEV); - - assert(req->sreq->cmd.mode == req_mode); + assert(req->sreq->cmd.mode == req->mode); } return req; } static void virtio_scsi_do_tmf(VirtIOSCSI *s, VirtIOSCSIReq *req) { - SCSIDevice *d = virtio_scsi_device_find(s, req->req.tmf->lun); + SCSIDevice *d = virtio_scsi_device_find(s, req->req.tmf.lun); SCSIRequest *r, *next; BusChild *kid; int target; /* Here VIRTIO_SCSI_S_OK means "FUNCTION COMPLETE". */ - req->resp.tmf->response = VIRTIO_SCSI_S_OK; + req->resp.tmf.response = VIRTIO_SCSI_S_OK; - switch (req->req.tmf->subtype) { + tswap32s(&req->req.tmf.subtype); + switch (req->req.tmf.subtype) { case VIRTIO_SCSI_T_TMF_ABORT_TASK: case VIRTIO_SCSI_T_TMF_QUERY_TASK: if (!d) { goto fail; } - if (d->lun != virtio_scsi_get_lun(req->req.tmf->lun)) { + if (d->lun != virtio_scsi_get_lun(req->req.tmf.lun)) { goto incorrect_lun; } QTAILQ_FOREACH_SAFE(r, &d->requests, next, next) { VirtIOSCSIReq *cmd_req = r->hba_private; - if (cmd_req && cmd_req->req.cmd->tag == req->req.tmf->tag) { + if (cmd_req && cmd_req->req.cmd.tag == req->req.tmf.tag) { break; } } @@ -200,11 +257,11 @@ static void virtio_scsi_do_tmf(VirtIOSCSI *s, VirtIOSCSIReq *req) * check for it in the loop above. */ assert(r->hba_private); - if (req->req.tmf->subtype == VIRTIO_SCSI_T_TMF_QUERY_TASK) { + if (req->req.tmf.subtype == VIRTIO_SCSI_T_TMF_QUERY_TASK) { /* "If the specified command is present in the task set, then * return a service response set to FUNCTION SUCCEEDED". */ - req->resp.tmf->response = VIRTIO_SCSI_S_FUNCTION_SUCCEEDED; + req->resp.tmf.response = VIRTIO_SCSI_S_FUNCTION_SUCCEEDED; } else { scsi_req_cancel(r); } @@ -215,7 +272,7 @@ static void virtio_scsi_do_tmf(VirtIOSCSI *s, VirtIOSCSIReq *req) if (!d) { goto fail; } - if (d->lun != virtio_scsi_get_lun(req->req.tmf->lun)) { + if (d->lun != virtio_scsi_get_lun(req->req.tmf.lun)) { goto incorrect_lun; } s->resetting++; @@ -229,16 +286,16 @@ static void virtio_scsi_do_tmf(VirtIOSCSI *s, VirtIOSCSIReq *req) if (!d) { goto fail; } - if (d->lun != virtio_scsi_get_lun(req->req.tmf->lun)) { + if (d->lun != virtio_scsi_get_lun(req->req.tmf.lun)) { goto incorrect_lun; } QTAILQ_FOREACH_SAFE(r, &d->requests, next, next) { if (r->hba_private) { - if (req->req.tmf->subtype == VIRTIO_SCSI_T_TMF_QUERY_TASK_SET) { + if (req->req.tmf.subtype == VIRTIO_SCSI_T_TMF_QUERY_TASK_SET) { /* "If there is any command present in the task set, then * return a service response set to FUNCTION SUCCEEDED". */ - req->resp.tmf->response = VIRTIO_SCSI_S_FUNCTION_SUCCEEDED; + req->resp.tmf.response = VIRTIO_SCSI_S_FUNCTION_SUCCEEDED; break; } else { scsi_req_cancel(r); @@ -248,7 +305,7 @@ static void virtio_scsi_do_tmf(VirtIOSCSI *s, VirtIOSCSIReq *req) break; case VIRTIO_SCSI_T_TMF_I_T_NEXUS_RESET: - target = req->req.tmf->lun[1]; + target = req->req.tmf.lun[1]; s->resetting++; QTAILQ_FOREACH(kid, &s->bus.qbus.children, sibling) { d = DO_UPCAST(SCSIDevice, qdev, kid->child); @@ -261,18 +318,18 @@ static void virtio_scsi_do_tmf(VirtIOSCSI *s, VirtIOSCSIReq *req) case VIRTIO_SCSI_T_TMF_CLEAR_ACA: default: - req->resp.tmf->response = VIRTIO_SCSI_S_FUNCTION_REJECTED; + req->resp.tmf.response = VIRTIO_SCSI_S_FUNCTION_REJECTED; break; } return; incorrect_lun: - req->resp.tmf->response = VIRTIO_SCSI_S_INCORRECT_LUN; + req->resp.tmf.response = VIRTIO_SCSI_S_INCORRECT_LUN; return; fail: - req->resp.tmf->response = VIRTIO_SCSI_S_BAD_TARGET; + req->resp.tmf.response = VIRTIO_SCSI_S_BAD_TARGET; } static void virtio_scsi_handle_ctrl(VirtIODevice *vdev, VirtQueue *vq) @@ -281,57 +338,70 @@ static void virtio_scsi_handle_ctrl(VirtIODevice *vdev, VirtQueue *vq) VirtIOSCSIReq *req; while ((req = virtio_scsi_pop_req(s, vq))) { - int out_size, in_size; - if (req->elem.out_num < 1 || req->elem.in_num < 1) { + int type; + + if (iov_to_buf(req->elem.out_sg, req->elem.out_num, 0, + &type, sizeof(type)) < sizeof(type)) { virtio_scsi_bad_req(); continue; } - out_size = req->elem.out_sg[0].iov_len; - in_size = req->elem.in_sg[0].iov_len; - if (req->req.tmf->type == VIRTIO_SCSI_T_TMF) { - if (out_size < sizeof(VirtIOSCSICtrlTMFReq) || - in_size < sizeof(VirtIOSCSICtrlTMFResp)) { + tswap32s(&req->req.tmf.type); + if (req->req.tmf.type == VIRTIO_SCSI_T_TMF) { + if (virtio_scsi_parse_req(req, sizeof(VirtIOSCSICtrlTMFReq), + sizeof(VirtIOSCSICtrlTMFResp)) < 0) { virtio_scsi_bad_req(); + } else { + virtio_scsi_do_tmf(s, req); } - virtio_scsi_do_tmf(s, req); - } else if (req->req.tmf->type == VIRTIO_SCSI_T_AN_QUERY || - req->req.tmf->type == VIRTIO_SCSI_T_AN_SUBSCRIBE) { - if (out_size < sizeof(VirtIOSCSICtrlANReq) || - in_size < sizeof(VirtIOSCSICtrlANResp)) { + } else if (req->req.tmf.type == VIRTIO_SCSI_T_AN_QUERY || + req->req.tmf.type == VIRTIO_SCSI_T_AN_SUBSCRIBE) { + if (virtio_scsi_parse_req(req, sizeof(VirtIOSCSICtrlANReq), + sizeof(VirtIOSCSICtrlANResp)) < 0) { virtio_scsi_bad_req(); + } else { + req->resp.an.event_actual = 0; + req->resp.an.response = VIRTIO_SCSI_S_OK; } - req->resp.an->event_actual = 0; - req->resp.an->response = VIRTIO_SCSI_S_OK; } virtio_scsi_complete_req(req); } } +static void virtio_scsi_complete_cmd_req(VirtIOSCSIReq *req) +{ + /* Sense data is not in req->resp and is copied separately + * in virtio_scsi_command_complete. + */ + req->resp_size = sizeof(VirtIOSCSICmdResp); + virtio_scsi_complete_req(req); +} + static void virtio_scsi_command_complete(SCSIRequest *r, uint32_t status, size_t resid) { VirtIOSCSIReq *req = r->hba_private; - VirtIOSCSI *s = req->dev; - VirtIOSCSICommon *vs = VIRTIO_SCSI_COMMON(s); + uint8_t sense[SCSI_SENSE_BUF_SIZE]; uint32_t sense_len; if (r->io_canceled) { return; } - req->resp.cmd->response = VIRTIO_SCSI_S_OK; - req->resp.cmd->status = status; - if (req->resp.cmd->status == GOOD) { - req->resp.cmd->resid = tswap32(resid); + req->resp.cmd.response = VIRTIO_SCSI_S_OK; + req->resp.cmd.status = status; + if (req->resp.cmd.status == GOOD) { + req->resp.cmd.resid = tswap32(resid); } else { - req->resp.cmd->resid = 0; - sense_len = scsi_req_get_sense(r, req->resp.cmd->sense, - vs->sense_size); - req->resp.cmd->sense_len = tswap32(sense_len); + req->resp.cmd.resid = 0; + sense_len = scsi_req_get_sense(r, sense, sizeof(sense)); + sense_len = MIN(sense_len, req->resp_iov.size - sizeof(req->resp.cmd)); + qemu_iovec_from_buf(&req->resp_iov, sizeof(req->resp.cmd), + &req->resp, sense_len); + req->resp.cmd.sense_len = tswap32(sense_len); } - virtio_scsi_complete_req(req); + virtio_scsi_complete_cmd_req(req); } static QEMUSGList *virtio_scsi_get_sg_list(SCSIRequest *r) @@ -349,17 +419,17 @@ static void virtio_scsi_request_cancelled(SCSIRequest *r) return; } if (req->dev->resetting) { - req->resp.cmd->response = VIRTIO_SCSI_S_RESET; + req->resp.cmd.response = VIRTIO_SCSI_S_RESET; } else { - req->resp.cmd->response = VIRTIO_SCSI_S_ABORTED; + req->resp.cmd.response = VIRTIO_SCSI_S_ABORTED; } - virtio_scsi_complete_req(req); + virtio_scsi_complete_cmd_req(req); } static void virtio_scsi_fail_cmd_req(VirtIOSCSIReq *req) { - req->resp.cmd->response = VIRTIO_SCSI_S_FAILURE; - virtio_scsi_complete_req(req); + req->resp.cmd.response = VIRTIO_SCSI_S_FAILURE; + virtio_scsi_complete_cmd_req(req); } static void virtio_scsi_handle_cmd(VirtIODevice *vdev, VirtQueue *vq) @@ -373,43 +443,35 @@ static void virtio_scsi_handle_cmd(VirtIODevice *vdev, VirtQueue *vq) while ((req = virtio_scsi_pop_req(s, vq))) { SCSIDevice *d; - int out_size, in_size; - if (req->elem.out_num < 1 || req->elem.in_num < 1) { - virtio_scsi_bad_req(); - } + int rc; - out_size = req->elem.out_sg[0].iov_len; - in_size = req->elem.in_sg[0].iov_len; - if (out_size < sizeof(VirtIOSCSICmdReq) + vs->cdb_size || - in_size < sizeof(VirtIOSCSICmdResp) + vs->sense_size) { - virtio_scsi_bad_req(); - } - - if (req->elem.out_num > 1 && req->elem.in_num > 1) { - virtio_scsi_fail_cmd_req(req); + rc = virtio_scsi_parse_req(req, sizeof(VirtIOSCSICmdReq) + vs->cdb_size, + sizeof(VirtIOSCSICmdResp) + vs->sense_size); + if (rc < 0) { + if (rc == -ENOTSUP) { + virtio_scsi_fail_cmd_req(req); + } else { + virtio_scsi_bad_req(); + } continue; } - d = virtio_scsi_device_find(s, req->req.cmd->lun); + d = virtio_scsi_device_find(s, req->req.cmd.lun); if (!d) { - req->resp.cmd->response = VIRTIO_SCSI_S_BAD_TARGET; - virtio_scsi_complete_req(req); + req->resp.cmd.response = VIRTIO_SCSI_S_BAD_TARGET; + virtio_scsi_complete_cmd_req(req); continue; } - req->sreq = scsi_req_new(d, req->req.cmd->tag, - virtio_scsi_get_lun(req->req.cmd->lun), - req->req.cmd->cdb, req); - - if (req->sreq->cmd.mode != SCSI_XFER_NONE) { - int req_mode = - (req->elem.in_num > 1 ? SCSI_XFER_FROM_DEV : SCSI_XFER_TO_DEV); - - if (req->sreq->cmd.mode != req_mode || - req->sreq->cmd.xfer > req->qsgl.size) { - req->resp.cmd->response = VIRTIO_SCSI_S_OVERRUN; - virtio_scsi_complete_req(req); - continue; - } + req->sreq = scsi_req_new(d, req->req.cmd.tag, + virtio_scsi_get_lun(req->req.cmd.lun), + req->req.cdb, req); + + if (req->sreq->cmd.mode != SCSI_XFER_NONE + && (req->sreq->cmd.mode != req->mode || + req->sreq->cmd.xfer > req->qsgl.size)) { + req->resp.cmd.response = VIRTIO_SCSI_S_OVERRUN; + virtio_scsi_complete_cmd_req(req); + continue; } n = scsi_req_enqueue(req->sreq); @@ -513,7 +575,7 @@ static void virtio_scsi_push_event(VirtIOSCSI *s, SCSIDevice *dev, return; } - if (req->elem.out_num || req->elem.in_num != 1) { + if (req->elem.out_num) { virtio_scsi_bad_req(); } @@ -522,12 +584,12 @@ static void virtio_scsi_push_event(VirtIOSCSI *s, SCSIDevice *dev, s->events_dropped = false; } - in_size = req->elem.in_sg[0].iov_len; + in_size = iov_size(req->elem.in_sg, req->elem.in_num); if (in_size < sizeof(VirtIOSCSIEvent)) { virtio_scsi_bad_req(); } - evt = req->resp.event; + evt = &req->resp.event; memset(evt, 0, sizeof(VirtIOSCSIEvent)); evt->event = event; evt->reason = reason; @@ -605,7 +667,9 @@ static struct SCSIBusInfo virtio_scsi_scsi_info = { .load_request = virtio_scsi_load_request, }; -void virtio_scsi_common_realize(DeviceState *dev, Error **errp) +void virtio_scsi_common_realize(DeviceState *dev, Error **errp, + HandleOutput ctrl, HandleOutput evt, + HandleOutput cmd) { VirtIODevice *vdev = VIRTIO_DEVICE(dev); VirtIOSCSICommon *s = VIRTIO_SCSI_COMMON(dev); @@ -619,12 +683,12 @@ void virtio_scsi_common_realize(DeviceState *dev, Error **errp) s->cdb_size = VIRTIO_SCSI_CDB_SIZE; s->ctrl_vq = virtio_add_queue(vdev, VIRTIO_SCSI_VQ_SIZE, - virtio_scsi_handle_ctrl); + ctrl); s->event_vq = virtio_add_queue(vdev, VIRTIO_SCSI_VQ_SIZE, - virtio_scsi_handle_event); + evt); for (i = 0; i < s->conf.num_queues; i++) { s->cmd_vqs[i] = virtio_add_queue(vdev, VIRTIO_SCSI_VQ_SIZE, - virtio_scsi_handle_cmd); + cmd); } } @@ -635,7 +699,9 @@ static void virtio_scsi_device_realize(DeviceState *dev, Error **errp) static int virtio_scsi_id; Error *err = NULL; - virtio_scsi_common_realize(dev, &err); + virtio_scsi_common_realize(dev, &err, virtio_scsi_handle_ctrl, + virtio_scsi_handle_event, + virtio_scsi_handle_cmd); if (err != NULL) { error_propagate(errp, err); return; diff --git a/include/block/scsi.h b/include/block/scsi.h index 9ab045b613..edde960d18 100644 --- a/include/block/scsi.h +++ b/include/block/scsi.h @@ -143,6 +143,8 @@ #define READ_CD 0xbe #define SEND_DVD_STRUCTURE 0xbf +const char *scsi_command_name(uint8_t cmd); + /* * SERVICE ACTION IN subcodes */ diff --git a/include/hw/i386/pc.h b/include/hw/i386/pc.h index 76d4c6e725..19f78ea336 100644 --- a/include/hw/i386/pc.h +++ b/include/hw/i386/pc.h @@ -322,6 +322,10 @@ bool e820_get_entry(int, uint32_t, uint64_t *, uint64_t *); #define PC_COMPAT_2_0 \ {\ + .driver = "virtio-scsi-pci",\ + .property = "any_layout",\ + .value = "off",\ + },{\ .driver = "PIIX4_PM",\ .property = "memory-hotplug-support",\ .value = "off",\ diff --git a/include/hw/virtio/virtio-scsi.h b/include/hw/virtio/virtio-scsi.h index 42b102487a..a8f618578b 100644 --- a/include/hw/virtio/virtio-scsi.h +++ b/include/hw/virtio/virtio-scsi.h @@ -84,14 +84,13 @@ #define VIRTIO_SCSI_EVT_RESET_RESCAN 1 #define VIRTIO_SCSI_EVT_RESET_REMOVED 2 -/* SCSI command request, followed by data-out */ +/* SCSI command request, followed by CDB and data-out */ typedef struct { uint8_t lun[8]; /* Logical Unit Number */ uint64_t tag; /* Command identifier */ uint8_t task_attr; /* Task attribute */ uint8_t prio; uint8_t crn; - uint8_t cdb[]; } QEMU_PACKED VirtIOSCSICmdReq; /* Response, followed by sense data and data-in */ @@ -101,7 +100,6 @@ typedef struct { uint16_t status_qualifier; /* Status qualifier */ uint8_t status; /* Command completion status */ uint8_t response; /* Response values */ - uint8_t sense[]; } QEMU_PACKED VirtIOSCSICmdResp; /* Task Management Request */ @@ -186,7 +184,12 @@ typedef struct { DEFINE_PROP_BIT("param_change", _state, _feature_field, \ VIRTIO_SCSI_F_CHANGE, true) -void virtio_scsi_common_realize(DeviceState *dev, Error **errp); +typedef void (*HandleOutput)(VirtIODevice *, VirtQueue *); + +void virtio_scsi_common_realize(DeviceState *dev, Error **errp, + HandleOutput ctrl, HandleOutput evt, + HandleOutput cmd); + void virtio_scsi_common_unrealize(DeviceState *dev, Error **errp); #endif /* _QEMU_VIRTIO_SCSI_H */ diff --git a/include/qemu-common.h b/include/qemu-common.h index 66ceceb2ad..ae76197532 100644 --- a/include/qemu-common.h +++ b/include/qemu-common.h @@ -315,9 +315,9 @@ void qemu_iovec_init_external(QEMUIOVector *qiov, struct iovec *iov, int niov); void qemu_iovec_add(QEMUIOVector *qiov, void *base, size_t len); void qemu_iovec_concat(QEMUIOVector *dst, QEMUIOVector *src, size_t soffset, size_t sbytes); -void qemu_iovec_concat_iov(QEMUIOVector *dst, - struct iovec *src_iov, unsigned int src_cnt, - size_t soffset, size_t sbytes); +size_t qemu_iovec_concat_iov(QEMUIOVector *dst, + struct iovec *src_iov, unsigned int src_cnt, + size_t soffset, size_t sbytes); bool qemu_iovec_is_zero(QEMUIOVector *qiov); void qemu_iovec_destroy(QEMUIOVector *qiov); void qemu_iovec_reset(QEMUIOVector *qiov); diff --git a/include/qemu/aes.h b/include/qemu/aes.h index c10666059f..a006da2224 100644 --- a/include/qemu/aes.h +++ b/include/qemu/aes.h @@ -10,6 +10,15 @@ struct aes_key_st { }; typedef struct aes_key_st AES_KEY; +/* FreeBSD has its own AES_set_decrypt_key in -lcrypto, avoid conflicts */ +#ifdef __FreeBSD__ +#define AES_set_encrypt_key QEMU_AES_set_encrypt_key +#define AES_set_decrypt_key QEMU_AES_set_decrypt_key +#define AES_encrypt QEMU_AES_encrypt +#define AES_decrypt QEMU_AES_decrypt +#define AES_cbc_encrypt QEMU_AES_cbc_encrypt +#endif + int AES_set_encrypt_key(const unsigned char *userKey, const int bits, AES_KEY *key); int AES_set_decrypt_key(const unsigned char *userKey, const int bits, diff --git a/include/qemu/bitops.h b/include/qemu/bitops.h index 340b1e73bd..7e2d5c996e 100644 --- a/include/qemu/bitops.h +++ b/include/qemu/bitops.h @@ -157,7 +157,17 @@ unsigned long find_next_zero_bit(const unsigned long *addr, static inline unsigned long find_first_bit(const unsigned long *addr, unsigned long size) { - return find_next_bit(addr, size, 0); + unsigned long result, tmp; + + for (result = 0; result < size; result += BITS_PER_LONG) { + tmp = *addr++; + if (tmp) { + result += ctzl(tmp); + return result < size ? result : size; + } + } + /* Not found */ + return size; } /** @@ -1755,6 +1755,22 @@ int kvm_cpu_exec(CPUState *cpu) case KVM_EXIT_INTERNAL_ERROR: ret = kvm_handle_internal_error(cpu, run); break; + case KVM_EXIT_SYSTEM_EVENT: + switch (run->system_event.type) { + case KVM_SYSTEM_EVENT_SHUTDOWN: + qemu_system_shutdown_request(); + ret = EXCP_INTERRUPT; + break; + case KVM_SYSTEM_EVENT_RESET: + qemu_system_reset_request(); + ret = EXCP_INTERRUPT; + break; + default: + DPRINTF("kvm_arch_handle_exit\n"); + ret = kvm_arch_handle_exit(cpu, run); + break; + } + break; default: DPRINTF("kvm_arch_handle_exit\n"); ret = kvm_arch_handle_exit(cpu, run); diff --git a/linux-user/mips/target_cpu.h b/linux-user/mips/target_cpu.h index ba8e9eb1f9..19b8855000 100644 --- a/linux-user/mips/target_cpu.h +++ b/linux-user/mips/target_cpu.h @@ -30,7 +30,7 @@ static inline void cpu_clone_regs(CPUMIPSState *env, target_ulong newsp) static inline void cpu_set_tls(CPUMIPSState *env, target_ulong newtls) { - env->tls_value = newtls; + env->active_tc.CP0_UserLocal = newtls; } #endif diff --git a/linux-user/syscall.c b/linux-user/syscall.c index c134c32d6f..7d7407920b 100644 --- a/linux-user/syscall.c +++ b/linux-user/syscall.c @@ -8702,7 +8702,7 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, #ifdef TARGET_NR_set_thread_area case TARGET_NR_set_thread_area: #if defined(TARGET_MIPS) - ((CPUMIPSState *) cpu_env)->tls_value = arg1; + ((CPUMIPSState *) cpu_env)->active_tc.CP0_UserLocal = arg1; ret = 0; break; #elif defined(TARGET_CRIS) diff --git a/target-arm/cpu-qom.h b/target-arm/cpu-qom.h index edc7f262fc..eaee9447ee 100644 --- a/target-arm/cpu-qom.h +++ b/target-arm/cpu-qom.h @@ -94,6 +94,12 @@ typedef struct ARMCPU { /* 'compatible' string for this CPU for Linux device trees */ const char *dtb_compatible; + /* PSCI version for this CPU + * Bits[31:16] = Major Version + * Bits[15:0] = Minor Version + */ + uint32_t psci_version; + /* Should CPU start in PSCI powered-off state? */ bool start_powered_off; @@ -102,6 +108,9 @@ typedef struct ARMCPU { */ uint32_t kvm_target; + /* KVM init features for this CPU */ + uint32_t kvm_init_features[7]; + /* The instance init functions for implementation-specific subclasses * set these fields to specify the implementation-dependent values of * various constant registers and reset values of non-constant diff --git a/target-arm/cpu.c b/target-arm/cpu.c index b8778350f7..05e52e0e83 100644 --- a/target-arm/cpu.c +++ b/target-arm/cpu.c @@ -260,6 +260,7 @@ static void arm_cpu_initfn(Object *obj) * picky DTB consumer will also provide a helpful error message. */ cpu->dtb_compatible = "qemu,unknown"; + cpu->psci_version = 1; /* By default assume PSCI v0.1 */ cpu->kvm_target = QEMU_KVM_ARM_TARGET_NONE; if (tcg_enabled() && !inited) { diff --git a/target-arm/cpu.h b/target-arm/cpu.h index 79e7f82515..369d4727ae 100644 --- a/target-arm/cpu.h +++ b/target-arm/cpu.h @@ -430,6 +430,22 @@ int arm_cpu_handle_mmu_fault(CPUState *cpu, vaddr address, int rw, /* Execution state bits. MRS read as zero, MSR writes ignored. */ #define CPSR_EXEC (CPSR_T | CPSR_IT | CPSR_J) +#define TTBCR_N (7U << 0) /* TTBCR.EAE==0 */ +#define TTBCR_T0SZ (7U << 0) /* TTBCR.EAE==1 */ +#define TTBCR_PD0 (1U << 4) +#define TTBCR_PD1 (1U << 5) +#define TTBCR_EPD0 (1U << 7) +#define TTBCR_IRGN0 (3U << 8) +#define TTBCR_ORGN0 (3U << 10) +#define TTBCR_SH0 (3U << 12) +#define TTBCR_T1SZ (3U << 16) +#define TTBCR_A1 (1U << 22) +#define TTBCR_EPD1 (1U << 23) +#define TTBCR_IRGN1 (3U << 24) +#define TTBCR_ORGN1 (3U << 26) +#define TTBCR_SH1 (1U << 28) +#define TTBCR_EAE (1U << 31) + /* Bit definitions for ARMv8 SPSR (PSTATE) format. * Only these are valid when in AArch64 mode; in * AArch32 mode SPSRs are basically CPSR-format. diff --git a/target-arm/helper.c b/target-arm/helper.c index 050c40981b..ed4d2bb419 100644 --- a/target-arm/helper.c +++ b/target-arm/helper.c @@ -312,7 +312,7 @@ static inline bool extended_addresses_enabled(CPUARMState *env) { return arm_el_is_aa64(env, 1) || ((arm_feature(env, ARM_FEATURE_LPAE) - && (env->cp15.c2_control & (1U << 31)))); + && (env->cp15.c2_control & TTBCR_EAE))); } static void dacr_write(CPUARMState *env, const ARMCPRegInfo *ri, uint64_t value) @@ -1413,11 +1413,22 @@ static void vmsa_ttbcr_raw_write(CPUARMState *env, const ARMCPRegInfo *ri, { int maskshift = extract32(value, 0, 3); - if (arm_feature(env, ARM_FEATURE_LPAE) && (value & (1 << 31))) { - value &= ~((7 << 19) | (3 << 14) | (0xf << 3)); - } else { - value &= 7; + if (!arm_feature(env, ARM_FEATURE_V8)) { + if (arm_feature(env, ARM_FEATURE_LPAE) && (value & TTBCR_EAE)) { + /* Pre ARMv8 bits [21:19], [15:14] and [6:3] are UNK/SBZP when + * using Long-desciptor translation table format */ + value &= ~((7 << 19) | (3 << 14) | (0xf << 3)); + } else if (arm_feature(env, ARM_FEATURE_EL3)) { + /* In an implementation that includes the Security Extensions + * TTBCR has additional fields PD0 [4] and PD1 [5] for + * Short-descriptor translation table format. + */ + value &= TTBCR_PD1 | TTBCR_PD0 | TTBCR_N; + } else { + value &= TTBCR_N; + } } + /* Note that we always calculate c2_mask and c2_base_mask, but * they are only used for short-descriptor tables (ie if EAE is 0); * for long-descriptor tables the TTBCR fields are used differently @@ -3540,17 +3551,24 @@ static inline int check_ap(CPUARMState *env, int ap, int domain_prot, } } -static uint32_t get_level1_table_address(CPUARMState *env, uint32_t address) +static bool get_level1_table_address(CPUARMState *env, uint32_t *table, + uint32_t address) { - uint32_t table; - - if (address & env->cp15.c2_mask) - table = env->cp15.ttbr1_el1 & 0xffffc000; - else - table = env->cp15.ttbr0_el1 & env->cp15.c2_base_mask; - - table |= (address >> 18) & 0x3ffc; - return table; + if (address & env->cp15.c2_mask) { + if ((env->cp15.c2_control & TTBCR_PD1)) { + /* Translation table walk disabled for TTBR1 */ + return false; + } + *table = env->cp15.ttbr1_el1 & 0xffffc000; + } else { + if ((env->cp15.c2_control & TTBCR_PD0)) { + /* Translation table walk disabled for TTBR0 */ + return false; + } + *table = env->cp15.ttbr0_el1 & env->cp15.c2_base_mask; + } + *table |= (address >> 18) & 0x3ffc; + return true; } static int get_phys_addr_v5(CPUARMState *env, uint32_t address, int access_type, @@ -3563,13 +3581,17 @@ static int get_phys_addr_v5(CPUARMState *env, uint32_t address, int access_type, uint32_t desc; int type; int ap; - int domain; + int domain = 0; int domain_prot; hwaddr phys_addr; /* Pagetable walk. */ /* Lookup l1 descriptor. */ - table = get_level1_table_address(env, address); + if (!get_level1_table_address(env, &table, address)) { + /* Section translation fault if page walk is disabled by PD0 or PD1 */ + code = 5; + goto do_fault; + } desc = ldl_phys(cs->as, table); type = (desc & 3); domain = (desc >> 5) & 0x0f; @@ -3667,7 +3689,11 @@ static int get_phys_addr_v6(CPUARMState *env, uint32_t address, int access_type, /* Pagetable walk. */ /* Lookup l1 descriptor. */ - table = get_level1_table_address(env, address); + if (!get_level1_table_address(env, &table, address)) { + /* Section translation fault if page walk is disabled by PD0 or PD1 */ + code = 5; + goto do_fault; + } desc = ldl_phys(cs->as, table); type = (desc & 3); if (type == 0 || (type == 3 && !arm_feature(env, ARM_FEATURE_PXN))) { @@ -3926,7 +3952,7 @@ static int get_phys_addr_lpae(CPUARMState *env, target_ulong address, * These are basically the same thing, although the number * of bits we pull in from the vaddr varies. */ - page_size = (1 << ((granule_sz * (4 - level)) + 3)); + page_size = (1ULL << ((granule_sz * (4 - level)) + 3)); descaddr |= (address & (page_size - 1)); /* Extract attributes from the descriptor and merge with table attrs */ attrs = extract64(descriptor, 2, 10) diff --git a/target-arm/kvm.c b/target-arm/kvm.c index 39202d7eea..319784d689 100644 --- a/target-arm/kvm.c +++ b/target-arm/kvm.c @@ -27,6 +27,17 @@ const KVMCapabilityInfo kvm_arch_required_capabilities[] = { KVM_CAP_LAST_INFO }; +int kvm_arm_vcpu_init(CPUState *cs) +{ + ARMCPU *cpu = ARM_CPU(cs); + struct kvm_vcpu_init init; + + init.target = cpu->kvm_target; + memcpy(init.features, cpu->kvm_init_features, sizeof(init.features)); + + return kvm_vcpu_ioctl(cs, KVM_ARM_VCPU_INIT, &init); +} + bool kvm_arm_create_scratch_host_vcpu(const uint32_t *cpus_to_try, int *fdarray, struct kvm_vcpu_init *init) diff --git a/target-arm/kvm32.c b/target-arm/kvm32.c index b79750c57e..068af7db57 100644 --- a/target-arm/kvm32.c +++ b/target-arm/kvm32.c @@ -166,7 +166,6 @@ static int compare_u64(const void *a, const void *b) int kvm_arch_init_vcpu(CPUState *cs) { - struct kvm_vcpu_init init; int i, ret, arraylen; uint64_t v; struct kvm_one_reg r; @@ -179,15 +178,22 @@ int kvm_arch_init_vcpu(CPUState *cs) return -EINVAL; } - init.target = cpu->kvm_target; - memset(init.features, 0, sizeof(init.features)); + /* Determine init features for this CPU */ + memset(cpu->kvm_init_features, 0, sizeof(cpu->kvm_init_features)); if (cpu->start_powered_off) { - init.features[0] = 1 << KVM_ARM_VCPU_POWER_OFF; + cpu->kvm_init_features[0] |= 1 << KVM_ARM_VCPU_POWER_OFF; + } + if (kvm_check_extension(cs->kvm_state, KVM_CAP_ARM_PSCI_0_2)) { + cpu->psci_version = 2; + cpu->kvm_init_features[0] |= 1 << KVM_ARM_VCPU_PSCI_0_2; } - ret = kvm_vcpu_ioctl(cs, KVM_ARM_VCPU_INIT, &init); + + /* Do KVM_ARM_VCPU_INIT ioctl */ + ret = kvm_arm_vcpu_init(cs); if (ret) { return ret; } + /* Query the kernel to make sure it supports 32 VFP * registers: QEMU's "cortex-a15" CPU is always a * VFP-D32 core. The simplest way to do this is just diff --git a/target-arm/kvm64.c b/target-arm/kvm64.c index 70f311bed6..5d217ca2ad 100644 --- a/target-arm/kvm64.c +++ b/target-arm/kvm64.c @@ -77,9 +77,8 @@ bool kvm_arm_get_host_cpu_features(ARMHostCPUClass *ahcc) int kvm_arch_init_vcpu(CPUState *cs) { - ARMCPU *cpu = ARM_CPU(cs); - struct kvm_vcpu_init init; int ret; + ARMCPU *cpu = ARM_CPU(cs); if (cpu->kvm_target == QEMU_KVM_ARM_TARGET_NONE || !arm_feature(&cpu->env, ARM_FEATURE_AARCH64)) { @@ -87,16 +86,25 @@ int kvm_arch_init_vcpu(CPUState *cs) return -EINVAL; } - init.target = cpu->kvm_target; - memset(init.features, 0, sizeof(init.features)); + /* Determine init features for this CPU */ + memset(cpu->kvm_init_features, 0, sizeof(cpu->kvm_init_features)); if (cpu->start_powered_off) { - init.features[0] = 1 << KVM_ARM_VCPU_POWER_OFF; + cpu->kvm_init_features[0] |= 1 << KVM_ARM_VCPU_POWER_OFF; + } + if (kvm_check_extension(cs->kvm_state, KVM_CAP_ARM_PSCI_0_2)) { + cpu->psci_version = 2; + cpu->kvm_init_features[0] |= 1 << KVM_ARM_VCPU_PSCI_0_2; + } + + /* Do KVM_ARM_VCPU_INIT ioctl */ + ret = kvm_arm_vcpu_init(cs); + if (ret) { + return ret; } - ret = kvm_vcpu_ioctl(cs, KVM_ARM_VCPU_INIT, &init); /* TODO : support for save/restore/reset of system regs via tuple list */ - return ret; + return 0; } #define AARCH64_CORE_REG(x) (KVM_REG_ARM64 | KVM_REG_SIZE_U64 | \ @@ -262,4 +270,8 @@ int kvm_arch_get_registers(CPUState *cs) void kvm_arm_reset_vcpu(ARMCPU *cpu) { + /* Re-init VCPU so that all registers are set to + * their respective reset values. + */ + kvm_arm_vcpu_init(CPU(cpu)); } diff --git a/target-arm/kvm_arm.h b/target-arm/kvm_arm.h index dc4e2336fa..af93105517 100644 --- a/target-arm/kvm_arm.h +++ b/target-arm/kvm_arm.h @@ -15,6 +15,18 @@ #include "exec/memory.h" /** + * kvm_arm_vcpu_init: + * @cs: CPUState + * + * Initialize (or reinitialize) the VCPU by invoking the + * KVM_ARM_VCPU_INIT ioctl with the CPU type and feature + * bitmask specified in the CPUState. + * + * Returns: 0 if success else < 0 error code + */ +int kvm_arm_vcpu_init(CPUState *cs); + +/** * kvm_arm_register_device: * @mr: memory region for this device * @devid: the KVM device ID diff --git a/target-arm/translate-a64.c b/target-arm/translate-a64.c index 63ad787e9f..33b5025fee 100644 --- a/target-arm/translate-a64.c +++ b/target-arm/translate-a64.c @@ -6539,7 +6539,7 @@ static void handle_simd_shift_fpint_conv(DisasContext *s, bool is_scalar, tcg_shift = tcg_const_i32(fracbits); if (is_double) { - int maxpass = is_scalar ? 1 : is_q ? 2 : 1; + int maxpass = is_scalar ? 1 : 2; for (pass = 0; pass < maxpass; pass++) { TCGv_i64 tcg_op = tcg_temp_new_i64(); @@ -9052,7 +9052,8 @@ static void disas_simd_3same_int(DisasContext *s, uint32_t insn) } if (size == 3) { - for (pass = 0; pass < (is_q ? 2 : 1); pass++) { + assert(is_q); + for (pass = 0; pass < 2; pass++) { TCGv_i64 tcg_op1 = tcg_temp_new_i64(); TCGv_i64 tcg_op2 = tcg_temp_new_i64(); TCGv_i64 tcg_res = tcg_temp_new_i64(); diff --git a/target-mips/cpu.h b/target-mips/cpu.h index a9b2c7ae38..8b9a92ebdc 100644 --- a/target-mips/cpu.h +++ b/target-mips/cpu.h @@ -168,6 +168,7 @@ struct TCState { target_ulong CP0_TCSchedule; target_ulong CP0_TCScheFBack; int32_t CP0_Debug_tcstatus; + target_ulong CP0_UserLocal; }; typedef struct CPUMIPSState CPUMIPSState; @@ -362,6 +363,7 @@ struct CPUMIPSState { int32_t CP0_Config3; #define CP0C3_M 31 #define CP0C3_ISA_ON_EXC 16 +#define CP0C3_ULRI 13 #define CP0C3_DSPP 10 #define CP0C3_LPA 7 #define CP0C3_VEIC 6 @@ -470,6 +472,8 @@ struct CPUMIPSState { /* MIPS DSP resources access. */ #define MIPS_HFLAG_DSP 0x40000 /* Enable access to MIPS DSP resources. */ #define MIPS_HFLAG_DSPR2 0x80000 /* Enable access to MIPS DSPR2 resources. */ + /* Extra flag about HWREna register. */ +#define MIPS_HFLAG_HWRENA_ULR 0x100000 /* ULR bit from HWREna is set. */ target_ulong btarget; /* Jump / branch target */ target_ulong bcond; /* Branch condition (if needed) */ @@ -479,8 +483,6 @@ struct CPUMIPSState { uint32_t CP0_TCStatus_rw_bitmask; /* Read/write bits in CP0_TCStatus */ int insn_flags; /* Supported instruction set */ - target_ulong tls_value; /* For usermode emulation */ - CPU_COMMON /* Fields from here on are preserved across CPU reset. */ @@ -523,7 +525,7 @@ void mips_cpu_list (FILE *f, fprintf_function cpu_fprintf); extern void cpu_wrdsp(uint32_t rs, uint32_t mask_num, CPUMIPSState *env); extern uint32_t cpu_rddsp(uint32_t mask_num, CPUMIPSState *env); -#define CPU_SAVE_VERSION 3 +#define CPU_SAVE_VERSION 4 /* MMU modes definitions. We carefully match the indices with our hflags layout. */ @@ -682,7 +684,8 @@ static inline void cpu_get_tb_cpu_state(CPUMIPSState *env, target_ulong *pc, { *pc = env->active_tc.PC; *cs_base = 0; - *flags = env->hflags & (MIPS_HFLAG_TMASK | MIPS_HFLAG_BMASK); + *flags = env->hflags & (MIPS_HFLAG_TMASK | MIPS_HFLAG_BMASK | + MIPS_HFLAG_HWRENA_ULR); } static inline int mips_vpe_active(CPUMIPSState *env) diff --git a/target-mips/machine.c b/target-mips/machine.c index 0a07db8540..0496faa910 100644 --- a/target-mips/machine.c +++ b/target-mips/machine.c @@ -25,6 +25,7 @@ static void save_tc(QEMUFile *f, TCState *tc) qemu_put_betls(f, &tc->CP0_TCSchedule); qemu_put_betls(f, &tc->CP0_TCScheFBack); qemu_put_sbe32s(f, &tc->CP0_Debug_tcstatus); + qemu_put_betls(f, &tc->CP0_UserLocal); } static void save_fpu(QEMUFile *f, CPUMIPSFPUContext *fpu) @@ -151,7 +152,7 @@ void cpu_save(QEMUFile *f, void *opaque) save_fpu(f, &env->fpus[i]); } -static void load_tc(QEMUFile *f, TCState *tc) +static void load_tc(QEMUFile *f, TCState *tc, int version_id) { int i; @@ -173,6 +174,9 @@ static void load_tc(QEMUFile *f, TCState *tc) qemu_get_betls(f, &tc->CP0_TCSchedule); qemu_get_betls(f, &tc->CP0_TCScheFBack); qemu_get_sbe32s(f, &tc->CP0_Debug_tcstatus); + if (version_id >= 4) { + qemu_get_betls(f, &tc->CP0_UserLocal); + } } static void load_fpu(QEMUFile *f, CPUMIPSFPUContext *fpu) @@ -194,11 +198,12 @@ int cpu_load(QEMUFile *f, void *opaque, int version_id) MIPSCPU *cpu = mips_env_get_cpu(env); int i; - if (version_id != 3) + if (version_id < 3) { return -EINVAL; + } /* Load active TC */ - load_tc(f, &env->active_tc); + load_tc(f, &env->active_tc, version_id); /* Load active FPU */ load_fpu(f, &env->active_fpu); @@ -298,8 +303,9 @@ int cpu_load(QEMUFile *f, void *opaque, int version_id) qemu_get_sbe32s(f, &env->CP0_DESAVE); /* Load inactive TC state */ - for (i = 0; i < MIPS_SHADOW_SET_MAX; i++) - load_tc(f, &env->tcs[i]); + for (i = 0; i < MIPS_SHADOW_SET_MAX; i++) { + load_tc(f, &env->tcs[i], version_id); + } for (i = 0; i < MIPS_FPU_MAX; i++) load_fpu(f, &env->fpus[i]); diff --git a/target-mips/op_helper.c b/target-mips/op_helper.c index 4704216834..27651a4a00 100644 --- a/target-mips/op_helper.c +++ b/target-mips/op_helper.c @@ -1297,7 +1297,19 @@ void helper_mtc0_srsconf4(CPUMIPSState *env, target_ulong arg1) void helper_mtc0_hwrena(CPUMIPSState *env, target_ulong arg1) { - env->CP0_HWREna = arg1 & 0x0000000F; + uint32_t mask = 0x0000000F; + + if (env->CP0_Config3 & (1 << CP0C3_ULRI)) { + mask |= (1 << 29); + + if (arg1 & (1 << 29)) { + env->hflags |= MIPS_HFLAG_HWRENA_ULR; + } else { + env->hflags &= ~MIPS_HFLAG_HWRENA_ULR; + } + } + + env->CP0_HWREna = arg1 & mask; } void helper_mtc0_count(CPUMIPSState *env, target_ulong arg1) diff --git a/target-mips/translate.c b/target-mips/translate.c index 76deb7b138..2b3befb0b4 100644 --- a/target-mips/translate.c +++ b/target-mips/translate.c @@ -1072,6 +1072,7 @@ typedef struct DisasContext { uint32_t hflags, saved_hflags; int bstate; target_ulong btarget; + bool ulri; } DisasContext; enum { @@ -4215,7 +4216,18 @@ static void gen_mfc0(DisasContext *ctx, TCGv arg, int reg, int sel) case 1: // gen_helper_mfc0_contextconfig(arg); /* SmartMIPS ASE */ rn = "ContextConfig"; + goto die; // break; + case 2: + if (ctx->ulri) { + tcg_gen_ld32s_tl(arg, cpu_env, + offsetof(CPUMIPSState, + active_tc.CP0_UserLocal)); + rn = "UserLocal"; + } else { + tcg_gen_movi_tl(arg, 0); + } + break; default: goto die; } @@ -4802,7 +4814,15 @@ static void gen_mtc0(DisasContext *ctx, TCGv arg, int reg, int sel) case 1: // gen_helper_mtc0_contextconfig(cpu_env, arg); /* SmartMIPS ASE */ rn = "ContextConfig"; + goto die; // break; + case 2: + if (ctx->ulri) { + tcg_gen_st_tl(arg, cpu_env, + offsetof(CPUMIPSState, active_tc.CP0_UserLocal)); + rn = "UserLocal"; + } + break; default: goto die; } @@ -4862,6 +4882,7 @@ static void gen_mtc0(DisasContext *ctx, TCGv arg, int reg, int sel) case 0: check_insn(ctx, ISA_MIPS32R2); gen_helper_mtc0_hwrena(cpu_env, arg); + ctx->bstate = BS_STOP; rn = "HWREna"; break; default: @@ -5406,7 +5427,17 @@ static void gen_dmfc0(DisasContext *ctx, TCGv arg, int reg, int sel) case 1: // gen_helper_dmfc0_contextconfig(arg); /* SmartMIPS ASE */ rn = "ContextConfig"; + goto die; // break; + case 2: + if (ctx->ulri) { + tcg_gen_ld_tl(arg, cpu_env, + offsetof(CPUMIPSState, active_tc.CP0_UserLocal)); + rn = "UserLocal"; + } else { + tcg_gen_movi_tl(arg, 0); + } + break; default: goto die; } @@ -5978,7 +6009,15 @@ static void gen_dmtc0(DisasContext *ctx, TCGv arg, int reg, int sel) case 1: // gen_helper_mtc0_contextconfig(cpu_env, arg); /* SmartMIPS ASE */ rn = "ContextConfig"; + goto die; // break; + case 2: + if (ctx->ulri) { + tcg_gen_st_tl(arg, cpu_env, + offsetof(CPUMIPSState, active_tc.CP0_UserLocal)); + rn = "UserLocal"; + } + break; default: goto die; } @@ -6038,6 +6077,7 @@ static void gen_dmtc0(DisasContext *ctx, TCGv arg, int reg, int sel) case 0: check_insn(ctx, ISA_MIPS32R2); gen_helper_mtc0_hwrena(cpu_env, arg); + ctx->bstate = BS_STOP; rn = "HWREna"; break; default: @@ -9060,12 +9100,20 @@ static void gen_rdhwr(DisasContext *ctx, int rt, int rd) break; case 29: #if defined(CONFIG_USER_ONLY) - tcg_gen_ld_tl(t0, cpu_env, offsetof(CPUMIPSState, tls_value)); + tcg_gen_ld_tl(t0, cpu_env, + offsetof(CPUMIPSState, active_tc.CP0_UserLocal)); gen_store_gpr(t0, rt); break; #else - /* XXX: Some CPUs implement this in hardware. - Not supported yet. */ + if ((ctx->hflags & MIPS_HFLAG_CP0) || + (ctx->hflags & MIPS_HFLAG_HWRENA_ULR)) { + tcg_gen_ld_tl(t0, cpu_env, + offsetof(CPUMIPSState, active_tc.CP0_UserLocal)); + gen_store_gpr(t0, rt); + } else { + generate_exception(ctx, EXCP_RI); + } + break; #endif default: /* Invalid */ MIPS_INVAL("rdhwr"); @@ -15609,6 +15657,7 @@ gen_intermediate_code_internal(MIPSCPU *cpu, TranslationBlock *tb, ctx.bstate = BS_NONE; /* Restore delay slot state from the tb context. */ ctx.hflags = (uint32_t)tb->flags; /* FIXME: maybe use 64 bits here? */ + ctx.ulri = env->CP0_Config3 & (1 << CP0C3_ULRI); restore_cpu_state(env, &ctx); #ifdef CONFIG_USER_ONLY ctx.mem_idx = MIPS_HFLAG_UM; diff --git a/tcg/optimize.c b/tcg/optimize.c index 16cebbe16d..34ae3c2857 100644 --- a/tcg/optimize.c +++ b/tcg/optimize.c @@ -911,12 +911,11 @@ static TCGArg *tcg_constant_folding(TCGContext *s, uint16_t *tcg_opc_ptr, break; } - /* 32-bit ops (non 64-bit ops and non load/store ops) generate - 32-bit results. For the result is zero test below, we can - ignore high bits, but for further optimizations we need to - record that the high bits contain garbage. */ + /* 32-bit ops generate 32-bit results. For the result is zero test + below, we can ignore high bits, but for further optimizations we + need to record that the high bits contain garbage. */ partmask = mask; - if (!(def->flags & (TCG_OPF_CALL_CLOBBER | TCG_OPF_64BIT))) { + if (!(def->flags & TCG_OPF_64BIT)) { mask |= ~(tcg_target_ulong)0xffffffffu; partmask &= 0xffffffffu; affected &= 0xffffffffu; diff --git a/ui/spice-display.c b/ui/spice-display.c index 03040b157f..66e25788ce 100644 --- a/ui/spice-display.c +++ b/ui/spice-display.c @@ -534,7 +534,7 @@ static void interface_release_resource(QXLInstance *sin, QXLCommandExt *ext; dprint(2, "%s/%d:\n", __func__, ssd->qxl.id); - ext = (void *)(rext.info->id); + ext = (void *)(intptr_t)(rext.info->id); switch (ext->cmd.type) { case QXL_CMD_DRAW: update = container_of(ext, SimpleSpiceUpdate, ext); diff --git a/ui/vnc-tls.c b/ui/vnc-tls.c index 50275de64f..63923265fd 100644 --- a/ui/vnc-tls.c +++ b/ui/vnc-tls.c @@ -443,10 +443,8 @@ static int vnc_set_x509_credential(VncDisplay *vd, { struct stat sb; - if (*cred) { - g_free(*cred); - *cred = NULL; - } + g_free(*cred); + *cred = NULL; *cred = g_malloc(strlen(certdir) + strlen(filename) + 2); @@ -935,6 +935,9 @@ static int vnc_update_client(VncState *vs, int has_dirty, bool sync) } vnc_job_push(job); + if (sync) { + vnc_jobs_join(vs); + } vs->force_update = 0; return n; } @@ -2972,10 +2975,8 @@ static void vnc_display_close(DisplayState *ds) if (!vs) return; - if (vs->display) { - g_free(vs->display); - vs->display = NULL; - } + g_free(vs->display); + vs->display = NULL; if (vs->lsock != -1) { qemu_set_fd_handler2(vs->lsock, NULL, NULL, NULL, NULL); close(vs->lsock); @@ -3010,13 +3011,8 @@ int vnc_display_password(DisplayState *ds, const char *password) return -EINVAL; } - if (vs->password) { - g_free(vs->password); - vs->password = NULL; - } - if (password) { - vs->password = g_strdup(password); - } + g_free(vs->password); + vs->password = g_strdup(password); return 0; } diff --git a/util/iov.c b/util/iov.c index 49f88388f8..2b4f46da75 100644 --- a/util/iov.c +++ b/util/iov.c @@ -295,15 +295,15 @@ void qemu_iovec_add(QEMUIOVector *qiov, void *base, size_t len) * of src". * Only vector pointers are processed, not the actual data buffers. */ -void qemu_iovec_concat_iov(QEMUIOVector *dst, - struct iovec *src_iov, unsigned int src_cnt, - size_t soffset, size_t sbytes) +size_t qemu_iovec_concat_iov(QEMUIOVector *dst, + struct iovec *src_iov, unsigned int src_cnt, + size_t soffset, size_t sbytes) { int i; size_t done; if (!sbytes) { - return; + return 0; } assert(dst->nalloc != -1); for (i = 0, done = 0; done < sbytes && i < src_cnt; i++) { @@ -317,6 +317,8 @@ void qemu_iovec_concat_iov(QEMUIOVector *dst, } } assert(soffset == 0); /* offset beyond end of src */ + + return done; } /* diff --git a/util/oslib-win32.c b/util/oslib-win32.c index ee6bcf1256..507cedd84d 100644 --- a/util/oslib-win32.c +++ b/util/oslib-win32.c @@ -24,6 +24,10 @@ * 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. + * + * The implementation of g_poll (functions poll_rest, g_poll) at the end of + * this file are based on code from GNOME glib-2 and use a different license, + * see the license comment there. */ #include <windows.h> #include <glib.h> @@ -138,7 +142,7 @@ int inet_aton(const char *cp, struct in_addr *ia) { uint32_t addr = inet_addr(cp); if (addr == 0xffffffff) { - return 0; + return 0; } ia->s_addr = addr; return 1; @@ -240,115 +244,207 @@ char *qemu_get_exec_dir(void) } /* - * g_poll has a problem on Windows when using - * timeouts < 10ms, in glib/gpoll.c: + * The original implementation of g_poll from glib has a problem on Windows + * when using timeouts < 10 ms. * - * // If not, and we have a significant timeout, poll again with - * // timeout then. Note that this will return indication for only - * // one event, or only for messages. We ignore timeouts less than - * // ten milliseconds as they are mostly pointless on Windows, the - * // MsgWaitForMultipleObjectsEx() call will timeout right away - * // anyway. + * Whenever g_poll is called with timeout < 10 ms, it does a quick poll instead + * of wait. This causes significant performance degradation of QEMU. * - * if (retval == 0 && (timeout == INFINITE || timeout >= 10)) - * retval = poll_rest (poll_msgs, handles, nhandles, fds, nfds, timeout); + * The following code is a copy of the original code from glib/gpoll.c + * (glib commit 20f4d1820b8d4d0fc4447188e33efffd6d4a88d8 from 2014-02-19). + * Some debug code was removed and the code was reformatted. + * All other code modifications are marked with 'QEMU'. + */ + +/* + * gpoll.c: poll(2) abstraction + * Copyright 1998 Owen Taylor + * Copyright 2008 Red Hat, Inc. * - * So whenever g_poll is called with timeout < 10ms it does - * a quick poll instead of wait, this causes significant performance - * degradation of QEMU, thus we should use WaitForMultipleObjectsEx - * directly + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see <http://www.gnu.org/licenses/>. */ -gint g_poll_fixed(GPollFD *fds, guint nfds, gint timeout) + +static int poll_rest(gboolean poll_msgs, HANDLE *handles, gint nhandles, + GPollFD *fds, guint nfds, gint timeout) { - guint i; - HANDLE handles[MAXIMUM_WAIT_OBJECTS]; - gint nhandles = 0; - int num_completed = 0; + DWORD ready; + GPollFD *f; + int recursed_result; - for (i = 0; i < nfds; i++) { - gint j; + if (poll_msgs) { + /* Wait for either messages or handles + * -> Use MsgWaitForMultipleObjectsEx + */ + ready = MsgWaitForMultipleObjectsEx(nhandles, handles, timeout, + QS_ALLINPUT, MWMO_ALERTABLE); - if (fds[i].fd <= 0) { - continue; + if (ready == WAIT_FAILED) { + gchar *emsg = g_win32_error_message(GetLastError()); + g_warning("MsgWaitForMultipleObjectsEx failed: %s", emsg); + g_free(emsg); } - - /* don't add same handle several times + } else if (nhandles == 0) { + /* No handles to wait for, just the timeout */ + if (timeout == INFINITE) { + ready = WAIT_FAILED; + } else { + SleepEx(timeout, TRUE); + ready = WAIT_TIMEOUT; + } + } else { + /* Wait for just handles + * -> Use WaitForMultipleObjectsEx */ - for (j = 0; j < nhandles; j++) { - if (handles[j] == (HANDLE)fds[i].fd) { - break; - } + ready = + WaitForMultipleObjectsEx(nhandles, handles, FALSE, timeout, TRUE); + if (ready == WAIT_FAILED) { + gchar *emsg = g_win32_error_message(GetLastError()); + g_warning("WaitForMultipleObjectsEx failed: %s", emsg); + g_free(emsg); } + } - if (j == nhandles) { - if (nhandles == MAXIMUM_WAIT_OBJECTS) { - fprintf(stderr, "Too many handles to wait for!\n"); - break; - } else { - handles[nhandles++] = (HANDLE)fds[i].fd; + if (ready == WAIT_FAILED) { + return -1; + } else if (ready == WAIT_TIMEOUT || ready == WAIT_IO_COMPLETION) { + return 0; + } else if (poll_msgs && ready == WAIT_OBJECT_0 + nhandles) { + for (f = fds; f < &fds[nfds]; ++f) { + if (f->fd == G_WIN32_MSG_HANDLE && f->events & G_IO_IN) { + f->revents |= G_IO_IN; } } - } - for (i = 0; i < nfds; ++i) { - fds[i].revents = 0; - } + /* If we have a timeout, or no handles to poll, be satisfied + * with just noticing we have messages waiting. + */ + if (timeout != 0 || nhandles == 0) { + return 1; + } - if (timeout == -1) { - timeout = INFINITE; - } + /* If no timeout and handles to poll, recurse to poll them, + * too. + */ + recursed_result = poll_rest(FALSE, handles, nhandles, fds, nfds, 0); + return (recursed_result == -1) ? -1 : 1 + recursed_result; + } else if (/* QEMU: removed the following unneeded statement which causes + * a compiler warning: ready >= WAIT_OBJECT_0 && */ + ready < WAIT_OBJECT_0 + nhandles) { + for (f = fds; f < &fds[nfds]; ++f) { + if ((HANDLE) f->fd == handles[ready - WAIT_OBJECT_0]) { + f->revents = f->events; + } + } - if (nhandles == 0) { - if (timeout == INFINITE) { - return -1; - } else { - SleepEx(timeout, TRUE); - return 0; + /* If no timeout and polling several handles, recurse to poll + * the rest of them. + */ + if (timeout == 0 && nhandles > 1) { + /* Remove the handle that fired */ + int i; + if (ready < nhandles - 1) { + for (i = ready - WAIT_OBJECT_0 + 1; i < nhandles; i++) { + handles[i-1] = handles[i]; + } + } + nhandles--; + recursed_result = poll_rest(FALSE, handles, nhandles, fds, nfds, 0); + return (recursed_result == -1) ? -1 : 1 + recursed_result; } + return 1; } - while (1) { - DWORD res; - gint j; - - res = WaitForMultipleObjectsEx(nhandles, handles, FALSE, - timeout, TRUE); + return 0; +} - if (res == WAIT_FAILED) { - for (i = 0; i < nfds; ++i) { - fds[i].revents = 0; +gint g_poll(GPollFD *fds, guint nfds, gint timeout) +{ + HANDLE handles[MAXIMUM_WAIT_OBJECTS]; + gboolean poll_msgs = FALSE; + GPollFD *f; + gint nhandles = 0; + int retval; + + for (f = fds; f < &fds[nfds]; ++f) { + if (f->fd == G_WIN32_MSG_HANDLE && (f->events & G_IO_IN)) { + poll_msgs = TRUE; + } else if (f->fd > 0) { + /* Don't add the same handle several times into the array, as + * docs say that is not allowed, even if it actually does seem + * to work. + */ + gint i; + + for (i = 0; i < nhandles; i++) { + if (handles[i] == (HANDLE) f->fd) { + break; + } } - return -1; - } else if ((res == WAIT_TIMEOUT) || (res == WAIT_IO_COMPLETION) || - ((int)res < (int)WAIT_OBJECT_0) || - (res >= (WAIT_OBJECT_0 + nhandles))) { - break; - } - - for (i = 0; i < nfds; ++i) { - if (handles[res - WAIT_OBJECT_0] == (HANDLE)fds[i].fd) { - fds[i].revents = fds[i].events; + if (i == nhandles) { + if (nhandles == MAXIMUM_WAIT_OBJECTS) { + g_warning("Too many handles to wait for!\n"); + break; + } else { + handles[nhandles++] = (HANDLE) f->fd; + } } } + } - ++num_completed; + for (f = fds; f < &fds[nfds]; ++f) { + f->revents = 0; + } - if (nhandles <= 1) { - break; - } + if (timeout == -1) { + timeout = INFINITE; + } - /* poll the rest of the handles + /* Polling for several things? */ + if (nhandles > 1 || (nhandles > 0 && poll_msgs)) { + /* First check if one or several of them are immediately + * available + */ + retval = poll_rest(poll_msgs, handles, nhandles, fds, nfds, 0); + + /* If not, and we have a significant timeout, poll again with + * timeout then. Note that this will return indication for only + * one event, or only for messages. We ignore timeouts less than + * ten milliseconds as they are mostly pointless on Windows, the + * MsgWaitForMultipleObjectsEx() call will timeout right away + * anyway. + * + * Modification for QEMU: replaced timeout >= 10 by timeout > 0. */ - for (j = res - WAIT_OBJECT_0 + 1; j < nhandles; j++) { - handles[j - 1] = handles[j]; + if (retval == 0 && (timeout == INFINITE || timeout > 0)) { + retval = poll_rest(poll_msgs, handles, nhandles, + fds, nfds, timeout); } - --nhandles; + } else { + /* Just polling for one thing, so no need to check first if + * available immediately + */ + retval = poll_rest(poll_msgs, handles, nhandles, fds, nfds, timeout); + } - timeout = 0; + if (retval == -1) { + for (f = fds; f < &fds[nfds]; ++f) { + f->revents = 0; + } } - return num_completed; + return retval; } size_t getpagesize(void) |