diff options
324 files changed, 12655 insertions, 5349 deletions
diff --git a/Makefile.objs b/Makefile.objs index 69fdd48..06f74b8 100644 --- a/Makefile.objs +++ b/Makefile.objs @@ -89,7 +89,7 @@ endif ####################################################################### # Target-independent parts used in system and user emulation -common-obj-y += tcg-runtime.o cpus-common.o +common-obj-y += cpus-common.o common-obj-y += hw/ common-obj-y += qom/ common-obj-y += disas/ @@ -162,3 +162,4 @@ trace-events-y += target-s390x/trace-events trace-events-y += target-ppc/trace-events trace-events-y += qom/trace-events trace-events-y += linux-user/trace-events +trace-events-y += qapi/trace-events diff --git a/Makefile.target b/Makefile.target index 2c46091..7a5080e 100644 --- a/Makefile.target +++ b/Makefile.target @@ -94,6 +94,7 @@ obj-$(CONFIG_TCG_INTERPRETER) += disas/tci.o obj-y += fpu/softfloat.o obj-y += target-$(TARGET_BASE_ARCH)/ obj-y += disas.o +obj-y += tcg-runtime.o obj-$(call notempty,$(TARGET_XML_FILES)) += gdbstub-xml.o obj-$(call lnot,$(CONFIG_KVM)) += kvm-stub.o diff --git a/atomic_template.h b/atomic_template.h new file mode 100644 index 0000000..b400b2a --- /dev/null +++ b/atomic_template.h @@ -0,0 +1,215 @@ +/* + * Atomic helper templates + * Included from tcg-runtime.c and cputlb.c. + * + * Copyright (c) 2016 Red Hat, Inc + * + * 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/>. + */ + +#if DATA_SIZE == 16 +# define SUFFIX o +# define DATA_TYPE Int128 +# define BSWAP bswap128 +#elif DATA_SIZE == 8 +# define SUFFIX q +# define DATA_TYPE uint64_t +# define BSWAP bswap64 +#elif DATA_SIZE == 4 +# define SUFFIX l +# define DATA_TYPE uint32_t +# define BSWAP bswap32 +#elif DATA_SIZE == 2 +# define SUFFIX w +# define DATA_TYPE uint16_t +# define BSWAP bswap16 +#elif DATA_SIZE == 1 +# define SUFFIX b +# define DATA_TYPE uint8_t +# define BSWAP +#else +# error unsupported data size +#endif + +#if DATA_SIZE >= 4 +# define ABI_TYPE DATA_TYPE +#else +# define ABI_TYPE uint32_t +#endif + +/* Define host-endian atomic operations. Note that END is used within + the ATOMIC_NAME macro, and redefined below. */ +#if DATA_SIZE == 1 +# define END +#elif defined(HOST_WORDS_BIGENDIAN) +# define END _be +#else +# define END _le +#endif + +ABI_TYPE ATOMIC_NAME(cmpxchg)(CPUArchState *env, target_ulong addr, + ABI_TYPE cmpv, ABI_TYPE newv EXTRA_ARGS) +{ + DATA_TYPE *haddr = ATOMIC_MMU_LOOKUP; + return atomic_cmpxchg__nocheck(haddr, cmpv, newv); +} + +#if DATA_SIZE >= 16 +ABI_TYPE ATOMIC_NAME(ld)(CPUArchState *env, target_ulong addr EXTRA_ARGS) +{ + DATA_TYPE val, *haddr = ATOMIC_MMU_LOOKUP; + __atomic_load(haddr, &val, __ATOMIC_RELAXED); + return val; +} + +void ATOMIC_NAME(st)(CPUArchState *env, target_ulong addr, + ABI_TYPE val EXTRA_ARGS) +{ + DATA_TYPE *haddr = ATOMIC_MMU_LOOKUP; + __atomic_store(haddr, &val, __ATOMIC_RELAXED); +} +#else +ABI_TYPE ATOMIC_NAME(xchg)(CPUArchState *env, target_ulong addr, + ABI_TYPE val EXTRA_ARGS) +{ + DATA_TYPE *haddr = ATOMIC_MMU_LOOKUP; + return atomic_xchg__nocheck(haddr, val); +} + +#define GEN_ATOMIC_HELPER(X) \ +ABI_TYPE ATOMIC_NAME(X)(CPUArchState *env, target_ulong addr, \ + ABI_TYPE val EXTRA_ARGS) \ +{ \ + DATA_TYPE *haddr = ATOMIC_MMU_LOOKUP; \ + return atomic_##X(haddr, val); \ +} \ + +GEN_ATOMIC_HELPER(fetch_add) +GEN_ATOMIC_HELPER(fetch_and) +GEN_ATOMIC_HELPER(fetch_or) +GEN_ATOMIC_HELPER(fetch_xor) +GEN_ATOMIC_HELPER(add_fetch) +GEN_ATOMIC_HELPER(and_fetch) +GEN_ATOMIC_HELPER(or_fetch) +GEN_ATOMIC_HELPER(xor_fetch) + +#undef GEN_ATOMIC_HELPER +#endif /* DATA SIZE >= 16 */ + +#undef END + +#if DATA_SIZE > 1 + +/* Define reverse-host-endian atomic operations. Note that END is used + within the ATOMIC_NAME macro. */ +#ifdef HOST_WORDS_BIGENDIAN +# define END _le +#else +# define END _be +#endif + +ABI_TYPE ATOMIC_NAME(cmpxchg)(CPUArchState *env, target_ulong addr, + ABI_TYPE cmpv, ABI_TYPE newv EXTRA_ARGS) +{ + DATA_TYPE *haddr = ATOMIC_MMU_LOOKUP; + return BSWAP(atomic_cmpxchg__nocheck(haddr, BSWAP(cmpv), BSWAP(newv))); +} + +#if DATA_SIZE >= 16 +ABI_TYPE ATOMIC_NAME(ld)(CPUArchState *env, target_ulong addr EXTRA_ARGS) +{ + DATA_TYPE val, *haddr = ATOMIC_MMU_LOOKUP; + __atomic_load(haddr, &val, __ATOMIC_RELAXED); + return BSWAP(val); +} + +void ATOMIC_NAME(st)(CPUArchState *env, target_ulong addr, + ABI_TYPE val EXTRA_ARGS) +{ + DATA_TYPE *haddr = ATOMIC_MMU_LOOKUP; + val = BSWAP(val); + __atomic_store(haddr, &val, __ATOMIC_RELAXED); +} +#else +ABI_TYPE ATOMIC_NAME(xchg)(CPUArchState *env, target_ulong addr, + ABI_TYPE val EXTRA_ARGS) +{ + DATA_TYPE *haddr = ATOMIC_MMU_LOOKUP; + return BSWAP(atomic_xchg__nocheck(haddr, BSWAP(val))); +} + +#define GEN_ATOMIC_HELPER(X) \ +ABI_TYPE ATOMIC_NAME(X)(CPUArchState *env, target_ulong addr, \ + ABI_TYPE val EXTRA_ARGS) \ +{ \ + DATA_TYPE *haddr = ATOMIC_MMU_LOOKUP; \ + return BSWAP(atomic_##X(haddr, BSWAP(val))); \ +} + +GEN_ATOMIC_HELPER(fetch_and) +GEN_ATOMIC_HELPER(fetch_or) +GEN_ATOMIC_HELPER(fetch_xor) +GEN_ATOMIC_HELPER(and_fetch) +GEN_ATOMIC_HELPER(or_fetch) +GEN_ATOMIC_HELPER(xor_fetch) + +#undef GEN_ATOMIC_HELPER + +/* Note that for addition, we need to use a separate cmpxchg loop instead + of bswaps for the reverse-host-endian helpers. */ +ABI_TYPE ATOMIC_NAME(fetch_add)(CPUArchState *env, target_ulong addr, + ABI_TYPE val EXTRA_ARGS) +{ + DATA_TYPE *haddr = ATOMIC_MMU_LOOKUP; + DATA_TYPE ldo, ldn, ret, sto; + + ldo = atomic_read__nocheck(haddr); + while (1) { + ret = BSWAP(ldo); + sto = BSWAP(ret + val); + ldn = atomic_cmpxchg__nocheck(haddr, ldo, sto); + if (ldn == ldo) { + return ret; + } + ldo = ldn; + } +} + +ABI_TYPE ATOMIC_NAME(add_fetch)(CPUArchState *env, target_ulong addr, + ABI_TYPE val EXTRA_ARGS) +{ + DATA_TYPE *haddr = ATOMIC_MMU_LOOKUP; + DATA_TYPE ldo, ldn, ret, sto; + + ldo = atomic_read__nocheck(haddr); + while (1) { + ret = BSWAP(ldo) + val; + sto = BSWAP(ret); + ldn = atomic_cmpxchg__nocheck(haddr, ldo, sto); + if (ldn == ldo) { + return ret; + } + ldo = ldn; + } +} +#endif /* DATA_SIZE >= 16 */ + +#undef END +#endif /* DATA_SIZE > 1 */ + +#undef BSWAP +#undef ABI_TYPE +#undef DATA_TYPE +#undef SUFFIX +#undef DATA_SIZE diff --git a/backends/baum.c b/backends/baum.c index c537141..919844e 100644 --- a/backends/baum.c +++ b/backends/baum.c @@ -551,7 +551,7 @@ static void baum_chr_read(void *opaque) } } -static void baum_close(struct CharDriverState *chr) +static void baum_free(struct CharDriverState *chr) { BaumDriverState *baum = chr->opaque; @@ -566,6 +566,7 @@ static void baum_close(struct CharDriverState *chr) static CharDriverState *chr_baum_init(const char *id, ChardevBackend *backend, ChardevReturn *ret, + bool *be_opened, Error **errp) { ChardevCommon *common = backend->u.braille.data; @@ -589,7 +590,7 @@ static CharDriverState *chr_baum_init(const char *id, chr->opaque = baum; chr->chr_write = baum_write; chr->chr_accept_input = baum_accept_input; - chr->chr_close = baum_close; + chr->chr_free = baum_free; handle = g_malloc0(brlapi_getHandleSize()); baum->brlapi = handle; diff --git a/backends/msmouse.c b/backends/msmouse.c index 85d08f7..733ca80 100644 --- a/backends/msmouse.c +++ b/backends/msmouse.c @@ -133,7 +133,7 @@ static int msmouse_chr_write (struct CharDriverState *s, const uint8_t *buf, int return len; } -static void msmouse_chr_close (struct CharDriverState *chr) +static void msmouse_chr_free(struct CharDriverState *chr) { MouseState *mouse = chr->opaque; @@ -151,6 +151,7 @@ static QemuInputHandler msmouse_handler = { static CharDriverState *qemu_chr_open_msmouse(const char *id, ChardevBackend *backend, ChardevReturn *ret, + bool *be_opened, Error **errp) { ChardevCommon *common = backend->u.msmouse.data; @@ -162,9 +163,9 @@ static CharDriverState *qemu_chr_open_msmouse(const char *id, return NULL; } chr->chr_write = msmouse_chr_write; - chr->chr_close = msmouse_chr_close; + chr->chr_free = msmouse_chr_free; chr->chr_accept_input = msmouse_chr_accept_input; - chr->explicit_be_open = true; + *be_opened = false; mouse = g_new0(MouseState, 1); mouse->hs = qemu_input_handler_register((DeviceState *)mouse, diff --git a/backends/rng-egd.c b/backends/rng-egd.c index ba17c07..69c04b1 100644 --- a/backends/rng-egd.c +++ b/backends/rng-egd.c @@ -15,7 +15,6 @@ #include "sysemu/char.h" #include "qapi/error.h" #include "qapi/qmp/qerror.h" -#include "hw/qdev.h" /* just for DEFINE_PROP_CHR */ #define TYPE_RNG_EGD "rng-egd" #define RNG_EGD(obj) OBJECT_CHECK(RngEgd, (obj), TYPE_RNG_EGD) @@ -24,7 +23,7 @@ typedef struct RngEgd { RngBackend parent; - CharDriverState *chr; + CharBackend chr; char *chr_name; } RngEgd; @@ -43,7 +42,7 @@ static void rng_egd_request_entropy(RngBackend *b, RngRequest *req) /* XXX this blocks entire thread. Rewrite to use * qemu_chr_fe_write and background I/O callbacks */ - qemu_chr_fe_write_all(s->chr, header, sizeof(header)); + qemu_chr_fe_write_all(&s->chr, header, sizeof(header)); size -= len; } @@ -87,6 +86,7 @@ static void rng_egd_chr_read(void *opaque, const uint8_t *buf, int size) static void rng_egd_opened(RngBackend *b, Error **errp) { RngEgd *s = RNG_EGD(b); + CharDriverState *chr; if (s->chr_name == NULL) { error_setg(errp, QERR_INVALID_PARAMETER_VALUE, @@ -94,21 +94,19 @@ static void rng_egd_opened(RngBackend *b, Error **errp) return; } - s->chr = qemu_chr_find(s->chr_name); - if (s->chr == NULL) { + chr = qemu_chr_find(s->chr_name); + if (chr == NULL) { error_set(errp, ERROR_CLASS_DEVICE_NOT_FOUND, "Device '%s' not found", s->chr_name); return; } - - if (qemu_chr_fe_claim(s->chr) != 0) { - error_setg(errp, QERR_DEVICE_IN_USE, s->chr_name); + if (!qemu_chr_fe_init(&s->chr, chr, errp)) { return; } /* FIXME we should resubmit pending requests when the CDS reconnects. */ - qemu_chr_add_handlers(s->chr, rng_egd_chr_can_read, rng_egd_chr_read, - NULL, s); + qemu_chr_fe_set_handlers(&s->chr, rng_egd_chr_can_read, + rng_egd_chr_read, NULL, s, NULL, true); } static void rng_egd_set_chardev(Object *obj, const char *value, Error **errp) @@ -127,9 +125,10 @@ static void rng_egd_set_chardev(Object *obj, const char *value, Error **errp) static char *rng_egd_get_chardev(Object *obj, Error **errp) { RngEgd *s = RNG_EGD(obj); + CharDriverState *chr = qemu_chr_fe_get_driver(&s->chr); - if (s->chr && s->chr->label) { - return g_strdup(s->chr->label); + if (chr && chr->label) { + return g_strdup(chr->label); } return NULL; @@ -146,11 +145,7 @@ static void rng_egd_finalize(Object *obj) { RngEgd *s = RNG_EGD(obj); - if (s->chr) { - qemu_chr_add_handlers(s->chr, NULL, NULL, NULL, NULL); - qemu_chr_fe_release(s->chr); - } - + qemu_chr_fe_deinit(&s->chr); g_free(s->chr_name); } diff --git a/backends/testdev.c b/backends/testdev.c index 3ab1c90..60156e3 100644 --- a/backends/testdev.c +++ b/backends/testdev.c @@ -102,7 +102,7 @@ static int testdev_write(CharDriverState *chr, const uint8_t *buf, int len) return orig_len; } -static void testdev_close(struct CharDriverState *chr) +static void testdev_free(struct CharDriverState *chr) { TestdevCharState *testdev = chr->opaque; @@ -112,6 +112,7 @@ static void testdev_close(struct CharDriverState *chr) static CharDriverState *chr_testdev_init(const char *id, ChardevBackend *backend, ChardevReturn *ret, + bool *be_opened, Error **errp) { TestdevCharState *testdev; @@ -122,7 +123,7 @@ static CharDriverState *chr_testdev_init(const char *id, chr->opaque = testdev; chr->chr_write = testdev_write; - chr->chr_close = testdev_close; + chr->chr_free = testdev_free; return chr; } diff --git a/block/backup.c b/block/backup.c index 582bd0f..02dbe48 100644 --- a/block/backup.c +++ b/block/backup.c @@ -372,14 +372,14 @@ static int coroutine_fn backup_run_incremental(BackupBlockJob *job) int64_t end; int64_t last_cluster = -1; int64_t sectors_per_cluster = cluster_size_sectors(job); - HBitmapIter hbi; + BdrvDirtyBitmapIter *dbi; granularity = bdrv_dirty_bitmap_granularity(job->sync_bitmap); clusters_per_iter = MAX((granularity / job->cluster_size), 1); - bdrv_dirty_iter_init(job->sync_bitmap, &hbi); + dbi = bdrv_dirty_iter_new(job->sync_bitmap, 0); /* Find the next dirty sector(s) */ - while ((sector = hbitmap_iter_next(&hbi)) != -1) { + while ((sector = bdrv_dirty_iter_next(dbi)) != -1) { cluster = sector / sectors_per_cluster; /* Fake progress updates for any clusters we skipped */ @@ -391,7 +391,7 @@ static int coroutine_fn backup_run_incremental(BackupBlockJob *job) for (end = cluster + clusters_per_iter; cluster < end; cluster++) { do { if (yield_and_check(job)) { - return ret; + goto out; } ret = backup_do_cow(job, cluster * sectors_per_cluster, sectors_per_cluster, &error_is_read, @@ -399,7 +399,7 @@ static int coroutine_fn backup_run_incremental(BackupBlockJob *job) if ((ret < 0) && backup_error_action(job, error_is_read, -ret) == BLOCK_ERROR_ACTION_REPORT) { - return ret; + goto out; } } while (ret < 0); } @@ -407,7 +407,7 @@ static int coroutine_fn backup_run_incremental(BackupBlockJob *job) /* If the bitmap granularity is smaller than the backup granularity, * we need to advance the iterator pointer to the next cluster. */ if (granularity < job->cluster_size) { - bdrv_set_dirty_iter(&hbi, cluster * sectors_per_cluster); + bdrv_set_dirty_iter(dbi, cluster * sectors_per_cluster); } last_cluster = cluster - 1; @@ -419,6 +419,8 @@ static int coroutine_fn backup_run_incremental(BackupBlockJob *job) job->common.offset += ((end - last_cluster - 1) * job->cluster_size); } +out: + bdrv_dirty_iter_free(dbi); return ret; } diff --git a/block/block-backend.c b/block/block-backend.c index 1a724a8..c53ca30 100644 --- a/block/block-backend.c +++ b/block/block-backend.c @@ -1099,26 +1099,36 @@ BlockAIOCB *blk_aio_pwritev(BlockBackend *blk, int64_t offset, blk_aio_write_entry, flags, cb, opaque); } +static void blk_aio_flush_entry(void *opaque) +{ + BlkAioEmAIOCB *acb = opaque; + BlkRwCo *rwco = &acb->rwco; + + rwco->ret = blk_co_flush(rwco->blk); + blk_aio_complete(acb); +} + BlockAIOCB *blk_aio_flush(BlockBackend *blk, BlockCompletionFunc *cb, void *opaque) { - if (!blk_is_available(blk)) { - return blk_abort_aio_request(blk, cb, opaque, -ENOMEDIUM); - } + return blk_aio_prwv(blk, 0, 0, NULL, blk_aio_flush_entry, 0, cb, opaque); +} + +static void blk_aio_pdiscard_entry(void *opaque) +{ + BlkAioEmAIOCB *acb = opaque; + BlkRwCo *rwco = &acb->rwco; - return bdrv_aio_flush(blk_bs(blk), cb, opaque); + rwco->ret = blk_co_pdiscard(rwco->blk, rwco->offset, acb->bytes); + blk_aio_complete(acb); } BlockAIOCB *blk_aio_pdiscard(BlockBackend *blk, int64_t offset, int count, BlockCompletionFunc *cb, void *opaque) { - int ret = blk_check_byte_request(blk, offset, count); - if (ret < 0) { - return blk_abort_aio_request(blk, cb, opaque, ret); - } - - return bdrv_aio_pdiscard(blk_bs(blk), offset, count, cb, opaque); + return blk_aio_prwv(blk, offset, count, NULL, blk_aio_pdiscard_entry, 0, + cb, opaque); } void blk_aio_cancel(BlockAIOCB *acb) @@ -1131,23 +1141,50 @@ void blk_aio_cancel_async(BlockAIOCB *acb) bdrv_aio_cancel_async(acb); } -int blk_ioctl(BlockBackend *blk, unsigned long int req, void *buf) +int blk_co_ioctl(BlockBackend *blk, unsigned long int req, void *buf) { if (!blk_is_available(blk)) { return -ENOMEDIUM; } - return bdrv_ioctl(blk_bs(blk), req, buf); + return bdrv_co_ioctl(blk_bs(blk), req, buf); +} + +static void blk_ioctl_entry(void *opaque) +{ + BlkRwCo *rwco = opaque; + rwco->ret = blk_co_ioctl(rwco->blk, rwco->offset, + rwco->qiov->iov[0].iov_base); +} + +int blk_ioctl(BlockBackend *blk, unsigned long int req, void *buf) +{ + return blk_prw(blk, req, buf, 0, blk_ioctl_entry, 0); +} + +static void blk_aio_ioctl_entry(void *opaque) +{ + BlkAioEmAIOCB *acb = opaque; + BlkRwCo *rwco = &acb->rwco; + + rwco->ret = blk_co_ioctl(rwco->blk, rwco->offset, + rwco->qiov->iov[0].iov_base); + blk_aio_complete(acb); } BlockAIOCB *blk_aio_ioctl(BlockBackend *blk, unsigned long int req, void *buf, BlockCompletionFunc *cb, void *opaque) { - if (!blk_is_available(blk)) { - return blk_abort_aio_request(blk, cb, opaque, -ENOMEDIUM); - } + QEMUIOVector qiov; + struct iovec iov; - return bdrv_aio_ioctl(blk_bs(blk), req, buf, cb, opaque); + iov = (struct iovec) { + .iov_base = buf, + .iov_len = 0, + }; + qemu_iovec_init_external(&qiov, &iov, 1); + + return blk_aio_prwv(blk, req, 0, &qiov, blk_aio_ioctl_entry, 0, cb, opaque); } int blk_co_pdiscard(BlockBackend *blk, int64_t offset, int count) @@ -1169,13 +1206,15 @@ int blk_co_flush(BlockBackend *blk) return bdrv_co_flush(blk_bs(blk)); } -int blk_flush(BlockBackend *blk) +static void blk_flush_entry(void *opaque) { - if (!blk_is_available(blk)) { - return -ENOMEDIUM; - } + BlkRwCo *rwco = opaque; + rwco->ret = blk_co_flush(rwco->blk); +} - return bdrv_flush(blk_bs(blk)); +int blk_flush(BlockBackend *blk) +{ + return blk_prw(blk, 0, NULL, 0, blk_flush_entry, 0); } void blk_drain(BlockBackend *blk) @@ -1555,14 +1594,15 @@ int blk_truncate(BlockBackend *blk, int64_t offset) return bdrv_truncate(blk_bs(blk), offset); } -int blk_pdiscard(BlockBackend *blk, int64_t offset, int count) +static void blk_pdiscard_entry(void *opaque) { - int ret = blk_check_byte_request(blk, offset, count); - if (ret < 0) { - return ret; - } + BlkRwCo *rwco = opaque; + rwco->ret = blk_co_pdiscard(rwco->blk, rwco->offset, rwco->qiov->size); +} - return bdrv_pdiscard(blk_bs(blk), offset, count); +int blk_pdiscard(BlockBackend *blk, int64_t offset, int count) +{ + return blk_prw(blk, offset, NULL, count, blk_pdiscard_entry, 0); } int blk_save_vmstate(BlockBackend *blk, const uint8_t *buf, diff --git a/block/dirty-bitmap.c b/block/dirty-bitmap.c index f2bfdcf..519737c 100644 --- a/block/dirty-bitmap.c +++ b/block/dirty-bitmap.c @@ -38,13 +38,20 @@ */ struct BdrvDirtyBitmap { HBitmap *bitmap; /* Dirty sector bitmap implementation */ + HBitmap *meta; /* Meta dirty bitmap */ BdrvDirtyBitmap *successor; /* Anonymous child; implies frozen status */ char *name; /* Optional non-empty unique ID */ int64_t size; /* Size of the bitmap (Number of sectors) */ bool disabled; /* Bitmap is read-only */ + int active_iterators; /* How many iterators are active */ QLIST_ENTRY(BdrvDirtyBitmap) list; }; +struct BdrvDirtyBitmapIter { + HBitmapIter hbi; + BdrvDirtyBitmap *bitmap; +}; + BdrvDirtyBitmap *bdrv_find_dirty_bitmap(BlockDriverState *bs, const char *name) { BdrvDirtyBitmap *bm; @@ -97,6 +104,66 @@ BdrvDirtyBitmap *bdrv_create_dirty_bitmap(BlockDriverState *bs, return bitmap; } +/* bdrv_create_meta_dirty_bitmap + * + * Create a meta dirty bitmap that tracks the changes of bits in @bitmap. I.e. + * when a dirty status bit in @bitmap is changed (either from reset to set or + * the other way around), its respective meta dirty bitmap bit will be marked + * dirty as well. + * + * @bitmap: the block dirty bitmap for which to create a meta dirty bitmap. + * @chunk_size: how many bytes of bitmap data does each bit in the meta bitmap + * track. + */ +void bdrv_create_meta_dirty_bitmap(BdrvDirtyBitmap *bitmap, + int chunk_size) +{ + assert(!bitmap->meta); + bitmap->meta = hbitmap_create_meta(bitmap->bitmap, + chunk_size * BITS_PER_BYTE); +} + +void bdrv_release_meta_dirty_bitmap(BdrvDirtyBitmap *bitmap) +{ + assert(bitmap->meta); + hbitmap_free_meta(bitmap->bitmap); + bitmap->meta = NULL; +} + +int bdrv_dirty_bitmap_get_meta(BlockDriverState *bs, + BdrvDirtyBitmap *bitmap, int64_t sector, + int nb_sectors) +{ + uint64_t i; + int sectors_per_bit = 1 << hbitmap_granularity(bitmap->meta); + + /* To optimize: we can make hbitmap to internally check the range in a + * coarse level, or at least do it word by word. */ + for (i = sector; i < sector + nb_sectors; i += sectors_per_bit) { + if (hbitmap_get(bitmap->meta, i)) { + return true; + } + } + return false; +} + +void bdrv_dirty_bitmap_reset_meta(BlockDriverState *bs, + BdrvDirtyBitmap *bitmap, int64_t sector, + int nb_sectors) +{ + hbitmap_reset(bitmap->meta, sector, nb_sectors); +} + +int64_t bdrv_dirty_bitmap_size(const BdrvDirtyBitmap *bitmap) +{ + return bitmap->size; +} + +const char *bdrv_dirty_bitmap_name(const BdrvDirtyBitmap *bitmap) +{ + return bitmap->name; +} + bool bdrv_dirty_bitmap_frozen(BdrvDirtyBitmap *bitmap) { return bitmap->successor; @@ -212,6 +279,7 @@ void bdrv_dirty_bitmap_truncate(BlockDriverState *bs) QLIST_FOREACH(bitmap, &bs->dirty_bitmaps, list) { assert(!bdrv_dirty_bitmap_frozen(bitmap)); + assert(!bitmap->active_iterators); hbitmap_truncate(bitmap->bitmap, size); bitmap->size = size; } @@ -224,7 +292,9 @@ static void bdrv_do_release_matching_dirty_bitmap(BlockDriverState *bs, BdrvDirtyBitmap *bm, *next; QLIST_FOREACH_SAFE(bm, &bs->dirty_bitmaps, list, next) { if ((!bitmap || bm == bitmap) && (!only_named || bm->name)) { + assert(!bm->active_iterators); assert(!bdrv_dirty_bitmap_frozen(bm)); + assert(!bm->meta); QLIST_REMOVE(bm, list); hbitmap_free(bm->bitmap); g_free(bm->name); @@ -235,6 +305,9 @@ static void bdrv_do_release_matching_dirty_bitmap(BlockDriverState *bs, } } } + if (bitmap) { + abort(); + } } void bdrv_release_dirty_bitmap(BlockDriverState *bs, BdrvDirtyBitmap *bitmap) @@ -320,9 +393,43 @@ uint32_t bdrv_dirty_bitmap_granularity(BdrvDirtyBitmap *bitmap) return BDRV_SECTOR_SIZE << hbitmap_granularity(bitmap->bitmap); } -void bdrv_dirty_iter_init(BdrvDirtyBitmap *bitmap, HBitmapIter *hbi) +uint32_t bdrv_dirty_bitmap_meta_granularity(BdrvDirtyBitmap *bitmap) +{ + return BDRV_SECTOR_SIZE << hbitmap_granularity(bitmap->meta); +} + +BdrvDirtyBitmapIter *bdrv_dirty_iter_new(BdrvDirtyBitmap *bitmap, + uint64_t first_sector) { - hbitmap_iter_init(hbi, bitmap->bitmap, 0); + BdrvDirtyBitmapIter *iter = g_new(BdrvDirtyBitmapIter, 1); + hbitmap_iter_init(&iter->hbi, bitmap->bitmap, first_sector); + iter->bitmap = bitmap; + bitmap->active_iterators++; + return iter; +} + +BdrvDirtyBitmapIter *bdrv_dirty_meta_iter_new(BdrvDirtyBitmap *bitmap) +{ + BdrvDirtyBitmapIter *iter = g_new(BdrvDirtyBitmapIter, 1); + hbitmap_iter_init(&iter->hbi, bitmap->meta, 0); + iter->bitmap = bitmap; + bitmap->active_iterators++; + return iter; +} + +void bdrv_dirty_iter_free(BdrvDirtyBitmapIter *iter) +{ + if (!iter) { + return; + } + assert(iter->bitmap->active_iterators > 0); + iter->bitmap->active_iterators--; + g_free(iter); +} + +int64_t bdrv_dirty_iter_next(BdrvDirtyBitmapIter *iter) +{ + return hbitmap_iter_next(&iter->hbi); } void bdrv_set_dirty_bitmap(BdrvDirtyBitmap *bitmap, @@ -360,6 +467,43 @@ void bdrv_undo_clear_dirty_bitmap(BdrvDirtyBitmap *bitmap, HBitmap *in) hbitmap_free(tmp); } +uint64_t bdrv_dirty_bitmap_serialization_size(const BdrvDirtyBitmap *bitmap, + uint64_t start, uint64_t count) +{ + return hbitmap_serialization_size(bitmap->bitmap, start, count); +} + +uint64_t bdrv_dirty_bitmap_serialization_align(const BdrvDirtyBitmap *bitmap) +{ + return hbitmap_serialization_granularity(bitmap->bitmap); +} + +void bdrv_dirty_bitmap_serialize_part(const BdrvDirtyBitmap *bitmap, + uint8_t *buf, uint64_t start, + uint64_t count) +{ + hbitmap_serialize_part(bitmap->bitmap, buf, start, count); +} + +void bdrv_dirty_bitmap_deserialize_part(BdrvDirtyBitmap *bitmap, + uint8_t *buf, uint64_t start, + uint64_t count, bool finish) +{ + hbitmap_deserialize_part(bitmap->bitmap, buf, start, count, finish); +} + +void bdrv_dirty_bitmap_deserialize_zeroes(BdrvDirtyBitmap *bitmap, + uint64_t start, uint64_t count, + bool finish) +{ + hbitmap_deserialize_zeroes(bitmap->bitmap, start, count, finish); +} + +void bdrv_dirty_bitmap_deserialize_finish(BdrvDirtyBitmap *bitmap) +{ + hbitmap_deserialize_finish(bitmap->bitmap); +} + void bdrv_set_dirty(BlockDriverState *bs, int64_t cur_sector, int64_t nr_sectors) { @@ -373,15 +517,19 @@ void bdrv_set_dirty(BlockDriverState *bs, int64_t cur_sector, } /** - * Advance an HBitmapIter to an arbitrary offset. + * Advance a BdrvDirtyBitmapIter to an arbitrary offset. */ -void bdrv_set_dirty_iter(HBitmapIter *hbi, int64_t offset) +void bdrv_set_dirty_iter(BdrvDirtyBitmapIter *iter, int64_t sector_num) { - assert(hbi->hb); - hbitmap_iter_init(hbi, hbi->hb, offset); + hbitmap_iter_init(&iter->hbi, iter->hbi.hb, sector_num); } int64_t bdrv_get_dirty_count(BdrvDirtyBitmap *bitmap) { return hbitmap_count(bitmap->bitmap); } + +int64_t bdrv_get_meta_dirty_count(BdrvDirtyBitmap *bitmap) +{ + return hbitmap_count(bitmap->meta); +} @@ -2196,35 +2196,6 @@ BlockAIOCB *bdrv_aio_flush(BlockDriverState *bs, return &acb->common; } -static void coroutine_fn bdrv_aio_pdiscard_co_entry(void *opaque) -{ - BlockAIOCBCoroutine *acb = opaque; - BlockDriverState *bs = acb->common.bs; - - acb->req.error = bdrv_co_pdiscard(bs, acb->req.offset, acb->req.bytes); - bdrv_co_complete(acb); -} - -BlockAIOCB *bdrv_aio_pdiscard(BlockDriverState *bs, int64_t offset, int count, - BlockCompletionFunc *cb, void *opaque) -{ - Coroutine *co; - BlockAIOCBCoroutine *acb; - - trace_bdrv_aio_pdiscard(bs, offset, count, opaque); - - acb = qemu_aio_get(&bdrv_em_co_aiocb_info, bs, cb, opaque); - acb->need_bh = true; - acb->req.error = -EINPROGRESS; - acb->req.offset = offset; - acb->req.bytes = count; - co = qemu_coroutine_create(bdrv_aio_pdiscard_co_entry, acb); - qemu_coroutine_enter(co); - - bdrv_co_maybe_schedule_bh(acb); - return &acb->common; -} - void *qemu_aio_get(const AIOCBInfo *aiocb_info, BlockDriverState *bs, BlockCompletionFunc *cb, void *opaque) { @@ -2521,7 +2492,7 @@ int bdrv_pdiscard(BlockDriverState *bs, int64_t offset, int count) return rwco.ret; } -static int bdrv_co_do_ioctl(BlockDriverState *bs, int req, void *buf) +int bdrv_co_ioctl(BlockDriverState *bs, int req, void *buf) { BlockDriver *drv = bs->drv; BdrvTrackedRequest tracked_req; @@ -2531,86 +2502,26 @@ static int bdrv_co_do_ioctl(BlockDriverState *bs, int req, void *buf) BlockAIOCB *acb; tracked_request_begin(&tracked_req, bs, 0, 0, BDRV_TRACKED_IOCTL); - if (!drv || !drv->bdrv_aio_ioctl) { + if (!drv || (!drv->bdrv_aio_ioctl && !drv->bdrv_co_ioctl)) { co.ret = -ENOTSUP; goto out; } - acb = drv->bdrv_aio_ioctl(bs, req, buf, bdrv_co_io_em_complete, &co); - if (!acb) { - co.ret = -ENOTSUP; - goto out; + if (drv->bdrv_co_ioctl) { + co.ret = drv->bdrv_co_ioctl(bs, req, buf); + } else { + acb = drv->bdrv_aio_ioctl(bs, req, buf, bdrv_co_io_em_complete, &co); + if (!acb) { + co.ret = -ENOTSUP; + goto out; + } + qemu_coroutine_yield(); } - qemu_coroutine_yield(); out: tracked_request_end(&tracked_req); return co.ret; } -typedef struct { - BlockDriverState *bs; - int req; - void *buf; - int ret; -} BdrvIoctlCoData; - -static void coroutine_fn bdrv_co_ioctl_entry(void *opaque) -{ - BdrvIoctlCoData *data = opaque; - data->ret = bdrv_co_do_ioctl(data->bs, data->req, data->buf); -} - -/* needed for generic scsi interface */ -int bdrv_ioctl(BlockDriverState *bs, unsigned long int req, void *buf) -{ - BdrvIoctlCoData data = { - .bs = bs, - .req = req, - .buf = buf, - .ret = -EINPROGRESS, - }; - - if (qemu_in_coroutine()) { - /* Fast-path if already in coroutine context */ - bdrv_co_ioctl_entry(&data); - } else { - Coroutine *co = qemu_coroutine_create(bdrv_co_ioctl_entry, &data); - - qemu_coroutine_enter(co); - while (data.ret == -EINPROGRESS) { - aio_poll(bdrv_get_aio_context(bs), true); - } - } - return data.ret; -} - -static void coroutine_fn bdrv_co_aio_ioctl_entry(void *opaque) -{ - BlockAIOCBCoroutine *acb = opaque; - acb->req.error = bdrv_co_do_ioctl(acb->common.bs, - acb->req.req, acb->req.buf); - bdrv_co_complete(acb); -} - -BlockAIOCB *bdrv_aio_ioctl(BlockDriverState *bs, - unsigned long int req, void *buf, - BlockCompletionFunc *cb, void *opaque) -{ - BlockAIOCBCoroutine *acb = qemu_aio_get(&bdrv_em_co_aiocb_info, - bs, cb, opaque); - Coroutine *co; - - acb->need_bh = true; - acb->req.error = -EINPROGRESS; - acb->req.req = req; - acb->req.buf = buf; - co = qemu_coroutine_create(bdrv_co_aio_ioctl_entry, acb); - qemu_coroutine_enter(co); - - bdrv_co_maybe_schedule_bh(acb); - return &acb->common; -} - void *qemu_blockalign(BlockDriverState *bs, size_t size) { return qemu_memalign(bdrv_opt_mem_align(bs), size); diff --git a/block/iscsi.c b/block/iscsi.c index 46ddc35..71bd523 100644 --- a/block/iscsi.c +++ b/block/iscsi.c @@ -202,6 +202,10 @@ static inline unsigned exp_random(double mean) #define SCSI_SENSE_ASCQ_PARAMETER_LIST_LENGTH_ERROR 0x1a00 #endif +#ifndef LIBISCSI_API_VERSION +#define LIBISCSI_API_VERSION 20130701 +#endif + static int iscsi_translate_sense(struct scsi_sense *sense) { int ret; @@ -592,6 +596,20 @@ iscsi_co_writev_flags(BlockDriverState *bs, int64_t sector_num, int nb_sectors, iscsi_co_init_iscsitask(iscsilun, &iTask); retry: if (iscsilun->use_16_for_rw) { +#if LIBISCSI_API_VERSION >= (20160603) + iTask.task = iscsi_write16_iov_task(iscsilun->iscsi, iscsilun->lun, lba, + NULL, num_sectors * iscsilun->block_size, + iscsilun->block_size, 0, 0, fua, 0, 0, + iscsi_co_generic_cb, &iTask, + (struct scsi_iovec *)iov->iov, iov->niov); + } else { + iTask.task = iscsi_write10_iov_task(iscsilun->iscsi, iscsilun->lun, lba, + NULL, num_sectors * iscsilun->block_size, + iscsilun->block_size, 0, 0, fua, 0, 0, + iscsi_co_generic_cb, &iTask, + (struct scsi_iovec *)iov->iov, iov->niov); + } +#else iTask.task = iscsi_write16_task(iscsilun->iscsi, iscsilun->lun, lba, NULL, num_sectors * iscsilun->block_size, iscsilun->block_size, 0, 0, fua, 0, 0, @@ -602,11 +620,14 @@ retry: iscsilun->block_size, 0, 0, fua, 0, 0, iscsi_co_generic_cb, &iTask); } +#endif if (iTask.task == NULL) { return -ENOMEM; } +#if LIBISCSI_API_VERSION < (20160603) 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(); @@ -789,6 +810,21 @@ static int coroutine_fn iscsi_co_readv(BlockDriverState *bs, iscsi_co_init_iscsitask(iscsilun, &iTask); retry: if (iscsilun->use_16_for_rw) { +#if LIBISCSI_API_VERSION >= (20160603) + iTask.task = iscsi_read16_iov_task(iscsilun->iscsi, iscsilun->lun, lba, + num_sectors * iscsilun->block_size, + iscsilun->block_size, 0, 0, 0, 0, 0, + iscsi_co_generic_cb, &iTask, + (struct scsi_iovec *)iov->iov, iov->niov); + } else { + iTask.task = iscsi_read10_iov_task(iscsilun->iscsi, iscsilun->lun, lba, + num_sectors * iscsilun->block_size, + iscsilun->block_size, + 0, 0, 0, 0, 0, + iscsi_co_generic_cb, &iTask, + (struct scsi_iovec *)iov->iov, iov->niov); + } +#else iTask.task = iscsi_read16_task(iscsilun->iscsi, iscsilun->lun, lba, num_sectors * iscsilun->block_size, iscsilun->block_size, 0, 0, 0, 0, 0, @@ -800,11 +836,13 @@ retry: 0, 0, 0, 0, 0, iscsi_co_generic_cb, &iTask); } +#endif if (iTask.task == NULL) { return -ENOMEM; } +#if LIBISCSI_API_VERSION < (20160603) scsi_task_set_iov_in(iTask.task, (struct scsi_iovec *) iov->iov, iov->niov); - +#endif while (!iTask.complete) { iscsi_set_events(iscsilun); qemu_coroutine_yield(); @@ -1606,7 +1644,13 @@ static int iscsi_open(BlockDriverState *bs, QDict *options, int flags, ret = -ENOMEM; goto out; } - +#if LIBISCSI_API_VERSION >= (20160603) + if (iscsi_init_transport(iscsi, iscsi_url->transport)) { + error_setg(errp, ("Error initializing transport.")); + ret = -EINVAL; + goto out; + } +#endif if (iscsi_set_targetname(iscsi, iscsi_url->target)) { error_setg(errp, "iSCSI: Failed to set target name."); ret = -EINVAL; @@ -1649,7 +1693,7 @@ static int iscsi_open(BlockDriverState *bs, QDict *options, int flags, /* timeout handling is broken in libiscsi before 1.15.0 */ timeout = parse_timeout(iscsi_url->target); -#if defined(LIBISCSI_API_VERSION) && LIBISCSI_API_VERSION >= 20150621 +#if LIBISCSI_API_VERSION >= 20150621 iscsi_set_timeout(iscsi, timeout); #else if (timeout) { @@ -2010,9 +2054,48 @@ static BlockDriver bdrv_iscsi = { .bdrv_attach_aio_context = iscsi_attach_aio_context, }; +#if LIBISCSI_API_VERSION >= (20160603) +static BlockDriver bdrv_iser = { + .format_name = "iser", + .protocol_name = "iser", + + .instance_size = sizeof(IscsiLun), + .bdrv_needs_filename = true, + .bdrv_file_open = iscsi_open, + .bdrv_close = iscsi_close, + .bdrv_create = iscsi_create, + .create_opts = &iscsi_create_opts, + .bdrv_reopen_prepare = iscsi_reopen_prepare, + .bdrv_reopen_commit = iscsi_reopen_commit, + .bdrv_invalidate_cache = iscsi_invalidate_cache, + + .bdrv_getlength = iscsi_getlength, + .bdrv_get_info = iscsi_get_info, + .bdrv_truncate = iscsi_truncate, + .bdrv_refresh_limits = iscsi_refresh_limits, + + .bdrv_co_get_block_status = iscsi_co_get_block_status, + .bdrv_co_pdiscard = iscsi_co_pdiscard, + .bdrv_co_pwrite_zeroes = iscsi_co_pwrite_zeroes, + .bdrv_co_readv = iscsi_co_readv, + .bdrv_co_writev_flags = iscsi_co_writev_flags, + .bdrv_co_flush_to_disk = iscsi_co_flush, + +#ifdef __linux__ + .bdrv_aio_ioctl = iscsi_aio_ioctl, +#endif + + .bdrv_detach_aio_context = iscsi_detach_aio_context, + .bdrv_attach_aio_context = iscsi_attach_aio_context, +}; +#endif + static void iscsi_block_init(void) { bdrv_register(&bdrv_iscsi); +#if LIBISCSI_API_VERSION >= (20160603) + bdrv_register(&bdrv_iser); +#endif } block_init(iscsi_block_init); diff --git a/block/mirror.c b/block/mirror.c index f9d1fec..a433e68 100644 --- a/block/mirror.c +++ b/block/mirror.c @@ -55,7 +55,7 @@ typedef struct MirrorBlockJob { int64_t bdev_length; unsigned long *cow_bitmap; BdrvDirtyBitmap *dirty_bitmap; - HBitmapIter hbi; + BdrvDirtyBitmapIter *dbi; uint8_t *buf; QSIMPLEQ_HEAD(, MirrorBuffer) buf_free; int buf_free_count; @@ -330,10 +330,10 @@ static uint64_t coroutine_fn mirror_iteration(MirrorBlockJob *s) int max_io_sectors = MAX((s->buf_size >> BDRV_SECTOR_BITS) / MAX_IN_FLIGHT, MAX_IO_SECTORS); - sector_num = hbitmap_iter_next(&s->hbi); + sector_num = bdrv_dirty_iter_next(s->dbi); if (sector_num < 0) { - bdrv_dirty_iter_init(s->dirty_bitmap, &s->hbi); - sector_num = hbitmap_iter_next(&s->hbi); + bdrv_set_dirty_iter(s->dbi, 0); + sector_num = bdrv_dirty_iter_next(s->dbi); trace_mirror_restart_iter(s, bdrv_get_dirty_count(s->dirty_bitmap)); assert(sector_num >= 0); } @@ -349,7 +349,7 @@ static uint64_t coroutine_fn mirror_iteration(MirrorBlockJob *s) /* Find the number of consective dirty chunks following the first dirty * one, and wait for in flight requests in them. */ while (nb_chunks * sectors_per_chunk < (s->buf_size >> BDRV_SECTOR_BITS)) { - int64_t hbitmap_next; + int64_t next_dirty; int64_t next_sector = sector_num + nb_chunks * sectors_per_chunk; int64_t next_chunk = next_sector / sectors_per_chunk; if (next_sector >= end || @@ -360,13 +360,13 @@ static uint64_t coroutine_fn mirror_iteration(MirrorBlockJob *s) break; } - hbitmap_next = hbitmap_iter_next(&s->hbi); - if (hbitmap_next > next_sector || hbitmap_next < 0) { + next_dirty = bdrv_dirty_iter_next(s->dbi); + if (next_dirty > next_sector || next_dirty < 0) { /* The bitmap iterator's cache is stale, refresh it */ - bdrv_set_dirty_iter(&s->hbi, next_sector); - hbitmap_next = hbitmap_iter_next(&s->hbi); + bdrv_set_dirty_iter(s->dbi, next_sector); + next_dirty = bdrv_dirty_iter_next(s->dbi); } - assert(hbitmap_next == next_sector); + assert(next_dirty == next_sector); nb_chunks++; } @@ -679,7 +679,8 @@ static void coroutine_fn mirror_run(void *opaque) } } - bdrv_dirty_iter_init(s->dirty_bitmap, &s->hbi); + assert(!s->dbi); + s->dbi = bdrv_dirty_iter_new(s->dirty_bitmap, 0); for (;;) { uint64_t delay_ns = 0; int64_t cnt, delta; @@ -793,6 +794,7 @@ immediate_exit: qemu_vfree(s->buf); g_free(s->cow_bitmap); g_free(s->in_flight_bitmap); + bdrv_dirty_iter_free(s->dbi); bdrv_release_dirty_bitmap(bs, s->dirty_bitmap); data = g_malloc(sizeof(*data)); diff --git a/block/nbd.c b/block/nbd.c index 1ec64ab..6e837f8 100644 --- a/block/nbd.c +++ b/block/nbd.c @@ -32,6 +32,9 @@ #include "qemu/uri.h" #include "block/block_int.h" #include "qemu/module.h" +#include "qapi-visit.h" +#include "qapi/qobject-input-visitor.h" +#include "qapi/qobject-output-visitor.h" #include "qapi/qmp/qdict.h" #include "qapi/qmp/qjson.h" #include "qapi/qmp/qint.h" @@ -44,7 +47,8 @@ typedef struct BDRVNBDState { NbdClientSession client; /* For nbd_refresh_filename() */ - char *path, *host, *port, *export, *tlscredsid; + SocketAddress *saddr; + char *export, *tlscredsid; } BDRVNBDState; static int nbd_parse_uri(const char *filename, QDict *options) @@ -90,9 +94,13 @@ static int nbd_parse_uri(const char *filename, QDict *options) ret = -EINVAL; goto out; } - qdict_put(options, "path", qstring_from_str(qp->p[0].value)); + qdict_put(options, "server.type", qstring_from_str("unix")); + qdict_put(options, "server.data.path", + qstring_from_str(qp->p[0].value)); } else { QString *host; + char *port_str; + /* nbd[+tcp]://host[:port]/export */ if (!uri->server) { ret = -EINVAL; @@ -107,12 +115,12 @@ static int nbd_parse_uri(const char *filename, QDict *options) host = qstring_from_str(uri->server); } - qdict_put(options, "host", host); - if (uri->port) { - char* port_str = g_strdup_printf("%d", uri->port); - qdict_put(options, "port", qstring_from_str(port_str)); - g_free(port_str); - } + qdict_put(options, "server.type", qstring_from_str("inet")); + qdict_put(options, "server.data.host", host); + + port_str = g_strdup_printf("%d", uri->port ?: NBD_DEFAULT_PORT); + qdict_put(options, "server.data.port", qstring_from_str(port_str)); + g_free(port_str); } out: @@ -123,6 +131,26 @@ out: return ret; } +static bool nbd_has_filename_options_conflict(QDict *options, Error **errp) +{ + const QDictEntry *e; + + for (e = qdict_first(options); e; e = qdict_next(options, e)) { + if (!strcmp(e->key, "host") || + !strcmp(e->key, "port") || + !strcmp(e->key, "path") || + !strcmp(e->key, "export") || + strstart(e->key, "server.", NULL)) + { + error_setg(errp, "Option '%s' cannot be used with a file name", + e->key); + return true; + } + } + + return false; +} + static void nbd_parse_filename(const char *filename, QDict *options, Error **errp) { @@ -131,12 +159,7 @@ static void nbd_parse_filename(const char *filename, QDict *options, const char *host_spec; const char *unixpath; - if (qdict_haskey(options, "host") - || qdict_haskey(options, "port") - || qdict_haskey(options, "path")) - { - error_setg(errp, "host/port/path and a file name may not be specified " - "at the same time"); + if (nbd_has_filename_options_conflict(options, errp)) { return; } @@ -173,7 +196,8 @@ static void nbd_parse_filename(const char *filename, QDict *options, /* are we a UNIX or TCP socket? */ if (strstart(host_spec, "unix:", &unixpath)) { - qdict_put(options, "path", qstring_from_str(unixpath)); + qdict_put(options, "server.type", qstring_from_str("unix")); + qdict_put(options, "server.data.path", qstring_from_str(unixpath)); } else { InetSocketAddress *addr = NULL; @@ -182,8 +206,9 @@ static void nbd_parse_filename(const char *filename, QDict *options, goto out; } - qdict_put(options, "host", qstring_from_str(addr->host)); - qdict_put(options, "port", qstring_from_str(addr->port)); + qdict_put(options, "server.type", qstring_from_str("inet")); + qdict_put(options, "server.data.host", qstring_from_str(addr->host)); + qdict_put(options, "server.data.port", qstring_from_str(addr->port)); qapi_free_InetSocketAddress(addr); } @@ -191,47 +216,81 @@ out: g_free(file); } -static SocketAddress *nbd_config(BDRVNBDState *s, QemuOpts *opts, Error **errp) +static bool nbd_process_legacy_socket_options(QDict *output_options, + QemuOpts *legacy_opts, + Error **errp) { - SocketAddress *saddr; + const char *path = qemu_opt_get(legacy_opts, "path"); + const char *host = qemu_opt_get(legacy_opts, "host"); + const char *port = qemu_opt_get(legacy_opts, "port"); + const QDictEntry *e; - s->path = g_strdup(qemu_opt_get(opts, "path")); - s->host = g_strdup(qemu_opt_get(opts, "host")); + if (!path && !host && !port) { + return true; + } - if (!s->path == !s->host) { - if (s->path) { - error_setg(errp, "path and host may not be used at the same time."); - } else { - error_setg(errp, "one of path and host must be specified."); + for (e = qdict_first(output_options); e; e = qdict_next(output_options, e)) + { + if (strstart(e->key, "server.", NULL)) { + error_setg(errp, "Cannot use 'server' and path/host/port at the " + "same time"); + return false; } - return NULL; } - saddr = g_new0(SocketAddress, 1); + if (path && host) { + error_setg(errp, "path and host may not be used at the same time"); + return false; + } else if (path) { + if (port) { + error_setg(errp, "port may not be used without host"); + return false; + } - if (s->path) { - UnixSocketAddress *q_unix; - saddr->type = SOCKET_ADDRESS_KIND_UNIX; - q_unix = saddr->u.q_unix.data = g_new0(UnixSocketAddress, 1); - q_unix->path = g_strdup(s->path); - } else { - InetSocketAddress *inet; + qdict_put(output_options, "server.type", qstring_from_str("unix")); + qdict_put(output_options, "server.data.path", qstring_from_str(path)); + } else if (host) { + qdict_put(output_options, "server.type", qstring_from_str("inet")); + qdict_put(output_options, "server.data.host", qstring_from_str(host)); + qdict_put(output_options, "server.data.port", + qstring_from_str(port ?: stringify(NBD_DEFAULT_PORT))); + } - s->port = g_strdup(qemu_opt_get(opts, "port")); + return true; +} - saddr->type = SOCKET_ADDRESS_KIND_INET; - inet = saddr->u.inet.data = g_new0(InetSocketAddress, 1); - inet->host = g_strdup(s->host); - inet->port = g_strdup(s->port); - if (!inet->port) { - inet->port = g_strdup_printf("%d", NBD_DEFAULT_PORT); - } +static SocketAddress *nbd_config(BDRVNBDState *s, QDict *options, Error **errp) +{ + SocketAddress *saddr = NULL; + QDict *addr = NULL; + QObject *crumpled_addr = NULL; + Visitor *iv = NULL; + Error *local_err = NULL; + + qdict_extract_subqdict(options, &addr, "server."); + if (!qdict_size(addr)) { + error_setg(errp, "NBD server address missing"); + goto done; } - s->client.is_unix = saddr->type == SOCKET_ADDRESS_KIND_UNIX; + crumpled_addr = qdict_crumple(addr, errp); + if (!crumpled_addr) { + goto done; + } - s->export = g_strdup(qemu_opt_get(opts, "export")); + iv = qobject_input_visitor_new(crumpled_addr, true); + visit_type_SocketAddress(iv, NULL, &saddr, &local_err); + if (local_err) { + error_propagate(errp, local_err); + goto done; + } + + s->client.is_unix = saddr->type == SOCKET_ADDRESS_KIND_UNIX; +done: + QDECREF(addr); + qobject_decref(crumpled_addr); + visit_free(iv); return saddr; } @@ -333,7 +392,6 @@ static int nbd_open(BlockDriverState *bs, QDict *options, int flags, QemuOpts *opts = NULL; Error *local_err = NULL; QIOChannelSocket *sioc = NULL; - SocketAddress *saddr = NULL; QCryptoTLSCreds *tlscreds = NULL; const char *hostname = NULL; int ret = -EINVAL; @@ -345,12 +403,19 @@ static int nbd_open(BlockDriverState *bs, QDict *options, int flags, goto error; } + /* Translate @host, @port, and @path to a SocketAddress */ + if (!nbd_process_legacy_socket_options(options, opts, errp)) { + goto error; + } + /* Pop the config into our state object. Exit if invalid. */ - saddr = nbd_config(s, opts, errp); - if (!saddr) { + s->saddr = nbd_config(s, options, errp); + if (!s->saddr) { goto error; } + s->export = g_strdup(qemu_opt_get(opts, "export")); + s->tlscredsid = g_strdup(qemu_opt_get(opts, "tls-creds")); if (s->tlscredsid) { tlscreds = nbd_get_tls_creds(s->tlscredsid, errp); @@ -358,17 +423,17 @@ static int nbd_open(BlockDriverState *bs, QDict *options, int flags, goto error; } - if (saddr->type != SOCKET_ADDRESS_KIND_INET) { + if (s->saddr->type != SOCKET_ADDRESS_KIND_INET) { error_setg(errp, "TLS only supported over IP sockets"); goto error; } - hostname = saddr->u.inet.data->host; + hostname = s->saddr->u.inet.data->host; } /* establish TCP connection, return error if it fails * TODO: Configurable retry-until-timeout behaviour. */ - sioc = nbd_establish_connection(saddr, errp); + sioc = nbd_establish_connection(s->saddr, errp); if (!sioc) { ret = -ECONNREFUSED; goto error; @@ -385,13 +450,10 @@ static int nbd_open(BlockDriverState *bs, QDict *options, int flags, object_unref(OBJECT(tlscreds)); } if (ret < 0) { - g_free(s->path); - g_free(s->host); - g_free(s->port); + qapi_free_SocketAddress(s->saddr); g_free(s->export); g_free(s->tlscredsid); } - qapi_free_SocketAddress(saddr); qemu_opts_del(opts); return ret; } @@ -413,9 +475,7 @@ static void nbd_close(BlockDriverState *bs) nbd_client_close(bs); - g_free(s->path); - g_free(s->host); - g_free(s->port); + qapi_free_SocketAddress(s->saddr); g_free(s->export); g_free(s->tlscredsid); } @@ -442,45 +502,51 @@ static void nbd_refresh_filename(BlockDriverState *bs, QDict *options) { BDRVNBDState *s = bs->opaque; QDict *opts = qdict_new(); + QObject *saddr_qdict; + Visitor *ov; + const char *host = NULL, *port = NULL, *path = NULL; + + if (s->saddr->type == SOCKET_ADDRESS_KIND_INET) { + const InetSocketAddress *inet = s->saddr->u.inet.data; + if (!inet->has_ipv4 && !inet->has_ipv6 && !inet->has_to) { + host = inet->host; + port = inet->port; + } + } else if (s->saddr->type == SOCKET_ADDRESS_KIND_UNIX) { + path = s->saddr->u.q_unix.data->path; + } - qdict_put_obj(opts, "driver", QOBJECT(qstring_from_str("nbd"))); + qdict_put(opts, "driver", qstring_from_str("nbd")); - if (s->path && s->export) { + if (path && s->export) { snprintf(bs->exact_filename, sizeof(bs->exact_filename), - "nbd+unix:///%s?socket=%s", s->export, s->path); - } else if (s->path && !s->export) { + "nbd+unix:///%s?socket=%s", s->export, path); + } else if (path && !s->export) { snprintf(bs->exact_filename, sizeof(bs->exact_filename), - "nbd+unix://?socket=%s", s->path); - } else if (!s->path && s->export && s->port) { + "nbd+unix://?socket=%s", path); + } else if (host && s->export) { snprintf(bs->exact_filename, sizeof(bs->exact_filename), - "nbd://%s:%s/%s", s->host, s->port, s->export); - } else if (!s->path && s->export && !s->port) { + "nbd://%s:%s/%s", host, port, s->export); + } else if (host && !s->export) { snprintf(bs->exact_filename, sizeof(bs->exact_filename), - "nbd://%s/%s", s->host, s->export); - } else if (!s->path && !s->export && s->port) { - snprintf(bs->exact_filename, sizeof(bs->exact_filename), - "nbd://%s:%s", s->host, s->port); - } else if (!s->path && !s->export && !s->port) { - snprintf(bs->exact_filename, sizeof(bs->exact_filename), - "nbd://%s", s->host); + "nbd://%s:%s", host, port); } - if (s->path) { - qdict_put_obj(opts, "path", QOBJECT(qstring_from_str(s->path))); - } else if (s->port) { - qdict_put_obj(opts, "host", QOBJECT(qstring_from_str(s->host))); - qdict_put_obj(opts, "port", QOBJECT(qstring_from_str(s->port))); - } else { - qdict_put_obj(opts, "host", QOBJECT(qstring_from_str(s->host))); - } + ov = qobject_output_visitor_new(&saddr_qdict); + visit_type_SocketAddress(ov, NULL, &s->saddr, &error_abort); + visit_complete(ov, &saddr_qdict); + assert(qobject_type(saddr_qdict) == QTYPE_QDICT); + + qdict_put_obj(opts, "server", saddr_qdict); + if (s->export) { - qdict_put_obj(opts, "export", QOBJECT(qstring_from_str(s->export))); + qdict_put(opts, "export", qstring_from_str(s->export)); } if (s->tlscredsid) { - qdict_put_obj(opts, "tls-creds", - QOBJECT(qstring_from_str(s->tlscredsid))); + qdict_put(opts, "tls-creds", qstring_from_str(s->tlscredsid)); } + qdict_flatten(opts); bs->full_open_options = opts; } diff --git a/block/qapi.c b/block/qapi.c index 6f947e3..a62e862 100644 --- a/block/qapi.c +++ b/block/qapi.c @@ -29,7 +29,7 @@ #include "block/write-threshold.h" #include "qmp-commands.h" #include "qapi-visit.h" -#include "qapi/qmp-output-visitor.h" +#include "qapi/qobject-output-visitor.h" #include "qapi/qmp/types.h" #include "sysemu/block-backend.h" #include "qemu/cutils.h" @@ -691,13 +691,14 @@ void bdrv_image_info_specific_dump(fprintf_function func_fprintf, void *f, ImageInfoSpecific *info_spec) { QObject *obj, *data; - Visitor *v = qmp_output_visitor_new(&obj); + Visitor *v = qobject_output_visitor_new(&obj); visit_type_ImageInfoSpecific(v, NULL, &info_spec, &error_abort); visit_complete(v, &obj); assert(qobject_type(obj) == QTYPE_QDICT); data = qdict_get(qobject_to_qdict(obj), "data"); dump_qobject(func_fprintf, f, 1, data); + qobject_decref(obj); visit_free(v); } diff --git a/block/qcow2-cluster.c b/block/qcow2-cluster.c index 61d1ffd..928c1e2 100644 --- a/block/qcow2-cluster.c +++ b/block/qcow2-cluster.c @@ -1558,7 +1558,7 @@ fail: * clusters. */ static int zero_single_l2(BlockDriverState *bs, uint64_t offset, - uint64_t nb_clusters) + uint64_t nb_clusters, int flags) { BDRVQcow2State *s = bs->opaque; uint64_t *l2_table; @@ -1582,7 +1582,7 @@ static int zero_single_l2(BlockDriverState *bs, uint64_t offset, /* Update L2 entries */ qcow2_cache_entry_mark_dirty(bs, s->l2_table_cache, l2_table); - if (old_offset & QCOW_OFLAG_COMPRESSED) { + if (old_offset & QCOW_OFLAG_COMPRESSED || flags & BDRV_REQ_MAY_UNMAP) { l2_table[l2_index + i] = cpu_to_be64(QCOW_OFLAG_ZERO); qcow2_free_any_clusters(bs, old_offset, 1, QCOW2_DISCARD_REQUEST); } else { @@ -1595,7 +1595,8 @@ static int zero_single_l2(BlockDriverState *bs, uint64_t offset, return nb_clusters; } -int qcow2_zero_clusters(BlockDriverState *bs, uint64_t offset, int nb_sectors) +int qcow2_zero_clusters(BlockDriverState *bs, uint64_t offset, int nb_sectors, + int flags) { BDRVQcow2State *s = bs->opaque; uint64_t nb_clusters; @@ -1612,7 +1613,7 @@ int qcow2_zero_clusters(BlockDriverState *bs, uint64_t offset, int nb_sectors) s->cache_discards = true; while (nb_clusters > 0) { - ret = zero_single_l2(bs, offset, nb_clusters); + ret = zero_single_l2(bs, offset, nb_clusters, flags); if (ret < 0) { goto fail; } diff --git a/block/qcow2.c b/block/qcow2.c index e11c7c9..6d5689a 100644 --- a/block/qcow2.c +++ b/block/qcow2.c @@ -1155,6 +1155,7 @@ static int qcow2_open(BlockDriverState *bs, QDict *options, int flags, /* Initialise locks */ qemu_co_mutex_init(&s->lock); + bs->supported_zero_flags = BDRV_REQ_MAY_UNMAP; /* Repair image if dirty */ if (!(flags & (BDRV_O_CHECK | BDRV_O_INACTIVE)) && !bs->read_only && @@ -2477,7 +2478,7 @@ static coroutine_fn int qcow2_co_pwrite_zeroes(BlockDriverState *bs, trace_qcow2_pwrite_zeroes(qemu_coroutine_self(), offset, count); /* Whatever is left can use real zero clusters */ - ret = qcow2_zero_clusters(bs, offset, count >> BDRV_SECTOR_BITS); + ret = qcow2_zero_clusters(bs, offset, count >> BDRV_SECTOR_BITS, flags); qemu_co_mutex_unlock(&s->lock); return ret; diff --git a/block/qcow2.h b/block/qcow2.h index 9ce5a37..92203a8 100644 --- a/block/qcow2.h +++ b/block/qcow2.h @@ -547,7 +547,8 @@ uint64_t qcow2_alloc_compressed_cluster_offset(BlockDriverState *bs, int qcow2_alloc_cluster_link_l2(BlockDriverState *bs, QCowL2Meta *m); int qcow2_discard_clusters(BlockDriverState *bs, uint64_t offset, int nb_sectors, enum qcow2_discard_type type, bool full_discard); -int qcow2_zero_clusters(BlockDriverState *bs, uint64_t offset, int nb_sectors); +int qcow2_zero_clusters(BlockDriverState *bs, uint64_t offset, int nb_sectors, + int flags); int qcow2_expand_zero_clusters(BlockDriverState *bs, BlockDriverAmendStatusCB *status_cb, diff --git a/block/quorum.c b/block/quorum.c index 9cf876f..d122299 100644 --- a/block/quorum.c +++ b/block/quorum.c @@ -130,7 +130,7 @@ struct QuorumAIOCB { bool is_read; int vote_ret; - int child_iter; /* which child to read in fifo pattern */ + int children_read; /* how many children have been read from */ }; static bool quorum_vote(QuorumAIOCB *acb); @@ -156,22 +156,7 @@ static AIOCBInfo quorum_aiocb_info = { static void quorum_aio_finalize(QuorumAIOCB *acb) { - int i, ret = 0; - - if (acb->vote_ret) { - ret = acb->vote_ret; - } - - acb->common.cb(acb->common.opaque, ret); - - if (acb->is_read) { - /* on the quorum case acb->child_iter == s->num_children - 1 */ - for (i = 0; i <= acb->child_iter; i++) { - qemu_vfree(acb->qcrs[i].buf); - qemu_iovec_destroy(&acb->qcrs[i].qiov); - } - } - + acb->common.cb(acb->common.opaque, acb->vote_ret); g_free(acb->qcrs); qemu_aio_unref(acb); } @@ -283,39 +268,52 @@ static void quorum_copy_qiov(QEMUIOVector *dest, QEMUIOVector *source) } } -static void quorum_aio_cb(void *opaque, int ret) +static void quorum_report_bad_acb(QuorumChildRequest *sacb, int ret) +{ + QuorumAIOCB *acb = sacb->parent; + QuorumOpType type = acb->is_read ? QUORUM_OP_TYPE_READ : QUORUM_OP_TYPE_WRITE; + quorum_report_bad(type, acb->sector_num, acb->nb_sectors, + sacb->aiocb->bs->node_name, ret); +} + +static void quorum_fifo_aio_cb(void *opaque, int ret) { QuorumChildRequest *sacb = opaque; QuorumAIOCB *acb = sacb->parent; BDRVQuorumState *s = acb->common.bs->opaque; - bool rewrite = false; - if (ret == 0) { - acb->success_count++; - } else { - QuorumOpType type; - type = acb->is_read ? QUORUM_OP_TYPE_READ : QUORUM_OP_TYPE_WRITE; - quorum_report_bad(type, acb->sector_num, acb->nb_sectors, - sacb->aiocb->bs->node_name, ret); - } + assert(acb->is_read && s->read_pattern == QUORUM_READ_PATTERN_FIFO); + + if (ret < 0) { + quorum_report_bad_acb(sacb, ret); - if (acb->is_read && s->read_pattern == QUORUM_READ_PATTERN_FIFO) { /* We try to read next child in FIFO order if we fail to read */ - if (ret < 0 && (acb->child_iter + 1) < s->num_children) { - acb->child_iter++; + if (acb->children_read < s->num_children) { read_fifo_child(acb); return; } - - if (ret == 0) { - quorum_copy_qiov(acb->qiov, &acb->qcrs[acb->child_iter].qiov); - } - acb->vote_ret = ret; - quorum_aio_finalize(acb); - return; } + acb->vote_ret = ret; + + /* FIXME: rewrite failed children if acb->children_read > 1? */ + quorum_aio_finalize(acb); +} + +static void quorum_aio_cb(void *opaque, int ret) +{ + QuorumChildRequest *sacb = opaque; + QuorumAIOCB *acb = sacb->parent; + BDRVQuorumState *s = acb->common.bs->opaque; + bool rewrite = false; + int i; + sacb->ret = ret; + if (ret == 0) { + acb->success_count++; + } else { + quorum_report_bad_acb(sacb, ret); + } acb->count++; assert(acb->count <= s->num_children); assert(acb->success_count <= s->num_children); @@ -326,6 +324,10 @@ static void quorum_aio_cb(void *opaque, int ret) /* Do the vote on read */ if (acb->is_read) { rewrite = quorum_vote(acb); + for (i = 0; i < s->num_children; i++) { + qemu_vfree(acb->qcrs[i].buf); + qemu_iovec_destroy(&acb->qcrs[i].qiov); + } } else { quorum_has_too_much_io_failed(acb); } @@ -653,6 +655,7 @@ static BlockAIOCB *read_quorum_children(QuorumAIOCB *acb) BDRVQuorumState *s = acb->common.bs->opaque; int i; + acb->children_read = s->num_children; for (i = 0; i < s->num_children; i++) { acb->qcrs[i].buf = qemu_blockalign(s->children[i]->bs, acb->qiov->size); qemu_iovec_init(&acb->qcrs[i].qiov, acb->qiov->niov); @@ -671,16 +674,11 @@ static BlockAIOCB *read_quorum_children(QuorumAIOCB *acb) static BlockAIOCB *read_fifo_child(QuorumAIOCB *acb) { BDRVQuorumState *s = acb->common.bs->opaque; + int n = acb->children_read++; - acb->qcrs[acb->child_iter].buf = - qemu_blockalign(s->children[acb->child_iter]->bs, acb->qiov->size); - qemu_iovec_init(&acb->qcrs[acb->child_iter].qiov, acb->qiov->niov); - qemu_iovec_clone(&acb->qcrs[acb->child_iter].qiov, acb->qiov, - acb->qcrs[acb->child_iter].buf); - acb->qcrs[acb->child_iter].aiocb = - bdrv_aio_readv(s->children[acb->child_iter], acb->sector_num, - &acb->qcrs[acb->child_iter].qiov, acb->nb_sectors, - quorum_aio_cb, &acb->qcrs[acb->child_iter]); + acb->qcrs[n].aiocb = bdrv_aio_readv(s->children[n], acb->sector_num, + acb->qiov, acb->nb_sectors, + quorum_fifo_aio_cb, &acb->qcrs[n]); return &acb->common; } @@ -696,13 +694,12 @@ static BlockAIOCB *quorum_aio_readv(BlockDriverState *bs, QuorumAIOCB *acb = quorum_aio_get(s, bs, qiov, sector_num, nb_sectors, cb, opaque); acb->is_read = true; + acb->children_read = 0; if (s->read_pattern == QUORUM_READ_PATTERN_QUORUM) { - acb->child_iter = s->num_children - 1; return read_quorum_children(acb); } - acb->child_iter = 0; return read_fifo_child(acb); } diff --git a/block/raw-posix.c b/block/raw-posix.c index 166e9d1..247e47b 100644 --- a/block/raw-posix.c +++ b/block/raw-posix.c @@ -443,6 +443,7 @@ static int raw_open_common(BlockDriverState *bs, QDict *options, fd = qemu_open(filename, s->open_flags, 0644); if (fd < 0) { ret = -errno; + error_setg_errno(errp, errno, "Could not open '%s'", filename); if (ret == -EROFS) { ret = -EACCES; } @@ -2068,13 +2069,23 @@ static bool hdev_is_sg(BlockDriverState *bs) #if defined(__linux__) + BDRVRawState *s = bs->opaque; struct stat st; struct sg_scsi_id scsiid; int sg_version; + int ret; + + if (stat(bs->filename, &st) < 0 || !S_ISCHR(st.st_mode)) { + return false; + } - if (stat(bs->filename, &st) >= 0 && S_ISCHR(st.st_mode) && - !bdrv_ioctl(bs, SG_GET_VERSION_NUM, &sg_version) && - !bdrv_ioctl(bs, SG_GET_SCSI_ID, &scsiid)) { + ret = ioctl(s->fd, SG_GET_VERSION_NUM, &sg_version); + if (ret < 0) { + return false; + } + + ret = ioctl(s->fd, SG_GET_SCSI_ID, &scsiid); + if (ret >= 0) { DPRINTF("SG device found: type=%d, version=%d\n", scsiid.scsi_type, sg_version); return true; diff --git a/block/raw-win32.c b/block/raw-win32.c index 734bb10..800fabd 100644 --- a/block/raw-win32.c +++ b/block/raw-win32.c @@ -373,6 +373,7 @@ static int raw_open(BlockDriverState *bs, QDict *options, int flags, if (s->hfile == INVALID_HANDLE_VALUE) { int err = GetLastError(); + error_setg_win32(errp, err, "Could not open '%s'", filename); if (err == ERROR_ACCESS_DENIED) { ret = -EACCES; } else { diff --git a/block/raw_bsd.c b/block/raw_bsd.c index 588d408..fc16ec1 100644 --- a/block/raw_bsd.c +++ b/block/raw_bsd.c @@ -176,12 +176,9 @@ static void raw_lock_medium(BlockDriverState *bs, bool locked) bdrv_lock_medium(bs->file->bs, locked); } -static BlockAIOCB *raw_aio_ioctl(BlockDriverState *bs, - unsigned long int req, void *buf, - BlockCompletionFunc *cb, - void *opaque) +static int raw_co_ioctl(BlockDriverState *bs, unsigned long int req, void *buf) { - return bdrv_aio_ioctl(bs->file->bs, req, buf, cb, opaque); + return bdrv_co_ioctl(bs->file->bs, req, buf); } static int raw_has_zero_init(BlockDriverState *bs) @@ -261,7 +258,7 @@ BlockDriver bdrv_raw = { .bdrv_media_changed = &raw_media_changed, .bdrv_eject = &raw_eject, .bdrv_lock_medium = &raw_lock_medium, - .bdrv_aio_ioctl = &raw_aio_ioctl, + .bdrv_co_ioctl = &raw_co_ioctl, .create_opts = &raw_create_opts, .bdrv_has_zero_init = &raw_has_zero_init }; diff --git a/block/rbd.c b/block/rbd.c index 6f9eb6f..f6e1d4b 100644 --- a/block/rbd.c +++ b/block/rbd.c @@ -733,7 +733,7 @@ static BlockAIOCB *qemu_rbd_aio_readv(BlockDriverState *bs, void *opaque) { return rbd_start_aio(bs, sector_num << BDRV_SECTOR_BITS, qiov, - nb_sectors << BDRV_SECTOR_BITS, cb, opaque, + (int64_t) nb_sectors << BDRV_SECTOR_BITS, cb, opaque, RBD_AIO_READ); } @@ -745,7 +745,7 @@ static BlockAIOCB *qemu_rbd_aio_writev(BlockDriverState *bs, void *opaque) { return rbd_start_aio(bs, sector_num << BDRV_SECTOR_BITS, qiov, - nb_sectors << BDRV_SECTOR_BITS, cb, opaque, + (int64_t) nb_sectors << BDRV_SECTOR_BITS, cb, opaque, RBD_AIO_WRITE); } diff --git a/block/replication.c b/block/replication.c index 3bd1cf1..8bbfc8f 100644 --- a/block/replication.c +++ b/block/replication.c @@ -101,6 +101,11 @@ static int replication_open(BlockDriverState *bs, QDict *options, if (!strcmp(mode, "primary")) { s->mode = REPLICATION_MODE_PRIMARY; + top_id = qemu_opt_get(opts, REPLICATION_TOP_ID); + if (top_id) { + error_setg(&local_err, "The primary side does not support option top-id"); + goto fail; + } } else if (!strcmp(mode, "secondary")) { s->mode = REPLICATION_MODE_SECONDARY; top_id = qemu_opt_get(opts, REPLICATION_TOP_ID); diff --git a/block/throttle-groups.c b/block/throttle-groups.c index 59545e2..17b2efb 100644 --- a/block/throttle-groups.c +++ b/block/throttle-groups.c @@ -168,6 +168,22 @@ static BlockBackend *throttle_group_next_blk(BlockBackend *blk) return blk_by_public(next); } +/* + * Return whether a BlockBackend has pending requests. + * + * This assumes that tg->lock is held. + * + * @blk: the BlockBackend + * @is_write: the type of operation (read/write) + * @ret: whether the BlockBackend has pending requests. + */ +static inline bool blk_has_pending_reqs(BlockBackend *blk, + bool is_write) +{ + const BlockBackendPublic *blkp = blk_get_public(blk); + return blkp->pending_reqs[is_write]; +} + /* Return the next BlockBackend in the round-robin sequence with pending I/O * requests. * @@ -188,7 +204,7 @@ static BlockBackend *next_throttle_token(BlockBackend *blk, bool is_write) /* get next bs round in round robin style */ token = throttle_group_next_blk(token); - while (token != start && !blkp->pending_reqs[is_write]) { + while (token != start && !blk_has_pending_reqs(token, is_write)) { token = throttle_group_next_blk(token); } @@ -196,10 +212,13 @@ static BlockBackend *next_throttle_token(BlockBackend *blk, bool is_write) * then decide the token is the current bs because chances are * the current bs get the current request queued. */ - if (token == start && !blkp->pending_reqs[is_write]) { + if (token == start && !blk_has_pending_reqs(token, is_write)) { token = blk; } + /* Either we return the original BB, or one with pending requests */ + assert(token == blk || blk_has_pending_reqs(token, is_write)); + return token; } @@ -257,7 +276,7 @@ static void schedule_next_request(BlockBackend *blk, bool is_write) /* Check if there's any pending request to schedule next */ token = next_throttle_token(blk, is_write); - if (!blkp->pending_reqs[is_write]) { + if (!blk_has_pending_reqs(token, is_write)) { return; } @@ -271,7 +290,7 @@ static void schedule_next_request(BlockBackend *blk, bool is_write) qemu_co_queue_next(&blkp->throttled_reqs[is_write])) { token = blk; } else { - ThrottleTimers *tt = &blkp->throttle_timers; + ThrottleTimers *tt = &blk_get_public(token)->throttle_timers; int64_t now = qemu_clock_get_ns(tt->clock_type); timer_mod(tt->timers[is_write], now + 1); tg->any_timer_armed[is_write] = true; diff --git a/block/trace-events b/block/trace-events index 05fa13c..aff8a96 100644 --- a/block/trace-events +++ b/block/trace-events @@ -9,7 +9,6 @@ blk_co_preadv(void *blk, void *bs, int64_t offset, unsigned int bytes, int flags blk_co_pwritev(void *blk, void *bs, int64_t offset, unsigned int bytes, int flags) "blk %p bs %p offset %"PRId64" bytes %u flags %x" # block/io.c -bdrv_aio_pdiscard(void *bs, int64_t offset, int count, void *opaque) "bs %p offset %"PRId64" count %d opaque %p" bdrv_aio_flush(void *bs, void *opaque) "bs %p opaque %p" bdrv_aio_readv(void *bs, int64_t sector_num, int nb_sectors, void *opaque) "bs %p sector_num %"PRId64" nb_sectors %d opaque %p" bdrv_aio_writev(void *bs, int64_t sector_num, int nb_sectors, void *opaque) "bs %p sector_num %"PRId64" nb_sectors %d opaque %p" @@ -43,7 +43,7 @@ #include "qapi/qmp/types.h" #include "qapi-visit.h" #include "qapi/qmp/qerror.h" -#include "qapi/qmp-output-visitor.h" +#include "qapi/qobject-output-visitor.h" #include "qapi/util.h" #include "sysemu/sysemu.h" #include "block/block_int.h" @@ -3776,7 +3776,7 @@ void qmp_blockdev_add(BlockdevOptions *options, Error **errp) { BlockDriverState *bs; QObject *obj; - Visitor *v = qmp_output_visitor_new(&obj); + Visitor *v = qobject_output_visitor_new(&obj); QDict *qdict; Error *local_err = NULL; diff --git a/bsd-user/main.c b/bsd-user/main.c index 4fd7b63..714a692 100644 --- a/bsd-user/main.c +++ b/bsd-user/main.c @@ -651,7 +651,7 @@ void cpu_loop(CPUSPARCState *env) static void usage(void) { printf("qemu-" TARGET_NAME " version " QEMU_VERSION QEMU_PKGVERSION - ", " QEMU_COPYRIGHT "\n" + "\n" QEMU_COPYRIGHT "\n" "usage: qemu-" TARGET_NAME " [options] program [arguments...]\n" "BSD CPU emulator (compiled for %s emulation)\n" "\n" @@ -1216,7 +1216,10 @@ case "$cpu" in cc_i386='$(CC) -m32' ;; x86_64) - CPU_CFLAGS="-m64" + # ??? Only extremely old AMD cpus do not have cmpxchg16b. + # If we truly care, we should simply detect this case at + # runtime and generate the fallback to serial emulation. + CPU_CFLAGS="-m64 -mcx16" LDFLAGS="-m64 $LDFLAGS" cc_i386='$(CC) -m32' ;; @@ -3911,6 +3914,36 @@ if compile_prog "" "" ; then setns=yes fi +# clock_adjtime probe +clock_adjtime=no +cat > $TMPC <<EOF +#include <time.h> + +int main(void) +{ + return clock_adjtime(0, 0); +} +EOF +clock_adjtime=no +if compile_prog "" "" ; then + clock_adjtime=yes +fi + +# syncfs probe +syncfs=no +cat > $TMPC <<EOF +#include <unistd.h> + +int main(void) +{ + return syncfs(0); +} +EOF +syncfs=no +if compile_prog "" "" ; then + syncfs=yes +fi + # Check if tools are available to build documentation. if test "$docs" != "no" ; then if has makeinfo && has pod2man; then @@ -4491,6 +4524,55 @@ if compile_prog "" "" ; then int128=yes fi +######################################### +# See if 128-bit atomic operations are supported. + +atomic128=no +if test "$int128" = "yes"; then + cat > $TMPC << EOF +int main(void) +{ + unsigned __int128 x = 0, y = 0; + y = __atomic_load_16(&x, 0); + __atomic_store_16(&x, y, 0); + __atomic_compare_exchange_16(&x, &y, x, 0, 0, 0); + return 0; +} +EOF + if compile_prog "" "" ; then + atomic128=yes + fi +fi + +######################################### +# See if 64-bit atomic operations are supported. +# Note that without __atomic builtins, we can only +# assume atomic loads/stores max at pointer size. + +cat > $TMPC << EOF +#include <stdint.h> +int main(void) +{ + uint64_t x = 0, y = 0; +#ifdef __ATOMIC_RELAXED + y = __atomic_load_8(&x, 0); + __atomic_store_8(&x, y, 0); + __atomic_compare_exchange_8(&x, &y, x, 0, 0, 0); + __atomic_exchange_8(&x, y, 0); + __atomic_fetch_add_8(&x, y, 0); +#else + typedef char is_host64[sizeof(void *) >= sizeof(uint64_t) ? 1 : -1]; + __sync_lock_test_and_set(&x, y); + __sync_val_compare_and_swap(&x, y, 0); + __sync_fetch_and_add(&x, y); +#endif + return 0; +} +EOF +if compile_prog "" "" ; then + atomic64=yes +fi + ######################################## # check if getauxval is available. @@ -5196,6 +5278,12 @@ fi if test "$setns" = "yes" ; then echo "CONFIG_SETNS=y" >> $config_host_mak fi +if test "$clock_adjtime" = "yes" ; then + echo "CONFIG_CLOCK_ADJTIME=y" >> $config_host_mak +fi +if test "$syncfs" = "yes" ; then + echo "CONFIG_SYNCFS=y" >> $config_host_mak +fi if test "$inotify" = "yes" ; then echo "CONFIG_INOTIFY=y" >> $config_host_mak fi @@ -5447,6 +5535,14 @@ if test "$int128" = "yes" ; then echo "CONFIG_INT128=y" >> $config_host_mak fi +if test "$atomic128" = "yes" ; then + echo "CONFIG_ATOMIC128=y" >> $config_host_mak +fi + +if test "$atomic64" = "yes" ; then + echo "CONFIG_ATOMIC64=y" >> $config_host_mak +fi + if test "$getauxval" = "yes" ; then echo "CONFIG_GETAUXVAL=y" >> $config_host_mak fi diff --git a/cpu-exec-common.c b/cpu-exec-common.c index 0cb4ae6..767d9c6 100644 --- a/cpu-exec-common.c +++ b/cpu-exec-common.c @@ -77,3 +77,9 @@ void cpu_loop_exit_restore(CPUState *cpu, uintptr_t pc) } siglongjmp(cpu->jmp_env, 1); } + +void cpu_loop_exit_atomic(CPUState *cpu, uintptr_t pc) +{ + cpu->exception_index = EXCP_ATOMIC; + cpu_loop_exit_restore(cpu, pc); +} @@ -151,12 +151,6 @@ static inline tcg_target_ulong cpu_tb_exec(CPUState *cpu, TranslationBlock *itb) && qemu_log_in_addr_range(itb->pc)) { #if defined(TARGET_I386) log_cpu_state(cpu, CPU_DUMP_CCOP); -#elif defined(TARGET_M68K) - /* ??? Should not modify env state for dumping. */ - cpu_m68k_flush_flags(env, env->cc_op); - env->cc_op = CC_OP_FLAGS; - env->sr = (env->sr & 0xffe0) | env->cc_dest | (env->cc_x << 4); - log_cpu_state(cpu, 0); #else log_cpu_state(cpu, 0); #endif @@ -222,6 +216,36 @@ static void cpu_exec_nocache(CPUState *cpu, int max_cycles, } #endif +static void cpu_exec_step(CPUState *cpu) +{ + CPUArchState *env = (CPUArchState *)cpu->env_ptr; + TranslationBlock *tb; + target_ulong cs_base, pc; + uint32_t flags; + + cpu_get_tb_cpu_state(env, &pc, &cs_base, &flags); + tb = tb_gen_code(cpu, pc, cs_base, flags, + 1 | CF_NOCACHE | CF_IGNORE_ICOUNT); + tb->orig_tb = NULL; + /* execute the generated code */ + trace_exec_tb_nocache(tb, pc); + cpu_tb_exec(cpu, tb); + tb_phys_invalidate(tb, -1); + tb_free(tb); +} + +void cpu_exec_step_atomic(CPUState *cpu) +{ + start_exclusive(); + + /* Since we got here, we know that parallel_cpus must be true. */ + parallel_cpus = false; + cpu_exec_step(cpu); + parallel_cpus = true; + + end_exclusive(); +} + struct tb_desc { target_ulong pc; target_ulong cs_base; @@ -1497,6 +1497,8 @@ static void tcg_exec_all(void) if (r == EXCP_DEBUG) { cpu_handle_guest_debug(cpu); break; + } else if (r == EXCP_ATOMIC) { + cpu_exec_step_atomic(cpu); } } else if (cpu->stop || cpu->stopped) { if (cpu->unplug) { @@ -23,15 +23,15 @@ #include "exec/memory.h" #include "exec/address-spaces.h" #include "exec/cpu_ldst.h" - #include "exec/cputlb.h" - #include "exec/memory-internal.h" #include "exec/ram_addr.h" #include "exec/exec-all.h" #include "tcg/tcg.h" #include "qemu/error-report.h" #include "exec/log.h" +#include "exec/helper-proto.h" +#include "qemu/atomic.h" /* DEBUG defines, enable DEBUG_TLB_LOG to log to the CPU_LOG_MMU target */ /* #define DEBUG_TLB */ @@ -498,6 +498,43 @@ tb_page_addr_t get_page_addr_code(CPUArchState *env1, target_ulong addr) return qemu_ram_addr_from_host_nofail(p); } +static uint64_t io_readx(CPUArchState *env, CPUIOTLBEntry *iotlbentry, + target_ulong addr, uintptr_t retaddr, int size) +{ + CPUState *cpu = ENV_GET_CPU(env); + hwaddr physaddr = iotlbentry->addr; + MemoryRegion *mr = iotlb_to_region(cpu, physaddr, iotlbentry->attrs); + uint64_t val; + + physaddr = (physaddr & TARGET_PAGE_MASK) + addr; + cpu->mem_io_pc = retaddr; + if (mr != &io_mem_rom && mr != &io_mem_notdirty && !cpu->can_do_io) { + cpu_io_recompile(cpu, retaddr); + } + + cpu->mem_io_vaddr = addr; + memory_region_dispatch_read(mr, physaddr, &val, size, iotlbentry->attrs); + return val; +} + +static void io_writex(CPUArchState *env, CPUIOTLBEntry *iotlbentry, + uint64_t val, target_ulong addr, + uintptr_t retaddr, int size) +{ + CPUState *cpu = ENV_GET_CPU(env); + hwaddr physaddr = iotlbentry->addr; + MemoryRegion *mr = iotlb_to_region(cpu, physaddr, iotlbentry->attrs); + + physaddr = (physaddr & TARGET_PAGE_MASK) + addr; + if (mr != &io_mem_rom && mr != &io_mem_notdirty && !cpu->can_do_io) { + cpu_io_recompile(cpu, retaddr); + } + + cpu->mem_io_vaddr = addr; + cpu->mem_io_pc = retaddr; + memory_region_dispatch_write(mr, physaddr, val, size, iotlbentry->attrs); +} + /* Return true if ADDR is present in the victim tlb, and has been copied back to the main tlb. */ static bool victim_tlb_hit(CPUArchState *env, size_t mmu_idx, size_t index, @@ -527,34 +564,178 @@ static bool victim_tlb_hit(CPUArchState *env, size_t mmu_idx, size_t index, victim_tlb_hit(env, mmu_idx, index, offsetof(CPUTLBEntry, TY), \ (ADDR) & TARGET_PAGE_MASK) +/* Probe for whether the specified guest write access is permitted. + * If it is not permitted then an exception will be taken in the same + * way as if this were a real write access (and we will not return). + * Otherwise the function will return, and there will be a valid + * entry in the TLB for this access. + */ +void probe_write(CPUArchState *env, target_ulong addr, int mmu_idx, + uintptr_t retaddr) +{ + int index = (addr >> TARGET_PAGE_BITS) & (CPU_TLB_SIZE - 1); + target_ulong tlb_addr = env->tlb_table[mmu_idx][index].addr_write; + + if ((addr & TARGET_PAGE_MASK) + != (tlb_addr & (TARGET_PAGE_MASK | TLB_INVALID_MASK))) { + /* TLB entry is for a different page */ + if (!VICTIM_TLB_HIT(addr_write, addr)) { + tlb_fill(ENV_GET_CPU(env), addr, MMU_DATA_STORE, mmu_idx, retaddr); + } + } +} + +/* Probe for a read-modify-write atomic operation. Do not allow unaligned + * operations, or io operations to proceed. Return the host address. */ +static void *atomic_mmu_lookup(CPUArchState *env, target_ulong addr, + TCGMemOpIdx oi, uintptr_t retaddr) +{ + size_t mmu_idx = get_mmuidx(oi); + size_t index = (addr >> TARGET_PAGE_BITS) & (CPU_TLB_SIZE - 1); + CPUTLBEntry *tlbe = &env->tlb_table[mmu_idx][index]; + target_ulong tlb_addr = tlbe->addr_write; + TCGMemOp mop = get_memop(oi); + int a_bits = get_alignment_bits(mop); + int s_bits = mop & MO_SIZE; + + /* Adjust the given return address. */ + retaddr -= GETPC_ADJ; + + /* Enforce guest required alignment. */ + if (unlikely(a_bits > 0 && (addr & ((1 << a_bits) - 1)))) { + /* ??? Maybe indicate atomic op to cpu_unaligned_access */ + cpu_unaligned_access(ENV_GET_CPU(env), addr, MMU_DATA_STORE, + mmu_idx, retaddr); + } + + /* Enforce qemu required alignment. */ + if (unlikely(addr & ((1 << s_bits) - 1))) { + /* We get here if guest alignment was not requested, + or was not enforced by cpu_unaligned_access above. + We might widen the access and emulate, but for now + mark an exception and exit the cpu loop. */ + goto stop_the_world; + } + + /* Check TLB entry and enforce page permissions. */ + if ((addr & TARGET_PAGE_MASK) + != (tlb_addr & (TARGET_PAGE_MASK | TLB_INVALID_MASK))) { + if (!VICTIM_TLB_HIT(addr_write, addr)) { + tlb_fill(ENV_GET_CPU(env), addr, MMU_DATA_STORE, mmu_idx, retaddr); + } + tlb_addr = tlbe->addr_write; + } + + /* Notice an IO access, or a notdirty page. */ + if (unlikely(tlb_addr & ~TARGET_PAGE_MASK)) { + /* There's really nothing that can be done to + support this apart from stop-the-world. */ + goto stop_the_world; + } + + /* Let the guest notice RMW on a write-only page. */ + if (unlikely(tlbe->addr_read != tlb_addr)) { + tlb_fill(ENV_GET_CPU(env), addr, MMU_DATA_LOAD, mmu_idx, retaddr); + /* Since we don't support reads and writes to different addresses, + and we do have the proper page loaded for write, this shouldn't + ever return. But just in case, handle via stop-the-world. */ + goto stop_the_world; + } + + return (void *)((uintptr_t)addr + tlbe->addend); + + stop_the_world: + cpu_loop_exit_atomic(ENV_GET_CPU(env), retaddr); +} + +#ifdef TARGET_WORDS_BIGENDIAN +# define TGT_BE(X) (X) +# define TGT_LE(X) BSWAP(X) +#else +# define TGT_BE(X) BSWAP(X) +# define TGT_LE(X) (X) +#endif + #define MMUSUFFIX _mmu -#define SHIFT 0 +#define DATA_SIZE 1 #include "softmmu_template.h" -#define SHIFT 1 +#define DATA_SIZE 2 #include "softmmu_template.h" -#define SHIFT 2 +#define DATA_SIZE 4 #include "softmmu_template.h" -#define SHIFT 3 +#define DATA_SIZE 8 #include "softmmu_template.h" -#undef MMUSUFFIX +/* First set of helpers allows passing in of OI and RETADDR. This makes + them callable from other helpers. */ + +#define EXTRA_ARGS , TCGMemOpIdx oi, uintptr_t retaddr +#define ATOMIC_NAME(X) \ + HELPER(glue(glue(glue(atomic_ ## X, SUFFIX), END), _mmu)) +#define ATOMIC_MMU_LOOKUP atomic_mmu_lookup(env, addr, oi, retaddr) + +#define DATA_SIZE 1 +#include "atomic_template.h" + +#define DATA_SIZE 2 +#include "atomic_template.h" + +#define DATA_SIZE 4 +#include "atomic_template.h" + +#ifdef CONFIG_ATOMIC64 +#define DATA_SIZE 8 +#include "atomic_template.h" +#endif + +#ifdef CONFIG_ATOMIC128 +#define DATA_SIZE 16 +#include "atomic_template.h" +#endif + +/* Second set of helpers are directly callable from TCG as helpers. */ + +#undef EXTRA_ARGS +#undef ATOMIC_NAME +#undef ATOMIC_MMU_LOOKUP +#define EXTRA_ARGS , TCGMemOpIdx oi +#define ATOMIC_NAME(X) HELPER(glue(glue(atomic_ ## X, SUFFIX), END)) +#define ATOMIC_MMU_LOOKUP atomic_mmu_lookup(env, addr, oi, GETPC()) + +#define DATA_SIZE 1 +#include "atomic_template.h" + +#define DATA_SIZE 2 +#include "atomic_template.h" + +#define DATA_SIZE 4 +#include "atomic_template.h" + +#ifdef CONFIG_ATOMIC64 +#define DATA_SIZE 8 +#include "atomic_template.h" +#endif + +/* Code access functions. */ + +#undef MMUSUFFIX #define MMUSUFFIX _cmmu #undef GETPC #define GETPC() ((uintptr_t)0) #define SOFTMMU_CODE_ACCESS -#define SHIFT 0 +#define DATA_SIZE 1 #include "softmmu_template.h" -#define SHIFT 1 +#define DATA_SIZE 2 #include "softmmu_template.h" -#define SHIFT 2 +#define DATA_SIZE 4 #include "softmmu_template.h" -#define SHIFT 3 +#define DATA_SIZE 8 #include "softmmu_template.h" diff --git a/default-configs/unicore32-linux-user.mak b/default-configs/unicore32-linux-user.mak deleted file mode 100644 index 6aafd21..0000000 --- a/default-configs/unicore32-linux-user.mak +++ /dev/null @@ -1 +0,0 @@ -# Default configuration for unicore32-linux-user diff --git a/dma-helpers.c b/dma-helpers.c index 9defc10..6f9d47c 100644 --- a/dma-helpers.c +++ b/dma-helpers.c @@ -73,6 +73,7 @@ typedef struct { AioContext *ctx; BlockAIOCB *acb; QEMUSGList *sg; + uint32_t align; uint64_t offset; DMADirection dir; int sg_cur_index; @@ -160,8 +161,9 @@ static void dma_blk_cb(void *opaque, int ret) return; } - if (dbs->iov.size & ~BDRV_SECTOR_MASK) { - qemu_iovec_discard_back(&dbs->iov, dbs->iov.size & ~BDRV_SECTOR_MASK); + if (!QEMU_IS_ALIGNED(dbs->iov.size, dbs->align)) { + qemu_iovec_discard_back(&dbs->iov, + QEMU_ALIGN_DOWN(dbs->iov.size, dbs->align)); } dbs->acb = dbs->io_func(dbs->offset, &dbs->iov, @@ -199,7 +201,7 @@ static const AIOCBInfo dma_aiocb_info = { }; BlockAIOCB *dma_blk_io(AioContext *ctx, - QEMUSGList *sg, uint64_t offset, + QEMUSGList *sg, uint64_t offset, uint32_t align, DMAIOFunc *io_func, void *io_func_opaque, BlockCompletionFunc *cb, void *opaque, DMADirection dir) @@ -212,6 +214,7 @@ BlockAIOCB *dma_blk_io(AioContext *ctx, dbs->sg = sg; dbs->ctx = ctx; dbs->offset = offset; + dbs->align = align; dbs->sg_cur_index = 0; dbs->sg_cur_byte = 0; dbs->dir = dir; @@ -234,11 +237,11 @@ BlockAIOCB *dma_blk_read_io_func(int64_t offset, QEMUIOVector *iov, } BlockAIOCB *dma_blk_read(BlockBackend *blk, - QEMUSGList *sg, uint64_t offset, + QEMUSGList *sg, uint64_t offset, uint32_t align, void (*cb)(void *opaque, int ret), void *opaque) { - return dma_blk_io(blk_get_aio_context(blk), - sg, offset, dma_blk_read_io_func, blk, cb, opaque, + return dma_blk_io(blk_get_aio_context(blk), sg, offset, align, + dma_blk_read_io_func, blk, cb, opaque, DMA_DIRECTION_FROM_DEVICE); } @@ -252,11 +255,11 @@ BlockAIOCB *dma_blk_write_io_func(int64_t offset, QEMUIOVector *iov, } BlockAIOCB *dma_blk_write(BlockBackend *blk, - QEMUSGList *sg, uint64_t offset, + QEMUSGList *sg, uint64_t offset, uint32_t align, void (*cb)(void *opaque, int ret), void *opaque) { - return dma_blk_io(blk_get_aio_context(blk), - sg, offset, dma_blk_write_io_func, blk, cb, opaque, + return dma_blk_io(blk_get_aio_context(blk), sg, offset, align, + dma_blk_write_io_func, blk, cb, opaque, DMA_DIRECTION_TO_DEVICE); } diff --git a/docs/atomics.txt b/docs/atomics.txt index c95950b..3ef5d85 100644 --- a/docs/atomics.txt +++ b/docs/atomics.txt @@ -15,7 +15,8 @@ Macros defined by qemu/atomic.h fall in three camps: - compiler barriers: barrier(); - weak atomic access and manual memory barriers: atomic_read(), - atomic_set(), smp_rmb(), smp_wmb(), smp_mb(), smp_read_barrier_depends(); + atomic_set(), smp_rmb(), smp_wmb(), smp_mb(), smp_mb_acquire(), + smp_mb_release(), smp_read_barrier_depends(); - sequentially consistent atomic access: everything else. @@ -111,8 +112,8 @@ consistent primitives. When using this model, variables are accessed with atomic_read() and atomic_set(), and restrictions to the ordering of accesses is enforced -using the smp_rmb(), smp_wmb(), smp_mb() and smp_read_barrier_depends() -memory barriers. +using the memory barrier macros: smp_rmb(), smp_wmb(), smp_mb(), +smp_mb_acquire(), smp_mb_release(), smp_read_barrier_depends(). atomic_read() and atomic_set() prevents the compiler from using optimizations that might otherwise optimize accesses out of existence @@ -124,7 +125,7 @@ other threads, and which are local to the current thread or protected by other, more mundane means. Memory barriers control the order of references to shared memory. -They come in four kinds: +They come in six kinds: - smp_rmb() guarantees that all the LOAD operations specified before the barrier will appear to happen before all the LOAD operations @@ -142,6 +143,16 @@ They come in four kinds: In other words, smp_wmb() puts a partial ordering on stores, but is not required to have any effect on loads. +- smp_mb_acquire() guarantees that all the LOAD operations specified before + the barrier will appear to happen before all the LOAD or STORE operations + specified after the barrier with respect to the other components of + the system. + +- smp_mb_release() guarantees that all the STORE operations specified *after* + the barrier will appear to happen after all the LOAD or STORE operations + specified *before* the barrier with respect to the other components of + the system. + - smp_mb() guarantees that all the LOAD and STORE operations specified before the barrier will appear to happen before all the LOAD and STORE operations specified after the barrier with respect to the other @@ -149,8 +160,9 @@ They come in four kinds: smp_mb() puts a partial ordering on both loads and stores. It is stronger than both a read and a write memory barrier; it implies both - smp_rmb() and smp_wmb(), but it also prevents STOREs coming before the - barrier from overtaking LOADs coming after the barrier and vice versa. + smp_mb_acquire() and smp_mb_release(), but it also prevents STOREs + coming before the barrier from overtaking LOADs coming after the + barrier and vice versa. - smp_read_barrier_depends() is a weaker kind of read barrier. On most processors, whenever two loads are performed such that the @@ -173,24 +185,21 @@ They come in four kinds: This is the set of barriers that is required *between* two atomic_read() and atomic_set() operations to achieve sequential consistency: - | 2nd operation | - |-----------------------------------------| - 1st operation | (after last) | atomic_read | atomic_set | - ---------------+--------------+-------------+------------| - (before first) | | none | smp_wmb() | - ---------------+--------------+-------------+------------| - atomic_read | smp_rmb() | smp_rmb()* | ** | - ---------------+--------------+-------------+------------| - atomic_set | none | smp_mb()*** | smp_wmb() | - ---------------+--------------+-------------+------------| + | 2nd operation | + |-----------------------------------------------| + 1st operation | (after last) | atomic_read | atomic_set | + ---------------+----------------+-------------+----------------| + (before first) | | none | smp_mb_release | + ---------------+----------------+-------------+----------------| + atomic_read | smp_mb_acquire | smp_rmb | ** | + ---------------+----------------+-------------+----------------| + atomic_set | none | smp_mb()*** | smp_wmb() | + ---------------+----------------+-------------+----------------| * Or smp_read_barrier_depends(). - ** This requires a load-store barrier. How to achieve this varies - depending on the machine, but in practice smp_rmb()+smp_wmb() - should have the desired effect. For example, on PowerPC the - lwsync instruction is a combined load-load, load-store and - store-store barrier. + ** This requires a load-store barrier. This is achieved by + either smp_mb_acquire() or smp_mb_release(). *** This requires a store-load barrier. On most machines, the only way to achieve this is a full barrier. @@ -199,11 +208,11 @@ and atomic_set() operations to achieve sequential consistency: You can see that the two possible definitions of atomic_mb_read() and atomic_mb_set() are the following: - 1) atomic_mb_read(p) = atomic_read(p); smp_rmb() - atomic_mb_set(p, v) = smp_wmb(); atomic_set(p, v); smp_mb() + 1) atomic_mb_read(p) = atomic_read(p); smp_mb_acquire() + atomic_mb_set(p, v) = smp_mb_release(); atomic_set(p, v); smp_mb() - 2) atomic_mb_read(p) = smp_mb() atomic_read(p); smp_rmb() - atomic_mb_set(p, v) = smp_wmb(); atomic_set(p, v); + 2) atomic_mb_read(p) = smp_mb() atomic_read(p); smp_mb_acquire() + atomic_mb_set(p, v) = smp_mb_release(); atomic_set(p, v); Usually the former is used, because smp_mb() is expensive and a program normally has more reads than writes. Therefore it makes more sense to @@ -222,7 +231,7 @@ place barriers instead: thread 1 thread 1 ------------------------- ------------------------ (other writes) - smp_wmb() + smp_mb_release() atomic_mb_set(&a, x) atomic_set(&a, x) smp_wmb() atomic_mb_set(&b, y) atomic_set(&b, y) @@ -233,7 +242,13 @@ place barriers instead: y = atomic_mb_read(&b) y = atomic_read(&b) smp_rmb() x = atomic_mb_read(&a) x = atomic_read(&a) - smp_rmb() + smp_mb_acquire() + + Note that the barrier between the stores in thread 1, and between + the loads in thread 2, has been optimized here to a write or a + read memory barrier respectively. On some architectures, notably + ARMv7, smp_mb_acquire and smp_mb_release are just as expensive as + smp_mb, but smp_rmb and/or smp_wmb are more efficient. - sometimes, a thread is accessing many variables that are otherwise unrelated to each other (for example because, apart from the current @@ -246,12 +261,12 @@ place barriers instead: n = 0; n = 0; for (i = 0; i < 10; i++) => for (i = 0; i < 10; i++) n += atomic_mb_read(&a[i]); n += atomic_read(&a[i]); - smp_rmb(); + smp_mb_acquire(); Similarly, atomic_mb_set() can be transformed as follows: smp_mb(): - smp_wmb(); + smp_mb_release(); for (i = 0; i < 10; i++) => for (i = 0; i < 10; i++) atomic_mb_set(&a[i], false); atomic_set(&a[i], false); smp_mb(); @@ -261,7 +276,7 @@ The two tricks can be combined. In this case, splitting a loop in two lets you hoist the barriers out of the loops _and_ eliminate the expensive smp_mb(): - smp_wmb(); + smp_mb_release(); for (i = 0; i < 10; i++) { => for (i = 0; i < 10; i++) atomic_mb_set(&a[i], false); atomic_set(&a[i], false); atomic_mb_set(&b[i], false); smb_wmb(); @@ -312,8 +327,8 @@ access and for data dependency barriers: smp_read_barrier_depends(); z = b[y]; -smp_wmb() also pairs with atomic_mb_read(), and smp_rmb() also pairs -with atomic_mb_set(). +smp_wmb() also pairs with atomic_mb_read() and smp_mb_acquire(). +and smp_rmb() also pairs with atomic_mb_set() and smp_mb_release(). COMPARISON WITH LINUX KERNEL MEMORY BARRIERS @@ -359,8 +374,9 @@ and memory barriers, and the equivalents in QEMU: note that smp_store_mb() is a little weaker than atomic_mb_set(). atomic_mb_read() compiles to the same instructions as Linux's smp_load_acquire(), but this should be treated as an implementation - detail. If required, QEMU might later add atomic_load_acquire() and - atomic_store_release() macros. + detail. QEMU does have atomic_load_acquire() and atomic_store_release() + macros, but for now they are only used within atomic.h. This may + change in the future. SOURCES diff --git a/docs/qapi-code-gen.txt b/docs/qapi-code-gen.txt index 5d4c2cd..2841c51 100644 --- a/docs/qapi-code-gen.txt +++ b/docs/qapi-code-gen.txt @@ -1005,7 +1005,7 @@ Example: Error *err = NULL; Visitor *v; - v = qmp_output_visitor_new(ret_out); + v = qobject_output_visitor_new(ret_out); visit_type_UserDefOne(v, "unused", &ret_in, &err); if (!err) { visit_complete(v, ret_out); @@ -1024,7 +1024,7 @@ Example: Visitor *v; UserDefOneList *arg1 = NULL; - v = qmp_input_visitor_new(QOBJECT(args), true); + v = qobject_input_visitor_new(QOBJECT(args), true); visit_start_struct(v, NULL, NULL, 0, &err); if (err) { goto out; diff --git a/docs/qmp-commands.txt b/docs/qmp-commands.txt index 3220fb1..284576d 100644 --- a/docs/qmp-commands.txt +++ b/docs/qmp-commands.txt @@ -1090,11 +1090,11 @@ Arguments: Example: -> { "execute": "blockdev-add", - "arguments": { "options": { "driver": "qcow2", - "node-name": "node1534", - "file": { "driver": "file", - "filename": "hd1.qcow2" }, - "backing": "" } } } + "arguments": { "driver": "qcow2", + "node-name": "node1534", + "file": { "driver": "file", + "filename": "hd1.qcow2" }, + "backing": "" } } <- { "return": {} } @@ -3130,41 +3130,37 @@ This command is still a work in progress. It doesn't support all block drivers among other things. Stay away from it unless you want to help with its development. -Arguments: - -- "options": block driver options +For the arguments, see the QAPI schema documentation of BlockdevOptions. Example (1): -> { "execute": "blockdev-add", - "arguments": { "options" : { "driver": "qcow2", - "file": { "driver": "file", - "filename": "test.qcow2" } } } } + "arguments": { "driver": "qcow2", + "file": { "driver": "file", + "filename": "test.qcow2" } } } <- { "return": {} } Example (2): -> { "execute": "blockdev-add", "arguments": { - "options": { - "driver": "qcow2", - "node-name": "my_disk", - "discard": "unmap", - "cache": { - "direct": true, - "writeback": true - }, - "file": { - "driver": "file", - "filename": "/tmp/test.qcow2" - }, - "backing": { - "driver": "raw", - "file": { - "driver": "file", - "filename": "/dev/fdset/4" - } - } + "driver": "qcow2", + "node-name": "my_disk", + "discard": "unmap", + "cache": { + "direct": true, + "writeback": true + }, + "file": { + "driver": "file", + "filename": "/tmp/test.qcow2" + }, + "backing": { + "driver": "raw", + "file": { + "driver": "file", + "filename": "/dev/fdset/4" + } } } } @@ -3191,13 +3187,11 @@ Example: -> { "execute": "blockdev-add", "arguments": { - "options": { - "driver": "qcow2", - "node-name": "node0", - "file": { - "driver": "file", - "filename": "test.qcow2" - } + "driver": "qcow2", + "node-name": "node0", + "file": { + "driver": "file", + "filename": "test.qcow2" } } } @@ -3342,10 +3336,10 @@ Arguments: Example: -> { "execute": "blockdev-add", - "arguments": { "options": { "node-name": "node0", - "driver": "raw", - "file": { "driver": "file", - "filename": "fedora.iso" } } } } + "arguments": { { "node-name": "node0", + "driver": "raw", + "file": { "driver": "file", + "filename": "fedora.iso" } } } <- { "return": {} } @@ -3383,10 +3377,10 @@ Example: Add a new node to a quorum -> { "execute": "blockdev-add", - "arguments": { "options": { "driver": "raw", - "node-name": "new_node", - "file": { "driver": "file", - "filename": "test.raw" } } } } + "arguments": { "driver": "raw", + "node-name": "new_node", + "file": { "driver": "file", + "filename": "test.raw" } } } <- { "return": {} } -> { "execute": "x-blockdev-change", "arguments": { "parent": "disk1", @@ -93,6 +93,11 @@ static MemoryRegion io_mem_unassigned; #endif +#ifdef TARGET_PAGE_BITS_VARY +int target_page_bits; +bool target_page_bits_decided; +#endif + struct CPUTailQ cpus = QTAILQ_HEAD_INITIALIZER(cpus); /* current CPU in the current thread. It is only valid inside cpu_exec() */ @@ -102,8 +107,37 @@ __thread CPUState *current_cpu; 2 = Adaptive rate instruction counting. */ int use_icount; +bool set_preferred_target_page_bits(int bits) +{ + /* The target page size is the lowest common denominator for all + * the CPUs in the system, so we can only make it smaller, never + * larger. And we can't make it smaller once we've committed to + * a particular size. + */ +#ifdef TARGET_PAGE_BITS_VARY + assert(bits >= TARGET_PAGE_BITS_MIN); + if (target_page_bits == 0 || target_page_bits > bits) { + if (target_page_bits_decided) { + return false; + } + target_page_bits = bits; + } +#endif + return true; +} + #if !defined(CONFIG_USER_ONLY) +static void finalize_target_page_bits(void) +{ +#ifdef TARGET_PAGE_BITS_VARY + if (target_page_bits == 0) { + target_page_bits = TARGET_PAGE_BITS_MIN; + } + target_page_bits_decided = true; +#endif +} + typedef struct PhysPageEntry PhysPageEntry; struct PhysPageEntry { @@ -153,7 +187,7 @@ typedef struct subpage_t { MemoryRegion iomem; AddressSpace *as; hwaddr base; - uint16_t sub_section[TARGET_PAGE_SIZE]; + uint16_t sub_section[]; } subpage_t; #define PHYS_SECTION_UNASSIGNED 0 @@ -318,9 +352,9 @@ static inline bool section_covers_addr(const MemoryRegionSection *section, /* Memory topology clips a memory region to [0, 2^64); size.hi > 0 means * the section must cover the entire address space. */ - return section->size.hi || + return int128_gethi(section->size) || range_covers_byte(section->offset_within_address_space, - section->size.lo, addr); + int128_getlo(section->size), addr); } static MemoryRegionSection *phys_page_find(PhysPageEntry lp, hwaddr addr, @@ -596,7 +630,7 @@ AddressSpace *cpu_get_address_space(CPUState *cpu, int asidx) } #endif -void cpu_exec_exit(CPUState *cpu) +void cpu_exec_unrealizefn(CPUState *cpu) { CPUClass *cc = CPU_GET_CLASS(cpu); @@ -610,11 +644,8 @@ void cpu_exec_exit(CPUState *cpu) } } -void cpu_exec_init(CPUState *cpu, Error **errp) +void cpu_exec_initfn(CPUState *cpu) { - CPUClass *cc ATTRIBUTE_UNUSED = CPU_GET_CLASS(cpu); - Error *local_err ATTRIBUTE_UNUSED = NULL; - cpu->as = NULL; cpu->num_ases = 0; @@ -635,6 +666,11 @@ void cpu_exec_init(CPUState *cpu, Error **errp) cpu->memory = system_memory; object_ref(OBJECT(cpu->memory)); #endif +} + +void cpu_exec_realizefn(CPUState *cpu, Error **errp) +{ + CPUClass *cc ATTRIBUTE_UNUSED = CPU_GET_CLASS(cpu); cpu_list_add(cpu); @@ -1254,7 +1290,12 @@ static void *file_ram_alloc(RAMBlock *block, } block->page_size = qemu_fd_getpagesize(fd); - block->mr->align = MAX(block->page_size, QEMU_VMALLOC_ALIGN); + block->mr->align = block->page_size; +#if defined(__s390x__) + if (kvm_enabled()) { + block->mr->align = MAX(block->mr->align, QEMU_VMALLOC_ALIGN); + } +#endif if (memory < block->page_size) { error_setg(errp, "memory size 0x" RAM_ADDR_FMT " must be equal to " @@ -2210,8 +2251,7 @@ static subpage_t *subpage_init(AddressSpace *as, hwaddr base) { subpage_t *mmio; - mmio = g_malloc0(sizeof(subpage_t)); - + mmio = g_malloc0(sizeof(subpage_t) + TARGET_PAGE_SIZE * sizeof(uint16_t)); mmio->as = as; mmio->base = base; memory_region_init_io(&mmio->iomem, NULL, &subpage_ops, mmio, @@ -2803,6 +2843,14 @@ void cpu_register_map_client(QEMUBH *bh) void cpu_exec_init_all(void) { qemu_mutex_init(&ram_list.mutex); + /* The data structures we set up here depend on knowing the page size, + * so no more changes can be made after this point. + * In an ideal world, nothing we did before we had finished the + * machine setup would care about the target page size, and we could + * do this much later, rather than requiring board models to state + * up front what their requirements are. + */ + finalize_target_page_bits(); io_mem_init(); memory_map_init(); qemu_mutex_init(&map_client_list_lock); @@ -303,7 +303,7 @@ typedef struct GDBState { int fd; int running_state; #else - CharDriverState *chr; + CharBackend chr; CharDriverState *mon_chr; #endif char syscall_buf[256]; @@ -404,7 +404,7 @@ static void put_buffer(GDBState *s, const uint8_t *buf, int len) #else /* XXX this blocks entire thread. Rewrite to use * qemu_chr_fe_write and background I/O callbacks */ - qemu_chr_fe_write_all(s->chr, buf, len); + qemu_chr_fe_write_all(&s->chr, buf, len); #endif } @@ -1471,6 +1471,9 @@ void gdb_exit(CPUArchState *env, int code) { GDBState *s; char buf[4]; +#ifndef CONFIG_USER_ONLY + CharDriverState *chr; +#endif s = gdbserver_state; if (!s) { @@ -1481,7 +1484,8 @@ void gdb_exit(CPUArchState *env, int code) return; } #else - if (!s->chr) { + chr = qemu_chr_fe_get_driver(&s->chr); + if (!chr) { return; } #endif @@ -1490,7 +1494,8 @@ void gdb_exit(CPUArchState *env, int code) put_packet(s, buf); #ifndef CONFIG_USER_ONLY - qemu_chr_delete(s->chr); + qemu_chr_fe_deinit(&s->chr); + qemu_chr_delete(chr); #endif } @@ -1745,13 +1750,9 @@ int gdbserver_start(const char *device) sigaction(SIGINT, &act, NULL); } #endif - chr = qemu_chr_new_noreplay("gdb", device, NULL); + chr = qemu_chr_new_noreplay("gdb", device); if (!chr) return -1; - - qemu_chr_fe_claim_no_fail(chr); - qemu_chr_add_handlers(chr, gdb_chr_can_receive, gdb_chr_receive, - gdb_chr_event, NULL); } s = gdbserver_state; @@ -1766,14 +1767,20 @@ int gdbserver_start(const char *device) mon_chr->chr_write = gdb_monitor_write; monitor_init(mon_chr, 0); } else { - if (s->chr) - qemu_chr_delete(s->chr); + if (qemu_chr_fe_get_driver(&s->chr)) { + qemu_chr_delete(qemu_chr_fe_get_driver(&s->chr)); + } mon_chr = s->mon_chr; memset(s, 0, sizeof(GDBState)); + s->mon_chr = mon_chr; } s->c_cpu = first_cpu; s->g_cpu = first_cpu; - s->chr = chr; + if (chr) { + qemu_chr_fe_init(&s->chr, chr, &error_abort); + qemu_chr_fe_set_handlers(&s->chr, gdb_chr_can_receive, gdb_chr_receive, + gdb_chr_event, NULL, NULL, true); + } s->state = chr ? RS_IDLE : RS_INACTIVE; s->mon_chr = mon_chr; s->current_syscall_cb = NULL; @@ -2002,7 +2002,7 @@ void hmp_chardev_add(Monitor *mon, const QDict *qdict) if (opts == NULL) { error_setg(&err, "Parsing chardev args failed"); } else { - qemu_chr_new_from_opts(opts, NULL, &err); + qemu_chr_new_from_opts(opts, &err); qemu_opts_del(opts); } hmp_handle_error(mon, &err); diff --git a/hw/acpi/cpu.c b/hw/acpi/cpu.c index 902f5c9..5ac89fe 100644 --- a/hw/acpi/cpu.c +++ b/hw/acpi/cpu.c @@ -531,6 +531,11 @@ void build_cpus_aml(Aml *table, MachineState *machine, CPUHotplugFeatures opts, apic->flags = cpu_to_le32(1); break; } + case ACPI_APIC_LOCAL_X2APIC: { + AcpiMadtProcessorX2Apic *apic = (void *)madt_buf->data; + apic->flags = cpu_to_le32(1); + break; + } default: assert(0); } diff --git a/hw/acpi/cpu_hotplug.c b/hw/acpi/cpu_hotplug.c index e19d902..f15a240 100644 --- a/hw/acpi/cpu_hotplug.c +++ b/hw/acpi/cpu_hotplug.c @@ -15,6 +15,7 @@ #include "qapi/error.h" #include "qom/cpu.h" #include "hw/i386/pc.h" +#include "qemu/error-report.h" #define CPU_EJECT_METHOD "CPEJ" #define CPU_MAT_METHOD "CPMA" @@ -63,7 +64,8 @@ static void acpi_set_cpu_present_bit(AcpiCpuHotplug *g, CPUState *cpu, cpu_id = k->get_arch_id(cpu); if ((cpu_id / 8) >= ACPI_GPE_PROC_LEN) { - error_setg(errp, "acpi: invalid cpu id: %" PRIi64, cpu_id); + object_property_set_bool(g->device, false, "cpu-hotplug-legacy", + &error_abort); return; } @@ -85,13 +87,14 @@ void legacy_acpi_cpu_hotplug_init(MemoryRegion *parent, Object *owner, { CPUState *cpu; - CPU_FOREACH(cpu) { - acpi_set_cpu_present_bit(gpe_cpu, cpu, &error_abort); - } memory_region_init_io(&gpe_cpu->io, owner, &AcpiCpuHotplug_ops, gpe_cpu, "acpi-cpu-hotplug", ACPI_GPE_PROC_LEN); memory_region_add_subregion(parent, base, &gpe_cpu->io); gpe_cpu->device = owner; + + CPU_FOREACH(cpu) { + acpi_set_cpu_present_bit(gpe_cpu, cpu, &error_abort); + } } void acpi_switch_to_modern_cphp(AcpiCpuHotplug *gpe_cpu, @@ -234,7 +237,11 @@ void build_legacy_cpu_hotplug_aml(Aml *ctx, MachineState *machine, /* The current AML generator can cover the APIC ID range [0..255], * inclusive, for VCPU hotplug. */ QEMU_BUILD_BUG_ON(ACPI_CPU_HOTPLUG_ID_LIMIT > 256); - g_assert(pcms->apic_id_limit <= ACPI_CPU_HOTPLUG_ID_LIMIT); + if (pcms->apic_id_limit > ACPI_CPU_HOTPLUG_ID_LIMIT) { + error_report("max_cpus is too large. APIC ID of last CPU is %u", + pcms->apic_id_limit - 1); + exit(1); + } /* create PCI0.PRES device and its _CRS to reserve CPU hotplug MMIO */ dev = aml_device("PCI0." stringify(CPU_HOTPLUG_RESOURCE_DEVICE)); diff --git a/hw/alpha/dp264.c b/hw/alpha/dp264.c index f1267b5..d6431fd 100644 --- a/hw/alpha/dp264.c +++ b/hw/alpha/dp264.c @@ -88,7 +88,7 @@ static void clipper_init(MachineState *machine) pci_vga_init(pci_bus); /* Serial code setup. */ - serial_hds_isa_init(isa_bus, MAX_SERIAL_PORTS); + serial_hds_isa_init(isa_bus, 0, MAX_SERIAL_PORTS); /* Network setup. e1000 is good enough, failing Tulip support. */ for (i = 0; i < nb_nics; i++) { diff --git a/hw/arm/fsl-imx25.c b/hw/arm/fsl-imx25.c index b4e358d..7bb7be76 100644 --- a/hw/arm/fsl-imx25.c +++ b/hw/arm/fsl-imx25.c @@ -125,7 +125,7 @@ static void fsl_imx25_realize(DeviceState *dev, Error **errp) if (!chr) { char label[20]; snprintf(label, sizeof(label), "imx31.uart%d", i); - chr = qemu_chr_new(label, "null", NULL); + chr = qemu_chr_new(label, "null"); } qdev_prop_set_chr(DEVICE(&s->uart[i]), "chardev", chr); diff --git a/hw/arm/fsl-imx31.c b/hw/arm/fsl-imx31.c index fe204ac..f23672b 100644 --- a/hw/arm/fsl-imx31.c +++ b/hw/arm/fsl-imx31.c @@ -114,7 +114,7 @@ static void fsl_imx31_realize(DeviceState *dev, Error **errp) if (!chr) { char label[20]; snprintf(label, sizeof(label), "imx31.uart%d", i); - chr = qemu_chr_new(label, "null", NULL); + chr = qemu_chr_new(label, "null"); } qdev_prop_set_chr(DEVICE(&s->uart[i]), "chardev", chr); diff --git a/hw/arm/fsl-imx6.c b/hw/arm/fsl-imx6.c index 6a1bf26..e93532f 100644 --- a/hw/arm/fsl-imx6.c +++ b/hw/arm/fsl-imx6.c @@ -193,7 +193,7 @@ static void fsl_imx6_realize(DeviceState *dev, Error **errp) if (!chr) { char *label = g_strdup_printf("imx6.uart%d", i + 1); - chr = qemu_chr_new(label, "null", NULL); + chr = qemu_chr_new(label, "null"); g_free(label); serial_hds[i] = chr; } diff --git a/hw/arm/musicpal.c b/hw/arm/musicpal.c index 7527037..cbbca4e 100644 --- a/hw/arm/musicpal.c +++ b/hw/arm/musicpal.c @@ -384,18 +384,24 @@ static NetClientInfo net_mv88w8618_info = { .cleanup = eth_cleanup, }; -static int mv88w8618_eth_init(SysBusDevice *sbd) +static void mv88w8618_eth_init(Object *obj) { + SysBusDevice *sbd = SYS_BUS_DEVICE(obj); DeviceState *dev = DEVICE(sbd); mv88w8618_eth_state *s = MV88W8618_ETH(dev); sysbus_init_irq(sbd, &s->irq); - s->nic = qemu_new_nic(&net_mv88w8618_info, &s->conf, - object_get_typename(OBJECT(dev)), dev->id, s); - memory_region_init_io(&s->iomem, OBJECT(s), &mv88w8618_eth_ops, s, + memory_region_init_io(&s->iomem, obj, &mv88w8618_eth_ops, s, "mv88w8618-eth", MP_ETH_SIZE); sysbus_init_mmio(sbd, &s->iomem); - return 0; +} + +static void mv88w8618_eth_realize(DeviceState *dev, Error **errp) +{ + mv88w8618_eth_state *s = MV88W8618_ETH(dev); + + s->nic = qemu_new_nic(&net_mv88w8618_info, &s->conf, + object_get_typename(OBJECT(dev)), dev->id, s); } static const VMStateDescription mv88w8618_eth_vmsd = { @@ -423,17 +429,17 @@ static Property mv88w8618_eth_properties[] = { static void mv88w8618_eth_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); - SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); - k->init = mv88w8618_eth_init; dc->vmsd = &mv88w8618_eth_vmsd; dc->props = mv88w8618_eth_properties; + dc->realize = mv88w8618_eth_realize; } static const TypeInfo mv88w8618_eth_info = { .name = TYPE_MV88W8618_ETH, .parent = TYPE_SYS_BUS_DEVICE, .instance_size = sizeof(mv88w8618_eth_state), + .instance_init = mv88w8618_eth_init, .class_init = mv88w8618_eth_class_init, }; @@ -615,23 +621,26 @@ static const GraphicHwOps musicpal_gfx_ops = { .gfx_update = lcd_refresh, }; -static int musicpal_lcd_init(SysBusDevice *sbd) +static void musicpal_lcd_realize(DeviceState *dev, Error **errp) +{ + musicpal_lcd_state *s = MUSICPAL_LCD(dev); + s->con = graphic_console_init(dev, 0, &musicpal_gfx_ops, s); + qemu_console_resize(s->con, 128 * 3, 64 * 3); +} + +static void musicpal_lcd_init(Object *obj) { + SysBusDevice *sbd = SYS_BUS_DEVICE(obj); DeviceState *dev = DEVICE(sbd); musicpal_lcd_state *s = MUSICPAL_LCD(dev); s->brightness = 7; - memory_region_init_io(&s->iomem, OBJECT(s), &musicpal_lcd_ops, s, + memory_region_init_io(&s->iomem, obj, &musicpal_lcd_ops, s, "musicpal-lcd", MP_LCD_SIZE); sysbus_init_mmio(sbd, &s->iomem); - s->con = graphic_console_init(dev, 0, &musicpal_gfx_ops, s); - qemu_console_resize(s->con, 128*3, 64*3); - qdev_init_gpio_in(dev, musicpal_lcd_gpio_brightness_in, 3); - - return 0; } static const VMStateDescription musicpal_lcd_vmsd = { @@ -652,16 +661,16 @@ static const VMStateDescription musicpal_lcd_vmsd = { static void musicpal_lcd_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); - SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); - k->init = musicpal_lcd_init; dc->vmsd = &musicpal_lcd_vmsd; + dc->realize = musicpal_lcd_realize; } static const TypeInfo musicpal_lcd_info = { .name = TYPE_MUSICPAL_LCD, .parent = TYPE_SYS_BUS_DEVICE, .instance_size = sizeof(musicpal_lcd_state), + .instance_init = musicpal_lcd_init, .class_init = musicpal_lcd_class_init, }; @@ -748,16 +757,16 @@ static const MemoryRegionOps mv88w8618_pic_ops = { .endianness = DEVICE_NATIVE_ENDIAN, }; -static int mv88w8618_pic_init(SysBusDevice *dev) +static void mv88w8618_pic_init(Object *obj) { + SysBusDevice *dev = SYS_BUS_DEVICE(obj); mv88w8618_pic_state *s = MV88W8618_PIC(dev); qdev_init_gpio_in(DEVICE(dev), mv88w8618_pic_set_irq, 32); sysbus_init_irq(dev, &s->parent_irq); - memory_region_init_io(&s->iomem, OBJECT(s), &mv88w8618_pic_ops, s, + memory_region_init_io(&s->iomem, obj, &mv88w8618_pic_ops, s, "musicpal-pic", MP_PIC_SIZE); sysbus_init_mmio(dev, &s->iomem); - return 0; } static const VMStateDescription mv88w8618_pic_vmsd = { @@ -774,9 +783,7 @@ static const VMStateDescription mv88w8618_pic_vmsd = { static void mv88w8618_pic_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); - SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); - k->init = mv88w8618_pic_init; dc->reset = mv88w8618_pic_reset; dc->vmsd = &mv88w8618_pic_vmsd; } @@ -785,6 +792,7 @@ static const TypeInfo mv88w8618_pic_info = { .name = TYPE_MV88W8618_PIC, .parent = TYPE_SYS_BUS_DEVICE, .instance_size = sizeof(mv88w8618_pic_state), + .instance_init = mv88w8618_pic_init, .class_init = mv88w8618_pic_class_init, }; @@ -913,8 +921,9 @@ static const MemoryRegionOps mv88w8618_pit_ops = { .endianness = DEVICE_NATIVE_ENDIAN, }; -static int mv88w8618_pit_init(SysBusDevice *dev) +static void mv88w8618_pit_init(Object *obj) { + SysBusDevice *dev = SYS_BUS_DEVICE(obj); mv88w8618_pit_state *s = MV88W8618_PIT(dev); int i; @@ -924,10 +933,9 @@ static int mv88w8618_pit_init(SysBusDevice *dev) mv88w8618_timer_init(dev, &s->timer[i], 1000000); } - memory_region_init_io(&s->iomem, OBJECT(s), &mv88w8618_pit_ops, s, + memory_region_init_io(&s->iomem, obj, &mv88w8618_pit_ops, s, "musicpal-pit", MP_PIT_SIZE); sysbus_init_mmio(dev, &s->iomem); - return 0; } static const VMStateDescription mv88w8618_timer_vmsd = { @@ -955,9 +963,7 @@ static const VMStateDescription mv88w8618_pit_vmsd = { static void mv88w8618_pit_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); - SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); - k->init = mv88w8618_pit_init; dc->reset = mv88w8618_pit_reset; dc->vmsd = &mv88w8618_pit_vmsd; } @@ -966,6 +972,7 @@ static const TypeInfo mv88w8618_pit_info = { .name = TYPE_MV88W8618_PIT, .parent = TYPE_SYS_BUS_DEVICE, .instance_size = sizeof(mv88w8618_pit_state), + .instance_init = mv88w8618_pit_init, .class_init = mv88w8618_pit_class_init, }; @@ -1018,15 +1025,15 @@ static const MemoryRegionOps mv88w8618_flashcfg_ops = { .endianness = DEVICE_NATIVE_ENDIAN, }; -static int mv88w8618_flashcfg_init(SysBusDevice *dev) +static void mv88w8618_flashcfg_init(Object *obj) { + SysBusDevice *dev = SYS_BUS_DEVICE(obj); mv88w8618_flashcfg_state *s = MV88W8618_FLASHCFG(dev); s->cfgr0 = 0xfffe4285; /* Default as set by U-Boot for 8 MB flash */ - memory_region_init_io(&s->iomem, OBJECT(s), &mv88w8618_flashcfg_ops, s, + memory_region_init_io(&s->iomem, obj, &mv88w8618_flashcfg_ops, s, "musicpal-flashcfg", MP_FLASHCFG_SIZE); sysbus_init_mmio(dev, &s->iomem); - return 0; } static const VMStateDescription mv88w8618_flashcfg_vmsd = { @@ -1042,9 +1049,7 @@ static const VMStateDescription mv88w8618_flashcfg_vmsd = { static void mv88w8618_flashcfg_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); - SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); - k->init = mv88w8618_flashcfg_init; dc->vmsd = &mv88w8618_flashcfg_vmsd; } @@ -1052,6 +1057,7 @@ static const TypeInfo mv88w8618_flashcfg_info = { .name = TYPE_MV88W8618_FLASHCFG, .parent = TYPE_SYS_BUS_DEVICE, .instance_size = sizeof(mv88w8618_flashcfg_state), + .instance_init = mv88w8618_flashcfg_init, .class_init = mv88w8618_flashcfg_class_init, }; @@ -1350,22 +1356,21 @@ static void musicpal_gpio_reset(DeviceState *d) s->isr = 0; } -static int musicpal_gpio_init(SysBusDevice *sbd) +static void musicpal_gpio_init(Object *obj) { + SysBusDevice *sbd = SYS_BUS_DEVICE(obj); DeviceState *dev = DEVICE(sbd); musicpal_gpio_state *s = MUSICPAL_GPIO(dev); sysbus_init_irq(sbd, &s->irq); - memory_region_init_io(&s->iomem, OBJECT(s), &musicpal_gpio_ops, s, + memory_region_init_io(&s->iomem, obj, &musicpal_gpio_ops, s, "musicpal-gpio", MP_GPIO_SIZE); sysbus_init_mmio(sbd, &s->iomem); qdev_init_gpio_out(dev, s->out, ARRAY_SIZE(s->out)); qdev_init_gpio_in(dev, musicpal_gpio_pin_event, 32); - - return 0; } static const VMStateDescription musicpal_gpio_vmsd = { @@ -1386,9 +1391,7 @@ static const VMStateDescription musicpal_gpio_vmsd = { static void musicpal_gpio_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); - SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); - k->init = musicpal_gpio_init; dc->reset = musicpal_gpio_reset; dc->vmsd = &musicpal_gpio_vmsd; } @@ -1397,6 +1400,7 @@ static const TypeInfo musicpal_gpio_info = { .name = TYPE_MUSICPAL_GPIO, .parent = TYPE_SYS_BUS_DEVICE, .instance_size = sizeof(musicpal_gpio_state), + .instance_init = musicpal_gpio_init, .class_init = musicpal_gpio_class_init, }; @@ -1516,12 +1520,13 @@ static void musicpal_key_event(void *opaque, int keycode) s->kbd_extended = 0; } -static int musicpal_key_init(SysBusDevice *sbd) +static void musicpal_key_init(Object *obj) { + SysBusDevice *sbd = SYS_BUS_DEVICE(obj); DeviceState *dev = DEVICE(sbd); musicpal_key_state *s = MUSICPAL_KEY(dev); - memory_region_init(&s->iomem, OBJECT(s), "dummy", 0); + memory_region_init(&s->iomem, obj, "dummy", 0); sysbus_init_mmio(sbd, &s->iomem); s->kbd_extended = 0; @@ -1530,8 +1535,6 @@ static int musicpal_key_init(SysBusDevice *sbd) qdev_init_gpio_out(dev, s->out, ARRAY_SIZE(s->out)); qemu_add_kbd_event_handler(musicpal_key_event, s); - - return 0; } static const VMStateDescription musicpal_key_vmsd = { @@ -1548,9 +1551,7 @@ static const VMStateDescription musicpal_key_vmsd = { static void musicpal_key_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); - SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); - k->init = musicpal_key_init; dc->vmsd = &musicpal_key_vmsd; } @@ -1558,6 +1559,7 @@ static const TypeInfo musicpal_key_info = { .name = TYPE_MUSICPAL_KEY, .parent = TYPE_SYS_BUS_DEVICE, .instance_size = sizeof(musicpal_key_state), + .instance_init = musicpal_key_init, .class_init = musicpal_key_class_init, }; diff --git a/hw/arm/omap2.c b/hw/arm/omap2.c index 7e11c65..6f05c98 100644 --- a/hw/arm/omap2.c +++ b/hw/arm/omap2.c @@ -621,7 +621,7 @@ struct omap_sti_s { qemu_irq irq; MemoryRegion iomem; MemoryRegion iomem_fifo; - CharDriverState *chr; + CharBackend chr; uint32_t sysconfig; uint32_t systest; @@ -771,14 +771,15 @@ static void omap_sti_fifo_write(void *opaque, hwaddr addr, /* Flush channel <i>value</i>. */ /* XXX this blocks entire thread. Rewrite to use * qemu_chr_fe_write and background I/O callbacks */ - qemu_chr_fe_write_all(s->chr, (const uint8_t *) "\r", 1); + qemu_chr_fe_write_all(&s->chr, (const uint8_t *) "\r", 1); } else if (ch == STI_TRACE_CONSOLE_CHANNEL || 1) { if (value == 0xc0 || value == 0xc3) { /* Open channel <i>ch</i>. */ - } else if (value == 0x00) - qemu_chr_fe_write_all(s->chr, (const uint8_t *) "\n", 1); - else - qemu_chr_fe_write_all(s->chr, &byte, 1); + } else if (value == 0x00) { + qemu_chr_fe_write_all(&s->chr, (const uint8_t *) "\n", 1); + } else { + qemu_chr_fe_write_all(&s->chr, &byte, 1); + } } } @@ -798,7 +799,8 @@ static struct omap_sti_s *omap_sti_init(struct omap_target_agent_s *ta, s->irq = irq; omap_sti_reset(s); - s->chr = chr ?: qemu_chr_new("null", "null", NULL); + qemu_chr_fe_init(&s->chr, chr ?: qemu_chr_new("null", "null"), + &error_abort); memory_region_init_io(&s->iomem, NULL, &omap_sti_ops, s, "omap.sti", omap_l4_region_size(ta, 0)); diff --git a/hw/arm/pxa2xx.c b/hw/arm/pxa2xx.c index 9898287..42cdde0 100644 --- a/hw/arm/pxa2xx.c +++ b/hw/arm/pxa2xx.c @@ -1764,7 +1764,7 @@ struct PXA2xxFIrState { qemu_irq rx_dma; qemu_irq tx_dma; uint32_t enable; - CharDriverState *chr; + CharBackend chr; uint8_t control[3]; uint8_t status[2]; @@ -1898,14 +1898,16 @@ static void pxa2xx_fir_write(void *opaque, hwaddr addr, pxa2xx_fir_update(s); break; case ICDR: - if (s->control[2] & (1 << 2)) /* TXP */ + if (s->control[2] & (1 << 2)) { /* TXP */ ch = value; - else + } else { ch = ~value; - if (s->chr && s->enable && (s->control[0] & (1 << 3))) /* TXE */ + } + if (s->enable && (s->control[0] & (1 << 3))) { /* TXE */ /* XXX this blocks entire thread. Rewrite to use * qemu_chr_fe_write and background I/O callbacks */ - qemu_chr_fe_write_all(s->chr, &ch, 1); + qemu_chr_fe_write_all(&s->chr, &ch, 1); + } break; case ICSR0: s->status[0] &= ~(value & 0x66); @@ -1973,11 +1975,8 @@ static void pxa2xx_fir_realize(DeviceState *dev, Error **errp) { PXA2xxFIrState *s = PXA2XX_FIR(dev); - if (s->chr) { - qemu_chr_fe_claim_no_fail(s->chr); - qemu_chr_add_handlers(s->chr, pxa2xx_fir_is_empty, - pxa2xx_fir_rx, pxa2xx_fir_event, s); - } + qemu_chr_fe_set_handlers(&s->chr, pxa2xx_fir_is_empty, + pxa2xx_fir_rx, pxa2xx_fir_event, s, NULL, true); } static bool pxa2xx_fir_vmstate_validate(void *opaque, int version_id) diff --git a/hw/arm/pxa2xx_gpio.c b/hw/arm/pxa2xx_gpio.c index 576a8eb..521dbad 100644 --- a/hw/arm/pxa2xx_gpio.c +++ b/hw/arm/pxa2xx_gpio.c @@ -280,23 +280,28 @@ DeviceState *pxa2xx_gpio_init(hwaddr base, return dev; } -static int pxa2xx_gpio_initfn(SysBusDevice *sbd) +static void pxa2xx_gpio_initfn(Object *obj) { + SysBusDevice *sbd = SYS_BUS_DEVICE(obj); DeviceState *dev = DEVICE(sbd); PXA2xxGPIOInfo *s = PXA2XX_GPIO(dev); - s->cpu = ARM_CPU(qemu_get_cpu(s->ncpu)); - - qdev_init_gpio_in(dev, pxa2xx_gpio_set, s->lines); - qdev_init_gpio_out(dev, s->handler, s->lines); - - memory_region_init_io(&s->iomem, OBJECT(s), &pxa_gpio_ops, s, "pxa2xx-gpio", 0x1000); + memory_region_init_io(&s->iomem, obj, &pxa_gpio_ops, + s, "pxa2xx-gpio", 0x1000); sysbus_init_mmio(sbd, &s->iomem); sysbus_init_irq(sbd, &s->irq0); sysbus_init_irq(sbd, &s->irq1); sysbus_init_irq(sbd, &s->irqX); +} - return 0; +static void pxa2xx_gpio_realize(DeviceState *dev, Error **errp) +{ + PXA2xxGPIOInfo *s = PXA2XX_GPIO(dev); + + s->cpu = ARM_CPU(qemu_get_cpu(s->ncpu)); + + qdev_init_gpio_in(dev, pxa2xx_gpio_set, s->lines); + qdev_init_gpio_out(dev, s->handler, s->lines); } /* @@ -336,18 +341,18 @@ static Property pxa2xx_gpio_properties[] = { static void pxa2xx_gpio_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); - SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); - k->init = pxa2xx_gpio_initfn; dc->desc = "PXA2xx GPIO controller"; dc->props = pxa2xx_gpio_properties; dc->vmsd = &vmstate_pxa2xx_gpio_regs; + dc->realize = pxa2xx_gpio_realize; } static const TypeInfo pxa2xx_gpio_info = { .name = TYPE_PXA2XX_GPIO, .parent = TYPE_SYS_BUS_DEVICE, .instance_size = sizeof(PXA2xxGPIOInfo), + .instance_init = pxa2xx_gpio_initfn, .class_init = pxa2xx_gpio_class_init, }; diff --git a/hw/arm/strongarm.c b/hw/arm/strongarm.c index 021cbf9..3311cc3 100644 --- a/hw/arm/strongarm.c +++ b/hw/arm/strongarm.c @@ -912,7 +912,7 @@ typedef struct StrongARMUARTState { SysBusDevice parent_obj; MemoryRegion iomem; - CharDriverState *chr; + CharBackend chr; qemu_irq irq; uint8_t utcr0; @@ -1020,9 +1020,7 @@ static void strongarm_uart_update_parameters(StrongARMUARTState *s) ssp.data_bits = data_bits; ssp.stop_bits = stop_bits; s->char_transmit_time = (NANOSECONDS_PER_SECOND / speed) * frame_size; - if (s->chr) { - qemu_chr_fe_ioctl(s->chr, CHR_IOCTL_SERIAL_SET_PARAMS, &ssp); - } + qemu_chr_fe_ioctl(&s->chr, CHR_IOCTL_SERIAL_SET_PARAMS, &ssp); DPRINTF(stderr, "%s speed=%d parity=%c data=%d stop=%d\n", s->chr->label, speed, parity, data_bits, stop_bits); @@ -1107,10 +1105,10 @@ static void strongarm_uart_tx(void *opaque) if (s->utcr3 & UTCR3_LBM) /* loopback */ { strongarm_uart_receive(s, &s->tx_fifo[s->tx_start], 1); - } else if (s->chr) { + } else if (qemu_chr_fe_get_driver(&s->chr)) { /* XXX this blocks entire thread. Rewrite to use * qemu_chr_fe_write and background I/O callbacks */ - qemu_chr_fe_write_all(s->chr, &s->tx_fifo[s->tx_start], 1); + qemu_chr_fe_write_all(&s->chr, &s->tx_fifo[s->tx_start], 1); } s->tx_start = (s->tx_start + 1) % 8; @@ -1238,14 +1236,17 @@ static void strongarm_uart_init(Object *obj) s->rx_timeout_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, strongarm_uart_rx_to, s); s->tx_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, strongarm_uart_tx, s); +} - if (s->chr) { - qemu_chr_add_handlers(s->chr, - strongarm_uart_can_receive, - strongarm_uart_receive, - strongarm_uart_event, - s); - } +static void strongarm_uart_realize(DeviceState *dev, Error **errp) +{ + StrongARMUARTState *s = STRONGARM_UART(dev); + + qemu_chr_fe_set_handlers(&s->chr, + strongarm_uart_can_receive, + strongarm_uart_receive, + strongarm_uart_event, + s, NULL, true); } static void strongarm_uart_reset(DeviceState *dev) @@ -1320,6 +1321,7 @@ static void strongarm_uart_class_init(ObjectClass *klass, void *data) dc->reset = strongarm_uart_reset; dc->vmsd = &vmstate_strongarm_uart_regs; dc->props = strongarm_uart_properties; + dc->realize = strongarm_uart_realize; } static const TypeInfo strongarm_uart_info = { @@ -1520,19 +1522,19 @@ static int strongarm_ssp_post_load(void *opaque, int version_id) return 0; } -static int strongarm_ssp_init(SysBusDevice *sbd) +static void strongarm_ssp_init(Object *obj) { + SysBusDevice *sbd = SYS_BUS_DEVICE(obj); DeviceState *dev = DEVICE(sbd); StrongARMSSPState *s = STRONGARM_SSP(dev); sysbus_init_irq(sbd, &s->irq); - memory_region_init_io(&s->iomem, OBJECT(s), &strongarm_ssp_ops, s, + memory_region_init_io(&s->iomem, obj, &strongarm_ssp_ops, s, "ssp", 0x1000); sysbus_init_mmio(sbd, &s->iomem); s->bus = ssi_create_bus(dev, "ssi"); - return 0; } static void strongarm_ssp_reset(DeviceState *dev) @@ -1562,9 +1564,7 @@ static const VMStateDescription vmstate_strongarm_ssp_regs = { static void strongarm_ssp_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); - SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); - k->init = strongarm_ssp_init; dc->desc = "StrongARM SSP controller"; dc->reset = strongarm_ssp_reset; dc->vmsd = &vmstate_strongarm_ssp_regs; @@ -1574,6 +1574,7 @@ static const TypeInfo strongarm_ssp_info = { .name = TYPE_STRONGARM_SSP, .parent = TYPE_SYS_BUS_DEVICE, .instance_size = sizeof(StrongARMSSPState), + .instance_init = strongarm_ssp_init, .class_init = strongarm_ssp_class_init, }; diff --git a/hw/arm/virt-acpi-build.c b/hw/arm/virt-acpi-build.c index fa0655a..5fc10df 100644 --- a/hw/arm/virt-acpi-build.c +++ b/hw/arm/virt-acpi-build.c @@ -384,6 +384,61 @@ build_rsdp(GArray *rsdp_table, BIOSLinker *linker, unsigned rsdt_tbl_offset) } static void +build_iort(GArray *table_data, BIOSLinker *linker, VirtGuestInfo *guest_info) +{ + int iort_start = table_data->len; + AcpiIortIdMapping *idmap; + AcpiIortItsGroup *its; + AcpiIortTable *iort; + size_t node_size, iort_length; + AcpiIortRC *rc; + + iort = acpi_data_push(table_data, sizeof(*iort)); + + iort_length = sizeof(*iort); + iort->node_count = cpu_to_le32(2); /* RC and ITS nodes */ + iort->node_offset = cpu_to_le32(sizeof(*iort)); + + /* ITS group node */ + node_size = sizeof(*its) + sizeof(uint32_t); + iort_length += node_size; + its = acpi_data_push(table_data, node_size); + + its->type = ACPI_IORT_NODE_ITS_GROUP; + its->length = cpu_to_le16(node_size); + its->its_count = cpu_to_le32(1); + its->identifiers[0] = 0; /* MADT translation_id */ + + /* Root Complex Node */ + node_size = sizeof(*rc) + sizeof(*idmap); + iort_length += node_size; + rc = acpi_data_push(table_data, node_size); + + rc->type = ACPI_IORT_NODE_PCI_ROOT_COMPLEX; + rc->length = cpu_to_le16(node_size); + rc->mapping_count = cpu_to_le32(1); + rc->mapping_offset = cpu_to_le32(sizeof(*rc)); + + /* fully coherent device */ + rc->memory_properties.cache_coherency = cpu_to_le32(1); + rc->memory_properties.memory_flags = 0x3; /* CCA = CPM = DCAS = 1 */ + rc->pci_segment_number = 0; /* MCFG pci_segment */ + + /* Identity RID mapping covering the whole input RID range */ + idmap = &rc->id_mapping_array[0]; + idmap->input_base = 0; + idmap->id_count = cpu_to_le32(0xFFFF); + idmap->output_base = 0; + /* output IORT node is the ITS group node (the first node) */ + idmap->output_reference = cpu_to_le32(iort->node_offset); + + iort->length = cpu_to_le32(iort_length); + + build_header(linker, table_data, (void *)(table_data->data + iort_start), + "IORT", table_data->len - iort_start, 0, NULL, NULL); +} + +static void build_spcr(GArray *table_data, BIOSLinker *linker, VirtGuestInfo *guest_info) { AcpiSerialPortConsoleRedirection *spcr; @@ -667,17 +722,6 @@ void virt_acpi_build(VirtGuestInfo *guest_info, AcpiBuildTables *tables) ACPI_BUILD_TABLE_FILE, tables_blob, 64, false /* high memory */); - /* - * The ACPI v5.1 tables for Hardware-reduced ACPI platform are: - * RSDP - * RSDT - * FADT - * GTDT - * MADT - * MCFG - * DSDT - */ - /* DSDT is pointed to by FADT */ dsdt = tables_blob->len; build_dsdt(tables_blob, tables->linker, guest_info); @@ -703,6 +747,11 @@ void virt_acpi_build(VirtGuestInfo *guest_info, AcpiBuildTables *tables) build_srat(tables_blob, tables->linker, guest_info); } + if (its_class_name() && !guest_info->no_its) { + acpi_add_table(table_offsets, tables_blob); + build_iort(tables_blob, tables->linker, guest_info); + } + /* RSDT is pointed to by RSDP */ rsdt = tables_blob->len; build_rsdt(tables_blob, tables->linker, table_offsets, NULL, NULL); diff --git a/hw/arm/virt.c b/hw/arm/virt.c index 895446f..070bbf8 100644 --- a/hw/arm/virt.c +++ b/hw/arm/virt.c @@ -1494,11 +1494,13 @@ static void virt_machine_class_init(ObjectClass *oc, void *data) * it later in machvirt_init, where we have more information about the * configuration of the particular instance. */ - mc->max_cpus = MAX_CPUMASK_BITS; + mc->max_cpus = 255; mc->has_dynamic_sysbus = true; mc->block_default_type = IF_VIRTIO; mc->no_cdrom = 1; mc->pci_allow_0_address = true; + /* We know we will never create a pre-ARMv7 CPU which needs 1K pages */ + mc->minimum_page_bits = 12; } static const TypeInfo virt_machine_info = { @@ -1570,6 +1572,8 @@ static void virt_machine_2_7_options(MachineClass *mc) SET_MACHINE_COMPAT(mc, VIRT_COMPAT_2_7); /* ITS was introduced with 2.8 */ vmc->no_its = true; + /* Stick with 1K pages for migration compatibility */ + mc->minimum_page_bits = 0; } DEFINE_VIRT_MACHINE(2, 7) diff --git a/hw/audio/intel-hda.c b/hw/audio/intel-hda.c index cd95340..537face 100644 --- a/hw/audio/intel-hda.c +++ b/hw/audio/intel-hda.c @@ -416,7 +416,8 @@ static bool intel_hda_xfer(HDACodecDevice *dev, uint32_t stnr, bool output, } left = len; - while (left > 0) { + s = st->bentries; + while (left > 0 && s-- > 0) { copy = left; if (copy > st->bsize - st->lpib) copy = st->bsize - st->lpib; diff --git a/hw/block/fdc.c b/hw/block/fdc.c index b79873a..17d29e7 100644 --- a/hw/block/fdc.c +++ b/hw/block/fdc.c @@ -35,6 +35,7 @@ #include "qemu/timer.h" #include "hw/isa/isa.h" #include "hw/sysbus.h" +#include "hw/block/block.h" #include "sysemu/block-backend.h" #include "sysemu/blockdev.h" #include "sysemu/sysemu.h" @@ -52,6 +53,35 @@ } \ } while (0) + +/********************************************************/ +/* qdev floppy bus */ + +#define TYPE_FLOPPY_BUS "floppy-bus" +#define FLOPPY_BUS(obj) OBJECT_CHECK(FloppyBus, (obj), TYPE_FLOPPY_BUS) + +typedef struct FDCtrl FDCtrl; +typedef struct FDrive FDrive; +static FDrive *get_drv(FDCtrl *fdctrl, int unit); + +typedef struct FloppyBus { + BusState bus; + FDCtrl *fdc; +} FloppyBus; + +static const TypeInfo floppy_bus_info = { + .name = TYPE_FLOPPY_BUS, + .parent = TYPE_BUS, + .instance_size = sizeof(FloppyBus), +}; + +static void floppy_bus_create(FDCtrl *fdc, FloppyBus *bus, DeviceState *dev) +{ + qbus_create_inplace(bus, sizeof(FloppyBus), TYPE_FLOPPY_BUS, dev, NULL); + bus->fdc = fdc; +} + + /********************************************************/ /* Floppy drive emulation */ @@ -148,14 +178,12 @@ static FDriveSize drive_size(FloppyDriveType drive) #define FD_SECTOR_SC 2 /* Sector size code */ #define FD_RESET_SENSEI_COUNT 4 /* Number of sense interrupts on RESET */ -typedef struct FDCtrl FDCtrl; - /* Floppy disk drive emulation */ typedef enum FDiskFlags { FDISK_DBL_SIDES = 0x01, } FDiskFlags; -typedef struct FDrive { +struct FDrive { FDCtrl *fdctrl; BlockBackend *blk; /* Drive status */ @@ -176,7 +204,7 @@ typedef struct FDrive { uint8_t media_rate; /* Data rate of medium */ bool media_validated; /* Have we validated the media? */ -} FDrive; +}; static FloppyDriveType get_fallback_drive_type(FDrive *drv); @@ -441,6 +469,135 @@ static void fd_revalidate(FDrive *drv) } } +static void fd_change_cb(void *opaque, bool load) +{ + FDrive *drive = opaque; + + drive->media_changed = 1; + drive->media_validated = false; + fd_revalidate(drive); +} + +static const BlockDevOps fd_block_ops = { + .change_media_cb = fd_change_cb, +}; + + +#define TYPE_FLOPPY_DRIVE "floppy" +#define FLOPPY_DRIVE(obj) \ + OBJECT_CHECK(FloppyDrive, (obj), TYPE_FLOPPY_DRIVE) + +typedef struct FloppyDrive { + DeviceState qdev; + uint32_t unit; + BlockConf conf; + FloppyDriveType type; +} FloppyDrive; + +static Property floppy_drive_properties[] = { + DEFINE_PROP_UINT32("unit", FloppyDrive, unit, -1), + DEFINE_BLOCK_PROPERTIES(FloppyDrive, conf), + DEFINE_PROP_DEFAULT("drive-type", FloppyDrive, type, + FLOPPY_DRIVE_TYPE_AUTO, qdev_prop_fdc_drive_type, + FloppyDriveType), + DEFINE_PROP_END_OF_LIST(), +}; + +static int floppy_drive_init(DeviceState *qdev) +{ + FloppyDrive *dev = FLOPPY_DRIVE(qdev); + FloppyBus *bus = FLOPPY_BUS(qdev->parent_bus); + FDrive *drive; + int ret; + + if (dev->unit == -1) { + for (dev->unit = 0; dev->unit < MAX_FD; dev->unit++) { + drive = get_drv(bus->fdc, dev->unit); + if (!drive->blk) { + break; + } + } + } + + if (dev->unit >= MAX_FD) { + error_report("Can't create floppy unit %d, bus supports only %d units", + dev->unit, MAX_FD); + return -1; + } + + drive = get_drv(bus->fdc, dev->unit); + if (drive->blk) { + error_report("Floppy unit %d is in use", dev->unit); + return -1; + } + + if (!dev->conf.blk) { + /* Anonymous BlockBackend for an empty drive */ + dev->conf.blk = blk_new(); + ret = blk_attach_dev(dev->conf.blk, qdev); + assert(ret == 0); + } + + blkconf_blocksizes(&dev->conf); + if (dev->conf.logical_block_size != 512 || + dev->conf.physical_block_size != 512) + { + error_report("Physical and logical block size must be 512 for floppy"); + return -1; + } + + /* rerror/werror aren't supported by fdc and therefore not even registered + * with qdev. So set the defaults manually before they are used in + * blkconf_apply_backend_options(). */ + dev->conf.rerror = BLOCKDEV_ON_ERROR_AUTO; + dev->conf.werror = BLOCKDEV_ON_ERROR_AUTO; + blkconf_apply_backend_options(&dev->conf); + + /* 'enospc' is the default for -drive, 'report' is what blk_new() gives us + * for empty drives. */ + if (blk_get_on_error(dev->conf.blk, 0) != BLOCKDEV_ON_ERROR_ENOSPC && + blk_get_on_error(dev->conf.blk, 0) != BLOCKDEV_ON_ERROR_REPORT) { + error_report("fdc doesn't support drive option werror"); + return -1; + } + if (blk_get_on_error(dev->conf.blk, 1) != BLOCKDEV_ON_ERROR_REPORT) { + error_report("fdc doesn't support drive option rerror"); + return -1; + } + + drive->blk = dev->conf.blk; + drive->fdctrl = bus->fdc; + + fd_init(drive); + blk_set_dev_ops(drive->blk, &fd_block_ops, drive); + + /* Keep 'type' qdev property and FDrive->drive in sync */ + drive->drive = dev->type; + pick_drive_type(drive); + dev->type = drive->drive; + + fd_revalidate(drive); + + return 0; +} + +static void floppy_drive_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *k = DEVICE_CLASS(klass); + k->init = floppy_drive_init; + set_bit(DEVICE_CATEGORY_STORAGE, k->categories); + k->bus_type = TYPE_FLOPPY_BUS; + k->props = floppy_drive_properties; + k->desc = "virtual floppy drive"; +} + +static const TypeInfo floppy_drive_info = { + .name = TYPE_FLOPPY_DRIVE, + .parent = TYPE_DEVICE, + .instance_size = sizeof(FloppyDrive), + .class_init = floppy_drive_class_init, +}; + /********************************************************/ /* Intel 82078 floppy disk controller emulation */ @@ -684,8 +841,13 @@ struct FDCtrl { /* Power down config (also with status regB access mode */ uint8_t pwrd; /* Floppy drives */ + FloppyBus bus; uint8_t num_floppies; FDrive drives[MAX_FD]; + struct { + BlockBackend *blk; + FloppyDriveType type; + } qdev_for_drives[MAX_FD]; int reset_sensei; uint32_t check_media_rate; FloppyDriveType fallback; /* type=auto failure fallback */ @@ -1159,9 +1321,9 @@ static inline FDrive *drv3(FDCtrl *fdctrl) } #endif -static FDrive *get_cur_drv(FDCtrl *fdctrl) +static FDrive *get_drv(FDCtrl *fdctrl, int unit) { - switch (fdctrl->cur_drv) { + switch (unit) { case 0: return drv0(fdctrl); case 1: return drv1(fdctrl); #if MAX_FD == 4 @@ -1172,6 +1334,11 @@ static FDrive *get_cur_drv(FDCtrl *fdctrl) } } +static FDrive *get_cur_drv(FDCtrl *fdctrl) +{ + return get_drv(fdctrl, fdctrl->cur_drv); +} + /* Status A register : 0x00 (read-only) */ static uint32_t fdctrl_read_statusA(FDCtrl *fdctrl) { @@ -2331,46 +2498,49 @@ static void fdctrl_result_timer(void *opaque) } } -static void fdctrl_change_cb(void *opaque, bool load) -{ - FDrive *drive = opaque; - - drive->media_changed = 1; - drive->media_validated = false; - fd_revalidate(drive); -} - -static const BlockDevOps fdctrl_block_ops = { - .change_media_cb = fdctrl_change_cb, -}; - /* Init functions */ -static void fdctrl_connect_drives(FDCtrl *fdctrl, Error **errp) +static void fdctrl_connect_drives(FDCtrl *fdctrl, Error **errp, + DeviceState *fdc_dev) { unsigned int i; FDrive *drive; + DeviceState *dev; + BlockBackend *blk; + Error *local_err = NULL; for (i = 0; i < MAX_FD; i++) { drive = &fdctrl->drives[i]; drive->fdctrl = fdctrl; - if (drive->blk) { - if (blk_get_on_error(drive->blk, 0) != BLOCKDEV_ON_ERROR_ENOSPC) { - error_setg(errp, "fdc doesn't support drive option werror"); - return; - } - if (blk_get_on_error(drive->blk, 1) != BLOCKDEV_ON_ERROR_REPORT) { - error_setg(errp, "fdc doesn't support drive option rerror"); - return; - } + /* If the drive is not present, we skip creating the qdev device, but + * still have to initialise the controller. */ + blk = fdctrl->qdev_for_drives[i].blk; + if (!blk) { + fd_init(drive); + fd_revalidate(drive); + continue; + } + + dev = qdev_create(&fdctrl->bus.bus, "floppy"); + qdev_prop_set_uint32(dev, "unit", i); + qdev_prop_set_enum(dev, "drive-type", fdctrl->qdev_for_drives[i].type); + + blk_ref(blk); + blk_detach_dev(blk, fdc_dev); + fdctrl->qdev_for_drives[i].blk = NULL; + qdev_prop_set_drive(dev, "drive", blk, &local_err); + blk_unref(blk); + + if (local_err) { + error_propagate(errp, local_err); + return; } - fd_init(drive); - if (drive->blk) { - blk_set_dev_ops(drive->blk, &fdctrl_block_ops, drive); - pick_drive_type(drive); + object_property_set_bool(OBJECT(dev), true, "realized", &local_err); + if (local_err) { + error_propagate(errp, local_err); + return; } - fd_revalidate(drive); } } @@ -2442,7 +2612,8 @@ void sun4m_fdctrl_init(qemu_irq irq, hwaddr io_base, *fdc_tc = qdev_get_gpio_in(dev, 0); } -static void fdctrl_realize_common(FDCtrl *fdctrl, Error **errp) +static void fdctrl_realize_common(DeviceState *dev, FDCtrl *fdctrl, + Error **errp) { int i, j; static int command_tables_inited = 0; @@ -2480,7 +2651,9 @@ static void fdctrl_realize_common(FDCtrl *fdctrl, Error **errp) k->register_channel(fdctrl->dma, fdctrl->dma_chann, &fdctrl_transfer_handler, fdctrl); } - fdctrl_connect_drives(fdctrl, errp); + + floppy_bus_create(fdctrl, &fdctrl->bus, dev); + fdctrl_connect_drives(fdctrl, errp, dev); } static const MemoryRegionPortio fdc_portio_list[] = { @@ -2508,7 +2681,7 @@ static void isabus_fdc_realize(DeviceState *dev, Error **errp) } qdev_set_legacy_instance_id(dev, isa->iobase, 2); - fdctrl_realize_common(fdctrl, &err); + fdctrl_realize_common(dev, fdctrl, &err); if (err != NULL) { error_propagate(errp, err); return; @@ -2559,7 +2732,7 @@ static void sysbus_fdc_common_realize(DeviceState *dev, Error **errp) FDCtrlSysBus *sys = SYSBUS_FDC(dev); FDCtrl *fdctrl = &sys->state; - fdctrl_realize_common(fdctrl, errp); + fdctrl_realize_common(dev, fdctrl, errp); } FloppyDriveType isa_fdc_get_drive_type(ISADevice *fdc, int i) @@ -2606,14 +2779,14 @@ static Property isa_fdc_properties[] = { DEFINE_PROP_UINT32("iobase", FDCtrlISABus, iobase, 0x3f0), DEFINE_PROP_UINT32("irq", FDCtrlISABus, irq, 6), DEFINE_PROP_UINT32("dma", FDCtrlISABus, dma, 2), - DEFINE_PROP_DRIVE("driveA", FDCtrlISABus, state.drives[0].blk), - DEFINE_PROP_DRIVE("driveB", FDCtrlISABus, state.drives[1].blk), + DEFINE_PROP_DRIVE("driveA", FDCtrlISABus, state.qdev_for_drives[0].blk), + DEFINE_PROP_DRIVE("driveB", FDCtrlISABus, state.qdev_for_drives[1].blk), DEFINE_PROP_BIT("check_media_rate", FDCtrlISABus, state.check_media_rate, 0, true), - DEFINE_PROP_DEFAULT("fdtypeA", FDCtrlISABus, state.drives[0].drive, + DEFINE_PROP_DEFAULT("fdtypeA", FDCtrlISABus, state.qdev_for_drives[0].type, FLOPPY_DRIVE_TYPE_AUTO, qdev_prop_fdc_drive_type, FloppyDriveType), - DEFINE_PROP_DEFAULT("fdtypeB", FDCtrlISABus, state.drives[1].drive, + DEFINE_PROP_DEFAULT("fdtypeB", FDCtrlISABus, state.qdev_for_drives[1].type, FLOPPY_DRIVE_TYPE_AUTO, qdev_prop_fdc_drive_type, FloppyDriveType), DEFINE_PROP_DEFAULT("fallback", FDCtrlISABus, state.fallback, @@ -2665,12 +2838,12 @@ static const VMStateDescription vmstate_sysbus_fdc ={ }; static Property sysbus_fdc_properties[] = { - DEFINE_PROP_DRIVE("driveA", FDCtrlSysBus, state.drives[0].blk), - DEFINE_PROP_DRIVE("driveB", FDCtrlSysBus, state.drives[1].blk), - DEFINE_PROP_DEFAULT("fdtypeA", FDCtrlSysBus, state.drives[0].drive, + DEFINE_PROP_DRIVE("driveA", FDCtrlSysBus, state.qdev_for_drives[0].blk), + DEFINE_PROP_DRIVE("driveB", FDCtrlSysBus, state.qdev_for_drives[1].blk), + DEFINE_PROP_DEFAULT("fdtypeA", FDCtrlSysBus, state.qdev_for_drives[0].type, FLOPPY_DRIVE_TYPE_AUTO, qdev_prop_fdc_drive_type, FloppyDriveType), - DEFINE_PROP_DEFAULT("fdtypeB", FDCtrlSysBus, state.drives[1].drive, + DEFINE_PROP_DEFAULT("fdtypeB", FDCtrlSysBus, state.qdev_for_drives[1].type, FLOPPY_DRIVE_TYPE_AUTO, qdev_prop_fdc_drive_type, FloppyDriveType), DEFINE_PROP_DEFAULT("fallback", FDCtrlISABus, state.fallback, @@ -2695,8 +2868,8 @@ static const TypeInfo sysbus_fdc_info = { }; static Property sun4m_fdc_properties[] = { - DEFINE_PROP_DRIVE("drive", FDCtrlSysBus, state.drives[0].blk), - DEFINE_PROP_DEFAULT("fdtype", FDCtrlSysBus, state.drives[0].drive, + DEFINE_PROP_DRIVE("drive", FDCtrlSysBus, state.qdev_for_drives[0].blk), + DEFINE_PROP_DEFAULT("fdtype", FDCtrlSysBus, state.qdev_for_drives[0].type, FLOPPY_DRIVE_TYPE_AUTO, qdev_prop_fdc_drive_type, FloppyDriveType), DEFINE_PROP_DEFAULT("fallback", FDCtrlISABus, state.fallback, @@ -2744,6 +2917,8 @@ static void fdc_register_types(void) type_register_static(&sysbus_fdc_type_info); type_register_static(&sysbus_fdc_info); type_register_static(&sun4m_fdc_info); + type_register_static(&floppy_bus_info); + type_register_static(&floppy_drive_info); } type_init(fdc_register_types) diff --git a/hw/block/nvme.c b/hw/block/nvme.c index cef3bb4..b380142 100644 --- a/hw/block/nvme.c +++ b/hw/block/nvme.c @@ -258,8 +258,10 @@ static uint16_t nvme_rw(NvmeCtrl *n, NvmeNamespace *ns, NvmeCmd *cmd, req->has_sg = true; dma_acct_start(n->conf.blk, &req->acct, &req->qsg, acct); req->aiocb = is_write ? - dma_blk_write(n->conf.blk, &req->qsg, data_offset, nvme_rw_cb, req) : - dma_blk_read(n->conf.blk, &req->qsg, data_offset, nvme_rw_cb, req); + dma_blk_write(n->conf.blk, &req->qsg, data_offset, BDRV_SECTOR_SIZE, + nvme_rw_cb, req) : + dma_blk_read(n->conf.blk, &req->qsg, data_offset, BDRV_SECTOR_SIZE, + nvme_rw_cb, req); return NVME_NO_COMPLETE; } diff --git a/hw/bt/hci-csr.c b/hw/bt/hci-csr.c index b77c036..fbb3109 100644 --- a/hw/bt/hci-csr.c +++ b/hw/bt/hci-csr.c @@ -78,15 +78,17 @@ enum { static inline void csrhci_fifo_wake(struct csrhci_s *s) { + CharBackend *be = s->chr.be; + if (!s->enable || !s->out_len) return; /* XXX: Should wait for s->modem_state & CHR_TIOCM_RTS? */ - if (s->chr.chr_can_read && s->chr.chr_can_read(s->chr.handler_opaque) && - s->chr.chr_read) { - s->chr.chr_read(s->chr.handler_opaque, - s->outfifo + s->out_start ++, 1); - s->out_len --; + if (be && be->chr_can_read && be->chr_can_read(be->opaque) && + be->chr_read) { + be->chr_read(be->opaque, + s->outfifo + s->out_start++, 1); + s->out_len--; if (s->out_start >= s->out_size) { s->out_start = 0; s->out_size = FIFO_LEN; @@ -466,7 +468,6 @@ CharDriverState *uart_hci_init(void) s->chr.opaque = s; s->chr.chr_write = csrhci_write; s->chr.chr_ioctl = csrhci_ioctl; - s->chr.avail_connections = 1; s->hci = qemu_next_hci(); s->hci->opaque = s; diff --git a/hw/char/bcm2835_aux.c b/hw/char/bcm2835_aux.c index f7a845d..4d46ad6 100644 --- a/hw/char/bcm2835_aux.c +++ b/hw/char/bcm2835_aux.c @@ -79,9 +79,7 @@ static uint64_t bcm2835_aux_read(void *opaque, hwaddr offset, unsigned size) s->read_pos = 0; } } - if (s->chr) { - qemu_chr_accept_input(s->chr); - } + qemu_chr_fe_accept_input(&s->chr); bcm2835_aux_update(s); return c; @@ -168,11 +166,9 @@ static void bcm2835_aux_write(void *opaque, hwaddr offset, uint64_t value, case AUX_MU_IO_REG: /* "DLAB bit set means access baudrate register" is NYI */ ch = value; - if (s->chr) { - /* XXX this blocks entire thread. Rewrite to use - * qemu_chr_fe_write and background I/O callbacks */ - qemu_chr_fe_write_all(s->chr, &ch, 1); - } + /* XXX this blocks entire thread. Rewrite to use + * qemu_chr_fe_write and background I/O callbacks */ + qemu_chr_fe_write_all(&s->chr, &ch, 1); break; case AUX_MU_IER_REG: @@ -282,10 +278,8 @@ static void bcm2835_aux_realize(DeviceState *dev, Error **errp) { BCM2835AuxState *s = BCM2835_AUX(dev); - if (s->chr) { - qemu_chr_add_handlers(s->chr, bcm2835_aux_can_receive, - bcm2835_aux_receive, NULL, s); - } + qemu_chr_fe_set_handlers(&s->chr, bcm2835_aux_can_receive, + bcm2835_aux_receive, NULL, s, NULL, true); } static Property bcm2835_aux_props[] = { diff --git a/hw/char/cadence_uart.c b/hw/char/cadence_uart.c index e3bc52f..c2b9154 100644 --- a/hw/char/cadence_uart.c +++ b/hw/char/cadence_uart.c @@ -142,9 +142,7 @@ static void uart_rx_reset(CadenceUARTState *s) { s->rx_wpos = 0; s->rx_count = 0; - if (s->chr) { - qemu_chr_accept_input(s->chr); - } + qemu_chr_fe_accept_input(&s->chr); } static void uart_tx_reset(CadenceUARTState *s) @@ -156,10 +154,8 @@ static void uart_send_breaks(CadenceUARTState *s) { int break_enabled = 1; - if (s->chr) { - qemu_chr_fe_ioctl(s->chr, CHR_IOCTL_SERIAL_SET_BREAK, - &break_enabled); - } + qemu_chr_fe_ioctl(&s->chr, CHR_IOCTL_SERIAL_SET_BREAK, + &break_enabled); } static void uart_parameters_setup(CadenceUARTState *s) @@ -210,9 +206,7 @@ static void uart_parameters_setup(CadenceUARTState *s) packet_size += ssp.data_bits + ssp.stop_bits; s->char_tx_time = (NANOSECONDS_PER_SECOND / ssp.speed) * packet_size; - if (s->chr) { - qemu_chr_fe_ioctl(s->chr, CHR_IOCTL_SERIAL_SET_PARAMS, &ssp); - } + qemu_chr_fe_ioctl(&s->chr, CHR_IOCTL_SERIAL_SET_PARAMS, &ssp); } static int uart_can_receive(void *opaque) @@ -278,7 +272,7 @@ static gboolean cadence_uart_xmit(GIOChannel *chan, GIOCondition cond, int ret; /* instant drain the fifo when there's no back-end */ - if (!s->chr) { + if (!qemu_chr_fe_get_driver(&s->chr)) { s->tx_count = 0; return FALSE; } @@ -287,7 +281,7 @@ static gboolean cadence_uart_xmit(GIOChannel *chan, GIOCondition cond, return FALSE; } - ret = qemu_chr_fe_write(s->chr, s->tx_fifo, s->tx_count); + ret = qemu_chr_fe_write(&s->chr, s->tx_fifo, s->tx_count); if (ret >= 0) { s->tx_count -= ret; @@ -295,7 +289,7 @@ static gboolean cadence_uart_xmit(GIOChannel *chan, GIOCondition cond, } if (s->tx_count) { - guint r = qemu_chr_fe_add_watch(s->chr, G_IO_OUT|G_IO_HUP, + guint r = qemu_chr_fe_add_watch(&s->chr, G_IO_OUT | G_IO_HUP, cadence_uart_xmit, s); if (!r) { s->tx_count = 0; @@ -368,9 +362,7 @@ static void uart_read_rx_fifo(CadenceUARTState *s, uint32_t *c) *c = s->rx_fifo[rx_rpos]; s->rx_count--; - if (s->chr) { - qemu_chr_accept_input(s->chr); - } + qemu_chr_fe_accept_input(&s->chr); } else { *c = 0; } @@ -474,10 +466,8 @@ static void cadence_uart_realize(DeviceState *dev, Error **errp) s->fifo_trigger_handle = timer_new_ns(QEMU_CLOCK_VIRTUAL, fifo_trigger_update, s); - if (s->chr) { - qemu_chr_add_handlers(s->chr, uart_can_receive, uart_receive, - uart_event, s); - } + qemu_chr_fe_set_handlers(&s->chr, uart_can_receive, uart_receive, + uart_event, s, NULL, true); } static void cadence_uart_init(Object *obj) diff --git a/hw/char/debugcon.c b/hw/char/debugcon.c index 4402033..80dce07 100644 --- a/hw/char/debugcon.c +++ b/hw/char/debugcon.c @@ -39,7 +39,7 @@ typedef struct DebugconState { MemoryRegion io; - CharDriverState *chr; + CharBackend chr; uint32_t readback; } DebugconState; @@ -62,7 +62,7 @@ static void debugcon_ioport_write(void *opaque, hwaddr addr, uint64_t val, /* XXX this blocks entire thread. Rewrite to use * qemu_chr_fe_write and background I/O callbacks */ - qemu_chr_fe_write_all(s->chr, &ch, 1); + qemu_chr_fe_write_all(&s->chr, &ch, 1); } @@ -87,12 +87,12 @@ static const MemoryRegionOps debugcon_ops = { static void debugcon_realize_core(DebugconState *s, Error **errp) { - if (!s->chr) { + if (!qemu_chr_fe_get_driver(&s->chr)) { error_setg(errp, "Can't create debugcon device, empty char device"); return; } - qemu_chr_add_handlers(s->chr, NULL, NULL, NULL, s); + qemu_chr_fe_set_handlers(&s->chr, NULL, NULL, NULL, s, NULL, true); } static void debugcon_isa_realizefn(DeviceState *dev, Error **errp) diff --git a/hw/char/digic-uart.c b/hw/char/digic-uart.c index e96a9b2..029f5bb 100644 --- a/hw/char/digic-uart.c +++ b/hw/char/digic-uart.c @@ -76,11 +76,9 @@ static void digic_uart_write(void *opaque, hwaddr addr, uint64_t value, switch (addr) { case R_TX: - if (s->chr) { - /* XXX this blocks entire thread. Rewrite to use - * qemu_chr_fe_write and background I/O callbacks */ - qemu_chr_fe_write_all(s->chr, &ch, 1); - } + /* XXX this blocks entire thread. Rewrite to use + * qemu_chr_fe_write and background I/O callbacks */ + qemu_chr_fe_write_all(&s->chr, &ch, 1); break; case R_ST: @@ -147,9 +145,8 @@ static void digic_uart_realize(DeviceState *dev, Error **errp) { DigicUartState *s = DIGIC_UART(dev); - if (s->chr) { - qemu_chr_add_handlers(s->chr, uart_can_rx, uart_rx, uart_event, s); - } + qemu_chr_fe_set_handlers(&s->chr, uart_can_rx, uart_rx, + uart_event, s, NULL, true); } static void digic_uart_init(Object *obj) diff --git a/hw/char/escc.c b/hw/char/escc.c index aa17397..d6662dc 100644 --- a/hw/char/escc.c +++ b/hw/char/escc.c @@ -88,7 +88,7 @@ typedef struct ChannelState { uint32_t reg; uint8_t wregs[SERIAL_REGS], rregs[SERIAL_REGS]; SERIOQueue queue; - CharDriverState *chr; + CharBackend chr; int e0_mode, led_mode, caps_lock_mode, num_lock_mode; int disabled; int clock; @@ -416,7 +416,7 @@ static void escc_update_parameters(ChannelState *s) int speed, parity, data_bits, stop_bits; QEMUSerialSetParams ssp; - if (!s->chr || s->type != ser) + if (!qemu_chr_fe_get_driver(&s->chr) || s->type != ser) return; if (s->wregs[W_TXCTRL1] & TXCTRL1_PAREN) { @@ -466,7 +466,7 @@ static void escc_update_parameters(ChannelState *s) ssp.data_bits = data_bits; ssp.stop_bits = stop_bits; trace_escc_update_parameters(CHN_C(s), speed, parity, data_bits, stop_bits); - qemu_chr_fe_ioctl(s->chr, CHR_IOCTL_SERIAL_SET_PARAMS, &ssp); + qemu_chr_fe_ioctl(&s->chr, CHR_IOCTL_SERIAL_SET_PARAMS, &ssp); } static void escc_mem_write(void *opaque, hwaddr addr, @@ -556,11 +556,11 @@ static void escc_mem_write(void *opaque, hwaddr addr, trace_escc_mem_writeb_data(CHN_C(s), val); s->tx = val; if (s->wregs[W_TXCTRL2] & TXCTRL2_TXEN) { // tx enabled - if (s->chr) + if (qemu_chr_fe_get_driver(&s->chr)) { /* XXX this blocks entire thread. Rewrite to use * qemu_chr_fe_write and background I/O callbacks */ - qemu_chr_fe_write_all(s->chr, &s->tx, 1); - else if (s->type == kbd && !s->disabled) { + qemu_chr_fe_write_all(&s->chr, &s->tx, 1); + } else if (s->type == kbd && !s->disabled) { handle_kbd_command(s, val); } } @@ -599,8 +599,7 @@ static uint64_t escc_mem_read(void *opaque, hwaddr addr, else ret = s->rx; trace_escc_mem_readb_data(CHN_C(s), ret); - if (s->chr) - qemu_chr_accept_input(s->chr); + qemu_chr_fe_accept_input(&s->chr); return ret; default: break; @@ -1013,10 +1012,11 @@ static void escc_realize(DeviceState *dev, Error **errp) ESCC_SIZE << s->it_shift); for (i = 0; i < 2; i++) { - if (s->chn[i].chr) { + if (qemu_chr_fe_get_driver(&s->chn[i].chr)) { s->chn[i].clock = s->frequency / 2; - qemu_chr_add_handlers(s->chn[i].chr, serial_can_receive, - serial_receive1, serial_event, &s->chn[i]); + qemu_chr_fe_set_handlers(&s->chn[i].chr, serial_can_receive, + serial_receive1, serial_event, + &s->chn[i], NULL, true); } } diff --git a/hw/char/etraxfs_ser.c b/hw/char/etraxfs_ser.c index c99cc5d..5438387 100644 --- a/hw/char/etraxfs_ser.c +++ b/hw/char/etraxfs_ser.c @@ -53,7 +53,7 @@ typedef struct ETRAXSerial { SysBusDevice parent_obj; MemoryRegion mmio; - CharDriverState *chr; + CharBackend chr; qemu_irq irq; int pending_tx; @@ -128,7 +128,7 @@ ser_write(void *opaque, hwaddr addr, case RW_DOUT: /* XXX this blocks entire thread. Rewrite to use * qemu_chr_fe_write and background I/O callbacks */ - qemu_chr_fe_write_all(s->chr, &ch, 1); + qemu_chr_fe_write_all(&s->chr, &ch, 1); s->regs[R_INTR] |= 3; s->pending_tx = 1; s->regs[addr] = value; @@ -231,11 +231,9 @@ static void etraxfs_ser_realize(DeviceState *dev, Error **errp) { ETRAXSerial *s = ETRAX_SERIAL(dev); - if (s->chr) { - qemu_chr_add_handlers(s->chr, - serial_can_receive, serial_receive, - serial_event, s); - } + qemu_chr_fe_set_handlers(&s->chr, + serial_can_receive, serial_receive, + serial_event, s, NULL, true); } static void etraxfs_ser_class_init(ObjectClass *klass, void *data) diff --git a/hw/char/exynos4210_uart.c b/hw/char/exynos4210_uart.c index 1107578..571c324 100644 --- a/hw/char/exynos4210_uart.c +++ b/hw/char/exynos4210_uart.c @@ -181,7 +181,7 @@ typedef struct Exynos4210UartState { Exynos4210UartFIFO rx; Exynos4210UartFIFO tx; - CharDriverState *chr; + CharBackend chr; qemu_irq irq; uint32_t channel; @@ -346,7 +346,7 @@ static void exynos4210_uart_update_parameters(Exynos4210UartState *s) ssp.data_bits = data_bits; ssp.stop_bits = stop_bits; - qemu_chr_fe_ioctl(s->chr, CHR_IOCTL_SERIAL_SET_PARAMS, &ssp); + qemu_chr_fe_ioctl(&s->chr, CHR_IOCTL_SERIAL_SET_PARAMS, &ssp); PRINT_DEBUG("UART%d: speed: %d, parity: %c, data: %d, stop: %d\n", s->channel, speed, parity, data_bits, stop_bits); @@ -383,13 +383,13 @@ static void exynos4210_uart_write(void *opaque, hwaddr offset, break; case UTXH: - if (s->chr) { + if (qemu_chr_fe_get_driver(&s->chr)) { s->reg[I_(UTRSTAT)] &= ~(UTRSTAT_TRANSMITTER_EMPTY | UTRSTAT_Tx_BUFFER_EMPTY); ch = (uint8_t)val; /* XXX this blocks entire thread. Rewrite to use * qemu_chr_fe_write and background I/O callbacks */ - qemu_chr_fe_write_all(s->chr, &ch, 1); + qemu_chr_fe_write_all(&s->chr, &ch, 1); #if DEBUG_Tx_DATA fprintf(stderr, "%c", ch); #endif @@ -606,7 +606,7 @@ DeviceState *exynos4210_uart_create(hwaddr addr, chr = serial_hds[channel]; if (!chr) { snprintf(label, ARRAY_SIZE(label), "%s%d", chr_name, channel); - chr = qemu_chr_new(label, "null", NULL); + chr = qemu_chr_new(label, "null"); if (!(chr)) { error_report("Can't assign serial port to UART%d", channel); exit(1); @@ -640,8 +640,9 @@ static int exynos4210_uart_init(SysBusDevice *dev) sysbus_init_irq(dev, &s->irq); - qemu_chr_add_handlers(s->chr, exynos4210_uart_can_receive, - exynos4210_uart_receive, exynos4210_uart_event, s); + qemu_chr_fe_set_handlers(&s->chr, exynos4210_uart_can_receive, + exynos4210_uart_receive, exynos4210_uart_event, + s, NULL, true); return 0; } diff --git a/hw/char/grlib_apbuart.c b/hw/char/grlib_apbuart.c index 778148a..db686e6 100644 --- a/hw/char/grlib_apbuart.c +++ b/hw/char/grlib_apbuart.c @@ -78,7 +78,7 @@ typedef struct UART { MemoryRegion iomem; qemu_irq irq; - CharDriverState *chr; + CharBackend chr; /* registers */ uint32_t status; @@ -201,11 +201,12 @@ static void grlib_apbuart_write(void *opaque, hwaddr addr, case DATA_OFFSET: case DATA_OFFSET + 3: /* When only one byte write */ /* Transmit when character device available and transmitter enabled */ - if ((uart->chr) && (uart->control & UART_TRANSMIT_ENABLE)) { + if (qemu_chr_fe_get_driver(&uart->chr) && + (uart->control & UART_TRANSMIT_ENABLE)) { c = value & 0xFF; /* XXX this blocks entire thread. Rewrite to use * qemu_chr_fe_write and background I/O callbacks */ - qemu_chr_fe_write_all(uart->chr, &c, 1); + qemu_chr_fe_write_all(&uart->chr, &c, 1); /* Generate interrupt */ if (uart->control & UART_TRANSMIT_INTERRUPT) { qemu_irq_pulse(uart->irq); @@ -242,11 +243,11 @@ static int grlib_apbuart_init(SysBusDevice *dev) { UART *uart = GRLIB_APB_UART(dev); - qemu_chr_add_handlers(uart->chr, - grlib_apbuart_can_receive, - grlib_apbuart_receive, - grlib_apbuart_event, - uart); + qemu_chr_fe_set_handlers(&uart->chr, + grlib_apbuart_can_receive, + grlib_apbuart_receive, + grlib_apbuart_event, + uart, NULL, true); sysbus_init_irq(dev, &uart->irq); diff --git a/hw/char/imx_serial.c b/hw/char/imx_serial.c index 5c3fa61..99545fc 100644 --- a/hw/char/imx_serial.c +++ b/hw/char/imx_serial.c @@ -121,9 +121,7 @@ static uint64_t imx_serial_read(void *opaque, hwaddr offset, s->usr2 &= ~USR2_RDR; s->uts1 |= UTS1_RXEMPTY; imx_update(s); - if (s->chr) { - qemu_chr_accept_input(s->chr); - } + qemu_chr_fe_accept_input(&s->chr); } return c; @@ -172,20 +170,19 @@ static void imx_serial_write(void *opaque, hwaddr offset, uint64_t value, unsigned size) { IMXSerialState *s = (IMXSerialState *)opaque; + CharDriverState *chr = qemu_chr_fe_get_driver(&s->chr); unsigned char ch; DPRINTF("write(offset=0x%" HWADDR_PRIx ", value = 0x%x) to %s\n", - offset, (unsigned int)value, s->chr ? s->chr->label : "NODEV"); + offset, (unsigned int)value, chr ? chr->label : "NODEV"); switch (offset >> 2) { case 0x10: /* UTXD */ ch = value; if (s->ucr2 & UCR2_TXEN) { - if (s->chr) { - /* XXX this blocks entire thread. Rewrite to use - * qemu_chr_fe_write and background I/O callbacks */ - qemu_chr_fe_write_all(s->chr, &ch, 1); - } + /* XXX this blocks entire thread. Rewrite to use + * qemu_chr_fe_write and background I/O callbacks */ + qemu_chr_fe_write_all(&s->chr, &ch, 1); s->usr1 &= ~USR1_TRDY; imx_update(s); s->usr1 |= USR1_TRDY; @@ -214,9 +211,7 @@ static void imx_serial_write(void *opaque, hwaddr offset, } if (value & UCR2_RXEN) { if (!(s->ucr2 & UCR2_RXEN)) { - if (s->chr) { - qemu_chr_accept_input(s->chr); - } + qemu_chr_fe_accept_input(&s->chr); } } s->ucr2 = value & 0xffff; @@ -318,12 +313,10 @@ static void imx_serial_realize(DeviceState *dev, Error **errp) { IMXSerialState *s = IMX_SERIAL(dev); - if (s->chr) { - qemu_chr_add_handlers(s->chr, imx_can_receive, imx_receive, - imx_event, s); - } else { - DPRINTF("No char dev for uart\n"); - } + DPRINTF("char dev for uart: %p\n", qemu_chr_fe_get_driver(&s->chr)); + + qemu_chr_fe_set_handlers(&s->chr, imx_can_receive, imx_receive, + imx_event, s, NULL, true); } static void imx_serial_init(Object *obj) diff --git a/hw/char/ipoctal232.c b/hw/char/ipoctal232.c index 2859fdd..93929c2 100644 --- a/hw/char/ipoctal232.c +++ b/hw/char/ipoctal232.c @@ -93,7 +93,7 @@ typedef struct SCC2698Block SCC2698Block; struct SCC2698Channel { IPOctalState *ipoctal; - CharDriverState *dev; + CharBackend dev; bool rx_enabled; uint8_t mr[2]; uint8_t mr_idx; @@ -288,9 +288,7 @@ static uint16_t io_read(IPackDevice *ip, uint8_t addr) if (ch->rx_pending == 0) { ch->sr &= ~SR_RXRDY; blk->isr &= ~ISR_RXRDY(channel); - if (ch->dev) { - qemu_chr_accept_input(ch->dev); - } + qemu_chr_fe_accept_input(&ch->dev); } else { ch->rhr_idx = (ch->rhr_idx + 1) % RX_FIFO_SIZE; } @@ -357,13 +355,11 @@ static void io_write(IPackDevice *ip, uint8_t addr, uint16_t val) case REG_THRa: case REG_THRb: if (ch->sr & SR_TXRDY) { + uint8_t thr = reg; DPRINTF("Write THR%c (0x%x)\n", channel + 'a', reg); - if (ch->dev) { - uint8_t thr = reg; - /* XXX this blocks entire thread. Rewrite to use - * qemu_chr_fe_write and background I/O callbacks */ - qemu_chr_fe_write_all(ch->dev, &thr, 1); - } + /* XXX this blocks entire thread. Rewrite to use + * qemu_chr_fe_write and background I/O callbacks */ + qemu_chr_fe_write_all(&ch->dev, &thr, 1); } else { DPRINTF("Write THR%c (0x%x), Tx disabled\n", channel + 'a', reg); } @@ -546,9 +542,10 @@ static void ipoctal_realize(DeviceState *dev, Error **errp) ch->ipoctal = s; /* Redirect IP-Octal channels to host character devices */ - if (ch->dev) { - qemu_chr_add_handlers(ch->dev, hostdev_can_receive, - hostdev_receive, hostdev_event, ch); + if (qemu_chr_fe_get_driver(&ch->dev)) { + qemu_chr_fe_set_handlers(&ch->dev, hostdev_can_receive, + hostdev_receive, hostdev_event, + ch, NULL, true); DPRINTF("Redirecting channel %u to %s\n", i, ch->dev->label); } else { DPRINTF("Could not redirect channel %u, no chardev set\n", i); diff --git a/hw/char/lm32_juart.c b/hw/char/lm32_juart.c index cb1ac76..f8c1e0d 100644 --- a/hw/char/lm32_juart.c +++ b/hw/char/lm32_juart.c @@ -44,7 +44,7 @@ enum { struct LM32JuartState { SysBusDevice parent_obj; - CharDriverState *chr; + CharBackend chr; uint32_t jtx; uint32_t jrx; @@ -75,11 +75,9 @@ void lm32_juart_set_jtx(DeviceState *d, uint32_t jtx) trace_lm32_juart_set_jtx(s->jtx); s->jtx = jtx; - if (s->chr) { - /* XXX this blocks entire thread. Rewrite to use - * qemu_chr_fe_write and background I/O callbacks */ - qemu_chr_fe_write_all(s->chr, &ch, 1); - } + /* XXX this blocks entire thread. Rewrite to use + * qemu_chr_fe_write and background I/O callbacks */ + qemu_chr_fe_write_all(&s->chr, &ch, 1); } void lm32_juart_set_jrx(DeviceState *d, uint32_t jtx) @@ -120,9 +118,8 @@ static void lm32_juart_realize(DeviceState *dev, Error **errp) { LM32JuartState *s = LM32_JUART(dev); - if (s->chr) { - qemu_chr_add_handlers(s->chr, juart_can_rx, juart_rx, juart_event, s); - } + qemu_chr_fe_set_handlers(&s->chr, juart_can_rx, juart_rx, + juart_event, s, NULL, true); } static const VMStateDescription vmstate_lm32_juart = { diff --git a/hw/char/lm32_uart.c b/hw/char/lm32_uart.c index be93697..7f3597c 100644 --- a/hw/char/lm32_uart.c +++ b/hw/char/lm32_uart.c @@ -97,7 +97,7 @@ struct LM32UartState { SysBusDevice parent_obj; MemoryRegion iomem; - CharDriverState *chr; + CharBackend chr; qemu_irq irq; uint32_t regs[R_MAX]; @@ -142,7 +142,7 @@ static uint64_t uart_read(void *opaque, hwaddr addr, r = s->regs[R_RXTX]; s->regs[R_LSR] &= ~LSR_DR; uart_update_irq(s); - qemu_chr_accept_input(s->chr); + qemu_chr_fe_accept_input(&s->chr); break; case R_IIR: case R_LSR: @@ -177,11 +177,9 @@ static void uart_write(void *opaque, hwaddr addr, addr >>= 2; switch (addr) { case R_RXTX: - if (s->chr) { - /* XXX this blocks entire thread. Rewrite to use - * qemu_chr_fe_write and background I/O callbacks */ - qemu_chr_fe_write_all(s->chr, &ch, 1); - } + /* XXX this blocks entire thread. Rewrite to use + * qemu_chr_fe_write and background I/O callbacks */ + qemu_chr_fe_write_all(&s->chr, &ch, 1); break; case R_IER: case R_LCR: @@ -267,9 +265,8 @@ static void lm32_uart_realize(DeviceState *dev, Error **errp) { LM32UartState *s = LM32_UART(dev); - if (s->chr) { - qemu_chr_add_handlers(s->chr, uart_can_rx, uart_rx, uart_event, s); - } + qemu_chr_fe_set_handlers(&s->chr, uart_can_rx, uart_rx, + uart_event, s, NULL, true); } static const VMStateDescription vmstate_lm32_uart = { diff --git a/hw/char/mcf_uart.c b/hw/char/mcf_uart.c index c184859..ecaa091 100644 --- a/hw/char/mcf_uart.c +++ b/hw/char/mcf_uart.c @@ -10,6 +10,7 @@ #include "hw/m68k/mcf.h" #include "sysemu/char.h" #include "exec/address-spaces.h" +#include "qapi/error.h" typedef struct { MemoryRegion iomem; @@ -26,7 +27,7 @@ typedef struct { int tx_enabled; int rx_enabled; qemu_irq irq; - CharDriverState *chr; + CharBackend chr; } mcf_uart_state; /* UART Status Register bits. */ @@ -92,7 +93,7 @@ uint64_t mcf_uart_read(void *opaque, hwaddr addr, if (s->fifo_len == 0) s->sr &= ~MCF_UART_RxRDY; mcf_uart_update(s); - qemu_chr_accept_input(s->chr); + qemu_chr_fe_accept_input(&s->chr); return val; } case 0x10: @@ -113,10 +114,9 @@ uint64_t mcf_uart_read(void *opaque, hwaddr addr, static void mcf_uart_do_tx(mcf_uart_state *s) { if (s->tx_enabled && (s->sr & MCF_UART_TxEMP) == 0) { - if (s->chr) - /* XXX this blocks entire thread. Rewrite to use - * qemu_chr_fe_write and background I/O callbacks */ - qemu_chr_fe_write_all(s->chr, (unsigned char *)&s->tb, 1); + /* XXX this blocks entire thread. Rewrite to use + * qemu_chr_fe_write and background I/O callbacks */ + qemu_chr_fe_write_all(&s->chr, (unsigned char *)&s->tb, 1); s->sr |= MCF_UART_TxEMP; } if (s->tx_enabled) { @@ -280,12 +280,12 @@ void *mcf_uart_init(qemu_irq irq, CharDriverState *chr) mcf_uart_state *s; s = g_malloc0(sizeof(mcf_uart_state)); - s->chr = chr; s->irq = irq; if (chr) { - qemu_chr_fe_claim_no_fail(chr); - qemu_chr_add_handlers(chr, mcf_uart_can_receive, mcf_uart_receive, - mcf_uart_event, s); + qemu_chr_fe_init(&s->chr, chr, &error_abort); + qemu_chr_fe_set_handlers(&s->chr, mcf_uart_can_receive, + mcf_uart_receive, mcf_uart_event, + s, NULL, true); } mcf_uart_reset(s); return s; diff --git a/hw/char/milkymist-uart.c b/hw/char/milkymist-uart.c index baddb37..ae8e2f3 100644 --- a/hw/char/milkymist-uart.c +++ b/hw/char/milkymist-uart.c @@ -61,7 +61,7 @@ struct MilkymistUartState { SysBusDevice parent_obj; MemoryRegion regs_region; - CharDriverState *chr; + CharBackend chr; qemu_irq irq; uint32_t regs[R_MAX]; @@ -124,9 +124,7 @@ static void uart_write(void *opaque, hwaddr addr, uint64_t value, addr >>= 2; switch (addr) { case R_RXTX: - if (s->chr) { - qemu_chr_fe_write_all(s->chr, &ch, 1); - } + qemu_chr_fe_write_all(&s->chr, &ch, 1); s->regs[R_STAT] |= STAT_TX_EVT; break; case R_DIV: @@ -138,7 +136,7 @@ static void uart_write(void *opaque, hwaddr addr, uint64_t value, case R_STAT: /* write one to clear bits */ s->regs[addr] &= ~(value & (STAT_RX_EVT | STAT_TX_EVT)); - qemu_chr_accept_input(s->chr); + qemu_chr_fe_accept_input(&s->chr); break; default: @@ -200,9 +198,8 @@ static void milkymist_uart_realize(DeviceState *dev, Error **errp) { MilkymistUartState *s = MILKYMIST_UART(dev); - if (s->chr) { - qemu_chr_add_handlers(s->chr, uart_can_rx, uart_rx, uart_event, s); - } + qemu_chr_fe_set_handlers(&s->chr, uart_can_rx, uart_rx, + uart_event, s, NULL, true); } static void milkymist_uart_init(Object *obj) diff --git a/hw/char/omap_uart.c b/hw/char/omap_uart.c index 415bec5..893ab10 100644 --- a/hw/char/omap_uart.c +++ b/hw/char/omap_uart.c @@ -63,7 +63,7 @@ struct omap_uart_s *omap_uart_init(hwaddr base, s->irq = irq; s->serial = serial_mm_init(get_system_memory(), base, 2, irq, omap_clk_getrate(fclk)/16, - chr ?: qemu_chr_new(label, "null", NULL), + chr ?: qemu_chr_new(label, "null"), DEVICE_NATIVE_ENDIAN); return s; } @@ -183,6 +183,6 @@ void omap_uart_attach(struct omap_uart_s *s, CharDriverState *chr) /* TODO: Should reuse or destroy current s->serial */ s->serial = serial_mm_init(get_system_memory(), s->base, 2, s->irq, omap_clk_getrate(s->fclk) / 16, - chr ?: qemu_chr_new("null", "null", NULL), + chr ?: qemu_chr_new("null", "null"), DEVICE_NATIVE_ENDIAN); } diff --git a/hw/char/parallel.c b/hw/char/parallel.c index da22e36..f2d5666 100644 --- a/hw/char/parallel.c +++ b/hw/char/parallel.c @@ -74,7 +74,7 @@ typedef struct ParallelState { uint8_t control; qemu_irq irq; int irq_pending; - CharDriverState *chr; + CharBackend chr; int hw_driver; int epp_timeout; uint32_t last_read_offset; /* For debugging */ @@ -131,7 +131,7 @@ parallel_ioport_write_sw(void *opaque, uint32_t addr, uint32_t val) if ((s->control & PARA_CTR_STROBE) == 0) /* XXX this blocks entire thread. Rewrite to use * qemu_chr_fe_write and background I/O callbacks */ - qemu_chr_fe_write_all(s->chr, &s->dataw, 1); + qemu_chr_fe_write_all(&s->chr, &s->dataw, 1); } else { if (s->control & PARA_CTR_INTEN) { s->irq_pending = 1; @@ -161,7 +161,7 @@ static void parallel_ioport_write_hw(void *opaque, uint32_t addr, uint32_t val) if (s->dataw == val) return; pdebug("wd%02x\n", val); - qemu_chr_fe_ioctl(s->chr, CHR_IOCTL_PP_WRITE_DATA, &parm); + qemu_chr_fe_ioctl(&s->chr, CHR_IOCTL_PP_WRITE_DATA, &parm); s->dataw = val; break; case PARA_REG_STS: @@ -181,11 +181,11 @@ static void parallel_ioport_write_hw(void *opaque, uint32_t addr, uint32_t val) } else { dir = 0; } - qemu_chr_fe_ioctl(s->chr, CHR_IOCTL_PP_DATA_DIR, &dir); + qemu_chr_fe_ioctl(&s->chr, CHR_IOCTL_PP_DATA_DIR, &dir); parm &= ~PARA_CTR_DIR; } - qemu_chr_fe_ioctl(s->chr, CHR_IOCTL_PP_WRITE_CONTROL, &parm); + qemu_chr_fe_ioctl(&s->chr, CHR_IOCTL_PP_WRITE_CONTROL, &parm); s->control = val; break; case PARA_REG_EPP_ADDR: @@ -194,7 +194,8 @@ static void parallel_ioport_write_hw(void *opaque, uint32_t addr, uint32_t val) pdebug("wa%02x s\n", val); else { struct ParallelIOArg ioarg = { .buffer = &parm, .count = 1 }; - if (qemu_chr_fe_ioctl(s->chr, CHR_IOCTL_PP_EPP_WRITE_ADDR, &ioarg)) { + if (qemu_chr_fe_ioctl(&s->chr, + CHR_IOCTL_PP_EPP_WRITE_ADDR, &ioarg)) { s->epp_timeout = 1; pdebug("wa%02x t\n", val); } @@ -208,7 +209,7 @@ static void parallel_ioport_write_hw(void *opaque, uint32_t addr, uint32_t val) pdebug("we%02x s\n", val); else { struct ParallelIOArg ioarg = { .buffer = &parm, .count = 1 }; - if (qemu_chr_fe_ioctl(s->chr, CHR_IOCTL_PP_EPP_WRITE, &ioarg)) { + if (qemu_chr_fe_ioctl(&s->chr, CHR_IOCTL_PP_EPP_WRITE, &ioarg)) { s->epp_timeout = 1; pdebug("we%02x t\n", val); } @@ -233,7 +234,7 @@ parallel_ioport_eppdata_write_hw2(void *opaque, uint32_t addr, uint32_t val) pdebug("we%04x s\n", val); return; } - err = qemu_chr_fe_ioctl(s->chr, CHR_IOCTL_PP_EPP_WRITE, &ioarg); + err = qemu_chr_fe_ioctl(&s->chr, CHR_IOCTL_PP_EPP_WRITE, &ioarg); if (err) { s->epp_timeout = 1; pdebug("we%04x t\n", val); @@ -256,7 +257,7 @@ parallel_ioport_eppdata_write_hw4(void *opaque, uint32_t addr, uint32_t val) pdebug("we%08x s\n", val); return; } - err = qemu_chr_fe_ioctl(s->chr, CHR_IOCTL_PP_EPP_WRITE, &ioarg); + err = qemu_chr_fe_ioctl(&s->chr, CHR_IOCTL_PP_EPP_WRITE, &ioarg); if (err) { s->epp_timeout = 1; pdebug("we%08x t\n", val); @@ -308,13 +309,13 @@ static uint32_t parallel_ioport_read_hw(void *opaque, uint32_t addr) addr &= 7; switch(addr) { case PARA_REG_DATA: - qemu_chr_fe_ioctl(s->chr, CHR_IOCTL_PP_READ_DATA, &ret); + qemu_chr_fe_ioctl(&s->chr, CHR_IOCTL_PP_READ_DATA, &ret); if (s->last_read_offset != addr || s->datar != ret) pdebug("rd%02x\n", ret); s->datar = ret; break; case PARA_REG_STS: - qemu_chr_fe_ioctl(s->chr, CHR_IOCTL_PP_READ_STATUS, &ret); + qemu_chr_fe_ioctl(&s->chr, CHR_IOCTL_PP_READ_STATUS, &ret); ret &= ~PARA_STS_TMOUT; if (s->epp_timeout) ret |= PARA_STS_TMOUT; @@ -326,7 +327,7 @@ static uint32_t parallel_ioport_read_hw(void *opaque, uint32_t addr) /* s->control has some bits fixed to 1. It is zero only when it has not been yet written to. */ if (s->control == 0) { - qemu_chr_fe_ioctl(s->chr, CHR_IOCTL_PP_READ_CONTROL, &ret); + qemu_chr_fe_ioctl(&s->chr, CHR_IOCTL_PP_READ_CONTROL, &ret); if (s->last_read_offset != addr) pdebug("rc%02x\n", ret); s->control = ret; @@ -338,12 +339,14 @@ static uint32_t parallel_ioport_read_hw(void *opaque, uint32_t addr) } break; case PARA_REG_EPP_ADDR: - if ((s->control & (PARA_CTR_DIR|PARA_CTR_SIGNAL)) != (PARA_CTR_DIR|PARA_CTR_INIT)) + if ((s->control & (PARA_CTR_DIR | PARA_CTR_SIGNAL)) != + (PARA_CTR_DIR | PARA_CTR_INIT)) /* Controls not correct for EPP addr cycle, so do nothing */ pdebug("ra%02x s\n", ret); else { struct ParallelIOArg ioarg = { .buffer = &ret, .count = 1 }; - if (qemu_chr_fe_ioctl(s->chr, CHR_IOCTL_PP_EPP_READ_ADDR, &ioarg)) { + if (qemu_chr_fe_ioctl(&s->chr, + CHR_IOCTL_PP_EPP_READ_ADDR, &ioarg)) { s->epp_timeout = 1; pdebug("ra%02x t\n", ret); } @@ -352,12 +355,13 @@ static uint32_t parallel_ioport_read_hw(void *opaque, uint32_t addr) } break; case PARA_REG_EPP_DATA: - if ((s->control & (PARA_CTR_DIR|PARA_CTR_SIGNAL)) != (PARA_CTR_DIR|PARA_CTR_INIT)) + if ((s->control & (PARA_CTR_DIR | PARA_CTR_SIGNAL)) != + (PARA_CTR_DIR | PARA_CTR_INIT)) /* Controls not correct for EPP data cycle, so do nothing */ pdebug("re%02x s\n", ret); else { struct ParallelIOArg ioarg = { .buffer = &ret, .count = 1 }; - if (qemu_chr_fe_ioctl(s->chr, CHR_IOCTL_PP_EPP_READ, &ioarg)) { + if (qemu_chr_fe_ioctl(&s->chr, CHR_IOCTL_PP_EPP_READ, &ioarg)) { s->epp_timeout = 1; pdebug("re%02x t\n", ret); } @@ -385,7 +389,7 @@ parallel_ioport_eppdata_read_hw2(void *opaque, uint32_t addr) pdebug("re%04x s\n", eppdata); return eppdata; } - err = qemu_chr_fe_ioctl(s->chr, CHR_IOCTL_PP_EPP_READ, &ioarg); + err = qemu_chr_fe_ioctl(&s->chr, CHR_IOCTL_PP_EPP_READ, &ioarg); ret = le16_to_cpu(eppdata); if (err) { @@ -412,7 +416,7 @@ parallel_ioport_eppdata_read_hw4(void *opaque, uint32_t addr) pdebug("re%08x s\n", eppdata); return eppdata; } - err = qemu_chr_fe_ioctl(s->chr, CHR_IOCTL_PP_EPP_READ, &ioarg); + err = qemu_chr_fe_ioctl(&s->chr, CHR_IOCTL_PP_EPP_READ, &ioarg); ret = le32_to_cpu(eppdata); if (err) { @@ -508,7 +512,7 @@ static void parallel_isa_realizefn(DeviceState *dev, Error **errp) int base; uint8_t dummy; - if (!s->chr) { + if (!qemu_chr_fe_get_driver(&s->chr)) { error_setg(errp, "Can't create parallel device, empty char device"); return; } @@ -530,7 +534,7 @@ static void parallel_isa_realizefn(DeviceState *dev, Error **errp) isa_init_irq(isadev, &s->irq, isa->isairq); qemu_register_reset(parallel_reset, s); - if (qemu_chr_fe_ioctl(s->chr, CHR_IOCTL_PP_READ_STATUS, &dummy) == 0) { + if (qemu_chr_fe_ioctl(&s->chr, CHR_IOCTL_PP_READ_STATUS, &dummy) == 0) { s->hw_driver = 1; s->status = dummy; } @@ -605,7 +609,7 @@ bool parallel_mm_init(MemoryRegion *address_space, s = g_malloc0(sizeof(ParallelState)); s->irq = irq; - s->chr = chr; + qemu_chr_fe_init(&s->chr, chr, &error_abort); s->it_shift = it_shift; qemu_register_reset(parallel_reset, s); diff --git a/hw/char/pl011.c b/hw/char/pl011.c index 1a7911f..24ea973 100644 --- a/hw/char/pl011.c +++ b/hw/char/pl011.c @@ -36,7 +36,7 @@ typedef struct PL011State { int read_pos; int read_count; int read_trigger; - CharDriverState *chr; + CharBackend chr; qemu_irq irq; const unsigned char *id; } PL011State; @@ -87,9 +87,7 @@ static uint64_t pl011_read(void *opaque, hwaddr offset, trace_pl011_read_fifo(s->read_count); s->rsr = c >> 8; pl011_update(s); - if (s->chr) { - qemu_chr_accept_input(s->chr); - } + qemu_chr_fe_accept_input(&s->chr); r = c; break; case 1: /* UARTRSR */ @@ -168,10 +166,9 @@ static void pl011_write(void *opaque, hwaddr offset, case 0: /* UARTDR */ /* ??? Check if transmitter is enabled. */ ch = value; - if (s->chr) - /* XXX this blocks entire thread. Rewrite to use - * qemu_chr_fe_write and background I/O callbacks */ - qemu_chr_fe_write_all(s->chr, &ch, 1); + /* XXX this blocks entire thread. Rewrite to use + * qemu_chr_fe_write and background I/O callbacks */ + qemu_chr_fe_write_all(&s->chr, &ch, 1); s->int_level |= PL011_INT_TX; pl011_update(s); break; @@ -331,10 +328,8 @@ static void pl011_realize(DeviceState *dev, Error **errp) { PL011State *s = PL011(dev); - if (s->chr) { - qemu_chr_add_handlers(s->chr, pl011_can_receive, pl011_receive, - pl011_event, s); - } + qemu_chr_fe_set_handlers(&s->chr, pl011_can_receive, pl011_receive, + pl011_event, s, NULL, true); } static void pl011_class_init(ObjectClass *oc, void *data) diff --git a/hw/char/sclpconsole-lm.c b/hw/char/sclpconsole-lm.c index 9a56326..07d6ebd 100644 --- a/hw/char/sclpconsole-lm.c +++ b/hw/char/sclpconsole-lm.c @@ -37,7 +37,7 @@ typedef struct OprtnsCommand { typedef struct SCLPConsoleLM { SCLPEvent event; - CharDriverState *chr; + CharBackend chr; bool echo; /* immediate echo of input if true */ uint32_t write_errors; /* errors writing to char layer */ uint32_t length; /* length of byte stream in buffer */ @@ -91,7 +91,7 @@ static void chr_read(void *opaque, const uint8_t *buf, int size) if (scon->echo) { /* XXX this blocks entire thread. Rewrite to use * qemu_chr_fe_write and background I/O callbacks */ - qemu_chr_fe_write_all(scon->chr, buf, size); + qemu_chr_fe_write_all(&scon->chr, buf, size); } } @@ -195,14 +195,14 @@ static int write_console_data(SCLPEvent *event, const uint8_t *buf, int len) { SCLPConsoleLM *scon = SCLPLM_CONSOLE(event); - if (!scon->chr) { + if (!qemu_chr_fe_get_driver(&scon->chr)) { /* If there's no backend, we can just say we consumed all data. */ return len; } /* XXX this blocks entire thread. Rewrite to use * qemu_chr_fe_write and background I/O callbacks */ - return qemu_chr_fe_write_all(scon->chr, buf, len); + return qemu_chr_fe_write_all(&scon->chr, buf, len); } static int process_mdb(SCLPEvent *event, MDBO *mdbo) @@ -312,9 +312,8 @@ static int console_init(SCLPEvent *event) } console_available = true; - if (scon->chr) { - qemu_chr_add_handlers(scon->chr, chr_can_read, chr_read, NULL, scon); - } + qemu_chr_fe_set_handlers(&scon->chr, chr_can_read, + chr_read, NULL, scon, NULL, true); return 0; } diff --git a/hw/char/sclpconsole.c b/hw/char/sclpconsole.c index a75ad4f..b78f240 100644 --- a/hw/char/sclpconsole.c +++ b/hw/char/sclpconsole.c @@ -31,7 +31,7 @@ typedef struct ASCIIConsoleData { typedef struct SCLPConsole { SCLPEvent event; - CharDriverState *chr; + CharBackend chr; uint8_t iov[SIZE_BUFFER_VT220]; uint32_t iov_sclp; /* offset in buf for SCLP read operation */ uint32_t iov_bs; /* offset in buf for char layer read operation */ @@ -163,14 +163,14 @@ static ssize_t write_console_data(SCLPEvent *event, const uint8_t *buf, { SCLPConsole *scon = SCLP_CONSOLE(event); - if (!scon->chr) { + if (!qemu_chr_fe_get_driver(&scon->chr)) { /* If there's no backend, we can just say we consumed all data. */ return len; } /* XXX this blocks entire thread. Rewrite to use * qemu_chr_fe_write and background I/O callbacks */ - return qemu_chr_fe_write_all(scon->chr, buf, len); + return qemu_chr_fe_write_all(&scon->chr, buf, len); } static int write_event_data(SCLPEvent *event, EventBufferHeader *evt_buf_hdr) @@ -227,10 +227,8 @@ static int console_init(SCLPEvent *event) return -1; } console_available = true; - if (scon->chr) { - qemu_chr_add_handlers(scon->chr, chr_can_read, - chr_read, NULL, scon); - } + qemu_chr_fe_set_handlers(&scon->chr, chr_can_read, + chr_read, NULL, scon, NULL, true); return 0; } diff --git a/hw/char/serial-isa.c b/hw/char/serial-isa.c index 1594ec4..54d3a12 100644 --- a/hw/char/serial-isa.c +++ b/hw/char/serial-isa.c @@ -133,13 +133,14 @@ static void serial_isa_init(ISABus *bus, int index, CharDriverState *chr) qdev_init_nofail(dev); } -void serial_hds_isa_init(ISABus *bus, int n) +void serial_hds_isa_init(ISABus *bus, int from, int to) { int i; - assert(n <= MAX_SERIAL_PORTS); + assert(from >= 0); + assert(to <= MAX_SERIAL_PORTS); - for (i = 0; i < n; ++i) { + for (i = from; i < to; ++i) { if (serial_hds[i]) { serial_isa_init(bus, i, serial_hds[i]); } diff --git a/hw/char/serial.c b/hw/char/serial.c index 3442f47..ffbacd8 100644 --- a/hw/char/serial.c +++ b/hw/char/serial.c @@ -153,8 +153,9 @@ static void serial_update_parameters(SerialState *s) int speed, parity, data_bits, stop_bits, frame_size; QEMUSerialSetParams ssp; - if (s->divider == 0) + if (s->divider == 0 || s->divider > s->baudbase) { return; + } /* Start bit. */ frame_size = 1; @@ -181,7 +182,7 @@ static void serial_update_parameters(SerialState *s) ssp.data_bits = data_bits; ssp.stop_bits = stop_bits; s->char_transmit_time = (NANOSECONDS_PER_SECOND / speed) * frame_size; - qemu_chr_fe_ioctl(s->chr, CHR_IOCTL_SERIAL_SET_PARAMS, &ssp); + qemu_chr_fe_ioctl(&s->chr, CHR_IOCTL_SERIAL_SET_PARAMS, &ssp); DPRINTF("speed=%d parity=%c data=%d stop=%d\n", speed, parity, data_bits, stop_bits); @@ -194,7 +195,8 @@ static void serial_update_msl(SerialState *s) timer_del(s->modem_status_poll); - if (qemu_chr_fe_ioctl(s->chr,CHR_IOCTL_SERIAL_GET_TIOCM, &flags) == -ENOTSUP) { + if (qemu_chr_fe_ioctl(&s->chr, CHR_IOCTL_SERIAL_GET_TIOCM, + &flags) == -ENOTSUP) { s->poll_msl = -1; return; } @@ -259,11 +261,12 @@ static void serial_xmit(SerialState *s) if (s->mcr & UART_MCR_LOOP) { /* in loopback mode, say that we just received a char */ serial_receive1(s, &s->tsr, 1); - } else if (qemu_chr_fe_write(s->chr, &s->tsr, 1) != 1 && + } else if (qemu_chr_fe_write(&s->chr, &s->tsr, 1) != 1 && s->tsr_retry < MAX_XMIT_RETRY) { assert(s->watch_tag == 0); - s->watch_tag = qemu_chr_fe_add_watch(s->chr, G_IO_OUT|G_IO_HUP, - serial_watch_cb, s); + s->watch_tag = + qemu_chr_fe_add_watch(&s->chr, G_IO_OUT | G_IO_HUP, + serial_watch_cb, s); if (s->watch_tag > 0) { s->tsr_retry++; return; @@ -416,8 +419,8 @@ static void serial_ioport_write(void *opaque, hwaddr addr, uint64_t val, break_enable = (val >> 6) & 1; if (break_enable != s->last_break_enable) { s->last_break_enable = break_enable; - qemu_chr_fe_ioctl(s->chr, CHR_IOCTL_SERIAL_SET_BREAK, - &break_enable); + qemu_chr_fe_ioctl(&s->chr, CHR_IOCTL_SERIAL_SET_BREAK, + &break_enable); } } break; @@ -431,7 +434,7 @@ static void serial_ioport_write(void *opaque, hwaddr addr, uint64_t val, if (s->poll_msl >= 0 && old_mcr != s->mcr) { - qemu_chr_fe_ioctl(s->chr,CHR_IOCTL_SERIAL_GET_TIOCM, &flags); + qemu_chr_fe_ioctl(&s->chr, CHR_IOCTL_SERIAL_GET_TIOCM, &flags); flags &= ~(CHR_TIOCM_RTS | CHR_TIOCM_DTR); @@ -440,7 +443,7 @@ static void serial_ioport_write(void *opaque, hwaddr addr, uint64_t val, if (val & UART_MCR_DTR) flags |= CHR_TIOCM_DTR; - qemu_chr_fe_ioctl(s->chr,CHR_IOCTL_SERIAL_SET_TIOCM, &flags); + qemu_chr_fe_ioctl(&s->chr, CHR_IOCTL_SERIAL_SET_TIOCM, &flags); /* Update the modem status after a one-character-send wait-time, since there may be a response from the device/computer at the other end of the serial line */ timer_mod(s->modem_status_poll, qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + s->char_transmit_time); @@ -485,7 +488,7 @@ static uint64_t serial_ioport_read(void *opaque, hwaddr addr, unsigned size) serial_update_irq(s); if (!(s->mcr & UART_MCR_LOOP)) { /* in loopback mode, don't receive any data */ - qemu_chr_accept_input(s->chr); + qemu_chr_fe_accept_input(&s->chr); } } break; @@ -658,7 +661,7 @@ static int serial_post_load(void *opaque, int version_id) } assert(s->watch_tag == 0); - s->watch_tag = qemu_chr_fe_add_watch(s->chr, G_IO_OUT|G_IO_HUP, + s->watch_tag = qemu_chr_fe_add_watch(&s->chr, G_IO_OUT | G_IO_HUP, serial_watch_cb, s); } else { /* tsr_retry == 0 implies LSR.TEMT = 1 (transmitter empty). */ @@ -883,7 +886,7 @@ static void serial_reset(void *opaque) void serial_realize_core(SerialState *s, Error **errp) { - if (!s->chr) { + if (!qemu_chr_fe_get_driver(&s->chr)) { error_setg(errp, "Can't create serial device, empty char device"); return; } @@ -893,8 +896,8 @@ void serial_realize_core(SerialState *s, Error **errp) s->fifo_timeout_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, (QEMUTimerCB *) fifo_timeout_int, s); qemu_register_reset(serial_reset, s); - qemu_chr_add_handlers(s->chr, serial_can_receive1, serial_receive1, - serial_event, s); + qemu_chr_fe_set_handlers(&s->chr, serial_can_receive1, serial_receive1, + serial_event, s, NULL, true); fifo8_create(&s->recv_fifo, UART_FIFO_LENGTH); fifo8_create(&s->xmit_fifo, UART_FIFO_LENGTH); serial_reset(s); @@ -902,7 +905,7 @@ void serial_realize_core(SerialState *s, Error **errp) void serial_exit_core(SerialState *s) { - qemu_chr_add_handlers(s->chr, NULL, NULL, NULL, NULL); + qemu_chr_fe_deinit(&s->chr); qemu_unregister_reset(serial_reset, s); } @@ -932,7 +935,7 @@ SerialState *serial_init(int base, qemu_irq irq, int baudbase, s->irq = irq; s->baudbase = baudbase; - s->chr = chr; + qemu_chr_fe_init(&s->chr, chr, &error_abort); serial_realize_core(s, &error_fatal); vmstate_register(NULL, base, &vmstate_serial, s); @@ -989,7 +992,7 @@ SerialState *serial_mm_init(MemoryRegion *address_space, s->it_shift = it_shift; s->irq = irq; s->baudbase = baudbase; - s->chr = chr; + qemu_chr_fe_init(&s->chr, chr, &error_abort); serial_realize_core(s, &error_fatal); vmstate_register(NULL, base, &vmstate_serial, s); diff --git a/hw/char/sh_serial.c b/hw/char/sh_serial.c index 97ce562..9d35564 100644 --- a/hw/char/sh_serial.c +++ b/hw/char/sh_serial.c @@ -29,6 +29,7 @@ #include "hw/sh4/sh.h" #include "sysemu/char.h" #include "exec/address-spaces.h" +#include "qapi/error.h" //#define DEBUG_SERIAL @@ -62,7 +63,7 @@ typedef struct { int flags; int rtrg; - CharDriverState *chr; + CharBackend chr; qemu_irq eri; qemu_irq rxi; @@ -109,11 +110,11 @@ static void sh_serial_write(void *opaque, hwaddr offs, } return; case 0x0c: /* FTDR / TDR */ - if (s->chr) { + if (qemu_chr_fe_get_driver(&s->chr)) { ch = val; /* XXX this blocks entire thread. Rewrite to use * qemu_chr_fe_write and background I/O callbacks */ - qemu_chr_fe_write_all(s->chr, &ch, 1); + qemu_chr_fe_write_all(&s->chr, &ch, 1); } s->dr = val; s->flags &= ~SH_SERIAL_FLAG_TDE; @@ -395,12 +396,11 @@ void sh_serial_init(MemoryRegion *sysmem, 0, 0x28); memory_region_add_subregion(sysmem, A7ADDR(base), &s->iomem_a7); - s->chr = chr; - if (chr) { - qemu_chr_fe_claim_no_fail(chr); - qemu_chr_add_handlers(chr, sh_serial_can_receive1, sh_serial_receive1, - sh_serial_event, s); + qemu_chr_fe_init(&s->chr, chr, &error_abort); + qemu_chr_fe_set_handlers(&s->chr, sh_serial_can_receive1, + sh_serial_receive1, + sh_serial_event, s, NULL, true); } s->eri = eri_source; diff --git a/hw/char/spapr_vty.c b/hw/char/spapr_vty.c index 9aeafc0..31822fe 100644 --- a/hw/char/spapr_vty.c +++ b/hw/char/spapr_vty.c @@ -11,7 +11,7 @@ typedef struct VIOsPAPRVTYDevice { VIOsPAPRDevice sdev; - CharDriverState *chardev; + CharBackend chardev; uint32_t in, out; uint8_t buf[VTERM_BUFSIZE]; } VIOsPAPRVTYDevice; @@ -51,7 +51,7 @@ static int vty_getchars(VIOsPAPRDevice *sdev, uint8_t *buf, int max) buf[n++] = dev->buf[dev->out++ % VTERM_BUFSIZE]; } - qemu_chr_accept_input(dev->chardev); + qemu_chr_fe_accept_input(&dev->chardev); return n; } @@ -62,20 +62,20 @@ void vty_putchars(VIOsPAPRDevice *sdev, uint8_t *buf, int len) /* XXX this blocks entire thread. Rewrite to use * qemu_chr_fe_write and background I/O callbacks */ - qemu_chr_fe_write_all(dev->chardev, buf, len); + qemu_chr_fe_write_all(&dev->chardev, buf, len); } static void spapr_vty_realize(VIOsPAPRDevice *sdev, Error **errp) { VIOsPAPRVTYDevice *dev = VIO_SPAPR_VTY_DEVICE(sdev); - if (!dev->chardev) { + if (!qemu_chr_fe_get_driver(&dev->chardev)) { error_setg(errp, "chardev property not set"); return; } - qemu_chr_add_handlers(dev->chardev, vty_can_receive, - vty_receive, NULL, dev); + qemu_chr_fe_set_handlers(&dev->chardev, vty_can_receive, + vty_receive, NULL, dev, NULL, true); } /* Forward declaration */ diff --git a/hw/char/stm32f2xx_usart.c b/hw/char/stm32f2xx_usart.c index 4c6640d..59872e6 100644 --- a/hw/char/stm32f2xx_usart.c +++ b/hw/char/stm32f2xx_usart.c @@ -97,17 +97,13 @@ static uint64_t stm32f2xx_usart_read(void *opaque, hwaddr addr, case USART_SR: retvalue = s->usart_sr; s->usart_sr &= ~USART_SR_TC; - if (s->chr) { - qemu_chr_accept_input(s->chr); - } + qemu_chr_fe_accept_input(&s->chr); return retvalue; case USART_DR: DB_PRINT("Value: 0x%" PRIx32 ", %c\n", s->usart_dr, (char) s->usart_dr); s->usart_sr |= USART_SR_TXE; s->usart_sr &= ~USART_SR_RXNE; - if (s->chr) { - qemu_chr_accept_input(s->chr); - } + qemu_chr_fe_accept_input(&s->chr); qemu_set_irq(s->irq, 0); return s->usart_dr & 0x3FF; case USART_BRR: @@ -152,11 +148,9 @@ static void stm32f2xx_usart_write(void *opaque, hwaddr addr, case USART_DR: if (value < 0xF000) { ch = value; - if (s->chr) { - /* XXX this blocks entire thread. Rewrite to use - * qemu_chr_fe_write and background I/O callbacks */ - qemu_chr_fe_write_all(s->chr, &ch, 1); - } + /* XXX this blocks entire thread. Rewrite to use + * qemu_chr_fe_write and background I/O callbacks */ + qemu_chr_fe_write_all(&s->chr, &ch, 1); s->usart_sr |= USART_SR_TC; s->usart_sr &= ~USART_SR_TXE; } @@ -212,10 +206,8 @@ static void stm32f2xx_usart_realize(DeviceState *dev, Error **errp) { STM32F2XXUsartState *s = STM32F2XX_USART(dev); - if (s->chr) { - qemu_chr_add_handlers(s->chr, stm32f2xx_usart_can_receive, - stm32f2xx_usart_receive, NULL, s); - } + qemu_chr_fe_set_handlers(&s->chr, stm32f2xx_usart_can_receive, + stm32f2xx_usart_receive, NULL, s, NULL, true); } static void stm32f2xx_usart_class_init(ObjectClass *klass, void *data) diff --git a/hw/char/virtio-console.c b/hw/char/virtio-console.c index d44c18c..776205b 100644 --- a/hw/char/virtio-console.c +++ b/hw/char/virtio-console.c @@ -24,7 +24,7 @@ typedef struct VirtConsole { VirtIOSerialPort parent_obj; - CharDriverState *chr; + CharBackend chr; guint watch; } VirtConsole; @@ -49,12 +49,12 @@ static ssize_t flush_buf(VirtIOSerialPort *port, VirtConsole *vcon = VIRTIO_CONSOLE(port); ssize_t ret; - if (!vcon->chr) { + if (!qemu_chr_fe_get_driver(&vcon->chr)) { /* If there's no backend, we can just say we consumed all data. */ return len; } - ret = qemu_chr_fe_write(vcon->chr, buf, len); + ret = qemu_chr_fe_write(&vcon->chr, buf, len); trace_virtio_console_flush_buf(port->id, len, ret); if (ret < len) { @@ -92,7 +92,7 @@ static ssize_t flush_buf(VirtIOSerialPort *port, if (!k->is_console) { virtio_serial_throttle_port(port, true); if (!vcon->watch) { - vcon->watch = qemu_chr_fe_add_watch(vcon->chr, + vcon->watch = qemu_chr_fe_add_watch(&vcon->chr, G_IO_OUT|G_IO_HUP, chr_write_unblocked, vcon); } @@ -108,8 +108,8 @@ static void set_guest_connected(VirtIOSerialPort *port, int guest_connected) DeviceState *dev = DEVICE(port); VirtIOSerialPortClass *k = VIRTIO_SERIAL_PORT_GET_CLASS(port); - if (vcon->chr && !k->is_console) { - qemu_chr_fe_set_open(vcon->chr, guest_connected); + if (!k->is_console) { + qemu_chr_fe_set_open(&vcon->chr, guest_connected); } if (dev->id) { @@ -122,9 +122,7 @@ static void guest_writable(VirtIOSerialPort *port) { VirtConsole *vcon = VIRTIO_CONSOLE(port); - if (vcon->chr) { - qemu_chr_accept_input(vcon->chr); - } + qemu_chr_fe_accept_input(&vcon->chr); } /* Readiness of the guest to accept data on a port */ @@ -170,6 +168,7 @@ static void virtconsole_realize(DeviceState *dev, Error **errp) VirtIOSerialPort *port = VIRTIO_SERIAL_PORT(dev); VirtConsole *vcon = VIRTIO_CONSOLE(dev); VirtIOSerialPortClass *k = VIRTIO_SERIAL_PORT_GET_CLASS(dev); + CharDriverState *chr = qemu_chr_fe_get_driver(&vcon->chr); if (port->id == 0 && !k->is_console) { error_setg(errp, "Port number 0 on virtio-serial devices reserved " @@ -177,7 +176,7 @@ static void virtconsole_realize(DeviceState *dev, Error **errp) return; } - if (vcon->chr) { + if (chr) { /* * For consoles we don't block guest data transfer just * because nothing is connected - we'll just let it go @@ -188,14 +187,12 @@ static void virtconsole_realize(DeviceState *dev, Error **errp) * trigger open/close of the device */ if (k->is_console) { - vcon->chr->explicit_fe_open = 0; - qemu_chr_add_handlers(vcon->chr, chr_can_read, chr_read, - NULL, vcon); + qemu_chr_fe_set_handlers(&vcon->chr, chr_can_read, chr_read, + NULL, vcon, NULL, true); virtio_serial_open(port); } else { - vcon->chr->explicit_fe_open = 1; - qemu_chr_add_handlers(vcon->chr, chr_can_read, chr_read, - chr_event, vcon); + qemu_chr_fe_set_handlers(&vcon->chr, chr_can_read, chr_read, + chr_event, vcon, NULL, false); } } } diff --git a/hw/char/xen_console.c b/hw/char/xen_console.c index 83108b0..86cdc52 100644 --- a/hw/char/xen_console.c +++ b/hw/char/xen_console.c @@ -23,9 +23,11 @@ #include <sys/select.h> #include <termios.h> +#include "qapi/error.h" #include "hw/hw.h" #include "sysemu/char.h" #include "hw/xen/xen_backend.h" +#include "qapi/error.h" #include <xen/io/console.h> @@ -43,7 +45,7 @@ struct XenConsole { char console[XEN_BUFSIZE]; int ring_ref; void *sring; - CharDriverState *chr; + CharBackend chr; int backlog; }; @@ -148,11 +150,13 @@ static void xencons_send(struct XenConsole *con) ssize_t len, size; size = con->buffer.size - con->buffer.consumed; - if (con->chr) - len = qemu_chr_fe_write(con->chr, con->buffer.data + con->buffer.consumed, - size); - else + if (qemu_chr_fe_get_driver(&con->chr)) { + len = qemu_chr_fe_write(&con->chr, + con->buffer.data + con->buffer.consumed, + size); + } else { len = size; + } if (len < 1) { if (!con->backlog) { con->backlog = 1; @@ -196,13 +200,18 @@ static int con_init(struct XenDevice *xendev) /* no Xen override, use qemu output device */ if (output == NULL) { - con->chr = serial_hds[con->xendev.dev]; + if (con->xendev.dev) { + qemu_chr_fe_init(&con->chr, serial_hds[con->xendev.dev], + &error_abort); + } } else { snprintf(label, sizeof(label), "xencons%d", con->xendev.dev); - con->chr = qemu_chr_new(label, output, NULL); + qemu_chr_fe_init(&con->chr, + qemu_chr_new(label, output), &error_abort); } - xenstore_store_pv_console_info(con->xendev.dev, con->chr); + xenstore_store_pv_console_info(con->xendev.dev, + qemu_chr_fe_get_driver(&con->chr)); out: g_free(type); @@ -235,17 +244,8 @@ static int con_initialise(struct XenDevice *xendev) return -1; xen_be_bind_evtchn(&con->xendev); - if (con->chr) { - if (qemu_chr_fe_claim(con->chr) == 0) { - qemu_chr_add_handlers(con->chr, xencons_can_receive, - xencons_receive, NULL, con); - } else { - xen_be_printf(xendev, 0, - "xen_console_init error chardev %s already used\n", - con->chr->label); - con->chr = NULL; - } - } + qemu_chr_fe_set_handlers(&con->chr, xencons_can_receive, + xencons_receive, NULL, con, NULL, true); xen_be_printf(xendev, 1, "ring mfn %d, remote port %d, local port %d, limit %zd\n", con->ring_ref, @@ -259,10 +259,7 @@ static void con_disconnect(struct XenDevice *xendev) { struct XenConsole *con = container_of(xendev, struct XenConsole, xendev); - if (con->chr) { - qemu_chr_add_handlers(con->chr, NULL, NULL, NULL, NULL); - qemu_chr_fe_release(con->chr); - } + qemu_chr_fe_deinit(&con->chr); xen_be_unbind_evtchn(&con->xendev); if (con->sring) { diff --git a/hw/char/xilinx_uartlite.c b/hw/char/xilinx_uartlite.c index 3766dc2..37d313b 100644 --- a/hw/char/xilinx_uartlite.c +++ b/hw/char/xilinx_uartlite.c @@ -55,7 +55,7 @@ typedef struct XilinxUARTLite { SysBusDevice parent_obj; MemoryRegion mmio; - CharDriverState *chr; + CharBackend chr; qemu_irq irq; uint8_t rx_fifo[8]; @@ -107,7 +107,7 @@ uart_read(void *opaque, hwaddr addr, unsigned int size) s->rx_fifo_len--; uart_update_status(s); uart_update_irq(s); - qemu_chr_accept_input(s->chr); + qemu_chr_fe_accept_input(&s->chr); break; default: @@ -143,11 +143,9 @@ uart_write(void *opaque, hwaddr addr, break; case R_TX: - if (s->chr) - /* XXX this blocks entire thread. Rewrite to use - * qemu_chr_fe_write and background I/O callbacks */ - qemu_chr_fe_write_all(s->chr, &ch, 1); - + /* XXX this blocks entire thread. Rewrite to use + * qemu_chr_fe_write and background I/O callbacks */ + qemu_chr_fe_write_all(&s->chr, &ch, 1); s->regs[addr] = value; /* hax. */ @@ -213,8 +211,8 @@ static void xilinx_uartlite_realize(DeviceState *dev, Error **errp) { XilinxUARTLite *s = XILINX_UARTLITE(dev); - if (s->chr) - qemu_chr_add_handlers(s->chr, uart_can_rx, uart_rx, uart_event, s); + qemu_chr_fe_set_handlers(&s->chr, uart_can_rx, uart_rx, + uart_event, s, NULL, true); } static void xilinx_uartlite_init(Object *obj) diff --git a/hw/core/ptimer.c b/hw/core/ptimer.c index c45c835..3af82af 100644 --- a/hw/core/ptimer.c +++ b/hw/core/ptimer.c @@ -13,6 +13,9 @@ #include "sysemu/replay.h" #include "sysemu/qtest.h" +#define DELTA_ADJUST 1 +#define DELTA_NO_ADJUST -1 + struct ptimer_state { uint8_t enabled; /* 0 = disabled, 1 = periodic, 2 = oneshot. */ @@ -35,16 +38,21 @@ static void ptimer_trigger(ptimer_state *s) } } -static void ptimer_reload(ptimer_state *s) +static void ptimer_reload(ptimer_state *s, int delta_adjust) { uint32_t period_frac = s->period_frac; uint64_t period = s->period; + uint64_t delta = s->delta; - if (s->delta == 0) { + if (delta == 0 && !(s->policy_mask & PTIMER_POLICY_NO_IMMEDIATE_TRIGGER)) { ptimer_trigger(s); - s->delta = s->limit; } - if (s->delta == 0 || s->period == 0) { + + if (delta == 0 && !(s->policy_mask & PTIMER_POLICY_NO_IMMEDIATE_RELOAD)) { + delta = s->delta = s->limit; + } + + if (s->period == 0) { if (!qtest_enabled()) { fprintf(stderr, "Timer with period zero, disabling\n"); } @@ -53,6 +61,39 @@ static void ptimer_reload(ptimer_state *s) return; } + if (s->policy_mask & PTIMER_POLICY_WRAP_AFTER_ONE_PERIOD) { + if (delta_adjust != DELTA_NO_ADJUST) { + delta += delta_adjust; + } + } + + if (delta == 0 && (s->policy_mask & PTIMER_POLICY_CONTINUOUS_TRIGGER)) { + if (s->enabled == 1 && s->limit == 0) { + delta = 1; + } + } + + if (delta == 0 && (s->policy_mask & PTIMER_POLICY_NO_IMMEDIATE_TRIGGER)) { + if (delta_adjust != DELTA_NO_ADJUST) { + delta = 1; + } + } + + if (delta == 0 && (s->policy_mask & PTIMER_POLICY_NO_IMMEDIATE_RELOAD)) { + if (s->enabled == 1 && s->limit != 0) { + delta = 1; + } + } + + if (delta == 0) { + if (!qtest_enabled()) { + fprintf(stderr, "Timer with delta zero, disabling\n"); + } + timer_del(s->timer); + s->enabled = 0; + return; + } + /* * Artificially limit timeout rate to something * achievable under QEMU. Otherwise, QEMU spends all @@ -62,15 +103,15 @@ static void ptimer_reload(ptimer_state *s) * on the current generation of host machines. */ - if (s->enabled == 1 && (s->delta * period < 10000) && !use_icount) { - period = 10000 / s->delta; + if (s->enabled == 1 && (delta * period < 10000) && !use_icount) { + period = 10000 / delta; period_frac = 0; } s->last_event = s->next_event; - s->next_event = s->last_event + s->delta * period; + s->next_event = s->last_event + delta * period; if (period_frac) { - s->next_event += ((int64_t)period_frac * s->delta) >> 32; + s->next_event += ((int64_t)period_frac * delta) >> 32; } timer_mod(s->timer, s->next_event); } @@ -78,12 +119,35 @@ static void ptimer_reload(ptimer_state *s) static void ptimer_tick(void *opaque) { ptimer_state *s = (ptimer_state *)opaque; - ptimer_trigger(s); - s->delta = 0; + bool trigger = true; + if (s->enabled == 2) { + s->delta = 0; s->enabled = 0; } else { - ptimer_reload(s); + int delta_adjust = DELTA_ADJUST; + + if (s->delta == 0 || s->limit == 0) { + /* If a "continuous trigger" policy is not used and limit == 0, + we should error out. delta == 0 means that this tick is + caused by a "no immediate reload" policy, so it shouldn't + be adjusted. */ + delta_adjust = DELTA_NO_ADJUST; + } + + if (!(s->policy_mask & PTIMER_POLICY_NO_IMMEDIATE_TRIGGER)) { + /* Avoid re-trigger on deferred reload if "no immediate trigger" + policy isn't used. */ + trigger = (delta_adjust == DELTA_ADJUST); + } + + s->delta = s->limit; + + ptimer_reload(s, delta_adjust); + } + + if (trigger) { + ptimer_trigger(s); } } @@ -91,9 +155,10 @@ uint64_t ptimer_get_count(ptimer_state *s) { uint64_t counter; - if (s->enabled) { + if (s->enabled && s->delta != 0) { int64_t now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); int64_t next = s->next_event; + int64_t last = s->last_event; bool expired = (now - next >= 0); bool oneshot = (s->enabled == 2); @@ -118,7 +183,7 @@ uint64_t ptimer_get_count(ptimer_state *s) /* We need to divide time by period, where time is stored in rem (64-bit integer) and period is stored in period/period_frac (64.32 fixed point). - + Doing full precision division is hard, so scale values and do a 64-bit division. The result should be rounded down, so that the rounding error never causes the timer to go @@ -145,6 +210,35 @@ uint64_t ptimer_get_count(ptimer_state *s) div += 1; } counter = rem / div; + + if (s->policy_mask & PTIMER_POLICY_WRAP_AFTER_ONE_PERIOD) { + /* Before wrapping around, timer should stay with counter = 0 + for a one period. */ + if (!oneshot && s->delta == s->limit) { + if (now == last) { + /* Counter == delta here, check whether it was + adjusted and if it was, then right now it is + that "one period". */ + if (counter == s->limit + DELTA_ADJUST) { + return 0; + } + } else if (counter == s->limit) { + /* Since the counter is rounded down and now != last, + the counter == limit means that delta was adjusted + by +1 and right now it is that adjusted period. */ + return 0; + } + } + } + } + + if (s->policy_mask & PTIMER_POLICY_NO_COUNTER_ROUND_DOWN) { + /* If now == last then delta == limit, i.e. the counter already + represents the correct value. It would be rounded down a 1ns + later. */ + if (now != last) { + counter += 1; + } } } else { counter = s->delta; @@ -157,7 +251,7 @@ void ptimer_set_count(ptimer_state *s, uint64_t count) s->delta = count; if (s->enabled) { s->next_event = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); - ptimer_reload(s); + ptimer_reload(s, 0); } } @@ -174,7 +268,7 @@ void ptimer_run(ptimer_state *s, int oneshot) s->enabled = oneshot ? 2 : 1; if (was_disabled) { s->next_event = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); - ptimer_reload(s); + ptimer_reload(s, 0); } } @@ -198,7 +292,7 @@ void ptimer_set_period(ptimer_state *s, int64_t period) s->period_frac = 0; if (s->enabled) { s->next_event = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); - ptimer_reload(s); + ptimer_reload(s, 0); } } @@ -210,7 +304,7 @@ void ptimer_set_freq(ptimer_state *s, uint32_t freq) s->period_frac = (1000000000ll << 32) / freq; if (s->enabled) { s->next_event = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); - ptimer_reload(s); + ptimer_reload(s, 0); } } @@ -223,7 +317,7 @@ void ptimer_set_limit(ptimer_state *s, uint64_t limit, int reload) s->delta = limit; if (s->enabled && reload) { s->next_event = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); - ptimer_reload(s); + ptimer_reload(s, 0); } } diff --git a/hw/core/qdev-properties-system.c b/hw/core/qdev-properties-system.c index e55afe6..c35f0f5 100644 --- a/hw/core/qdev-properties-system.c +++ b/hw/core/qdev-properties-system.c @@ -160,55 +160,67 @@ PropertyInfo qdev_prop_drive = { /* --- character device --- */ -static void parse_chr(DeviceState *dev, const char *str, void **ptr, - const char *propname, Error **errp) +static void get_chr(Object *obj, Visitor *v, const char *name, void *opaque, + Error **errp) { - CharDriverState *chr = qemu_chr_find(str); - if (chr == NULL) { - error_setg(errp, "Property '%s.%s' can't find value '%s'", - object_get_typename(OBJECT(dev)), propname, str); - return; - } - if (qemu_chr_fe_claim(chr) != 0) { - error_setg(errp, "Property '%s.%s' can't take value '%s', it's in use", - object_get_typename(OBJECT(dev)), propname, str); - return; - } - *ptr = chr; + DeviceState *dev = DEVICE(obj); + CharBackend *be = qdev_get_prop_ptr(dev, opaque); + char *p; + + p = g_strdup(be->chr && be->chr->label ? be->chr->label : ""); + visit_type_str(v, name, &p, errp); + g_free(p); } -static void release_chr(Object *obj, const char *name, void *opaque) +static void set_chr(Object *obj, Visitor *v, const char *name, void *opaque, + Error **errp) { DeviceState *dev = DEVICE(obj); + Error *local_err = NULL; Property *prop = opaque; - CharDriverState **ptr = qdev_get_prop_ptr(dev, prop); - CharDriverState *chr = *ptr; + CharBackend *be = qdev_get_prop_ptr(dev, prop); + CharDriverState *s; + char *str; - if (chr) { - qemu_chr_add_handlers(chr, NULL, NULL, NULL, NULL); - qemu_chr_fe_release(chr); + if (dev->realized) { + qdev_prop_set_after_realize(dev, name, errp); + return; + } + + visit_type_str(v, name, &str, &local_err); + if (local_err) { + error_propagate(errp, local_err); + return; } -} + if (!*str) { + g_free(str); + be->chr = NULL; + return; + } -static char *print_chr(void *ptr) -{ - CharDriverState *chr = ptr; - const char *val = chr->label ? chr->label : ""; + s = qemu_chr_find(str); + g_free(str); + if (s == NULL) { + error_setg(errp, "Property '%s.%s' can't find value '%s'", + object_get_typename(obj), prop->name, str); + return; + } - return g_strdup(val); + if (!qemu_chr_fe_init(be, s, errp)) { + error_prepend(errp, "Property '%s.%s' can't take value '%s': ", + object_get_typename(obj), prop->name, str); + return; + } } -static void get_chr(Object *obj, Visitor *v, const char *name, void *opaque, - Error **errp) +static void release_chr(Object *obj, const char *name, void *opaque) { - get_pointer(obj, v, opaque, print_chr, name, errp); -} + DeviceState *dev = DEVICE(obj); + Property *prop = opaque; + CharBackend *be = qdev_get_prop_ptr(dev, prop); -static void set_chr(Object *obj, Visitor *v, const char *name, void *opaque, - Error **errp) -{ - set_pointer(obj, v, opaque, parse_chr, name, errp); + qemu_chr_fe_deinit(be); } PropertyInfo qdev_prop_chr = { diff --git a/hw/display/pl110.c b/hw/display/pl110.c index c069c0b..8c7dcc6 100644 --- a/hw/display/pl110.c +++ b/hw/display/pl110.c @@ -466,17 +466,16 @@ static const GraphicHwOps pl110_gfx_ops = { .gfx_update = pl110_update_display, }; -static int pl110_initfn(SysBusDevice *sbd) +static void pl110_realize(DeviceState *dev, Error **errp) { - DeviceState *dev = DEVICE(sbd); PL110State *s = PL110(dev); + SysBusDevice *sbd = SYS_BUS_DEVICE(dev); memory_region_init_io(&s->iomem, OBJECT(s), &pl110_ops, s, "pl110", 0x1000); sysbus_init_mmio(sbd, &s->iomem); sysbus_init_irq(sbd, &s->irq); qdev_init_gpio_in(dev, pl110_mux_ctrl_set, 1); s->con = graphic_console_init(dev, 0, &pl110_gfx_ops, s); - return 0; } static void pl110_init(Object *obj) @@ -503,11 +502,10 @@ static void pl111_init(Object *obj) static void pl110_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); - SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); - k->init = pl110_initfn; set_bit(DEVICE_CATEGORY_DISPLAY, dc->categories); dc->vmsd = &vmstate_pl110; + dc->realize = pl110_realize; } static const TypeInfo pl110_info = { diff --git a/hw/display/virtio-gpu.c b/hw/display/virtio-gpu.c index fa6fd0e..60bce94 100644 --- a/hw/display/virtio-gpu.c +++ b/hw/display/virtio-gpu.c @@ -333,6 +333,7 @@ static void virtio_gpu_resource_create_2d(VirtIOGPU *g, qemu_log_mask(LOG_GUEST_ERROR, "%s: host couldn't handle guest format %d\n", __func__, c2d.format); + g_free(res); cmd->error = VIRTIO_GPU_RESP_ERR_INVALID_PARAMETER; return; } diff --git a/hw/i2c/core.c b/hw/i2c/core.c index 4afbe0b..abd4c4c 100644 --- a/hw/i2c/core.c +++ b/hw/i2c/core.c @@ -88,7 +88,12 @@ int i2c_bus_busy(I2CBus *bus) return !QLIST_EMPTY(&bus->current_devs); } -/* Returns non-zero if the address is not valid. */ +/* + * Returns non-zero if the address is not valid. If this is called + * again without an intervening i2c_end_transfer(), like in the SMBus + * case where the operation is switched from write to read, this + * function will not rescan the bus and thus cannot fail. + */ /* TODO: Make this handle multiple masters. */ int i2c_start_transfer(I2CBus *bus, uint8_t address, int recv) { @@ -104,15 +109,25 @@ int i2c_start_transfer(I2CBus *bus, uint8_t address, int recv) bus->broadcast = true; } - QTAILQ_FOREACH(kid, &bus->qbus.children, sibling) { - DeviceState *qdev = kid->child; - I2CSlave *candidate = I2C_SLAVE(qdev); - if ((candidate->address == address) || (bus->broadcast)) { - node = g_malloc(sizeof(struct I2CNode)); - node->elt = candidate; - QLIST_INSERT_HEAD(&bus->current_devs, node, next); - if (!bus->broadcast) { - break; + /* + * If there are already devices in the list, that means we are in + * the middle of a transaction and we shouldn't rescan the bus. + * + * This happens with any SMBus transaction, even on a pure I2C + * device. The interface does a transaction start without + * terminating the previous transaction. + */ + if (QLIST_EMPTY(&bus->current_devs)) { + QTAILQ_FOREACH(kid, &bus->qbus.children, sibling) { + DeviceState *qdev = kid->child; + I2CSlave *candidate = I2C_SLAVE(qdev); + if ((candidate->address == address) || (bus->broadcast)) { + node = g_malloc(sizeof(struct I2CNode)); + node->elt = candidate; + QLIST_INSERT_HEAD(&bus->current_devs, node, next); + if (!bus->broadcast) { + break; + } } } } @@ -137,10 +152,6 @@ void i2c_end_transfer(I2CBus *bus) I2CSlaveClass *sc; I2CNode *node, *next; - if (QLIST_EMPTY(&bus->current_devs)) { - return; - } - QLIST_FOREACH_SAFE(node, &bus->current_devs, next, next) { sc = I2C_SLAVE_GET_CLASS(node->elt); if (sc->event) { diff --git a/hw/i2c/smbus.c b/hw/i2c/smbus.c index 3979b3d..5b4dd3e 100644 --- a/hw/i2c/smbus.c +++ b/hw/i2c/smbus.c @@ -248,7 +248,9 @@ int smbus_read_byte(I2CBus *bus, uint8_t addr, uint8_t command) return -1; } i2c_send(bus, command); - i2c_start_transfer(bus, addr, 1); + if (i2c_start_transfer(bus, addr, 1)) { + assert(0); + } data = i2c_recv(bus); i2c_nack(bus); i2c_end_transfer(bus); @@ -273,7 +275,9 @@ int smbus_read_word(I2CBus *bus, uint8_t addr, uint8_t command) return -1; } i2c_send(bus, command); - i2c_start_transfer(bus, addr, 1); + if (i2c_start_transfer(bus, addr, 1)) { + assert(0); + } data = i2c_recv(bus); data |= i2c_recv(bus) << 8; i2c_nack(bus); @@ -302,7 +306,9 @@ int smbus_read_block(I2CBus *bus, uint8_t addr, uint8_t command, uint8_t *data) return -1; } i2c_send(bus, command); - i2c_start_transfer(bus, addr, 1); + if (i2c_start_transfer(bus, addr, 1)) { + assert(0); + } len = i2c_recv(bus); if (len > 32) { len = 0; diff --git a/hw/i386/acpi-build.c b/hw/i386/acpi-build.c index e999654..93be96f 100644 --- a/hw/i386/acpi-build.c +++ b/hw/i386/acpi-build.c @@ -340,24 +340,38 @@ build_fadt(GArray *table_data, BIOSLinker *linker, AcpiPmInfo *pm, void pc_madt_cpu_entry(AcpiDeviceIf *adev, int uid, CPUArchIdList *apic_ids, GArray *entry) { - int apic_id; - AcpiMadtProcessorApic *apic = acpi_data_push(entry, sizeof *apic); - - apic_id = apic_ids->cpus[uid].arch_id; - apic->type = ACPI_APIC_PROCESSOR; - apic->length = sizeof(*apic); - apic->processor_id = uid; - apic->local_apic_id = apic_id; - if (apic_ids->cpus[uid].cpu != NULL) { - apic->flags = cpu_to_le32(1); + uint32_t apic_id = apic_ids->cpus[uid].arch_id; + + /* ACPI spec says that LAPIC entry for non present + * CPU may be omitted from MADT or it must be marked + * as disabled. However omitting non present CPU from + * MADT breaks hotplug on linux. So possible CPUs + * should be put in MADT but kept disabled. + */ + if (apic_id < 255) { + AcpiMadtProcessorApic *apic = acpi_data_push(entry, sizeof *apic); + + apic->type = ACPI_APIC_PROCESSOR; + apic->length = sizeof(*apic); + apic->processor_id = uid; + apic->local_apic_id = apic_id; + if (apic_ids->cpus[uid].cpu != NULL) { + apic->flags = cpu_to_le32(1); + } else { + apic->flags = cpu_to_le32(0); + } } else { - /* ACPI spec says that LAPIC entry for non present - * CPU may be omitted from MADT or it must be marked - * as disabled. However omitting non present CPU from - * MADT breaks hotplug on linux. So possible CPUs - * should be put in MADT but kept disabled. - */ - apic->flags = cpu_to_le32(0); + AcpiMadtProcessorX2Apic *apic = acpi_data_push(entry, sizeof *apic); + + apic->type = ACPI_APIC_LOCAL_X2APIC; + apic->length = sizeof(*apic); + apic->uid = cpu_to_le32(uid); + apic->x2apic_id = cpu_to_le32(apic_id); + if (apic_ids->cpus[uid].cpu != NULL) { + apic->flags = cpu_to_le32(1); + } else { + apic->flags = cpu_to_le32(0); + } } } @@ -369,11 +383,11 @@ build_madt(GArray *table_data, BIOSLinker *linker, PCMachineState *pcms) int madt_start = table_data->len; AcpiDeviceIfClass *adevc = ACPI_DEVICE_IF_GET_CLASS(pcms->acpi_dev); AcpiDeviceIf *adev = ACPI_DEVICE_IF(pcms->acpi_dev); + bool x2apic_mode = false; AcpiMultipleApicTable *madt; AcpiMadtIoApic *io_apic; AcpiMadtIntsrcovr *intsrcovr; - AcpiMadtLocalNmi *local_nmi; int i; madt = acpi_data_push(table_data, sizeof *madt); @@ -382,6 +396,9 @@ build_madt(GArray *table_data, BIOSLinker *linker, PCMachineState *pcms) for (i = 0; i < apic_ids->len; i++) { adevc->madt_cpu(adev, i, apic_ids, table_data); + if (apic_ids->cpus[i].arch_id > 254) { + x2apic_mode = true; + } } g_free(apic_ids); @@ -414,12 +431,25 @@ build_madt(GArray *table_data, BIOSLinker *linker, PCMachineState *pcms) intsrcovr->flags = cpu_to_le16(0xd); /* active high, level triggered */ } - local_nmi = acpi_data_push(table_data, sizeof *local_nmi); - local_nmi->type = ACPI_APIC_LOCAL_NMI; - local_nmi->length = sizeof(*local_nmi); - local_nmi->processor_id = 0xff; /* all processors */ - local_nmi->flags = cpu_to_le16(0); - local_nmi->lint = 1; /* ACPI_LINT1 */ + if (x2apic_mode) { + AcpiMadtLocalX2ApicNmi *local_nmi; + + local_nmi = acpi_data_push(table_data, sizeof *local_nmi); + local_nmi->type = ACPI_APIC_LOCAL_X2APIC_NMI; + local_nmi->length = sizeof(*local_nmi); + local_nmi->uid = 0xFFFFFFFF; /* all processors */ + local_nmi->flags = cpu_to_le16(0); + local_nmi->lint = 1; /* ACPI_LINT1 */ + } else { + AcpiMadtLocalNmi *local_nmi; + + local_nmi = acpi_data_push(table_data, sizeof *local_nmi); + local_nmi->type = ACPI_APIC_LOCAL_NMI; + local_nmi->length = sizeof(*local_nmi); + local_nmi->processor_id = 0xff; /* all processors */ + local_nmi->flags = cpu_to_le16(0); + local_nmi->lint = 1; /* ACPI_LINT1 */ + } build_header(linker, table_data, (void *)(table_data->data + madt_start), "APIC", @@ -2391,7 +2421,6 @@ static void build_srat(GArray *table_data, BIOSLinker *linker, MachineState *machine) { AcpiSystemResourceAffinityTable *srat; - AcpiSratProcessorAffinity *core; AcpiSratMemoryAffinity *numamem; int i; @@ -2411,18 +2440,33 @@ build_srat(GArray *table_data, BIOSLinker *linker, MachineState *machine) for (i = 0; i < apic_ids->len; i++) { int j = numa_get_node_for_cpu(i); - int apic_id = apic_ids->cpus[i].arch_id; + uint32_t apic_id = apic_ids->cpus[i].arch_id; - core = acpi_data_push(table_data, sizeof *core); - core->type = ACPI_SRAT_PROCESSOR_APIC; - core->length = sizeof(*core); - core->local_apic_id = apic_id; - if (j < nb_numa_nodes) { + if (apic_id < 255) { + AcpiSratProcessorAffinity *core; + + core = acpi_data_push(table_data, sizeof *core); + core->type = ACPI_SRAT_PROCESSOR_APIC; + core->length = sizeof(*core); + core->local_apic_id = apic_id; + if (j < nb_numa_nodes) { core->proximity_lo = j; + } + memset(core->proximity_hi, 0, 3); + core->local_sapic_eid = 0; + core->flags = cpu_to_le32(1); + } else { + AcpiSratProcessorX2ApicAffinity *core; + + core = acpi_data_push(table_data, sizeof *core); + core->type = ACPI_SRAT_PROCESSOR_x2APIC; + core->length = sizeof(*core); + core->x2apic_id = cpu_to_le32(apic_id); + if (j < nb_numa_nodes) { + core->proximity_domain = cpu_to_le32(j); + } + core->flags = cpu_to_le32(1); } - memset(core->proximity_hi, 0, 3); - core->local_sapic_eid = 0; - core->flags = cpu_to_le32(1); } diff --git a/hw/i386/kvm/apic.c b/hw/i386/kvm/apic.c index be55102..39b73e7 100644 --- a/hw/i386/kvm/apic.c +++ b/hw/i386/kvm/apic.c @@ -34,7 +34,11 @@ static void kvm_put_apic_state(APICCommonState *s, struct kvm_lapic_state *kapic int i; memset(kapic, 0, sizeof(*kapic)); - kvm_apic_set_reg(kapic, 0x2, s->id << 24); + if (kvm_has_x2apic_api() && s->apicbase & MSR_IA32_APICBASE_EXTD) { + kvm_apic_set_reg(kapic, 0x2, s->initial_apic_id); + } else { + kvm_apic_set_reg(kapic, 0x2, s->id << 24); + } kvm_apic_set_reg(kapic, 0x8, s->tpr); kvm_apic_set_reg(kapic, 0xd, s->log_dest << 24); kvm_apic_set_reg(kapic, 0xe, s->dest_mode << 28 | 0x0fffffff); @@ -59,7 +63,11 @@ void kvm_get_apic_state(DeviceState *dev, struct kvm_lapic_state *kapic) APICCommonState *s = APIC_COMMON(dev); int i, v; - s->id = kvm_apic_get_reg(kapic, 0x2) >> 24; + if (kvm_has_x2apic_api() && s->apicbase & MSR_IA32_APICBASE_EXTD) { + assert(kvm_apic_get_reg(kapic, 0x2) == s->initial_apic_id); + } else { + s->id = kvm_apic_get_reg(kapic, 0x2) >> 24; + } s->tpr = kvm_apic_get_reg(kapic, 0x8); s->arb_id = kvm_apic_get_reg(kapic, 0x9); s->log_dest = kvm_apic_get_reg(kapic, 0xd) >> 24; diff --git a/hw/i386/pc.c b/hw/i386/pc.c index f4b0cda..f56ea0f 100644 --- a/hw/i386/pc.c +++ b/hw/i386/pc.c @@ -68,6 +68,7 @@ #include "qapi-visit.h" #include "qom/cpu.h" #include "hw/nmi.h" +#include "hw/i386/intel_iommu.h" /* debug PC/ISA interrupts */ //#define DEBUG_IRQ @@ -746,17 +747,15 @@ static FWCfgState *bochs_bios_init(AddressSpace *as, PCMachineState *pcms) /* FW_CFG_MAX_CPUS is a bit confusing/problematic on x86: * - * SeaBIOS needs FW_CFG_MAX_CPUS for CPU hotplug, but the CPU hotplug - * QEMU<->SeaBIOS interface is not based on the "CPU index", but on the APIC - * ID of hotplugged CPUs[1]. This means that FW_CFG_MAX_CPUS is not the - * "maximum number of CPUs", but the "limit to the APIC ID values SeaBIOS - * may see". + * For machine types prior to 1.8, SeaBIOS needs FW_CFG_MAX_CPUS for + * building MPTable, ACPI MADT, ACPI CPU hotplug and ACPI SRAT table, + * that tables are based on xAPIC ID and QEMU<->SeaBIOS interface + * for CPU hotplug also uses APIC ID and not "CPU index". + * This means that FW_CFG_MAX_CPUS is not the "maximum number of CPUs", + * but the "limit to the APIC ID values SeaBIOS may see". * - * So, this means we must not use max_cpus, here, but the maximum possible - * APIC ID value, plus one. - * - * [1] The only kind of "CPU identifier" used between SeaBIOS and QEMU is - * the APIC ID, not the "CPU index" + * So for compatibility reasons with old BIOSes we are stuck with + * "etc/max-cpus" actually being apic_id_limit */ fw_cfg_add_i16(fw_cfg, FW_CFG_MAX_CPUS, (uint16_t)pcms->apic_id_limit); fw_cfg_add_i64(fw_cfg, FW_CFG_RAM_SIZE, (uint64_t)ram_size); @@ -1087,17 +1086,6 @@ void pc_acpi_smi_interrupt(void *opaque, int irq, int level) } } -static int pc_present_cpus_count(PCMachineState *pcms) -{ - int i, boot_cpus = 0; - for (i = 0; i < pcms->possible_cpus->len; i++) { - if (pcms->possible_cpus->cpus[i].cpu) { - boot_cpus++; - } - } - return boot_cpus; -} - static X86CPU *pc_new_cpu(const char *typename, int64_t apic_id, Error **errp) { @@ -1190,12 +1178,6 @@ void pc_cpus_init(PCMachineState *pcms) * This is used for FW_CFG_MAX_CPUS. See comments on bochs_bios_init(). */ pcms->apic_id_limit = x86_cpu_apic_id_from_index(max_cpus - 1) + 1; - if (pcms->apic_id_limit > ACPI_CPU_HOTPLUG_ID_LIMIT) { - error_report("max_cpus is too large. APIC ID of last CPU is %u", - pcms->apic_id_limit - 1); - exit(1); - } - pcms->possible_cpus = g_malloc0(sizeof(CPUArchIdList) + sizeof(CPUArchId) * max_cpus); for (i = 0; i < max_cpus; i++) { @@ -1240,6 +1222,19 @@ static void pc_build_feature_control_file(PCMachineState *pcms) fw_cfg_add_file(pcms->fw_cfg, "etc/msr_feature_control", val, sizeof(*val)); } +static void rtc_set_cpus_count(ISADevice *rtc, uint16_t cpus_count) +{ + if (cpus_count > 0xff) { + /* If the number of CPUs can't be represented in 8 bits, the + * BIOS must use "etc/boot-cpus". Set RTC field to 0 just + * to make old BIOSes fail more predictably. + */ + rtc_set_memory(rtc, 0x5f, 0); + } else { + rtc_set_memory(rtc, 0x5f, cpus_count - 1); + } +} + static void pc_machine_done(Notifier *notifier, void *data) { @@ -1248,7 +1243,7 @@ void pc_machine_done(Notifier *notifier, void *data) PCIBus *bus = pcms->bus; /* set the number of CPUs */ - rtc_set_memory(pcms->rtc, 0x5f, pc_present_cpus_count(pcms) - 1); + rtc_set_cpus_count(pcms->rtc, le16_to_cpu(pcms->boot_cpus_le)); if (bus) { int extra_hosts = 0; @@ -1269,8 +1264,28 @@ void pc_machine_done(Notifier *notifier, void *data) acpi_setup(); if (pcms->fw_cfg) { + MachineClass *mc = MACHINE_GET_CLASS(pcms); + pc_build_smbios(pcms->fw_cfg); pc_build_feature_control_file(pcms); + + if (mc->max_cpus > 255) { + fw_cfg_add_file(pcms->fw_cfg, "etc/boot-cpus", &pcms->boot_cpus_le, + sizeof(pcms->boot_cpus_le)); + } + } + + if (pcms->apic_id_limit > 255) { + IntelIOMMUState *iommu = INTEL_IOMMU_DEVICE(x86_iommu_get_default()); + + if (!iommu || !iommu->x86_iommu.intr_supported || + iommu->intr_eim != ON_OFF_AUTO_ON) { + error_report("current -smp configuration requires " + "Extended Interrupt Mode enabled. " + "You can add an IOMMU using: " + "-device intel-iommu,intremap=on,eim=on"); + exit(EXIT_FAILURE); + } } } @@ -1589,7 +1604,7 @@ void pc_basic_device_init(ISABus *isa_bus, qemu_irq *gsi, pcspk_init(isa_bus, pit); } - serial_hds_isa_init(isa_bus, MAX_SERIAL_PORTS); + serial_hds_isa_init(isa_bus, 0, MAX_SERIAL_PORTS); parallel_hds_isa_init(isa_bus, MAX_PARALLEL_PORTS); a20_line = qemu_allocate_irqs(handle_a20_line_change, first_cpu, 2); @@ -1794,9 +1809,11 @@ static void pc_cpu_plug(HotplugHandler *hotplug_dev, } } + /* increment the number of CPUs */ + pcms->boot_cpus_le = cpu_to_le16(le16_to_cpu(pcms->boot_cpus_le) + 1); if (dev->hotplugged) { - /* increment the number of CPUs */ - rtc_set_memory(pcms->rtc, 0x5f, rtc_get_memory(pcms->rtc, 0x5f) + 1); + /* Update the number of CPUs in CMOS */ + rtc_set_cpus_count(pcms->rtc, le16_to_cpu(pcms->boot_cpus_le)); } found_cpu = pc_find_cpu_slot(pcms, CPU(dev), NULL); @@ -1850,7 +1867,10 @@ static void pc_cpu_unplug_cb(HotplugHandler *hotplug_dev, found_cpu->cpu = NULL; object_unparent(OBJECT(dev)); - rtc_set_memory(pcms->rtc, 0x5f, rtc_get_memory(pcms->rtc, 0x5f) - 1); + /* decrement the number of CPUs */ + pcms->boot_cpus_le = cpu_to_le16(le16_to_cpu(pcms->boot_cpus_le) - 1); + /* Update the number of CPUs in CMOS */ + rtc_set_cpus_count(pcms->rtc, le16_to_cpu(pcms->boot_cpus_le)); out: error_propagate(errp, local_err); } diff --git a/hw/i386/pc_q35.c b/hw/i386/pc_q35.c index 0b214f2..b40d19e 100644 --- a/hw/i386/pc_q35.c +++ b/hw/i386/pc_q35.c @@ -291,6 +291,7 @@ static void pc_q35_machine_options(MachineClass *m) m->default_display = "std"; m->no_floppy = 1; m->has_dynamic_sysbus = true; + m->max_cpus = 288; } static void pc_q35_2_8_machine_options(MachineClass *m) @@ -306,6 +307,7 @@ static void pc_q35_2_7_machine_options(MachineClass *m) { pc_q35_2_8_machine_options(m); m->alias = NULL; + m->max_cpus = 255; SET_MACHINE_COMPAT(m, PC_COMPAT_2_7); } diff --git a/hw/i386/xen/xen_platform.c b/hw/i386/xen/xen_platform.c index f85635c..2e1e543 100644 --- a/hw/i386/xen/xen_platform.c +++ b/hw/i386/xen/xen_platform.c @@ -114,6 +114,10 @@ static void unplug_disks(PCIBus *b, PCIDevice *d, void *o) PCI_CLASS_STORAGE_IDE && strcmp(d->name, "xen-pci-passthrough") != 0) { pci_piix3_xen_ide_unplug(DEVICE(d)); + } else if (pci_get_word(d->config + PCI_CLASS_DEVICE) == + PCI_CLASS_STORAGE_SCSI + && strcmp(d->name, "xen-pci-passthrough") != 0) { + object_unparent(OBJECT(d)); } } @@ -307,13 +311,38 @@ static void xen_platform_ioport_writeb(void *opaque, hwaddr addr, uint64_t val, unsigned int size) { PCIXenPlatformState *s = opaque; + PCIDevice *pci_dev = PCI_DEVICE(s); switch (addr) { case 0: /* Platform flags */ platform_fixed_ioport_writeb(opaque, 0, (uint32_t)val); break; + case 4: + if (val == 1) { + /* + * SUSE unplug for Xenlinux + * xen-kmp used this since xen-3.0.4, instead the official protocol + * from xen-3.3+ It did an unconditional "outl(1, (ioaddr + 4));" + * Pre VMDP 1.7 used 4 and 8 depending on how VMDP was configured. + * If VMDP was to control both disk and LAN it would use 4. + * If it controlled just disk or just LAN, it would use 8 below. + */ + pci_unplug_disks(pci_dev->bus); + pci_unplug_nics(pci_dev->bus); + } + break; case 8: - log_writeb(s, (uint32_t)val); + switch (val) { + case 1: + pci_unplug_disks(pci_dev->bus); + break; + case 2: + pci_unplug_nics(pci_dev->bus); + break; + default: + log_writeb(s, (uint32_t)val); + break; + } break; default: break; diff --git a/hw/ide/ahci.c b/hw/ide/ahci.c index 63ead21..3c19bda 100644 --- a/hw/ide/ahci.c +++ b/hw/ide/ahci.c @@ -1009,6 +1009,7 @@ static void execute_ncq_command(NCQTransferState *ncq_tfs) &ncq_tfs->sglist, BLOCK_ACCT_READ); ncq_tfs->aiocb = dma_blk_read(ide_state->blk, &ncq_tfs->sglist, ncq_tfs->lba << BDRV_SECTOR_BITS, + BDRV_SECTOR_SIZE, ncq_cb, ncq_tfs); break; case WRITE_FPDMA_QUEUED: @@ -1022,6 +1023,7 @@ static void execute_ncq_command(NCQTransferState *ncq_tfs) &ncq_tfs->sglist, BLOCK_ACCT_WRITE); ncq_tfs->aiocb = dma_blk_write(ide_state->blk, &ncq_tfs->sglist, ncq_tfs->lba << BDRV_SECTOR_BITS, + BDRV_SECTOR_SIZE, ncq_cb, ncq_tfs); break; default: diff --git a/hw/ide/core.c b/hw/ide/core.c index 7291677..43709e5 100644 --- a/hw/ide/core.c +++ b/hw/ide/core.c @@ -882,15 +882,15 @@ static void ide_dma_cb(void *opaque, int ret) switch (s->dma_cmd) { case IDE_DMA_READ: s->bus->dma->aiocb = dma_blk_read(s->blk, &s->sg, offset, - ide_dma_cb, s); + BDRV_SECTOR_SIZE, ide_dma_cb, s); break; case IDE_DMA_WRITE: s->bus->dma->aiocb = dma_blk_write(s->blk, &s->sg, offset, - ide_dma_cb, s); + BDRV_SECTOR_SIZE, ide_dma_cb, s); break; case IDE_DMA_TRIM: s->bus->dma->aiocb = dma_blk_io(blk_get_aio_context(s->blk), - &s->sg, offset, + &s->sg, offset, BDRV_SECTOR_SIZE, ide_issue_trim, s->blk, ide_dma_cb, s, DMA_DIRECTION_TO_DEVICE); break; diff --git a/hw/ide/macio.c b/hw/ide/macio.c index 76f97c2..9742c00 100644 --- a/hw/ide/macio.c +++ b/hw/ide/macio.c @@ -52,187 +52,6 @@ static const int debug_macio = 0; #define MACIO_PAGE_SIZE 4096 -/* - * Unaligned DMA read/write access functions required for OS X/Darwin which - * don't perform DMA transactions on sector boundaries. These functions are - * modelled on bdrv_co_preadv()/bdrv_co_pwritev() and so should be easy to - * remove if the unaligned block APIs are ever exposed. - */ - -static void pmac_dma_read(BlockBackend *blk, - int64_t offset, unsigned int bytes, - void (*cb)(void *opaque, int ret), void *opaque) -{ - DBDMA_io *io = opaque; - MACIOIDEState *m = io->opaque; - IDEState *s = idebus_active_if(&m->bus); - dma_addr_t dma_addr; - int64_t sector_num; - int nsector; - uint64_t align = BDRV_SECTOR_SIZE; - size_t head_bytes, tail_bytes; - - qemu_iovec_destroy(&io->iov); - qemu_iovec_init(&io->iov, io->len / MACIO_PAGE_SIZE + 1); - - sector_num = (offset >> 9); - nsector = (io->len >> 9); - - MACIO_DPRINTF("--- DMA read transfer (0x%" HWADDR_PRIx ",0x%x): " - "sector_num: %" PRId64 ", nsector: %d\n", io->addr, io->len, - sector_num, nsector); - - dma_addr = io->addr; - io->dir = DMA_DIRECTION_FROM_DEVICE; - io->dma_len = io->len; - io->dma_mem = dma_memory_map(&address_space_memory, dma_addr, &io->dma_len, - io->dir); - - if (offset & (align - 1)) { - head_bytes = offset & (align - 1); - - MACIO_DPRINTF("--- DMA unaligned head: sector %" PRId64 ", " - "discarding %zu bytes\n", sector_num, head_bytes); - - qemu_iovec_add(&io->iov, &io->head_remainder, head_bytes); - - bytes += offset & (align - 1); - offset = offset & ~(align - 1); - } - - qemu_iovec_add(&io->iov, io->dma_mem, io->len); - - if ((offset + bytes) & (align - 1)) { - tail_bytes = (offset + bytes) & (align - 1); - - MACIO_DPRINTF("--- DMA unaligned tail: sector %" PRId64 ", " - "discarding bytes %zu\n", sector_num, tail_bytes); - - qemu_iovec_add(&io->iov, &io->tail_remainder, align - tail_bytes); - bytes = ROUND_UP(bytes, align); - } - - s->io_buffer_size -= io->len; - s->io_buffer_index += io->len; - - io->len = 0; - - MACIO_DPRINTF("--- Block read transfer - sector_num: %" PRIx64 " " - "nsector: %x\n", (offset >> 9), (bytes >> 9)); - - s->bus->dma->aiocb = blk_aio_preadv(blk, offset, &io->iov, 0, cb, io); -} - -static void pmac_dma_write(BlockBackend *blk, - int64_t offset, int bytes, - void (*cb)(void *opaque, int ret), void *opaque) -{ - DBDMA_io *io = opaque; - MACIOIDEState *m = io->opaque; - IDEState *s = idebus_active_if(&m->bus); - dma_addr_t dma_addr; - int64_t sector_num; - int nsector; - uint64_t align = BDRV_SECTOR_SIZE; - size_t head_bytes, tail_bytes; - bool unaligned_head = false, unaligned_tail = false; - - qemu_iovec_destroy(&io->iov); - qemu_iovec_init(&io->iov, io->len / MACIO_PAGE_SIZE + 1); - - sector_num = (offset >> 9); - nsector = (io->len >> 9); - - MACIO_DPRINTF("--- DMA write transfer (0x%" HWADDR_PRIx ",0x%x): " - "sector_num: %" PRId64 ", nsector: %d\n", io->addr, io->len, - sector_num, nsector); - - dma_addr = io->addr; - io->dir = DMA_DIRECTION_TO_DEVICE; - io->dma_len = io->len; - io->dma_mem = dma_memory_map(&address_space_memory, dma_addr, &io->dma_len, - io->dir); - - if (offset & (align - 1)) { - head_bytes = offset & (align - 1); - sector_num = ((offset & ~(align - 1)) >> 9); - - MACIO_DPRINTF("--- DMA unaligned head: pre-reading head sector %" - PRId64 "\n", sector_num); - - blk_pread(s->blk, (sector_num << 9), &io->head_remainder, align); - - qemu_iovec_add(&io->iov, &io->head_remainder, head_bytes); - qemu_iovec_add(&io->iov, io->dma_mem, io->len); - - bytes += offset & (align - 1); - offset = offset & ~(align - 1); - - unaligned_head = true; - } - - if ((offset + bytes) & (align - 1)) { - tail_bytes = (offset + bytes) & (align - 1); - sector_num = (((offset + bytes) & ~(align - 1)) >> 9); - - MACIO_DPRINTF("--- DMA unaligned tail: pre-reading tail sector %" - PRId64 "\n", sector_num); - - blk_pread(s->blk, (sector_num << 9), &io->tail_remainder, align); - - if (!unaligned_head) { - qemu_iovec_add(&io->iov, io->dma_mem, io->len); - } - - qemu_iovec_add(&io->iov, &io->tail_remainder + tail_bytes, - align - tail_bytes); - - bytes = ROUND_UP(bytes, align); - - unaligned_tail = true; - } - - if (!unaligned_head && !unaligned_tail) { - qemu_iovec_add(&io->iov, io->dma_mem, io->len); - } - - s->io_buffer_size -= io->len; - s->io_buffer_index += io->len; - - io->len = 0; - - MACIO_DPRINTF("--- Block write transfer - sector_num: %" PRIx64 " " - "nsector: %x\n", (offset >> 9), (bytes >> 9)); - - s->bus->dma->aiocb = blk_aio_pwritev(blk, offset, &io->iov, 0, cb, io); -} - -static void pmac_dma_trim(BlockBackend *blk, - int64_t offset, int bytes, - void (*cb)(void *opaque, int ret), void *opaque) -{ - DBDMA_io *io = opaque; - MACIOIDEState *m = io->opaque; - IDEState *s = idebus_active_if(&m->bus); - dma_addr_t dma_addr; - - qemu_iovec_destroy(&io->iov); - qemu_iovec_init(&io->iov, io->len / MACIO_PAGE_SIZE + 1); - - dma_addr = io->addr; - io->dir = DMA_DIRECTION_TO_DEVICE; - io->dma_len = io->len; - io->dma_mem = dma_memory_map(&address_space_memory, dma_addr, &io->dma_len, - io->dir); - - qemu_iovec_add(&io->iov, io->dma_mem, io->len); - s->io_buffer_size -= io->len; - s->io_buffer_index += io->len; - io->len = 0; - - s->bus->dma->aiocb = ide_issue_trim(offset, &io->iov, cb, io, blk); -} - static void pmac_ide_atapi_transfer_cb(void *opaque, int ret) { DBDMA_io *io = opaque; @@ -244,6 +63,7 @@ static void pmac_ide_atapi_transfer_cb(void *opaque, int ret) if (ret < 0) { MACIO_DPRINTF("DMA error: %d\n", ret); + qemu_sglist_destroy(&s->sg); ide_atapi_io_error(s, ret); goto done; } @@ -258,6 +78,7 @@ static void pmac_ide_atapi_transfer_cb(void *opaque, int ret) if (s->io_buffer_size <= 0) { MACIO_DPRINTF("End of IDE transfer\n"); + qemu_sglist_destroy(&s->sg); ide_atapi_cmd_ok(s); m->dma_active = false; goto done; @@ -282,7 +103,15 @@ static void pmac_ide_atapi_transfer_cb(void *opaque, int ret) /* Calculate current offset */ offset = ((int64_t)s->lba << 11) + s->io_buffer_index; - pmac_dma_read(s->blk, offset, io->len, pmac_ide_atapi_transfer_cb, io); + qemu_sglist_init(&s->sg, DEVICE(m), io->len / MACIO_PAGE_SIZE + 1, + &address_space_memory); + qemu_sglist_add(&s->sg, io->addr, io->len); + s->io_buffer_size -= io->len; + s->io_buffer_index += io->len; + io->len = 0; + + s->bus->dma->aiocb = dma_blk_read(s->blk, &s->sg, offset, 0x1, + pmac_ide_atapi_transfer_cb, io); return; done: @@ -310,6 +139,7 @@ static void pmac_ide_transfer_cb(void *opaque, int ret) if (ret < 0) { MACIO_DPRINTF("DMA error: %d\n", ret); + qemu_sglist_destroy(&s->sg); ide_dma_error(s); goto done; } @@ -324,6 +154,7 @@ static void pmac_ide_transfer_cb(void *opaque, int ret) if (s->io_buffer_size <= 0) { MACIO_DPRINTF("End of IDE transfer\n"); + qemu_sglist_destroy(&s->sg); s->status = READY_STAT | SEEK_STAT; ide_set_irq(s->bus); m->dma_active = false; @@ -338,15 +169,27 @@ static void pmac_ide_transfer_cb(void *opaque, int ret) /* Calculate number of sectors */ offset = (ide_get_sector(s) << 9) + s->io_buffer_index; + qemu_sglist_init(&s->sg, DEVICE(m), io->len / MACIO_PAGE_SIZE + 1, + &address_space_memory); + qemu_sglist_add(&s->sg, io->addr, io->len); + s->io_buffer_size -= io->len; + s->io_buffer_index += io->len; + io->len = 0; + switch (s->dma_cmd) { case IDE_DMA_READ: - pmac_dma_read(s->blk, offset, io->len, pmac_ide_transfer_cb, io); + s->bus->dma->aiocb = dma_blk_read(s->blk, &s->sg, offset, 0x1, + pmac_ide_atapi_transfer_cb, io); break; case IDE_DMA_WRITE: - pmac_dma_write(s->blk, offset, io->len, pmac_ide_transfer_cb, io); + s->bus->dma->aiocb = dma_blk_write(s->blk, &s->sg, offset, 0x1, + pmac_ide_transfer_cb, io); break; case IDE_DMA_TRIM: - pmac_dma_trim(s->blk, offset, io->len, pmac_ide_transfer_cb, io); + s->bus->dma->aiocb = dma_blk_io(blk_get_aio_context(s->blk), &s->sg, + offset, 0x1, ide_issue_trim, s->blk, + pmac_ide_transfer_cb, io, + DMA_DIRECTION_TO_DEVICE); break; default: abort(); diff --git a/hw/intc/apic_common.c b/hw/intc/apic_common.c index 8d01c9c..d78c885 100644 --- a/hw/intc/apic_common.c +++ b/hw/intc/apic_common.c @@ -22,6 +22,7 @@ #include "qapi/error.h" #include "qemu-common.h" #include "cpu.h" +#include "qapi/visitor.h" #include "hw/i386/apic.h" #include "hw/i386/apic_internal.h" #include "trace.h" @@ -39,6 +40,11 @@ void cpu_set_apic_base(DeviceState *dev, uint64_t val) if (dev) { APICCommonState *s = APIC_COMMON(dev); APICCommonClass *info = APIC_COMMON_GET_CLASS(s); + /* switching to x2APIC, reset possibly modified xAPIC ID */ + if (!(s->apicbase & MSR_IA32_APICBASE_EXTD) && + (val & MSR_IA32_APICBASE_EXTD)) { + s->id = s->initial_apic_id; + } info->set_base(s, val); } } @@ -242,6 +248,7 @@ static void apic_reset_common(DeviceState *dev) bsp = s->apicbase & MSR_IA32_APICBASE_BSP; s->apicbase = APIC_DEFAULT_ADDRESS | bsp | MSR_IA32_APICBASE_ENABLE; + s->id = s->initial_apic_id; s->vapic_paddr = 0; info->vapic_base_update(s); @@ -428,7 +435,6 @@ static const VMStateDescription vmstate_apic_common = { }; static Property apic_properties_common[] = { - DEFINE_PROP_UINT8("id", APICCommonState, id, -1), DEFINE_PROP_UINT8("version", APICCommonState, version, 0x14), DEFINE_PROP_BIT("vapic", APICCommonState, vapic_control, VAPIC_ENABLE_BIT, true), @@ -437,6 +443,49 @@ static Property apic_properties_common[] = { DEFINE_PROP_END_OF_LIST(), }; +static void apic_common_get_id(Object *obj, Visitor *v, const char *name, + void *opaque, Error **errp) +{ + APICCommonState *s = APIC_COMMON(obj); + int64_t value; + + value = s->apicbase & MSR_IA32_APICBASE_EXTD ? s->initial_apic_id : s->id; + visit_type_int(v, name, &value, errp); +} + +static void apic_common_set_id(Object *obj, Visitor *v, const char *name, + void *opaque, Error **errp) +{ + APICCommonState *s = APIC_COMMON(obj); + DeviceState *dev = DEVICE(obj); + Error *local_err = NULL; + int64_t value; + + if (dev->realized) { + qdev_prop_set_after_realize(dev, name, errp); + return; + } + + visit_type_int(v, name, &value, &local_err); + if (local_err) { + error_propagate(errp, local_err); + return; + } + + s->initial_apic_id = value; + s->id = (uint8_t)value; +} + +static void apic_common_initfn(Object *obj) +{ + APICCommonState *s = APIC_COMMON(obj); + + s->id = s->initial_apic_id = -1; + object_property_add(obj, "id", "int", + apic_common_get_id, + apic_common_set_id, NULL, NULL, NULL); +} + static void apic_common_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); @@ -456,6 +505,7 @@ static const TypeInfo apic_common_type = { .name = TYPE_APIC_COMMON, .parent = TYPE_DEVICE, .instance_size = sizeof(APICCommonState), + .instance_init = apic_common_initfn, .class_size = sizeof(APICCommonClass), .class_init = apic_common_class_init, .abstract = true, diff --git a/hw/ipmi/ipmi_bmc_extern.c b/hw/ipmi/ipmi_bmc_extern.c index d93e3f3..4b310e5 100644 --- a/hw/ipmi/ipmi_bmc_extern.c +++ b/hw/ipmi/ipmi_bmc_extern.c @@ -62,7 +62,7 @@ typedef struct IPMIBmcExtern { IPMIBmc parent; - CharDriverState *chr; + CharBackend chr; bool connected; @@ -105,7 +105,7 @@ static void continue_send(IPMIBmcExtern *ibe) goto check_reset; } send: - ret = qemu_chr_fe_write(ibe->chr, ibe->outbuf + ibe->outpos, + ret = qemu_chr_fe_write(&ibe->chr, ibe->outbuf + ibe->outpos, ibe->outlen - ibe->outpos); if (ret > 0) { ibe->outpos += ret; @@ -442,12 +442,13 @@ static void ipmi_bmc_extern_realize(DeviceState *dev, Error **errp) { IPMIBmcExtern *ibe = IPMI_BMC_EXTERN(dev); - if (!ibe->chr) { + if (!qemu_chr_fe_get_driver(&ibe->chr)) { error_setg(errp, "IPMI external bmc requires chardev attribute"); return; } - qemu_chr_add_handlers(ibe->chr, can_receive, receive, chr_event, ibe); + qemu_chr_fe_set_handlers(&ibe->chr, can_receive, receive, + chr_event, ibe, NULL, true); } static int ipmi_bmc_extern_post_migrate(void *opaque, int version_id) diff --git a/hw/isa/pc87312.c b/hw/isa/pc87312.c index c3ebf3e..b1c1a0a 100644 --- a/hw/isa/pc87312.c +++ b/hw/isa/pc87312.c @@ -283,7 +283,7 @@ static void pc87312_realize(DeviceState *dev, Error **errp) /* FIXME use a qdev chardev prop instead of parallel_hds[] */ chr = parallel_hds[0]; if (chr == NULL) { - chr = qemu_chr_new("par0", "null", NULL); + chr = qemu_chr_new("par0", "null"); } isa = isa_create(bus, "isa-parallel"); d = DEVICE(isa); @@ -303,7 +303,7 @@ static void pc87312_realize(DeviceState *dev, Error **errp) chr = serial_hds[i]; if (chr == NULL) { snprintf(name, sizeof(name), "ser%d", i); - chr = qemu_chr_new(name, "null", NULL); + chr = qemu_chr_new(name, "null"); } isa = isa_create(bus, "isa-serial"); d = DEVICE(isa); diff --git a/hw/mips/mips_fulong2e.c b/hw/mips/mips_fulong2e.c index 889cdc7..9a4dae4 100644 --- a/hw/mips/mips_fulong2e.c +++ b/hw/mips/mips_fulong2e.c @@ -374,7 +374,7 @@ static void mips_fulong2e_init(MachineState *machine) rtc_init(isa_bus, 2000, NULL); - serial_hds_isa_init(isa_bus, MAX_SERIAL_PORTS); + serial_hds_isa_init(isa_bus, 0, MAX_SERIAL_PORTS); parallel_hds_isa_init(isa_bus, 1); /* Sound card */ diff --git a/hw/mips/mips_malta.c b/hw/mips/mips_malta.c index e90857e..cf9bd3e 100644 --- a/hw/mips/mips_malta.c +++ b/hw/mips/mips_malta.c @@ -85,9 +85,10 @@ typedef struct { uint32_t i2coe; uint32_t i2cout; uint32_t i2csel; - CharDriverState *display; + CharBackend display; char display_text[9]; SerialState *uart; + bool display_inited; } MaltaFPGAState; #define TYPE_MIPS_MALTA "mips-malta" @@ -124,8 +125,10 @@ static void malta_fpga_update_display(void *opaque) } leds_text[8] = '\0'; - qemu_chr_fe_printf(s->display, "\e[H\n\n|\e[32m%-8.8s\e[00m|\r\n", leds_text); - qemu_chr_fe_printf(s->display, "\n\n\n\n|\e[31m%-8.8s\e[00m|", s->display_text); + qemu_chr_fe_printf(&s->display, "\e[H\n\n|\e[32m%-8.8s\e[00m|\r\n", + leds_text); + qemu_chr_fe_printf(&s->display, "\n\n\n\n|\e[31m%-8.8s\e[00m|", + s->display_text); } /* @@ -530,23 +533,29 @@ static void malta_fpga_reset(void *opaque) snprintf(s->display_text, 9, " "); } -static void malta_fpga_led_init(CharDriverState *chr) +static void malta_fgpa_display_event(void *opaque, int event) { - qemu_chr_fe_printf(chr, "\e[HMalta LEDBAR\r\n"); - qemu_chr_fe_printf(chr, "+--------+\r\n"); - qemu_chr_fe_printf(chr, "+ +\r\n"); - qemu_chr_fe_printf(chr, "+--------+\r\n"); - qemu_chr_fe_printf(chr, "\n"); - qemu_chr_fe_printf(chr, "Malta ASCII\r\n"); - qemu_chr_fe_printf(chr, "+--------+\r\n"); - qemu_chr_fe_printf(chr, "+ +\r\n"); - qemu_chr_fe_printf(chr, "+--------+\r\n"); + MaltaFPGAState *s = opaque; + + if (event == CHR_EVENT_OPENED && !s->display_inited) { + qemu_chr_fe_printf(&s->display, "\e[HMalta LEDBAR\r\n"); + qemu_chr_fe_printf(&s->display, "+--------+\r\n"); + qemu_chr_fe_printf(&s->display, "+ +\r\n"); + qemu_chr_fe_printf(&s->display, "+--------+\r\n"); + qemu_chr_fe_printf(&s->display, "\n"); + qemu_chr_fe_printf(&s->display, "Malta ASCII\r\n"); + qemu_chr_fe_printf(&s->display, "+--------+\r\n"); + qemu_chr_fe_printf(&s->display, "+ +\r\n"); + qemu_chr_fe_printf(&s->display, "+--------+\r\n"); + s->display_inited = true; + } } static MaltaFPGAState *malta_fpga_init(MemoryRegion *address_space, hwaddr base, qemu_irq uart_irq, CharDriverState *uart_chr) { MaltaFPGAState *s; + CharDriverState *chr; s = (MaltaFPGAState *)g_malloc0(sizeof(MaltaFPGAState)); @@ -560,7 +569,10 @@ static MaltaFPGAState *malta_fpga_init(MemoryRegion *address_space, memory_region_add_subregion(address_space, base, &s->iomem_lo); memory_region_add_subregion(address_space, base + 0xa00, &s->iomem_hi); - s->display = qemu_chr_new("fpga", "vc:320x200", malta_fpga_led_init); + chr = qemu_chr_new("fpga", "vc:320x200"); + qemu_chr_fe_init(&s->display, chr, NULL); + qemu_chr_fe_set_handlers(&s->display, NULL, NULL, + malta_fgpa_display_event, s, NULL, true); s->uart = serial_mm_init(address_space, base + 0x900, 3, uart_irq, 230400, uart_chr, DEVICE_NATIVE_ENDIAN); @@ -1025,7 +1037,7 @@ void mips_malta_init(MachineState *machine) if (!serial_hds[i]) { char label[32]; snprintf(label, sizeof(label), "serial%d", i); - serial_hds[i] = qemu_chr_new(label, "null", NULL); + serial_hds[i] = qemu_chr_new(label, "null"); } } @@ -1215,7 +1227,7 @@ void mips_malta_init(MachineState *machine) isa_create_simple(isa_bus, "i8042"); rtc_init(isa_bus, 2000, NULL); - serial_hds_isa_init(isa_bus, 2); + serial_hds_isa_init(isa_bus, 0, 2); parallel_hds_isa_init(isa_bus, 1); for(i = 0; i < MAX_FD; i++) { diff --git a/hw/mips/mips_r4k.c b/hw/mips/mips_r4k.c index 16a59c7..27548c4 100644 --- a/hw/mips/mips_r4k.c +++ b/hw/mips/mips_r4k.c @@ -286,7 +286,7 @@ void mips_r4k_init(MachineState *machine) pit = pit_init(isa_bus, 0x40, 0, NULL); - serial_hds_isa_init(isa_bus, MAX_SERIAL_PORTS); + serial_hds_isa_init(isa_bus, 0, MAX_SERIAL_PORTS); isa_vga_init(isa_bus); diff --git a/hw/misc/ivshmem.c b/hw/misc/ivshmem.c index f803dfd..230e51b 100644 --- a/hw/misc/ivshmem.c +++ b/hw/misc/ivshmem.c @@ -88,7 +88,7 @@ typedef struct IVShmemState { /* exactly one of these two may be set */ HostMemoryBackend *hostmem; /* with interrupts */ - CharDriverState *server_chr; /* without interrupts */ + CharBackend server_chr; /* without interrupts */ /* registers */ uint32_t intrmask; @@ -627,7 +627,7 @@ static void ivshmem_read(void *opaque, const uint8_t *buf, int size) msg = le64_to_cpu(s->msg_buf); s->msg_buffered_bytes = 0; - fd = qemu_chr_fe_get_msgfd(s->server_chr); + fd = qemu_chr_fe_get_msgfd(&s->server_chr); process_msg(s, msg, fd, &err); if (err) { @@ -642,8 +642,8 @@ static int64_t ivshmem_recv_msg(IVShmemState *s, int *pfd, Error **errp) n = 0; do { - ret = qemu_chr_fe_read_all(s->server_chr, (uint8_t *)&msg + n, - sizeof(msg) - n); + ret = qemu_chr_fe_read_all(&s->server_chr, (uint8_t *)&msg + n, + sizeof(msg) - n); if (ret < 0 && ret != -EINTR) { error_setg_errno(errp, -ret, "read from server failed"); return INT64_MIN; @@ -651,7 +651,7 @@ static int64_t ivshmem_recv_msg(IVShmemState *s, int *pfd, Error **errp) n += ret; } while (n < sizeof(msg)); - *pfd = qemu_chr_fe_get_msgfd(s->server_chr); + *pfd = qemu_chr_fe_get_msgfd(&s->server_chr); return msg; } @@ -868,10 +868,11 @@ static void ivshmem_common_realize(PCIDevice *dev, Error **errp) s->ivshmem_bar2 = host_memory_backend_get_memory(s->hostmem, &error_abort); } else { - assert(s->server_chr); + CharDriverState *chr = qemu_chr_fe_get_driver(&s->server_chr); + assert(chr); IVSHMEM_DPRINTF("using shared memory server (socket = %s)\n", - s->server_chr->filename); + chr->filename); /* we allocate enough space for 16 peers and grow as needed */ resize_peers(s, 16); @@ -893,8 +894,8 @@ static void ivshmem_common_realize(PCIDevice *dev, Error **errp) return; } - qemu_chr_add_handlers(s->server_chr, ivshmem_can_receive, - ivshmem_read, NULL, s); + qemu_chr_fe_set_handlers(&s->server_chr, ivshmem_can_receive, + ivshmem_read, NULL, s, NULL, true); if (ivshmem_setup_interrupts(s) < 0) { error_setg(errp, "failed to initialize interrupts"); @@ -1121,7 +1122,7 @@ static void ivshmem_doorbell_realize(PCIDevice *dev, Error **errp) { IVShmemState *s = IVSHMEM_COMMON(dev); - if (!s->server_chr) { + if (!qemu_chr_fe_get_driver(&s->server_chr)) { error_setg(errp, "You must specify a 'chardev'"); return; } @@ -1250,7 +1251,7 @@ static void ivshmem_realize(PCIDevice *dev, Error **errp) " or ivshmem-doorbell instead"); } - if (!!s->server_chr + !!s->shmobj != 1) { + if (!!qemu_chr_fe_get_driver(&s->server_chr) + !!s->shmobj != 1) { error_setg(errp, "You must specify either 'shm' or 'chardev'"); return; } diff --git a/hw/net/e1000e_core.c b/hw/net/e1000e_core.c index 6505983..2b11499 100644 --- a/hw/net/e1000e_core.c +++ b/hw/net/e1000e_core.c @@ -1278,11 +1278,10 @@ e1000e_write_lgcy_rx_descr(E1000ECore *core, uint8_t *desc, struct e1000_rx_desc *d = (struct e1000_rx_desc *) desc; - memset(d, 0, sizeof(*d)); - assert(!rss_info->enabled); d->length = cpu_to_le16(length); + d->csum = 0; e1000e_build_rx_metadata(core, pkt, pkt != NULL, rss_info, @@ -1291,6 +1290,7 @@ e1000e_write_lgcy_rx_descr(E1000ECore *core, uint8_t *desc, &d->special); d->errors = (uint8_t) (le32_to_cpu(status_flags) >> 24); d->status = (uint8_t) le32_to_cpu(status_flags); + d->special = 0; } static inline void @@ -1301,7 +1301,7 @@ e1000e_write_ext_rx_descr(E1000ECore *core, uint8_t *desc, { union e1000_rx_desc_extended *d = (union e1000_rx_desc_extended *) desc; - memset(d, 0, sizeof(*d)); + memset(&d->wb, 0, sizeof(d->wb)); d->wb.upper.length = cpu_to_le16(length); @@ -1325,7 +1325,7 @@ e1000e_write_ps_rx_descr(E1000ECore *core, uint8_t *desc, union e1000_rx_desc_packet_split *d = (union e1000_rx_desc_packet_split *) desc; - memset(d, 0, sizeof(*d)); + memset(&d->wb, 0, sizeof(d->wb)); d->wb.middle.length0 = cpu_to_le16((*written)[0]); diff --git a/hw/net/eepro100.c b/hw/net/eepro100.c index bab4dbf..4bf71f2d 100644 --- a/hw/net/eepro100.c +++ b/hw/net/eepro100.c @@ -1843,6 +1843,7 @@ static void pci_nic_uninit(PCIDevice *pci_dev) EEPRO100State *s = DO_UPCAST(EEPRO100State, dev, pci_dev); vmstate_unregister(&pci_dev->qdev, s->vmstate, s); + g_free(s->vmstate); eeprom93xx_free(&pci_dev->qdev, s->eeprom); qemu_del_nic(s->nic); } diff --git a/hw/net/pcnet.c b/hw/net/pcnet.c index 198a01f..6544553 100644 --- a/hw/net/pcnet.c +++ b/hw/net/pcnet.c @@ -302,7 +302,7 @@ static inline void pcnet_tmd_load(PCNetState *s, struct pcnet_TMD *tmd, uint32_t tbadr; int16_t length; int16_t status; - } xda; + } xda; s->phys_mem_read(s->dma_opaque, addr, (void *)&xda, sizeof(xda), 0); tmd->tbadr = le32_to_cpu(xda.tbadr) & 0xffffff; tmd->length = le16_to_cpu(xda.length); @@ -664,7 +664,9 @@ static inline int ladr_match(PCNetState *s, const uint8_t *buf, int size) static inline hwaddr pcnet_rdra_addr(PCNetState *s, int idx) { - while (idx < 1) idx += CSR_RCVRL(s); + while (idx < 1) { + idx += CSR_RCVRL(s); + } return s->rdra + ((CSR_RCVRL(s) - idx) * (BCR_SWSTYLE(s) ? 16 : 8)); } @@ -672,8 +674,10 @@ static inline int64_t pcnet_get_next_poll_time(PCNetState *s, int64_t current_ti { int64_t next_time = current_time + (65536 - (CSR_SPND(s) ? 0 : CSR_POLL(s))) * 30; - if (next_time <= current_time) + + if (next_time <= current_time) { next_time = current_time + 1; + } return next_time; } @@ -795,13 +799,13 @@ static void pcnet_init(PCNetState *s) mode = le16_to_cpu(initblk.mode); rlen = initblk.rlen >> 4; tlen = initblk.tlen >> 4; - ladrf[0] = le16_to_cpu(initblk.ladrf[0]); - ladrf[1] = le16_to_cpu(initblk.ladrf[1]); - ladrf[2] = le16_to_cpu(initblk.ladrf[2]); - ladrf[3] = le16_to_cpu(initblk.ladrf[3]); - padr[0] = le16_to_cpu(initblk.padr[0]); - padr[1] = le16_to_cpu(initblk.padr[1]); - padr[2] = le16_to_cpu(initblk.padr[2]); + ladrf[0] = le16_to_cpu(initblk.ladrf[0]); + ladrf[1] = le16_to_cpu(initblk.ladrf[1]); + ladrf[2] = le16_to_cpu(initblk.ladrf[2]); + ladrf[3] = le16_to_cpu(initblk.ladrf[3]); + padr[0] = le16_to_cpu(initblk.padr[0]); + padr[1] = le16_to_cpu(initblk.padr[1]); + padr[2] = le16_to_cpu(initblk.padr[2]); rdra = le32_to_cpu(initblk.rdra); tdra = le32_to_cpu(initblk.tdra); } else { @@ -809,13 +813,13 @@ static void pcnet_init(PCNetState *s) s->phys_mem_read(s->dma_opaque, PHYSADDR(s,CSR_IADR(s)), (uint8_t *)&initblk, sizeof(initblk), 0); mode = le16_to_cpu(initblk.mode); - ladrf[0] = le16_to_cpu(initblk.ladrf[0]); - ladrf[1] = le16_to_cpu(initblk.ladrf[1]); - ladrf[2] = le16_to_cpu(initblk.ladrf[2]); - ladrf[3] = le16_to_cpu(initblk.ladrf[3]); - padr[0] = le16_to_cpu(initblk.padr[0]); - padr[1] = le16_to_cpu(initblk.padr[1]); - padr[2] = le16_to_cpu(initblk.padr[2]); + ladrf[0] = le16_to_cpu(initblk.ladrf[0]); + ladrf[1] = le16_to_cpu(initblk.ladrf[1]); + ladrf[2] = le16_to_cpu(initblk.ladrf[2]); + ladrf[3] = le16_to_cpu(initblk.ladrf[3]); + padr[0] = le16_to_cpu(initblk.padr[0]); + padr[1] = le16_to_cpu(initblk.padr[1]); + padr[2] = le16_to_cpu(initblk.padr[2]); rdra = le32_to_cpu(initblk.rdra); tdra = le32_to_cpu(initblk.tdra); rlen = rdra >> 29; @@ -858,12 +862,12 @@ static void pcnet_start(PCNetState *s) printf("pcnet_start\n"); #endif - if (!CSR_DTX(s)) + if (!CSR_DTX(s)) { s->csr[0] |= 0x0010; /* set TXON */ - - if (!CSR_DRX(s)) + } + if (!CSR_DRX(s)) { s->csr[0] |= 0x0020; /* set RXON */ - + } s->csr[0] &= ~0x0004; /* clear STOP bit */ s->csr[0] |= 0x0002; pcnet_poll_timer(s); @@ -925,8 +929,7 @@ static void pcnet_rdte_poll(PCNetState *s) crda); } } else { - printf("pcnet: BAD RMD RDA=0x" TARGET_FMT_plx "\n", - crda); + printf("pcnet: BAD RMD RDA=0x" TARGET_FMT_plx "\n", crda); #endif } } @@ -1168,10 +1171,11 @@ ssize_t pcnet_receive(NetClientState *nc, const uint8_t *buf, size_t size_) #endif while (pktcount--) { - if (CSR_RCVRC(s) <= 1) + if (CSR_RCVRC(s) <= 1) { CSR_RCVRC(s) = CSR_RCVRL(s); - else + } else { CSR_RCVRC(s)--; + } } pcnet_rdte_poll(s); @@ -1207,7 +1211,7 @@ static void pcnet_transmit(PCNetState *s) s->tx_busy = 1; - txagain: +txagain: if (pcnet_tdte_poll(s)) { struct pcnet_TMD tmd; @@ -1251,7 +1255,7 @@ static void pcnet_transmit(PCNetState *s) s->phys_mem_read(s->dma_opaque, PHYSADDR(s, tmd.tbadr), s->buffer + s->xmit_pos, bcnt, CSR_BSWP(s)); s->xmit_pos += bcnt; - + if (!GET_FIELD(tmd.status, TMDS, ENP)) { goto txdone; } @@ -1276,21 +1280,22 @@ static void pcnet_transmit(PCNetState *s) s->csr[4] |= 0x0004; /* set TXSTRT */ s->xmit_pos = -1; - txdone: +txdone: SET_FIELD(&tmd.status, TMDS, OWN, 0); TMDSTORE(&tmd, PHYSADDR(s,CSR_CXDA(s))); - if (!CSR_TOKINTD(s) || (CSR_LTINTEN(s) && GET_FIELD(tmd.status, TMDS, LTINT))) + if (!CSR_TOKINTD(s) + || (CSR_LTINTEN(s) && GET_FIELD(tmd.status, TMDS, LTINT))) { s->csr[0] |= 0x0200; /* set TINT */ - - if (CSR_XMTRC(s)<=1) + } + if (CSR_XMTRC(s) <= 1) { CSR_XMTRC(s) = CSR_XMTRL(s); - else + } else { CSR_XMTRC(s)--; - if (count--) + } + if (count--) { goto txagain; - - } else - if (s->xmit_pos >= 0) { + } + } else if (s->xmit_pos >= 0) { struct pcnet_TMD tmd; TMDLOAD(&tmd, xmit_cxda); SET_FIELD(&tmd.misc, TMDM, BUFF, 1); @@ -1301,9 +1306,9 @@ static void pcnet_transmit(PCNetState *s) s->csr[0] |= 0x0200; /* set TINT */ if (!CSR_DXSUFLO(s)) { s->csr[0] &= ~0x0010; - } else - if (count--) - goto txagain; + } else if (count--) { + goto txagain; + } } s->tx_busy = 0; @@ -1315,13 +1320,11 @@ static void pcnet_poll(PCNetState *s) pcnet_rdte_poll(s); } - if (CSR_TDMD(s) || - (CSR_TXON(s) && !CSR_DPOLL(s) && pcnet_tdte_poll(s))) - { + if (CSR_TDMD(s) || (CSR_TXON(s) && !CSR_DPOLL(s) && pcnet_tdte_poll(s))) { /* prevent recursion */ - if (s->tx_busy) + if (s->tx_busy) { return; - + } pcnet_transmit(s); } } @@ -1340,15 +1343,16 @@ static void pcnet_poll_timer(void *opaque) if (!CSR_STOP(s) && !CSR_SPND(s) && !CSR_DPOLL(s)) { uint64_t now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) * 33; - if (!s->timer || !now) + if (!s->timer || !now) { s->timer = now; - else { + } else { uint64_t t = now - s->timer + CSR_POLL(s); if (t > 0xffffLL) { pcnet_poll(s); CSR_POLL(s) = CSR_PINT(s); - } else + } else { CSR_POLL(s) = t; + } } timer_mod(s->poll_timer, pcnet_get_next_poll_time(s,qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL))); @@ -1371,21 +1375,21 @@ static void pcnet_csr_writew(PCNetState *s, uint32_t rap, uint32_t new_value) val = (val & 0x007f) | (s->csr[0] & 0x7f00); /* IFF STOP, STRT and INIT are set, clear STRT and INIT */ - if ((val&7) == 7) - val &= ~3; - - if (!CSR_STOP(s) && (val & 4)) + if ((val & 7) == 7) { + val &= ~3; + } + if (!CSR_STOP(s) && (val & 4)) { pcnet_stop(s); - - if (!CSR_INIT(s) && (val & 1)) + } + if (!CSR_INIT(s) && (val & 1)) { pcnet_init(s); - - if (!CSR_STRT(s) && (val & 2)) + } + if (!CSR_STRT(s) && (val & 2)) { pcnet_start(s); - - if (CSR_TDMD(s)) + } + if (CSR_TDMD(s)) { pcnet_transmit(s); - + } return; case 1: case 2: @@ -1429,12 +1433,16 @@ static void pcnet_csr_writew(PCNetState *s, uint32_t rap, uint32_t new_value) case 47: /* POLLINT */ case 72: case 74: + break; case 76: /* RCVRL */ case 78: /* XMTRL */ + val = (val > 0) ? val : 512; + break; case 112: - if (CSR_STOP(s) || CSR_SPND(s)) - break; - return; + if (CSR_STOP(s) || CSR_SPND(s)) { + break; + } + return; case 3: break; case 4: @@ -1651,8 +1659,7 @@ void pcnet_ioport_writel(void *opaque, uint32_t addr, uint32_t val) pcnet_bcr_writew(s, s->rap, val & 0xffff); break; } - } else - if ((addr & 0x0f) == 0) { + } else if ((addr & 0x0f) == 0) { /* switch device to dword i/o mode */ pcnet_bcr_writew(s, BCR_BSBC, pcnet_bcr_readw(s, BCR_BSBC) | 0x0080); #ifdef PCNET_DEBUG_IO diff --git a/hw/net/rocker/rocker.c b/hw/net/rocker/rocker.c index 30f2ce4..e9d215a 100644 --- a/hw/net/rocker/rocker.c +++ b/hw/net/rocker/rocker.c @@ -860,7 +860,7 @@ static void rocker_io_writel(void *opaque, hwaddr addr, uint32_t val) rocker_msix_irq(r, val); break; case ROCKER_TEST_DMA_SIZE: - r->test_dma_size = val; + r->test_dma_size = val & 0xFFFF; break; case ROCKER_TEST_DMA_ADDR + 4: r->test_dma_addr = ((uint64_t)val) << 32 | r->lower32; diff --git a/hw/net/rtl8139.c b/hw/net/rtl8139.c index 3345bc6..f05e59c 100644 --- a/hw/net/rtl8139.c +++ b/hw/net/rtl8139.c @@ -2350,7 +2350,7 @@ static void rtl8139_cplus_transmit(RTL8139State *s) { int txcount = 0; - while (rtl8139_cplus_transmit_one(s)) + while (txcount < 64 && rtl8139_cplus_transmit_one(s)) { ++txcount; } diff --git a/hw/net/vmxnet3.c b/hw/net/vmxnet3.c index 90f6943..92f6af9 100644 --- a/hw/net/vmxnet3.c +++ b/hw/net/vmxnet3.c @@ -531,6 +531,7 @@ static void vmxnet3_complete_packet(VMXNET3State *s, int qidx, uint32_t tx_ridx) VMXNET3_RING_DUMP(VMW_RIPRN, "TXC", qidx, &s->txq_descr[qidx].comp_ring); + memset(&txcq_descr, 0, sizeof(txcq_descr)); txcq_descr.txdIdx = tx_ridx; txcq_descr.gen = vmxnet3_ring_curr_gen(&s->txq_descr[qidx].comp_ring); diff --git a/hw/ppc/spapr.c b/hw/ppc/spapr.c index ddb7438..486f57d 100644 --- a/hw/ppc/spapr.c +++ b/hw/ppc/spapr.c @@ -2438,7 +2438,7 @@ static void spapr_machine_class_init(ObjectClass *oc, void *data) mc->init = ppc_spapr_init; mc->reset = ppc_spapr_reset; mc->block_default_type = IF_SCSI; - mc->max_cpus = MAX_CPUMASK_BITS; + mc->max_cpus = 255; mc->no_parallel = 1; mc->default_boot_order = ""; mc->default_ram_size = 512 * M_BYTE; diff --git a/hw/scsi/scsi-disk.c b/hw/scsi/scsi-disk.c index 88beaf4..a963191 100644 --- a/hw/scsi/scsi-disk.c +++ b/hw/scsi/scsi-disk.c @@ -341,6 +341,7 @@ static void scsi_do_read(SCSIDiskReq *r, int ret) r->req.resid -= r->req.sg->size; r->req.aiocb = dma_blk_io(blk_get_aio_context(s->qdev.conf.blk), r->req.sg, r->sector << BDRV_SECTOR_BITS, + BDRV_SECTOR_SIZE, sdc->dma_readv, r, scsi_dma_complete, r, DMA_DIRECTION_FROM_DEVICE); } else { @@ -539,6 +540,7 @@ static void scsi_write_data(SCSIRequest *req) r->req.resid -= r->req.sg->size; r->req.aiocb = dma_blk_io(blk_get_aio_context(s->qdev.conf.blk), r->req.sg, r->sector << BDRV_SECTOR_BITS, + BDRV_SECTOR_SIZE, sdc->dma_writev, r, scsi_dma_complete, r, DMA_DIRECTION_TO_DEVICE); } else { diff --git a/hw/sparc64/sun4u.c b/hw/sparc64/sun4u.c index 3165e18..7b8134e 100644 --- a/hw/sparc64/sun4u.c +++ b/hw/sparc64/sun4u.c @@ -824,7 +824,7 @@ static void sun4uv_init(MemoryRegion *address_space_mem, i++; } - serial_hds_isa_init(isa_bus, MAX_SERIAL_PORTS); + serial_hds_isa_init(isa_bus, i, MAX_SERIAL_PORTS); parallel_hds_isa_init(isa_bus, MAX_PARALLEL_PORTS); for(i = 0; i < nb_nics; i++) diff --git a/hw/ssi/xilinx_spips.c b/hw/ssi/xilinx_spips.c index e2b77dc..da8adfa 100644 --- a/hw/ssi/xilinx_spips.c +++ b/hw/ssi/xilinx_spips.c @@ -607,6 +607,7 @@ static void xilinx_spips_realize(DeviceState *dev, Error **errp) XilinxSPIPS *s = XILINX_SPIPS(dev); SysBusDevice *sbd = SYS_BUS_DEVICE(dev); XilinxSPIPSClass *xsc = XILINX_SPIPS_GET_CLASS(s); + qemu_irq *cs; int i; DB_PRINT_L(0, "realized spips\n"); @@ -619,8 +620,10 @@ static void xilinx_spips_realize(DeviceState *dev, Error **errp) } s->cs_lines = g_new0(qemu_irq, s->num_cs * s->num_busses); - ssi_auto_connect_slaves(DEVICE(s), s->cs_lines, s->spi[0]); - ssi_auto_connect_slaves(DEVICE(s), s->cs_lines, s->spi[1]); + for (i = 0, cs = s->cs_lines; i < s->num_busses; ++i, cs += s->num_cs) { + ssi_auto_connect_slaves(DEVICE(s), cs, s->spi[i]); + } + sysbus_init_irq(sbd, &s->irq); for (i = 0; i < s->num_cs * s->num_busses; ++i) { sysbus_init_irq(sbd, &s->cs_lines[i]); diff --git a/hw/timer/a9gtimer.c b/hw/timer/a9gtimer.c index 772f85f..ce1dc63 100644 --- a/hw/timer/a9gtimer.c +++ b/hw/timer/a9gtimer.c @@ -82,15 +82,15 @@ static void a9_gtimer_update(A9GTimerState *s, bool sync) if ((s->control & R_CONTROL_TIMER_ENABLE) && (gtb->control & R_CONTROL_COMP_ENABLE)) { /* R2p0+, where the compare function is >= */ - while (gtb->compare < update.new) { + if (gtb->compare < update.new) { DB_PRINT("Compare event happened for CPU %d\n", i); gtb->status = 1; - if (gtb->control & R_CONTROL_AUTO_INCREMENT) { - DB_PRINT("Auto incrementing timer compare by %" PRId32 "\n", - gtb->inc); - gtb->compare += gtb->inc; - } else { - break; + if (gtb->control & R_CONTROL_AUTO_INCREMENT && gtb->inc) { + uint64_t inc = + QEMU_ALIGN_UP(update.new - gtb->compare, gtb->inc); + DB_PRINT("Auto incrementing timer compare by %" + PRId64 "\n", inc); + gtb->compare += inc; } } cdiff = (int64_t)gtb->compare - (int64_t)update.new + 1; diff --git a/hw/timer/arm_mptimer.c b/hw/timer/arm_mptimer.c index d66bbf0..daf6c48 100644 --- a/hw/timer/arm_mptimer.c +++ b/hw/timer/arm_mptimer.c @@ -20,22 +20,33 @@ */ #include "qemu/osdep.h" +#include "hw/ptimer.h" #include "hw/timer/arm_mptimer.h" #include "qapi/error.h" -#include "qemu/timer.h" +#include "qemu/main-loop.h" #include "qom/cpu.h" +#define PTIMER_POLICY \ + (PTIMER_POLICY_WRAP_AFTER_ONE_PERIOD | \ + PTIMER_POLICY_CONTINUOUS_TRIGGER | \ + PTIMER_POLICY_NO_IMMEDIATE_TRIGGER | \ + PTIMER_POLICY_NO_IMMEDIATE_RELOAD | \ + PTIMER_POLICY_NO_COUNTER_ROUND_DOWN) + /* This device implements the per-cpu private timer and watchdog block * which is used in both the ARM11MPCore and Cortex-A9MP. */ static inline int get_current_cpu(ARMMPTimerState *s) { - if (current_cpu->cpu_index >= s->num_cpu) { + int cpu_id = current_cpu ? current_cpu->cpu_index : 0; + + if (cpu_id >= s->num_cpu) { hw_error("arm_mptimer: num-cpu %d but this cpu is %d!\n", - s->num_cpu, current_cpu->cpu_index); + s->num_cpu, cpu_id); } - return current_cpu->cpu_index; + + return cpu_id; } static inline void timerblock_update_irq(TimerBlock *tb) @@ -44,33 +55,42 @@ static inline void timerblock_update_irq(TimerBlock *tb) } /* Return conversion factor from mpcore timer ticks to qemu timer ticks. */ -static inline uint32_t timerblock_scale(TimerBlock *tb) +static inline uint32_t timerblock_scale(uint32_t control) { - return (((tb->control >> 8) & 0xff) + 1) * 10; + return (((control >> 8) & 0xff) + 1) * 10; } -static void timerblock_reload(TimerBlock *tb, int restart) +static inline void timerblock_set_count(struct ptimer_state *timer, + uint32_t control, uint64_t *count) { - if (tb->count == 0) { - return; + /* PTimer would trigger interrupt for periodic timer when counter set + * to 0, MPtimer under certain condition only. + */ + if ((control & 3) == 3 && (control & 0xff00) == 0 && *count == 0) { + *count = ptimer_get_limit(timer); } - if (restart) { - tb->tick = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); + ptimer_set_count(timer, *count); +} + +static inline void timerblock_run(struct ptimer_state *timer, + uint32_t control, uint32_t load) +{ + if ((control & 1) && ((control & 0xff00) || load != 0)) { + ptimer_run(timer, !(control & 2)); } - tb->tick += (int64_t)tb->count * timerblock_scale(tb); - timer_mod(tb->timer, tb->tick); } static void timerblock_tick(void *opaque) { TimerBlock *tb = (TimerBlock *)opaque; - tb->status = 1; - if (tb->control & 2) { - tb->count = tb->load; - timerblock_reload(tb, 0); - } else { - tb->count = 0; + /* Periodic timer with load = 0 and prescaler != 0 would re-trigger + * IRQ after one period, otherwise it either stops or wraps around. + */ + if ((tb->control & 2) && (tb->control & 0xff00) == 0 && + ptimer_get_limit(tb->timer) == 0) { + ptimer_stop(tb->timer); } + tb->status = 1; timerblock_update_irq(tb); } @@ -78,21 +98,11 @@ static uint64_t timerblock_read(void *opaque, hwaddr addr, unsigned size) { TimerBlock *tb = (TimerBlock *)opaque; - int64_t val; switch (addr) { case 0: /* Load */ - return tb->load; + return ptimer_get_limit(tb->timer); case 4: /* Counter. */ - if (((tb->control & 1) == 0) || (tb->count == 0)) { - return 0; - } - /* Slow and ugly, but hopefully won't happen too often. */ - val = tb->tick - qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); - val /= timerblock_scale(tb); - if (val < 0) { - val = 0; - } - return val; + return ptimer_get_count(tb->timer); case 8: /* Control. */ return tb->control; case 12: /* Interrupt status. */ @@ -106,37 +116,45 @@ static void timerblock_write(void *opaque, hwaddr addr, uint64_t value, unsigned size) { TimerBlock *tb = (TimerBlock *)opaque; - int64_t old; + uint32_t control = tb->control; switch (addr) { case 0: /* Load */ - tb->load = value; - /* Fall through. */ - case 4: /* Counter. */ - if ((tb->control & 1) && tb->count) { - /* Cancel the previous timer. */ - timer_del(tb->timer); + /* Setting load to 0 stops the timer without doing the tick if + * prescaler = 0. + */ + if ((control & 1) && (control & 0xff00) == 0 && value == 0) { + ptimer_stop(tb->timer); } - tb->count = value; - if (tb->control & 1) { - timerblock_reload(tb, 1); + ptimer_set_limit(tb->timer, value, 1); + timerblock_run(tb->timer, control, value); + break; + case 4: /* Counter. */ + /* Setting counter to 0 stops the one-shot timer, or periodic with + * load = 0, without doing the tick if prescaler = 0. + */ + if ((control & 1) && (control & 0xff00) == 0 && value == 0 && + (!(control & 2) || ptimer_get_limit(tb->timer) == 0)) { + ptimer_stop(tb->timer); } + timerblock_set_count(tb->timer, control, &value); + timerblock_run(tb->timer, control, value); break; case 8: /* Control. */ - old = tb->control; - tb->control = value; + if ((control & 3) != (value & 3)) { + ptimer_stop(tb->timer); + } + if ((control & 0xff00) != (value & 0xff00)) { + ptimer_set_period(tb->timer, timerblock_scale(value)); + } if (value & 1) { - if ((old & 1) && (tb->count != 0)) { - /* Do nothing if timer is ticking right now. */ - break; + uint64_t count = ptimer_get_count(tb->timer); + /* Re-load periodic timer counter if needed. */ + if ((value & 2) && count == 0) { + timerblock_set_count(tb->timer, value, &count); } - if (tb->control & 2) { - tb->count = tb->load; - } - timerblock_reload(tb, 1); - } else if (old & 1) { - /* Shutdown the timer. */ - timer_del(tb->timer); + timerblock_run(tb->timer, value, count); } + tb->control = value; break; case 12: /* Interrupt status. */ tb->status &= ~value; @@ -186,13 +204,12 @@ static const MemoryRegionOps timerblock_ops = { static void timerblock_reset(TimerBlock *tb) { - tb->count = 0; - tb->load = 0; tb->control = 0; tb->status = 0; - tb->tick = 0; if (tb->timer) { - timer_del(tb->timer); + ptimer_stop(tb->timer); + ptimer_set_limit(tb->timer, 0, 1); + ptimer_set_period(tb->timer, timerblock_scale(0)); } } @@ -238,7 +255,8 @@ static void arm_mptimer_realize(DeviceState *dev, Error **errp) */ for (i = 0; i < s->num_cpu; i++) { TimerBlock *tb = &s->timerblock[i]; - tb->timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, timerblock_tick, tb); + QEMUBH *bh = qemu_bh_new(timerblock_tick, tb); + tb->timer = ptimer_init(bh, PTIMER_POLICY); sysbus_init_irq(sbd, &tb->irq); memory_region_init_io(&tb->iomem, OBJECT(s), &timerblock_ops, tb, "arm_mptimer_timerblock", 0x20); @@ -248,26 +266,23 @@ static void arm_mptimer_realize(DeviceState *dev, Error **errp) static const VMStateDescription vmstate_timerblock = { .name = "arm_mptimer_timerblock", - .version_id = 2, - .minimum_version_id = 2, + .version_id = 3, + .minimum_version_id = 3, .fields = (VMStateField[]) { - VMSTATE_UINT32(count, TimerBlock), - VMSTATE_UINT32(load, TimerBlock), VMSTATE_UINT32(control, TimerBlock), VMSTATE_UINT32(status, TimerBlock), - VMSTATE_INT64(tick, TimerBlock), - VMSTATE_TIMER_PTR(timer, TimerBlock), + VMSTATE_PTIMER(timer, TimerBlock), VMSTATE_END_OF_LIST() } }; static const VMStateDescription vmstate_arm_mptimer = { .name = "arm_mptimer", - .version_id = 2, - .minimum_version_id = 2, + .version_id = 3, + .minimum_version_id = 3, .fields = (VMStateField[]) { VMSTATE_STRUCT_VARRAY_UINT32(timerblock, ARMMPTimerState, num_cpu, - 2, vmstate_timerblock, TimerBlock), + 3, vmstate_timerblock, TimerBlock), VMSTATE_END_OF_LIST() } }; diff --git a/hw/timer/stm32f2xx_timer.c b/hw/timer/stm32f2xx_timer.c index 8c4c1f9..e5f5e14 100644 --- a/hw/timer/stm32f2xx_timer.c +++ b/hw/timer/stm32f2xx_timer.c @@ -217,7 +217,7 @@ static void stm32f2xx_timer_write(void *opaque, hwaddr offset, return; case TIM_PSC: timer_val = stm32f2xx_ns_to_ticks(s, now) - s->tick_offset; - s->tim_psc = value; + s->tim_psc = value & 0xFFFF; value = timer_val; break; case TIM_CNT: diff --git a/hw/usb/ccid-card-passthru.c b/hw/usb/ccid-card-passthru.c index 2eacea7..325129a 100644 --- a/hw/usb/ccid-card-passthru.c +++ b/hw/usb/ccid-card-passthru.c @@ -48,7 +48,7 @@ typedef struct PassthruState PassthruState; struct PassthruState { CCIDCardState base; - CharDriverState *cs; + CharBackend cs; uint8_t vscard_in_data[VSCARD_IN_SIZE]; uint32_t vscard_in_pos; uint32_t vscard_in_hdr; @@ -77,9 +77,9 @@ static void ccid_card_vscard_send_msg(PassthruState *s, scr_msg_header.length = htonl(length); /* XXX this blocks entire thread. Rewrite to use * qemu_chr_fe_write and background I/O callbacks */ - qemu_chr_fe_write_all(s->cs, (uint8_t *)&scr_msg_header, + qemu_chr_fe_write_all(&s->cs, (uint8_t *)&scr_msg_header, sizeof(VSCMsgHeader)); - qemu_chr_fe_write_all(s->cs, payload, length); + qemu_chr_fe_write_all(&s->cs, payload, length); } static void ccid_card_vscard_send_apdu(PassthruState *s, @@ -264,7 +264,10 @@ static void ccid_card_vscard_handle_message(PassthruState *card, static void ccid_card_vscard_drop_connection(PassthruState *card) { - qemu_chr_delete(card->cs); + CharDriverState *chr = qemu_chr_fe_get_driver(&card->cs); + + qemu_chr_fe_deinit(&card->cs); + qemu_chr_delete(chr); card->vscard_in_pos = card->vscard_in_hdr = 0; } @@ -309,8 +312,6 @@ static void ccid_card_vscard_event(void *opaque, int event) case CHR_EVENT_BREAK: card->vscard_in_pos = card->vscard_in_hdr = 0; break; - case CHR_EVENT_FOCUS: - break; case CHR_EVENT_OPENED: DPRINTF(card, D_INFO, "%s: CHR_EVENT_OPENED\n", __func__); break; @@ -324,7 +325,7 @@ static void passthru_apdu_from_guest( { PassthruState *card = PASSTHRU_CCID_CARD(base); - if (!card->cs) { + if (!qemu_chr_fe_get_driver(&card->cs)) { printf("ccid-passthru: no chardev, discarding apdu length %d\n", len); return; } @@ -345,12 +346,12 @@ static int passthru_initfn(CCIDCardState *base) card->vscard_in_pos = 0; card->vscard_in_hdr = 0; - if (card->cs) { + if (qemu_chr_fe_get_driver(&card->cs)) { DPRINTF(card, D_INFO, "initing chardev\n"); - qemu_chr_add_handlers(card->cs, + qemu_chr_fe_set_handlers(&card->cs, ccid_card_vscard_can_read, ccid_card_vscard_read, - ccid_card_vscard_event, card); + ccid_card_vscard_event, card, NULL, true); ccid_card_vscard_send_init(card); } else { error_report("missing chardev"); diff --git a/hw/usb/dev-serial.c b/hw/usb/dev-serial.c index 966ad84..6066d9b 100644 --- a/hw/usb/dev-serial.c +++ b/hw/usb/dev-serial.c @@ -103,7 +103,7 @@ typedef struct { uint8_t event_trigger; QEMUSerialSetParams params; int latency; /* ms */ - CharDriverState *cs; + CharBackend cs; } USBSerialState; #define TYPE_USB_SERIAL "usb-serial-dev" @@ -209,8 +209,10 @@ static uint8_t usb_get_modem_lines(USBSerialState *s) int flags; uint8_t ret; - if (qemu_chr_fe_ioctl(s->cs, CHR_IOCTL_SERIAL_GET_TIOCM, &flags) == -ENOTSUP) + if (qemu_chr_fe_ioctl(&s->cs, + CHR_IOCTL_SERIAL_GET_TIOCM, &flags) == -ENOTSUP) { return FTDI_CTS|FTDI_DSR|FTDI_RLSD; + } ret = 0; if (flags & CHR_TIOCM_CTS) @@ -260,7 +262,7 @@ static void usb_serial_handle_control(USBDevice *dev, USBPacket *p, case DeviceOutVendor | FTDI_SET_MDM_CTRL: { static int flags; - qemu_chr_fe_ioctl(s->cs,CHR_IOCTL_SERIAL_GET_TIOCM, &flags); + qemu_chr_fe_ioctl(&s->cs, CHR_IOCTL_SERIAL_GET_TIOCM, &flags); if (value & FTDI_SET_RTS) { if (value & FTDI_RTS) flags |= CHR_TIOCM_RTS; @@ -273,7 +275,7 @@ static void usb_serial_handle_control(USBDevice *dev, USBPacket *p, else flags &= ~CHR_TIOCM_DTR; } - qemu_chr_fe_ioctl(s->cs,CHR_IOCTL_SERIAL_SET_TIOCM, &flags); + qemu_chr_fe_ioctl(&s->cs, CHR_IOCTL_SERIAL_SET_TIOCM, &flags); break; } case DeviceOutVendor | FTDI_SET_FLOW_CTRL: @@ -292,7 +294,7 @@ static void usb_serial_handle_control(USBDevice *dev, USBPacket *p, divisor = 1; s->params.speed = (48000000 / 2) / (8 * divisor + subdivisor8); - qemu_chr_fe_ioctl(s->cs, CHR_IOCTL_SERIAL_SET_PARAMS, &s->params); + qemu_chr_fe_ioctl(&s->cs, CHR_IOCTL_SERIAL_SET_PARAMS, &s->params); break; } case DeviceOutVendor | FTDI_SET_DATA: @@ -321,7 +323,7 @@ static void usb_serial_handle_control(USBDevice *dev, USBPacket *p, DPRINTF("unsupported stop bits %d\n", value & FTDI_STOP); goto fail; } - qemu_chr_fe_ioctl(s->cs, CHR_IOCTL_SERIAL_SET_PARAMS, &s->params); + qemu_chr_fe_ioctl(&s->cs, CHR_IOCTL_SERIAL_SET_PARAMS, &s->params); /* TODO: TX ON/OFF */ break; case DeviceInVendor | FTDI_GET_MDM_ST: @@ -368,7 +370,7 @@ static void usb_serial_handle_data(USBDevice *dev, USBPacket *p) iov = p->iov.iov + i; /* XXX this blocks entire thread. Rewrite to use * qemu_chr_fe_write and background I/O callbacks */ - qemu_chr_fe_write_all(s->cs, iov->iov_base, iov->iov_len); + qemu_chr_fe_write_all(&s->cs, iov->iov_base, iov->iov_len); } p->actual_length = p->iov.size; break; @@ -464,8 +466,6 @@ static void usb_serial_event(void *opaque, int event) case CHR_EVENT_BREAK: s->event_trigger |= FTDI_BI; break; - case CHR_EVENT_FOCUS: - break; case CHR_EVENT_OPENED: if (!s->dev.attached) { usb_device_attach(&s->dev, &error_abort); @@ -483,12 +483,13 @@ static void usb_serial_realize(USBDevice *dev, Error **errp) { USBSerialState *s = USB_SERIAL_DEV(dev); Error *local_err = NULL; + CharDriverState *chr = qemu_chr_fe_get_driver(&s->cs); usb_desc_create_serial(dev); usb_desc_init(dev); dev->auto_attach = 0; - if (!s->cs) { + if (!chr) { error_setg(errp, "Property chardev is required"); return; } @@ -499,11 +500,11 @@ static void usb_serial_realize(USBDevice *dev, Error **errp) return; } - qemu_chr_add_handlers(s->cs, usb_serial_can_read, usb_serial_read, - usb_serial_event, s); + qemu_chr_fe_set_handlers(&s->cs, usb_serial_can_read, usb_serial_read, + usb_serial_event, s, NULL, true); usb_serial_handle_reset(dev); - if (s->cs->be_open && !dev->attached) { + if (chr->be_open && !dev->attached) { usb_device_attach(dev, &error_abort); } } @@ -547,7 +548,7 @@ static USBDevice *usb_serial_init(USBBus *bus, const char *filename) filename++; snprintf(label, sizeof(label), "usbserial%d", index++); - cdrv = qemu_chr_new(label, filename, NULL); + cdrv = qemu_chr_new(label, filename); if (!cdrv) return NULL; @@ -565,7 +566,7 @@ static USBDevice *usb_braille_init(USBBus *bus, const char *unused) USBDevice *dev; CharDriverState *cdrv; - cdrv = qemu_chr_new("braille", "braille", NULL); + cdrv = qemu_chr_new("braille", "braille"); if (!cdrv) return NULL; diff --git a/hw/usb/redirect.c b/hw/usb/redirect.c index d4ca026..528081e 100644 --- a/hw/usb/redirect.c +++ b/hw/usb/redirect.c @@ -105,7 +105,7 @@ struct PacketIdQueue { struct USBRedirDevice { USBDevice dev; /* Properties */ - CharDriverState *cs; + CharBackend cs; uint8_t debug; char *filter_str; int32_t bootindex; @@ -283,9 +283,10 @@ static gboolean usbredir_write_unblocked(GIOChannel *chan, GIOCondition cond, static int usbredir_write(void *priv, uint8_t *data, int count) { USBRedirDevice *dev = priv; + CharDriverState *chr = qemu_chr_fe_get_driver(&dev->cs); int r; - if (!dev->cs->be_open) { + if (!chr->be_open) { return 0; } @@ -294,10 +295,10 @@ static int usbredir_write(void *priv, uint8_t *data, int count) return 0; } - r = qemu_chr_fe_write(dev->cs, data, count); + r = qemu_chr_fe_write(&dev->cs, data, count); if (r < count) { if (!dev->watch) { - dev->watch = qemu_chr_fe_add_watch(dev->cs, G_IO_OUT|G_IO_HUP, + dev->watch = qemu_chr_fe_add_watch(&dev->cs, G_IO_OUT | G_IO_HUP, usbredir_write_unblocked, dev); } if (r < 0) { @@ -1375,7 +1376,7 @@ static void usbredir_realize(USBDevice *udev, Error **errp) USBRedirDevice *dev = USB_REDIRECT(udev); int i; - if (dev->cs == NULL) { + if (!qemu_chr_fe_get_driver(&dev->cs)) { error_setg(errp, QERR_MISSING_PARAMETER, "chardev"); return; } @@ -1406,8 +1407,9 @@ static void usbredir_realize(USBDevice *udev, Error **errp) dev->compatible_speedmask = USB_SPEED_MASK_FULL | USB_SPEED_MASK_HIGH; /* Let the backend know we are ready */ - qemu_chr_add_handlers(dev->cs, usbredir_chardev_can_read, - usbredir_chardev_read, usbredir_chardev_event, dev); + qemu_chr_fe_set_handlers(&dev->cs, usbredir_chardev_can_read, + usbredir_chardev_read, usbredir_chardev_event, + dev, NULL, true); qemu_add_vm_change_state_handler(usbredir_vm_state_change, dev); } @@ -1426,9 +1428,11 @@ static void usbredir_cleanup_device_queues(USBRedirDevice *dev) static void usbredir_handle_destroy(USBDevice *udev) { USBRedirDevice *dev = USB_REDIRECT(udev); + CharDriverState *chr = qemu_chr_fe_get_driver(&dev->cs); + + qemu_chr_fe_deinit(&dev->cs); + qemu_chr_delete(chr); - qemu_chr_delete(dev->cs); - dev->cs = NULL; /* Note must be done after qemu_chr_close, as that causes a close event */ qemu_bh_delete(dev->chardev_close_bh); qemu_bh_delete(dev->device_reject_bh); diff --git a/hw/usb/xen-usb.c b/hw/usb/xen-usb.c index 174d715..de2ebd6 100644 --- a/hw/usb/xen-usb.c +++ b/hw/usb/xen-usb.c @@ -160,7 +160,7 @@ static int usbback_gnttab_map(struct usbback_req *usbback_req) for (i = 0; i < nr_segs; i++) { if ((unsigned)usbback_req->req.seg[i].offset + - (unsigned)usbback_req->req.seg[i].length > PAGE_SIZE) { + (unsigned)usbback_req->req.seg[i].length > XC_PAGE_SIZE) { xen_be_printf(xendev, 0, "segment crosses page boundary\n"); return -EINVAL; } @@ -183,7 +183,7 @@ static int usbback_gnttab_map(struct usbback_req *usbback_req) for (i = 0; i < usbback_req->nr_buffer_segs; i++) { seg = usbback_req->req.seg + i; - addr = usbback_req->buffer + i * PAGE_SIZE + seg->offset; + addr = usbback_req->buffer + i * XC_PAGE_SIZE + seg->offset; qemu_iovec_add(&usbback_req->packet.iov, addr, seg->length); } } diff --git a/hw/virtio/vhost-user.c b/hw/virtio/vhost-user.c index b57454a..7ee92b3 100644 --- a/hw/virtio/vhost-user.c +++ b/hw/virtio/vhost-user.c @@ -116,7 +116,7 @@ static bool ioeventfd_enabled(void) static int vhost_user_read(struct vhost_dev *dev, VhostUserMsg *msg) { - CharDriverState *chr = dev->opaque; + CharBackend *chr = dev->opaque; uint8_t *p = (uint8_t *) msg; int r, size = VHOST_USER_HDR_SIZE; @@ -196,7 +196,7 @@ static bool vhost_user_one_time_request(VhostUserRequest request) static int vhost_user_write(struct vhost_dev *dev, VhostUserMsg *msg, int *fds, int fd_num) { - CharDriverState *chr = dev->opaque; + CharBackend *chr = dev->opaque; int ret, size = VHOST_USER_HDR_SIZE + msg->size; /* diff --git a/hw/xtensa/xtfpga.c b/hw/xtensa/xtfpga.c index ac75949..dc6fdcc 100644 --- a/hw/xtensa/xtfpga.c +++ b/hw/xtensa/xtfpga.c @@ -265,7 +265,7 @@ static void lx_init(const LxBoardDesc *board, MachineState *machine) } if (!serial_hds[0]) { - serial_hds[0] = qemu_chr_new("serial0", "null", NULL); + serial_hds[0] = qemu_chr_new("serial0", "null"); } serial_mm_init(system_io, 0x0d050020, 2, xtensa_get_extint(env, 0), diff --git a/include/block/block.h b/include/block/block.h index 107c603..398a050 100644 --- a/include/block/block.h +++ b/include/block/block.h @@ -314,17 +314,11 @@ BlockAIOCB *bdrv_aio_writev(BdrvChild *child, int64_t sector_num, BlockCompletionFunc *cb, void *opaque); BlockAIOCB *bdrv_aio_flush(BlockDriverState *bs, BlockCompletionFunc *cb, void *opaque); -BlockAIOCB *bdrv_aio_pdiscard(BlockDriverState *bs, - int64_t offset, int count, - BlockCompletionFunc *cb, void *opaque); void bdrv_aio_cancel(BlockAIOCB *acb); void bdrv_aio_cancel_async(BlockAIOCB *acb); /* sg packet commands */ -int bdrv_ioctl(BlockDriverState *bs, unsigned long int req, void *buf); -BlockAIOCB *bdrv_aio_ioctl(BlockDriverState *bs, - unsigned long int req, void *buf, - BlockCompletionFunc *cb, void *opaque); +int bdrv_co_ioctl(BlockDriverState *bs, int req, void *buf); /* Invalidate any cached metadata used by image formats */ void bdrv_invalidate_cache(BlockDriverState *bs, Error **errp); diff --git a/include/block/block_int.h b/include/block/block_int.h index 3e79228..e96e9ad 100644 --- a/include/block/block_int.h +++ b/include/block/block_int.h @@ -244,6 +244,8 @@ struct BlockDriver { BlockAIOCB *(*bdrv_aio_ioctl)(BlockDriverState *bs, unsigned long int req, void *buf, BlockCompletionFunc *cb, void *opaque); + int coroutine_fn (*bdrv_co_ioctl)(BlockDriverState *bs, + unsigned long int req, void *buf); /* List of options for creating images, terminated by name == NULL */ QemuOptsList *create_opts; diff --git a/include/block/dirty-bitmap.h b/include/block/dirty-bitmap.h index ee3388f..9dea14b 100644 --- a/include/block/dirty-bitmap.h +++ b/include/block/dirty-bitmap.h @@ -8,6 +8,9 @@ BdrvDirtyBitmap *bdrv_create_dirty_bitmap(BlockDriverState *bs, uint32_t granularity, const char *name, Error **errp); +void bdrv_create_meta_dirty_bitmap(BdrvDirtyBitmap *bitmap, + int chunk_size); +void bdrv_release_meta_dirty_bitmap(BdrvDirtyBitmap *bitmap); int bdrv_dirty_bitmap_create_successor(BlockDriverState *bs, BdrvDirtyBitmap *bitmap, Error **errp); @@ -27,8 +30,11 @@ void bdrv_enable_dirty_bitmap(BdrvDirtyBitmap *bitmap); BlockDirtyInfoList *bdrv_query_dirty_bitmaps(BlockDriverState *bs); uint32_t bdrv_get_default_bitmap_granularity(BlockDriverState *bs); uint32_t bdrv_dirty_bitmap_granularity(BdrvDirtyBitmap *bitmap); +uint32_t bdrv_dirty_bitmap_meta_granularity(BdrvDirtyBitmap *bitmap); bool bdrv_dirty_bitmap_enabled(BdrvDirtyBitmap *bitmap); bool bdrv_dirty_bitmap_frozen(BdrvDirtyBitmap *bitmap); +const char *bdrv_dirty_bitmap_name(const BdrvDirtyBitmap *bitmap); +int64_t bdrv_dirty_bitmap_size(const BdrvDirtyBitmap *bitmap); DirtyBitmapStatus bdrv_dirty_bitmap_status(BdrvDirtyBitmap *bitmap); int bdrv_get_dirty(BlockDriverState *bs, BdrvDirtyBitmap *bitmap, int64_t sector); @@ -36,9 +42,34 @@ void bdrv_set_dirty_bitmap(BdrvDirtyBitmap *bitmap, int64_t cur_sector, int64_t nr_sectors); void bdrv_reset_dirty_bitmap(BdrvDirtyBitmap *bitmap, int64_t cur_sector, int64_t nr_sectors); -void bdrv_dirty_iter_init(BdrvDirtyBitmap *bitmap, struct HBitmapIter *hbi); -void bdrv_set_dirty_iter(struct HBitmapIter *hbi, int64_t offset); +int bdrv_dirty_bitmap_get_meta(BlockDriverState *bs, + BdrvDirtyBitmap *bitmap, int64_t sector, + int nb_sectors); +void bdrv_dirty_bitmap_reset_meta(BlockDriverState *bs, + BdrvDirtyBitmap *bitmap, int64_t sector, + int nb_sectors); +BdrvDirtyBitmapIter *bdrv_dirty_meta_iter_new(BdrvDirtyBitmap *bitmap); +BdrvDirtyBitmapIter *bdrv_dirty_iter_new(BdrvDirtyBitmap *bitmap, + uint64_t first_sector); +void bdrv_dirty_iter_free(BdrvDirtyBitmapIter *iter); +int64_t bdrv_dirty_iter_next(BdrvDirtyBitmapIter *iter); +void bdrv_set_dirty_iter(BdrvDirtyBitmapIter *hbi, int64_t sector_num); int64_t bdrv_get_dirty_count(BdrvDirtyBitmap *bitmap); +int64_t bdrv_get_meta_dirty_count(BdrvDirtyBitmap *bitmap); void bdrv_dirty_bitmap_truncate(BlockDriverState *bs); +uint64_t bdrv_dirty_bitmap_serialization_size(const BdrvDirtyBitmap *bitmap, + uint64_t start, uint64_t count); +uint64_t bdrv_dirty_bitmap_serialization_align(const BdrvDirtyBitmap *bitmap); +void bdrv_dirty_bitmap_serialize_part(const BdrvDirtyBitmap *bitmap, + uint8_t *buf, uint64_t start, + uint64_t count); +void bdrv_dirty_bitmap_deserialize_part(BdrvDirtyBitmap *bitmap, + uint8_t *buf, uint64_t start, + uint64_t count, bool finish); +void bdrv_dirty_bitmap_deserialize_zeroes(BdrvDirtyBitmap *bitmap, + uint64_t start, uint64_t count, + bool finish); +void bdrv_dirty_bitmap_deserialize_finish(BdrvDirtyBitmap *bitmap); + #endif diff --git a/include/exec/cpu-all.h b/include/exec/cpu-all.h index b6a7059..e9004e5 100644 --- a/include/exec/cpu-all.h +++ b/include/exec/cpu-all.h @@ -31,6 +31,7 @@ #define EXCP_DEBUG 0x10002 /* cpu stopped after a breakpoint or singlestep */ #define EXCP_HALTED 0x10003 /* cpu is halted (waiting for external event) */ #define EXCP_YIELD 0x10004 /* cpu wants to yield timeslice to another */ +#define EXCP_ATOMIC 0x10005 /* stop-the-world and emulate atomic */ /* some important defines: * @@ -189,6 +190,15 @@ void address_space_stq(AddressSpace *as, hwaddr addr, uint64_t val, /* page related stuff */ +#ifdef TARGET_PAGE_BITS_VARY +extern bool target_page_bits_decided; +extern int target_page_bits; +#define TARGET_PAGE_BITS ({ assert(target_page_bits_decided); \ + target_page_bits; }) +#else +#define TARGET_PAGE_BITS_MIN TARGET_PAGE_BITS +#endif + #define TARGET_PAGE_SIZE (1 << TARGET_PAGE_BITS) #define TARGET_PAGE_MASK ~(TARGET_PAGE_SIZE - 1) #define TARGET_PAGE_ALIGN(addr) (((addr) + TARGET_PAGE_SIZE - 1) & TARGET_PAGE_MASK) diff --git a/include/exec/exec-all.h b/include/exec/exec-all.h index 336a57c..cb624e4 100644 --- a/include/exec/exec-all.h +++ b/include/exec/exec-all.h @@ -57,9 +57,9 @@ TranslationBlock *tb_gen_code(CPUState *cpu, uint32_t flags, int cflags); -void cpu_exec_init(CPUState *cpu, Error **errp); void QEMU_NORETURN cpu_loop_exit(CPUState *cpu); void QEMU_NORETURN cpu_loop_exit_restore(CPUState *cpu, uintptr_t pc); +void QEMU_NORETURN cpu_loop_exit_atomic(CPUState *cpu, uintptr_t pc); #if !defined(CONFIG_USER_ONLY) void cpu_reloading_memory_map(void); diff --git a/include/exec/memory.h b/include/exec/memory.h index 10d7eac..79ccaab 100644 --- a/include/exec/memory.h +++ b/include/exec/memory.h @@ -255,8 +255,9 @@ struct MemoryListener { hwaddr addr, hwaddr len); /* Lower = earlier (during add), later (during del) */ unsigned priority; - AddressSpace *address_space_filter; + AddressSpace *address_space; QTAILQ_ENTRY(MemoryListener) link; + QTAILQ_ENTRY(MemoryListener) link_as; }; /** @@ -278,7 +279,7 @@ struct AddressSpace { struct AddressSpaceDispatch *dispatch; struct AddressSpaceDispatch *next_dispatch; MemoryListener dispatch_listener; - + QTAILQ_HEAD(memory_listeners_as, MemoryListener) listeners; QTAILQ_ENTRY(AddressSpace) address_spaces_link; }; diff --git a/include/hw/acpi/acpi-defs.h b/include/hw/acpi/acpi-defs.h index 9c1b7cb..d1d1d61 100644 --- a/include/hw/acpi/acpi-defs.h +++ b/include/hw/acpi/acpi-defs.h @@ -343,6 +343,24 @@ struct AcpiMadtLocalNmi { } QEMU_PACKED; typedef struct AcpiMadtLocalNmi AcpiMadtLocalNmi; +struct AcpiMadtProcessorX2Apic { + ACPI_SUB_HEADER_DEF + uint16_t reserved; + uint32_t x2apic_id; /* Processor's local x2APIC ID */ + uint32_t flags; + uint32_t uid; /* Processor object _UID */ +} QEMU_PACKED; +typedef struct AcpiMadtProcessorX2Apic AcpiMadtProcessorX2Apic; + +struct AcpiMadtLocalX2ApicNmi { + ACPI_SUB_HEADER_DEF + uint16_t flags; /* MPS INTI flags */ + uint32_t uid; /* Processor object _UID */ + uint8_t lint; /* Local APIC LINT# */ + uint8_t reserved[3]; /* Local APIC LINT# */ +} QEMU_PACKED; +typedef struct AcpiMadtLocalX2ApicNmi AcpiMadtLocalX2ApicNmi; + struct AcpiMadtGenericInterrupt { ACPI_SUB_HEADER_DEF uint16_t reserved; @@ -485,6 +503,17 @@ struct AcpiSratProcessorAffinity } QEMU_PACKED; typedef struct AcpiSratProcessorAffinity AcpiSratProcessorAffinity; +struct AcpiSratProcessorX2ApicAffinity { + ACPI_SUB_HEADER_DEF + uint16_t reserved; + uint32_t proximity_domain; + uint32_t x2apic_id; + uint32_t flags; + uint32_t clk_domain; + uint32_t reserved2; +} QEMU_PACKED; +typedef struct AcpiSratProcessorX2ApicAffinity AcpiSratProcessorX2ApicAffinity; + struct AcpiSratMemoryAffinity { ACPI_SUB_HEADER_DEF @@ -609,4 +638,72 @@ typedef struct AcpiDmarHardwareUnit AcpiDmarHardwareUnit; /* Masks for Flags field above */ #define ACPI_DMAR_INCLUDE_PCI_ALL 1 +/* + * Input Output Remapping Table (IORT) + * Conforms to "IO Remapping Table System Software on ARM Platforms", + * Document number: ARM DEN 0049B, October 2015 + */ + +struct AcpiIortTable { + ACPI_TABLE_HEADER_DEF /* ACPI common table header */ + uint32_t node_count; + uint32_t node_offset; + uint32_t reserved; +} QEMU_PACKED; +typedef struct AcpiIortTable AcpiIortTable; + +/* + * IORT node types + */ + +#define ACPI_IORT_NODE_HEADER_DEF /* Node format common fields */ \ + uint8_t type; \ + uint16_t length; \ + uint8_t revision; \ + uint32_t reserved; \ + uint32_t mapping_count; \ + uint32_t mapping_offset; + +/* Values for node Type above */ +enum { + ACPI_IORT_NODE_ITS_GROUP = 0x00, + ACPI_IORT_NODE_NAMED_COMPONENT = 0x01, + ACPI_IORT_NODE_PCI_ROOT_COMPLEX = 0x02, + ACPI_IORT_NODE_SMMU = 0x03, + ACPI_IORT_NODE_SMMU_V3 = 0x04 +}; + +struct AcpiIortIdMapping { + uint32_t input_base; + uint32_t id_count; + uint32_t output_base; + uint32_t output_reference; + uint32_t flags; +} QEMU_PACKED; +typedef struct AcpiIortIdMapping AcpiIortIdMapping; + +struct AcpiIortMemoryAccess { + uint32_t cache_coherency; + uint8_t hints; + uint16_t reserved; + uint8_t memory_flags; +} QEMU_PACKED; +typedef struct AcpiIortMemoryAccess AcpiIortMemoryAccess; + +struct AcpiIortItsGroup { + ACPI_IORT_NODE_HEADER_DEF + uint32_t its_count; + uint32_t identifiers[0]; +} QEMU_PACKED; +typedef struct AcpiIortItsGroup AcpiIortItsGroup; + +struct AcpiIortRC { + ACPI_IORT_NODE_HEADER_DEF + AcpiIortMemoryAccess memory_properties; + uint32_t ats_attribute; + uint32_t pci_segment_number; + AcpiIortIdMapping id_mapping_array[0]; +} QEMU_PACKED; +typedef struct AcpiIortRC AcpiIortRC; + #endif diff --git a/include/hw/boards.h b/include/hw/boards.h index e46a744..a51da9c 100644 --- a/include/hw/boards.h +++ b/include/hw/boards.h @@ -86,6 +86,12 @@ typedef struct { * Returns a @HotpluggableCPUList, which describes CPUs objects which * could be added with -device/device_add. * Caller is responsible for freeing returned list. + * @minimum_page_bits: + * If non-zero, the board promises never to create a CPU with a page size + * smaller than this, so QEMU can use a more efficient larger page + * size than the target architecture's minimum. (Attempting to create + * such a CPU will fail.) Note that changing this is a migration + * compatibility break for the machine. */ struct MachineClass { /*< private >*/ @@ -124,6 +130,7 @@ struct MachineClass { ram_addr_t default_ram_size; bool option_rom_has_mr; bool rom_file_has_mr; + int minimum_page_bits; HotplugHandler *(*get_hotplug_handler)(MachineState *machine, DeviceState *dev); diff --git a/include/hw/char/bcm2835_aux.h b/include/hw/char/bcm2835_aux.h index 42f0ee7..6865f15 100644 --- a/include/hw/char/bcm2835_aux.h +++ b/include/hw/char/bcm2835_aux.h @@ -22,7 +22,7 @@ typedef struct { /*< public >*/ MemoryRegion iomem; - CharDriverState *chr; + CharBackend chr; qemu_irq irq; uint8_t read_fifo[BCM2835_AUX_RX_FIFO_LEN]; diff --git a/include/hw/char/cadence_uart.h b/include/hw/char/cadence_uart.h index a12773c..ca75eb5 100644 --- a/include/hw/char/cadence_uart.h +++ b/include/hw/char/cadence_uart.h @@ -44,7 +44,7 @@ typedef struct { uint32_t rx_count; uint32_t tx_count; uint64_t char_tx_time; - CharDriverState *chr; + CharBackend chr; qemu_irq irq; QEMUTimer *fifo_trigger_handle; } CadenceUARTState; diff --git a/include/hw/char/digic-uart.h b/include/hw/char/digic-uart.h index 7b3f145..340c8e1 100644 --- a/include/hw/char/digic-uart.h +++ b/include/hw/char/digic-uart.h @@ -19,6 +19,7 @@ #define HW_CHAR_DIGIC_UART_H #include "hw/sysbus.h" +#include "sysemu/char.h" #define TYPE_DIGIC_UART "digic-uart" #define DIGIC_UART(obj) \ @@ -37,7 +38,7 @@ typedef struct DigicUartState { /*< public >*/ MemoryRegion regs_region; - CharDriverState *chr; + CharBackend chr; uint32_t reg_rx; uint32_t reg_st; diff --git a/include/hw/char/imx_serial.h b/include/hw/char/imx_serial.h index 6cd75c0..4cc3fbc 100644 --- a/include/hw/char/imx_serial.h +++ b/include/hw/char/imx_serial.h @@ -19,6 +19,7 @@ #define IMX_SERIAL_H #include "hw/sysbus.h" +#include "sysemu/char.h" #define TYPE_IMX_SERIAL "imx.serial" #define IMX_SERIAL(obj) OBJECT_CHECK(IMXSerialState, (obj), TYPE_IMX_SERIAL) @@ -96,7 +97,7 @@ typedef struct IMXSerialState { uint32_t ucr3; qemu_irq irq; - CharDriverState *chr; + CharBackend chr; } IMXSerialState; #endif diff --git a/include/hw/char/serial.h b/include/hw/char/serial.h index a4fd3d5..c928d7d 100644 --- a/include/hw/char/serial.h +++ b/include/hw/char/serial.h @@ -28,8 +28,10 @@ #include "hw/hw.h" #include "sysemu/sysemu.h" +#include "sysemu/char.h" #include "exec/memory.h" #include "qemu/fifo8.h" +#include "sysemu/char.h" #define UART_FIFO_LENGTH 16 /* 16550A Fifo Length */ @@ -52,7 +54,7 @@ struct SerialState { it can be reset while reading iir */ int thr_ipending; qemu_irq irq; - CharDriverState *chr; + CharBackend chr; int last_break_enable; int it_shift; int baudbase; @@ -94,6 +96,6 @@ SerialState *serial_mm_init(MemoryRegion *address_space, /* serial-isa.c */ #define TYPE_ISA_SERIAL "isa-serial" -void serial_hds_isa_init(ISABus *bus, int n); +void serial_hds_isa_init(ISABus *bus, int from, int to); #endif diff --git a/include/hw/char/stm32f2xx_usart.h b/include/hw/char/stm32f2xx_usart.h index b97f192..3267523 100644 --- a/include/hw/char/stm32f2xx_usart.h +++ b/include/hw/char/stm32f2xx_usart.h @@ -67,7 +67,7 @@ typedef struct { uint32_t usart_cr3; uint32_t usart_gtpr; - CharDriverState *chr; + CharBackend chr; qemu_irq irq; } STM32F2XXUsartState; #endif /* HW_STM32F2XX_USART_H */ diff --git a/include/hw/i386/apic_internal.h b/include/hw/i386/apic_internal.h index cdd11fb..1209eb4 100644 --- a/include/hw/i386/apic_internal.h +++ b/include/hw/i386/apic_internal.h @@ -160,7 +160,8 @@ struct APICCommonState { MemoryRegion io_memory; X86CPU *cpu; uint32_t apicbase; - uint8_t id; + uint8_t id; /* legacy APIC ID */ + uint32_t initial_apic_id; uint8_t version; uint8_t arb_id; uint8_t tpr; diff --git a/include/hw/i386/pc.h b/include/hw/i386/pc.h index b16c448..17fff80 100644 --- a/include/hw/i386/pc.h +++ b/include/hw/i386/pc.h @@ -37,6 +37,7 @@ /** * PCMachineState: * @acpi_dev: link to ACPI PM device that performs ACPI hotplug handling + * @boot_cpus_le: number of present VCPUs, referenced by 'etc/boot-cpus' fw_cfg */ struct PCMachineState { /*< private >*/ @@ -69,6 +70,7 @@ struct PCMachineState { bool apic_xrupt_override; unsigned apic_id_limit; CPUArchIdList *possible_cpus; + uint16_t boot_cpus_le; /* NUMA information: */ uint64_t numa_nodes; diff --git a/include/hw/ptimer.h b/include/hw/ptimer.h index 26c7fdc..48cccbd 100644 --- a/include/hw/ptimer.h +++ b/include/hw/ptimer.h @@ -35,6 +35,26 @@ */ #define PTIMER_POLICY_DEFAULT 0 +/* Periodic timer counter stays with "0" for a one period before wrapping + * around. */ +#define PTIMER_POLICY_WRAP_AFTER_ONE_PERIOD (1 << 0) + +/* Running periodic timer that has counter = limit = 0 would continuously + * re-trigger every period. */ +#define PTIMER_POLICY_CONTINUOUS_TRIGGER (1 << 1) + +/* Starting to run with/setting counter to "0" won't trigger immediately, + * but after a one period for both oneshot and periodic modes. */ +#define PTIMER_POLICY_NO_IMMEDIATE_TRIGGER (1 << 2) + +/* Starting to run with/setting counter to "0" won't re-load counter + * immediately, but after a one period. */ +#define PTIMER_POLICY_NO_IMMEDIATE_RELOAD (1 << 3) + +/* Make counter value of the running timer represent the actual value and + * not the one less. */ +#define PTIMER_POLICY_NO_COUNTER_ROUND_DOWN (1 << 4) + /* ptimer.c */ typedef struct ptimer_state ptimer_state; typedef void (*ptimer_cb)(void *opaque); diff --git a/include/hw/qdev-properties.h b/include/hw/qdev-properties.h index 2a9d2f9..306bbab 100644 --- a/include/hw/qdev-properties.h +++ b/include/hw/qdev-properties.h @@ -146,7 +146,7 @@ extern PropertyInfo qdev_prop_arraylen; DEFINE_PROP(_n, _s, _f, qdev_prop_ptr, void*) #define DEFINE_PROP_CHR(_n, _s, _f) \ - DEFINE_PROP(_n, _s, _f, qdev_prop_chr, CharDriverState*) + DEFINE_PROP(_n, _s, _f, qdev_prop_chr, CharBackend) #define DEFINE_PROP_STRING(_n, _s, _f) \ DEFINE_PROP(_n, _s, _f, qdev_prop_string, char*) #define DEFINE_PROP_NETDEV(_n, _s, _f) \ diff --git a/include/hw/timer/arm_mptimer.h b/include/hw/timer/arm_mptimer.h index b34cba0..c46d8d2 100644 --- a/include/hw/timer/arm_mptimer.h +++ b/include/hw/timer/arm_mptimer.h @@ -27,12 +27,9 @@ /* State of a single timer or watchdog block */ typedef struct { - uint32_t count; - uint32_t load; uint32_t control; uint32_t status; - int64_t tick; - QEMUTimer *timer; + struct ptimer_state *timer; qemu_irq irq; MemoryRegion iomem; } TimerBlock; diff --git a/include/qapi/qmp/qdict.h b/include/qapi/qmp/qdict.h index 71b8eb0..fe9a4c5 100644 --- a/include/qapi/qmp/qdict.h +++ b/include/qapi/qmp/qdict.h @@ -73,6 +73,7 @@ void qdict_flatten(QDict *qdict); void qdict_extract_subqdict(QDict *src, QDict **dst, const char *start); void qdict_array_split(QDict *src, QList **dst); int qdict_array_entries(QDict *src, const char *subqdict); +QObject *qdict_crumple(const QDict *src, Error **errp); void qdict_join(QDict *dest, QDict *src, bool overwrite); diff --git a/include/qapi/qmp-input-visitor.h b/include/qapi/qobject-input-visitor.h index f3ff5f3..cde328d 100644 --- a/include/qapi/qmp-input-visitor.h +++ b/include/qapi/qobject-input-visitor.h @@ -11,20 +11,20 @@ * */ -#ifndef QMP_INPUT_VISITOR_H -#define QMP_INPUT_VISITOR_H +#ifndef QOBJECT_INPUT_VISITOR_H +#define QOBJECT_INPUT_VISITOR_H #include "qapi/visitor.h" #include "qapi/qmp/qobject.h" -typedef struct QmpInputVisitor QmpInputVisitor; +typedef struct QObjectInputVisitor QObjectInputVisitor; /* - * Return a new input visitor that converts QMP to QAPI. + * Return a new input visitor that converts a QObject to a QAPI object. * * Set @strict to reject a parse that doesn't consume all keys of a * dictionary; otherwise excess input is ignored. */ -Visitor *qmp_input_visitor_new(QObject *obj, bool strict); +Visitor *qobject_input_visitor_new(QObject *obj, bool strict); #endif diff --git a/include/qapi/qmp-output-visitor.h b/include/qapi/qobject-output-visitor.h index 040fdda..8241877 100644 --- a/include/qapi/qmp-output-visitor.h +++ b/include/qapi/qobject-output-visitor.h @@ -11,20 +11,20 @@ * */ -#ifndef QMP_OUTPUT_VISITOR_H -#define QMP_OUTPUT_VISITOR_H +#ifndef QOBJECT_OUTPUT_VISITOR_H +#define QOBJECT_OUTPUT_VISITOR_H #include "qapi/visitor.h" #include "qapi/qmp/qobject.h" -typedef struct QmpOutputVisitor QmpOutputVisitor; +typedef struct QObjectOutputVisitor QObjectOutputVisitor; /* - * Create a new QMP output visitor. + * Create a new QObject output visitor. * * If everything else succeeds, pass @result to visit_complete() to * collect the result of the visit. */ -Visitor *qmp_output_visitor_new(QObject **result); +Visitor *qobject_output_visitor_new(QObject **result); #endif diff --git a/include/qapi/visitor.h b/include/qapi/visitor.h index 6c77a91..9bb6cba 100644 --- a/include/qapi/visitor.h +++ b/include/qapi/visitor.h @@ -25,14 +25,14 @@ * for doing work at each node of a QAPI graph; it can also be used * for a virtual walk, where there is no actual QAPI C struct. * - * There are four kinds of visitor classes: input visitors (QMP, + * There are four kinds of visitor classes: input visitors (QObject, * string, and QemuOpts) parse an external representation and build - * the corresponding QAPI graph, output visitors (QMP and string) take + * the corresponding QAPI graph, output visitors (QObject and string) take * a completed QAPI graph and generate an external representation, the * dealloc visitor can take a QAPI graph (possibly partially * constructed) and recursively free its resources, and the clone * visitor performs a deep clone of one QAPI object to another. While - * the dealloc and QMP input/output visitors are general, the string, + * the dealloc and QObject input/output visitors are general, the string, * QemuOpts, and clone visitors have some implementation limitations; * see the documentation for each visitor for more details on what it * supports. Also, see visitor-impl.h for the callback contracts diff --git a/include/qemu-common.h b/include/qemu-common.h index 9e8b0bd..1430390 100644 --- a/include/qemu-common.h +++ b/include/qemu-common.h @@ -80,6 +80,19 @@ void tcg_exec_init(unsigned long tb_size); bool tcg_enabled(void); void cpu_exec_init_all(void); +void cpu_exec_step_atomic(CPUState *cpu); + +/** + * set_preferred_target_page_bits: + * @bits: number of bits needed to represent an address within the page + * + * Set the preferred target page size (the actual target page + * size may be smaller than any given CPU's preference). + * Returns true on success, false on failure (which can only happen + * if this is called after the system has already finalized its + * choice of page size and the requested page size is smaller than that). + */ +bool set_preferred_target_page_bits(int bits); /** * Sends a (part of) iovec down a socket, yielding when the socket is full, or diff --git a/include/qemu/atomic.h b/include/qemu/atomic.h index c4f6950..878fa07 100644 --- a/include/qemu/atomic.h +++ b/include/qemu/atomic.h @@ -72,16 +72,16 @@ * Add one here, and similarly in smp_rmb() and smp_read_barrier_depends(). */ -#define smp_mb() ({ barrier(); __atomic_thread_fence(__ATOMIC_SEQ_CST); }) -#define smp_wmb() ({ barrier(); __atomic_thread_fence(__ATOMIC_RELEASE); }) -#define smp_rmb() ({ barrier(); __atomic_thread_fence(__ATOMIC_ACQUIRE); }) +#define smp_mb() ({ barrier(); __atomic_thread_fence(__ATOMIC_SEQ_CST); }) +#define smp_mb_release() ({ barrier(); __atomic_thread_fence(__ATOMIC_RELEASE); }) +#define smp_mb_acquire() ({ barrier(); __atomic_thread_fence(__ATOMIC_ACQUIRE); }) /* Most compilers currently treat consume and acquire the same, but really * no processors except Alpha need a barrier here. Leave it in if * using Thread Sanitizer to avoid warnings, otherwise optimize it away. */ #if defined(__SANITIZE_THREAD__) -#define smp_read_barrier_depends() ({ barrier(); __atomic_thread_fence(__ATOMIC_CONSUME); }) +#define smp_read_barrier_depends() ({ barrier(); __atomic_thread_fence(__ATOMIC_CONSUME); }) #elif defined(__alpha__) #define smp_read_barrier_depends() asm volatile("mb":::"memory") #else @@ -99,15 +99,21 @@ * no effect on the generated code but not using the atomic primitives * will get flagged by sanitizers as a violation. */ +#define atomic_read__nocheck(ptr) \ + __atomic_load_n(ptr, __ATOMIC_RELAXED) + #define atomic_read(ptr) \ ({ \ QEMU_BUILD_BUG_ON(sizeof(*ptr) > sizeof(void *)); \ - __atomic_load_n(ptr, __ATOMIC_RELAXED); \ + atomic_read__nocheck(ptr); \ }) +#define atomic_set__nocheck(ptr, i) \ + __atomic_store_n(ptr, i, __ATOMIC_RELAXED) + #define atomic_set(ptr, i) do { \ QEMU_BUILD_BUG_ON(sizeof(*ptr) > sizeof(void *)); \ - __atomic_store_n(ptr, i, __ATOMIC_RELAXED); \ + atomic_set__nocheck(ptr, i); \ } while(0) /* See above: most compilers currently treat consume and acquire the @@ -135,62 +141,43 @@ __atomic_store_n(ptr, i, __ATOMIC_RELEASE); \ } while(0) -/* atomic_mb_read/set semantics map Java volatile variables. They are - * less expensive on some platforms (notably POWER & ARMv7) than fully - * sequentially consistent operations. - * - * As long as they are used as paired operations they are safe to - * use. See docs/atomic.txt for more discussion. - */ - -#if defined(_ARCH_PPC) -#define atomic_mb_read(ptr) \ +#define atomic_load_acquire(ptr) \ ({ \ QEMU_BUILD_BUG_ON(sizeof(*ptr) > sizeof(void *)); \ typeof_strip_qual(*ptr) _val; \ - __atomic_load(ptr, &_val, __ATOMIC_RELAXED); \ - smp_rmb(); \ + __atomic_load(ptr, &_val, __ATOMIC_ACQUIRE); \ _val; \ }) -#define atomic_mb_set(ptr, i) do { \ +#define atomic_store_release(ptr, i) do { \ QEMU_BUILD_BUG_ON(sizeof(*ptr) > sizeof(void *)); \ - smp_wmb(); \ - __atomic_store_n(ptr, i, __ATOMIC_RELAXED); \ - smp_mb(); \ + __atomic_store_n(ptr, i, __ATOMIC_RELEASE); \ } while(0) -#else -#define atomic_mb_read(ptr) \ - ({ \ - QEMU_BUILD_BUG_ON(sizeof(*ptr) > sizeof(void *)); \ - typeof_strip_qual(*ptr) _val; \ - __atomic_load(ptr, &_val, __ATOMIC_SEQ_CST); \ - _val; \ - }) - -#define atomic_mb_set(ptr, i) do { \ - QEMU_BUILD_BUG_ON(sizeof(*ptr) > sizeof(void *)); \ - __atomic_store_n(ptr, i, __ATOMIC_SEQ_CST); \ -} while(0) -#endif /* All the remaining operations are fully sequentially consistent */ +#define atomic_xchg__nocheck(ptr, i) ({ \ + __atomic_exchange_n(ptr, (i), __ATOMIC_SEQ_CST); \ +}) + #define atomic_xchg(ptr, i) ({ \ QEMU_BUILD_BUG_ON(sizeof(*ptr) > sizeof(void *)); \ - __atomic_exchange_n(ptr, i, __ATOMIC_SEQ_CST); \ + atomic_xchg__nocheck(ptr, i); \ }) /* Returns the eventual value, failed or not */ -#define atomic_cmpxchg(ptr, old, new) \ - ({ \ - QEMU_BUILD_BUG_ON(sizeof(*ptr) > sizeof(void *)); \ +#define atomic_cmpxchg__nocheck(ptr, old, new) ({ \ typeof_strip_qual(*ptr) _old = (old); \ __atomic_compare_exchange_n(ptr, &_old, new, false, \ __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST); \ _old; \ - }) +}) + +#define atomic_cmpxchg(ptr, old, new) ({ \ + QEMU_BUILD_BUG_ON(sizeof(*ptr) > sizeof(void *)); \ + atomic_cmpxchg__nocheck(ptr, old, new); \ +}) /* Provide shorter names for GCC atomic builtins, return old value */ #define atomic_fetch_inc(ptr) __atomic_fetch_add(ptr, 1, __ATOMIC_SEQ_CST) @@ -199,6 +186,15 @@ #define atomic_fetch_sub(ptr, n) __atomic_fetch_sub(ptr, n, __ATOMIC_SEQ_CST) #define atomic_fetch_and(ptr, n) __atomic_fetch_and(ptr, n, __ATOMIC_SEQ_CST) #define atomic_fetch_or(ptr, n) __atomic_fetch_or(ptr, n, __ATOMIC_SEQ_CST) +#define atomic_fetch_xor(ptr, n) __atomic_fetch_xor(ptr, n, __ATOMIC_SEQ_CST) + +#define atomic_inc_fetch(ptr) __atomic_add_fetch(ptr, 1, __ATOMIC_SEQ_CST) +#define atomic_dec_fetch(ptr) __atomic_sub_fetch(ptr, 1, __ATOMIC_SEQ_CST) +#define atomic_add_fetch(ptr, n) __atomic_add_fetch(ptr, n, __ATOMIC_SEQ_CST) +#define atomic_sub_fetch(ptr, n) __atomic_sub_fetch(ptr, n, __ATOMIC_SEQ_CST) +#define atomic_and_fetch(ptr, n) __atomic_and_fetch(ptr, n, __ATOMIC_SEQ_CST) +#define atomic_or_fetch(ptr, n) __atomic_or_fetch(ptr, n, __ATOMIC_SEQ_CST) +#define atomic_xor_fetch(ptr, n) __atomic_xor_fetch(ptr, n, __ATOMIC_SEQ_CST) /* And even shorter names that return void. */ #define atomic_inc(ptr) ((void) __atomic_fetch_add(ptr, 1, __ATOMIC_SEQ_CST)) @@ -207,6 +203,7 @@ #define atomic_sub(ptr, n) ((void) __atomic_fetch_sub(ptr, n, __ATOMIC_SEQ_CST)) #define atomic_and(ptr, n) ((void) __atomic_fetch_and(ptr, n, __ATOMIC_SEQ_CST)) #define atomic_or(ptr, n) ((void) __atomic_fetch_or(ptr, n, __ATOMIC_SEQ_CST)) +#define atomic_xor(ptr, n) ((void) __atomic_fetch_xor(ptr, n, __ATOMIC_SEQ_CST)) #else /* __ATOMIC_RELAXED */ @@ -238,8 +235,8 @@ * here (a compiler barrier only). QEMU doesn't do accesses to write-combining * qemu memory or non-temporal load/stores from C code. */ -#define smp_wmb() barrier() -#define smp_rmb() barrier() +#define smp_mb_release() barrier() +#define smp_mb_acquire() barrier() /* * __sync_lock_test_and_set() is documented to be an acquire barrier only, @@ -248,11 +245,6 @@ */ #define atomic_xchg(ptr, i) (barrier(), __sync_lock_test_and_set(ptr, i)) -/* - * Load/store with Java volatile semantics. - */ -#define atomic_mb_set(ptr, i) ((void)atomic_xchg(ptr, i)) - #elif defined(_ARCH_PPC) /* @@ -263,13 +255,15 @@ * smp_mb has the same problem as on x86 for not-very-new GCC * (http://patchwork.ozlabs.org/patch/126184/, Nov 2011). */ -#define smp_wmb() ({ asm volatile("eieio" ::: "memory"); (void)0; }) +#define smp_wmb() ({ asm volatile("eieio" ::: "memory"); (void)0; }) #if defined(__powerpc64__) -#define smp_rmb() ({ asm volatile("lwsync" ::: "memory"); (void)0; }) +#define smp_mb_release() ({ asm volatile("lwsync" ::: "memory"); (void)0; }) +#define smp_mb_acquire() ({ asm volatile("lwsync" ::: "memory"); (void)0; }) #else -#define smp_rmb() ({ asm volatile("sync" ::: "memory"); (void)0; }) +#define smp_mb_release() ({ asm volatile("sync" ::: "memory"); (void)0; }) +#define smp_mb_acquire() ({ asm volatile("sync" ::: "memory"); (void)0; }) #endif -#define smp_mb() ({ asm volatile("sync" ::: "memory"); (void)0; }) +#define smp_mb() ({ asm volatile("sync" ::: "memory"); (void)0; }) #endif /* _ARCH_PPC */ @@ -277,18 +271,18 @@ * For (host) platforms we don't have explicit barrier definitions * for, we use the gcc __sync_synchronize() primitive to generate a * full barrier. This should be safe on all platforms, though it may - * be overkill for smp_wmb() and smp_rmb(). + * be overkill for smp_mb_acquire() and smp_mb_release(). */ #ifndef smp_mb -#define smp_mb() __sync_synchronize() +#define smp_mb() __sync_synchronize() #endif -#ifndef smp_wmb -#define smp_wmb() __sync_synchronize() +#ifndef smp_mb_acquire +#define smp_mb_acquire() __sync_synchronize() #endif -#ifndef smp_rmb -#define smp_rmb() __sync_synchronize() +#ifndef smp_mb_release +#define smp_mb_release() __sync_synchronize() #endif #ifndef smp_read_barrier_depends @@ -298,8 +292,11 @@ /* These will only be atomic if the processor does the fetch or store * in a single issue memory operation */ -#define atomic_read(ptr) (*(__typeof__(*ptr) volatile*) (ptr)) -#define atomic_set(ptr, i) ((*(__typeof__(*ptr) volatile*) (ptr)) = (i)) +#define atomic_read__nocheck(p) (*(__typeof__(*(p)) volatile*) (p)) +#define atomic_set__nocheck(p, i) ((*(__typeof__(*(p)) volatile*) (p)) = (i)) + +#define atomic_read(ptr) atomic_read__nocheck(ptr) +#define atomic_set(ptr, i) atomic_set__nocheck(ptr,i) /** * atomic_rcu_read - reads a RCU-protected pointer to a local variable @@ -341,41 +338,16 @@ atomic_set(ptr, i); \ } while (0) -/* These have the same semantics as Java volatile variables. - * See http://gee.cs.oswego.edu/dl/jmm/cookbook.html: - * "1. Issue a StoreStore barrier (wmb) before each volatile store." - * 2. Issue a StoreLoad barrier after each volatile store. - * Note that you could instead issue one before each volatile load, but - * this would be slower for typical programs using volatiles in which - * reads greatly outnumber writes. Alternatively, if available, you - * can implement volatile store as an atomic instruction (for example - * XCHG on x86) and omit the barrier. This may be more efficient if - * atomic instructions are cheaper than StoreLoad barriers. - * 3. Issue LoadLoad and LoadStore barriers after each volatile load." - * - * If you prefer to think in terms of "pairing" of memory barriers, - * an atomic_mb_read pairs with an atomic_mb_set. - * - * And for the few ia64 lovers that exist, an atomic_mb_read is a ld.acq, - * while an atomic_mb_set is a st.rel followed by a memory barrier. - * - * These are a bit weaker than __atomic_load/store with __ATOMIC_SEQ_CST - * (see docs/atomics.txt), and I'm not sure that __ATOMIC_ACQ_REL is enough. - * Just always use the barriers manually by the rules above. - */ -#define atomic_mb_read(ptr) ({ \ +#define atomic_load_acquire(ptr) ({ \ typeof(*ptr) _val = atomic_read(ptr); \ - smp_rmb(); \ + smp_mb_acquire(); \ _val; \ }) -#ifndef atomic_mb_set -#define atomic_mb_set(ptr, i) do { \ - smp_wmb(); \ +#define atomic_store_release(ptr, i) do { \ + smp_mb_release(); \ atomic_set(ptr, i); \ - smp_mb(); \ } while (0) -#endif #ifndef atomic_xchg #if defined(__clang__) @@ -385,15 +357,27 @@ #define atomic_xchg(ptr, i) (smp_mb(), __sync_lock_test_and_set(ptr, i)) #endif #endif +#define atomic_xchg__nocheck atomic_xchg /* Provide shorter names for GCC atomic builtins. */ #define atomic_fetch_inc(ptr) __sync_fetch_and_add(ptr, 1) #define atomic_fetch_dec(ptr) __sync_fetch_and_add(ptr, -1) -#define atomic_fetch_add __sync_fetch_and_add -#define atomic_fetch_sub __sync_fetch_and_sub -#define atomic_fetch_and __sync_fetch_and_and -#define atomic_fetch_or __sync_fetch_and_or -#define atomic_cmpxchg __sync_val_compare_and_swap +#define atomic_fetch_add(ptr, n) __sync_fetch_and_add(ptr, n) +#define atomic_fetch_sub(ptr, n) __sync_fetch_and_sub(ptr, n) +#define atomic_fetch_and(ptr, n) __sync_fetch_and_and(ptr, n) +#define atomic_fetch_or(ptr, n) __sync_fetch_and_or(ptr, n) +#define atomic_fetch_xor(ptr, n) __sync_fetch_and_xor(ptr, n) + +#define atomic_inc_fetch(ptr) __sync_add_and_fetch(ptr, 1) +#define atomic_dec_fetch(ptr) __sync_add_and_fetch(ptr, -1) +#define atomic_add_fetch(ptr, n) __sync_add_and_fetch(ptr, n) +#define atomic_sub_fetch(ptr, n) __sync_sub_and_fetch(ptr, n) +#define atomic_and_fetch(ptr, n) __sync_and_and_fetch(ptr, n) +#define atomic_or_fetch(ptr, n) __sync_or_and_fetch(ptr, n) +#define atomic_xor_fetch(ptr, n) __sync_xor_and_fetch(ptr, n) + +#define atomic_cmpxchg(ptr, old, new) __sync_val_compare_and_swap(ptr, old, new) +#define atomic_cmpxchg__nocheck(ptr, old, new) atomic_cmpxchg(ptr, old, new) /* And even shorter names that return void. */ #define atomic_inc(ptr) ((void) __sync_fetch_and_add(ptr, 1)) @@ -402,6 +386,42 @@ #define atomic_sub(ptr, n) ((void) __sync_fetch_and_sub(ptr, n)) #define atomic_and(ptr, n) ((void) __sync_fetch_and_and(ptr, n)) #define atomic_or(ptr, n) ((void) __sync_fetch_and_or(ptr, n)) +#define atomic_xor(ptr, n) ((void) __sync_fetch_and_xor(ptr, n)) #endif /* __ATOMIC_RELAXED */ + +#ifndef smp_wmb +#define smp_wmb() smp_mb_release() +#endif +#ifndef smp_rmb +#define smp_rmb() smp_mb_acquire() +#endif + +/* This is more efficient than a store plus a fence. */ +#if !defined(__SANITIZE_THREAD__) +#if defined(__i386__) || defined(__x86_64__) || defined(__s390x__) +#define atomic_mb_set(ptr, i) ((void)atomic_xchg(ptr, i)) +#endif +#endif + +/* atomic_mb_read/set semantics map Java volatile variables. They are + * less expensive on some platforms (notably POWER) than fully + * sequentially consistent operations. + * + * As long as they are used as paired operations they are safe to + * use. See docs/atomic.txt for more discussion. + */ + +#ifndef atomic_mb_read +#define atomic_mb_read(ptr) \ + atomic_load_acquire(ptr) +#endif + +#ifndef atomic_mb_set +#define atomic_mb_set(ptr, i) do { \ + atomic_store_release(ptr, i); \ + smp_mb(); \ +} while(0) +#endif + #endif /* QEMU_ATOMIC_H */ diff --git a/include/qemu/hbitmap.h b/include/qemu/hbitmap.h index 8ab721e..eb46475 100644 --- a/include/qemu/hbitmap.h +++ b/include/qemu/hbitmap.h @@ -146,6 +146,85 @@ void hbitmap_reset_all(HBitmap *hb); bool hbitmap_get(const HBitmap *hb, uint64_t item); /** + * hbitmap_serialization_granularity: + * @hb: HBitmap to operate on. + * + * Granularity of serialization chunks, used by other serialization functions. + * For every chunk: + * 1. Chunk start should be aligned to this granularity. + * 2. Chunk size should be aligned too, except for last chunk (for which + * start + count == hb->size) + */ +uint64_t hbitmap_serialization_granularity(const HBitmap *hb); + +/** + * hbitmap_serialization_size: + * @hb: HBitmap to operate on. + * @start: Starting bit + * @count: Number of bits + * + * Return number of bytes hbitmap_(de)serialize_part needs + */ +uint64_t hbitmap_serialization_size(const HBitmap *hb, + uint64_t start, uint64_t count); + +/** + * hbitmap_serialize_part + * @hb: HBitmap to operate on. + * @buf: Buffer to store serialized bitmap. + * @start: First bit to store. + * @count: Number of bits to store. + * + * Stores HBitmap data corresponding to given region. The format of saved data + * is linear sequence of bits, so it can be used by hbitmap_deserialize_part + * independently of endianness and size of HBitmap level array elements + */ +void hbitmap_serialize_part(const HBitmap *hb, uint8_t *buf, + uint64_t start, uint64_t count); + +/** + * hbitmap_deserialize_part + * @hb: HBitmap to operate on. + * @buf: Buffer to restore bitmap data from. + * @start: First bit to restore. + * @count: Number of bits to restore. + * @finish: Whether to call hbitmap_deserialize_finish automatically. + * + * Restores HBitmap data corresponding to given region. The format is the same + * as for hbitmap_serialize_part. + * + * If @finish is false, caller must call hbitmap_serialize_finish before using + * the bitmap. + */ +void hbitmap_deserialize_part(HBitmap *hb, uint8_t *buf, + uint64_t start, uint64_t count, + bool finish); + +/** + * hbitmap_deserialize_zeroes + * @hb: HBitmap to operate on. + * @start: First bit to restore. + * @count: Number of bits to restore. + * @finish: Whether to call hbitmap_deserialize_finish automatically. + * + * Fills the bitmap with zeroes. + * + * If @finish is false, caller must call hbitmap_serialize_finish before using + * the bitmap. + */ +void hbitmap_deserialize_zeroes(HBitmap *hb, uint64_t start, uint64_t count, + bool finish); + +/** + * hbitmap_deserialize_finish + * @hb: HBitmap to operate on. + * + * Repair HBitmap after calling hbitmap_deserialize_data. Actually, all HBitmap + * layers are restored here. + */ +void hbitmap_deserialize_finish(HBitmap *hb); + +/** * hbitmap_free: * @hb: HBitmap to operate on. * @@ -178,6 +257,27 @@ void hbitmap_iter_init(HBitmapIter *hbi, const HBitmap *hb, uint64_t first); */ unsigned long hbitmap_iter_skip_words(HBitmapIter *hbi); +/* hbitmap_create_meta: + * Create a "meta" hbitmap to track dirtiness of the bits in this HBitmap. + * The caller owns the created bitmap and must call hbitmap_free_meta(hb) to + * free it. + * + * Currently, we only guarantee that if a bit in the hbitmap is changed it + * will be reflected in the meta bitmap, but we do not yet guarantee the + * opposite. + * + * @hb: The HBitmap to operate on. + * @chunk_size: How many bits in @hb does one bit in the meta track. + */ +HBitmap *hbitmap_create_meta(HBitmap *hb, int chunk_size); + +/* hbitmap_free_meta: + * Free the meta bitmap of @hb. + * + * @hb: The HBitmap whose meta bitmap should be freed. + */ +void hbitmap_free_meta(HBitmap *hb); + /** * hbitmap_iter_next: * @hbi: HBitmapIter to operate on. diff --git a/include/qemu/int128.h b/include/qemu/int128.h index c598881..5c9890d 100644 --- a/include/qemu/int128.h +++ b/include/qemu/int128.h @@ -1,6 +1,149 @@ #ifndef INT128_H #define INT128_H +#ifdef CONFIG_INT128 +#include "qemu/bswap.h" + +typedef __int128_t Int128; + +static inline Int128 int128_make64(uint64_t a) +{ + return a; +} + +static inline Int128 int128_make128(uint64_t lo, uint64_t hi) +{ + return (__uint128_t)hi << 64 | lo; +} + +static inline uint64_t int128_get64(Int128 a) +{ + uint64_t r = a; + assert(r == a); + return r; +} + +static inline uint64_t int128_getlo(Int128 a) +{ + return a; +} + +static inline int64_t int128_gethi(Int128 a) +{ + return a >> 64; +} + +static inline Int128 int128_zero(void) +{ + return 0; +} + +static inline Int128 int128_one(void) +{ + return 1; +} + +static inline Int128 int128_2_64(void) +{ + return (Int128)1 << 64; +} + +static inline Int128 int128_exts64(int64_t a) +{ + return a; +} + +static inline Int128 int128_and(Int128 a, Int128 b) +{ + return a & b; +} + +static inline Int128 int128_rshift(Int128 a, int n) +{ + return a >> n; +} + +static inline Int128 int128_add(Int128 a, Int128 b) +{ + return a + b; +} + +static inline Int128 int128_neg(Int128 a) +{ + return -a; +} + +static inline Int128 int128_sub(Int128 a, Int128 b) +{ + return a - b; +} + +static inline bool int128_nonneg(Int128 a) +{ + return a >= 0; +} + +static inline bool int128_eq(Int128 a, Int128 b) +{ + return a == b; +} + +static inline bool int128_ne(Int128 a, Int128 b) +{ + return a != b; +} + +static inline bool int128_ge(Int128 a, Int128 b) +{ + return a >= b; +} + +static inline bool int128_lt(Int128 a, Int128 b) +{ + return a < b; +} + +static inline bool int128_le(Int128 a, Int128 b) +{ + return a <= b; +} + +static inline bool int128_gt(Int128 a, Int128 b) +{ + return a > b; +} + +static inline bool int128_nz(Int128 a) +{ + return a != 0; +} + +static inline Int128 int128_min(Int128 a, Int128 b) +{ + return a < b ? a : b; +} + +static inline Int128 int128_max(Int128 a, Int128 b) +{ + return a > b ? a : b; +} + +static inline void int128_addto(Int128 *a, Int128 b) +{ + *a += b; +} + +static inline void int128_subfrom(Int128 *a, Int128 b) +{ + *a -= b; +} + +static inline Int128 bswap128(Int128 a) +{ + return int128_make128(bswap64(int128_gethi(a)), bswap64(int128_getlo(a))); +} + +#else /* !CONFIG_INT128 */ typedef struct Int128 Int128; @@ -14,12 +157,27 @@ static inline Int128 int128_make64(uint64_t a) return (Int128) { a, 0 }; } +static inline Int128 int128_make128(uint64_t lo, uint64_t hi) +{ + return (Int128) { lo, hi }; +} + static inline uint64_t int128_get64(Int128 a) { assert(!a.hi); return a.lo; } +static inline uint64_t int128_getlo(Int128 a) +{ + return a.lo; +} + +static inline int64_t int128_gethi(Int128 a) +{ + return a.hi; +} + static inline Int128 int128_zero(void) { return int128_make64(0); @@ -53,9 +211,9 @@ static inline Int128 int128_rshift(Int128 a, int n) } h = a.hi >> (n & 63); if (n >= 64) { - return (Int128) { h, h >> 63 }; + return int128_make128(h, h >> 63); } else { - return (Int128) { (a.lo >> n) | ((uint64_t)a.hi << (64 - n)), h }; + return int128_make128((a.lo >> n) | ((uint64_t)a.hi << (64 - n)), h); } } @@ -69,18 +227,18 @@ static inline Int128 int128_add(Int128 a, Int128 b) * * So the carry is lo < a.lo. */ - return (Int128) { lo, (uint64_t)a.hi + b.hi + (lo < a.lo) }; + return int128_make128(lo, (uint64_t)a.hi + b.hi + (lo < a.lo)); } static inline Int128 int128_neg(Int128 a) { uint64_t lo = -a.lo; - return (Int128) { lo, ~(uint64_t)a.hi + !lo }; + return int128_make128(lo, ~(uint64_t)a.hi + !lo); } static inline Int128 int128_sub(Int128 a, Int128 b) { - return (Int128){ a.lo - b.lo, (uint64_t)a.hi - b.hi - (a.lo < b.lo) }; + return int128_make128(a.lo - b.lo, (uint64_t)a.hi - b.hi - (a.lo < b.lo)); } static inline bool int128_nonneg(Int128 a) @@ -143,4 +301,5 @@ static inline void int128_subfrom(Int128 *a, Int128 b) *a = int128_sub(*a, b); } -#endif +#endif /* CONFIG_INT128 */ +#endif /* INT128_H */ diff --git a/include/qemu/typedefs.h b/include/qemu/typedefs.h index b113fcf..1b8c30a 100644 --- a/include/qemu/typedefs.h +++ b/include/qemu/typedefs.h @@ -11,6 +11,7 @@ typedef struct AioContext AioContext; typedef struct AllwinnerAHCIState AllwinnerAHCIState; typedef struct AudioState AudioState; typedef struct BdrvDirtyBitmap BdrvDirtyBitmap; +typedef struct BdrvDirtyBitmapIter BdrvDirtyBitmapIter; typedef struct BlockBackend BlockBackend; typedef struct BlockBackendRootState BlockBackendRootState; typedef struct BlockDriverState BlockDriverState; diff --git a/include/qom/cpu.h b/include/qom/cpu.h index 6d481a1..633c3fc 100644 --- a/include/qom/cpu.h +++ b/include/qom/cpu.h @@ -946,7 +946,9 @@ AddressSpace *cpu_get_address_space(CPUState *cpu, int asidx); void QEMU_NORETURN cpu_abort(CPUState *cpu, const char *fmt, ...) GCC_FMT_ATTR(2, 3); -void cpu_exec_exit(CPUState *cpu); +void cpu_exec_initfn(CPUState *cpu); +void cpu_exec_realizefn(CPUState *cpu, Error **errp); +void cpu_exec_unrealizefn(CPUState *cpu); #ifdef CONFIG_SOFTMMU extern const struct VMStateDescription vmstate_cpu_common; diff --git a/include/sysemu/block-backend.h b/include/sysemu/block-backend.h index b07159b..6444e41 100644 --- a/include/sysemu/block-backend.h +++ b/include/sysemu/block-backend.h @@ -146,6 +146,7 @@ BlockAIOCB *blk_aio_pdiscard(BlockBackend *blk, int64_t offset, int count, BlockCompletionFunc *cb, void *opaque); void blk_aio_cancel(BlockAIOCB *acb); void blk_aio_cancel_async(BlockAIOCB *acb); +int blk_co_ioctl(BlockBackend *blk, unsigned long int req, void *buf); int blk_ioctl(BlockBackend *blk, unsigned long int req, void *buf); BlockAIOCB *blk_aio_ioctl(BlockBackend *blk, unsigned long int req, void *buf, BlockCompletionFunc *cb, void *opaque); diff --git a/include/sysemu/char.h b/include/sysemu/char.h index 19dad3f..0a14942 100644 --- a/include/sysemu/char.h +++ b/include/sysemu/char.h @@ -13,12 +13,13 @@ /* character device */ -#define CHR_EVENT_BREAK 0 /* serial break char */ -#define CHR_EVENT_FOCUS 1 /* focus to this terminal (modal input needed) */ -#define CHR_EVENT_OPENED 2 /* new connection established */ -#define CHR_EVENT_MUX_IN 3 /* mux-focus was set to this terminal */ -#define CHR_EVENT_MUX_OUT 4 /* mux-focus will move on */ -#define CHR_EVENT_CLOSED 5 /* connection closed */ +typedef enum { + CHR_EVENT_BREAK, /* serial break char */ + CHR_EVENT_OPENED, /* new connection established */ + CHR_EVENT_MUX_IN, /* mux-focus was set to this terminal */ + CHR_EVENT_MUX_OUT, /* mux-focus will move on */ + CHR_EVENT_CLOSED /* connection closed */ +} QEMUChrEvent; #define CHR_IOCTL_SERIAL_SET_PARAMS 1 @@ -72,10 +73,20 @@ typedef enum { QEMU_CHAR_FEATURE_LAST, } CharDriverFeature; +/* This is the backend as seen by frontend, the actual backend is + * CharDriverState */ +typedef struct CharBackend { + CharDriverState *chr; + IOEventHandler *chr_event; + IOCanReadHandler *chr_can_read; + IOReadHandler *chr_read; + void *opaque; + int tag; + int fe_open; +} CharBackend; struct CharDriverState { QemuMutex chr_write_lock; - void (*init)(struct CharDriverState *s); int (*chr_write)(struct CharDriverState *s, const uint8_t *buf, int len); int (*chr_sync_read)(struct CharDriverState *s, const uint8_t *buf, int len); @@ -87,25 +98,17 @@ struct CharDriverState { int (*set_msgfds)(struct CharDriverState *s, int *fds, int num); int (*chr_add_client)(struct CharDriverState *chr, int fd); int (*chr_wait_connected)(struct CharDriverState *chr, Error **errp); - IOEventHandler *chr_event; - IOCanReadHandler *chr_can_read; - IOReadHandler *chr_read; - void *handler_opaque; - void (*chr_close)(struct CharDriverState *chr); + void (*chr_free)(struct CharDriverState *chr); void (*chr_disconnect)(struct CharDriverState *chr); void (*chr_accept_input)(struct CharDriverState *chr); void (*chr_set_echo)(struct CharDriverState *chr, bool echo); void (*chr_set_fe_open)(struct CharDriverState *chr, int fe_open); - void (*chr_fe_event)(struct CharDriverState *chr, int event); + CharBackend *be; void *opaque; char *label; char *filename; int logfd; int be_open; - int fe_open; - int explicit_fe_open; - int explicit_be_open; - int avail_connections; int is_mux; guint fd_in_tag; bool replay; @@ -130,13 +133,11 @@ CharDriverState *qemu_chr_alloc(ChardevCommon *backend, Error **errp); * Create a new character backend from a QemuOpts list. * * @opts see qemu-config.c for a list of valid options - * @init not sure.. * * Returns: a new character backend */ CharDriverState *qemu_chr_new_from_opts(QemuOpts *opts, - void (*init)(struct CharDriverState *s), - Error **errp); + Error **errp); /** * @qemu_chr_parse_common: @@ -155,18 +156,19 @@ void qemu_chr_parse_common(QemuOpts *opts, ChardevCommon *backend); * * @label the name of the backend * @filename the URI - * @init not sure.. * * Returns: a new character backend */ -CharDriverState *qemu_chr_new(const char *label, const char *filename, - void (*init)(struct CharDriverState *s)); +CharDriverState *qemu_chr_new(const char *label, const char *filename); + + /** - * @qemu_chr_disconnect: + * @qemu_chr_fe_disconnect: * * Close a fd accpeted by character backend. + * Without associated CharDriver, do nothing. */ -void qemu_chr_disconnect(CharDriverState *chr); +void qemu_chr_fe_disconnect(CharBackend *be); /** * @qemu_chr_cleanup: @@ -176,11 +178,12 @@ void qemu_chr_disconnect(CharDriverState *chr); void qemu_chr_cleanup(void); /** - * @qemu_chr_wait_connected: + * @qemu_chr_fe_wait_connected: * - * Wait for characted backend to be connected. + * Wait for characted backend to be connected, return < 0 on error or + * if no assicated CharDriver. */ -int qemu_chr_wait_connected(CharDriverState *chr, Error **errp); +int qemu_chr_fe_wait_connected(CharBackend *be, Error **errp); /** * @qemu_chr_new_noreplay: @@ -191,12 +194,10 @@ int qemu_chr_wait_connected(CharDriverState *chr, Error **errp); * * @label the name of the backend * @filename the URI - * @init not sure.. * * Returns: a new character backend */ -CharDriverState *qemu_chr_new_noreplay(const char *label, const char *filename, - void (*init)(struct CharDriverState *s)); +CharDriverState *qemu_chr_new_noreplay(const char *label, const char *filename); /** * @qemu_chr_delete: @@ -219,37 +220,31 @@ void qemu_chr_free(CharDriverState *chr); * Ask the backend to override its normal echo setting. This only really * applies to the stdio backend and is used by the QMP server such that you * can see what you type if you try to type QMP commands. + * Without associated CharDriver, do nothing. * * @echo true to enable echo, false to disable echo */ -void qemu_chr_fe_set_echo(struct CharDriverState *chr, bool echo); +void qemu_chr_fe_set_echo(CharBackend *be, bool echo); /** * @qemu_chr_fe_set_open: * * Set character frontend open status. This is an indication that the * front end is ready (or not) to begin doing I/O. + * Without associated CharDriver, do nothing. */ -void qemu_chr_fe_set_open(struct CharDriverState *chr, int fe_open); - -/** - * @qemu_chr_fe_event: - * - * Send an event from the front end to the back end. - * - * @event the event to send - */ -void qemu_chr_fe_event(CharDriverState *s, int event); +void qemu_chr_fe_set_open(CharBackend *be, int fe_open); /** * @qemu_chr_fe_printf: * - * Write to a character backend using a printf style interface. - * This function is thread-safe. + * Write to a character backend using a printf style interface. This + * function is thread-safe. It does nothing without associated + * CharDriver. * * @fmt see #printf */ -void qemu_chr_fe_printf(CharDriverState *s, const char *fmt, ...) +void qemu_chr_fe_printf(CharBackend *be, const char *fmt, ...) GCC_FMT_ATTR(2, 3); /** @@ -258,13 +253,13 @@ void qemu_chr_fe_printf(CharDriverState *s, const char *fmt, ...) * If the backend is connected, create and add a #GSource that fires * when the given condition (typically G_IO_OUT|G_IO_HUP or G_IO_HUP) * is active; return the #GSource's tag. If it is disconnected, - * return 0. + * or without associated CharDriver, return 0. * * @cond the condition to poll for * @func the function to call when the condition happens * @user_data the opaque pointer to pass to @func */ -guint qemu_chr_fe_add_watch(CharDriverState *s, GIOCondition cond, +guint qemu_chr_fe_add_watch(CharBackend *be, GIOCondition cond, GIOFunc func, void *user_data); /** @@ -277,9 +272,9 @@ guint qemu_chr_fe_add_watch(CharDriverState *s, GIOCondition cond, * @buf the data * @len the number of bytes to send * - * Returns: the number of bytes consumed + * Returns: the number of bytes consumed (0 if no assicated CharDriver) */ -int qemu_chr_fe_write(CharDriverState *s, const uint8_t *buf, int len); +int qemu_chr_fe_write(CharBackend *be, const uint8_t *buf, int len); /** * @qemu_chr_fe_write_all: @@ -292,9 +287,9 @@ int qemu_chr_fe_write(CharDriverState *s, const uint8_t *buf, int len); * @buf the data * @len the number of bytes to send * - * Returns: the number of bytes consumed + * Returns: the number of bytes consumed (0 if no assicated CharDriver) */ -int qemu_chr_fe_write_all(CharDriverState *s, const uint8_t *buf, int len); +int qemu_chr_fe_write_all(CharBackend *be, const uint8_t *buf, int len); /** * @qemu_chr_fe_read_all: @@ -304,9 +299,9 @@ int qemu_chr_fe_write_all(CharDriverState *s, const uint8_t *buf, int len); * @buf the data buffer * @len the number of bytes to read * - * Returns: the number of bytes read + * Returns: the number of bytes read (0 if no assicated CharDriver) */ -int qemu_chr_fe_read_all(CharDriverState *s, uint8_t *buf, int len); +int qemu_chr_fe_read_all(CharBackend *be, uint8_t *buf, int len); /** * @qemu_chr_fe_ioctl: @@ -316,10 +311,11 @@ int qemu_chr_fe_read_all(CharDriverState *s, uint8_t *buf, int len); * @cmd see CHR_IOCTL_* * @arg the data associated with @cmd * - * Returns: if @cmd is not supported by the backend, -ENOTSUP, otherwise the - * return value depends on the semantics of @cmd + * Returns: if @cmd is not supported by the backend or there is no + * associated CharDriver, -ENOTSUP, otherwise the return + * value depends on the semantics of @cmd */ -int qemu_chr_fe_ioctl(CharDriverState *s, int cmd, void *arg); +int qemu_chr_fe_ioctl(CharBackend *be, int cmd, void *arg); /** * @qemu_chr_fe_get_msgfd: @@ -332,7 +328,7 @@ int qemu_chr_fe_ioctl(CharDriverState *s, int cmd, void *arg); * this function will return -1 until a client sends a new file * descriptor. */ -int qemu_chr_fe_get_msgfd(CharDriverState *s); +int qemu_chr_fe_get_msgfd(CharBackend *be); /** * @qemu_chr_fe_get_msgfds: @@ -345,7 +341,7 @@ int qemu_chr_fe_get_msgfd(CharDriverState *s); * this function will return -1 until a client sends a new set of file * descriptors. */ -int qemu_chr_fe_get_msgfds(CharDriverState *s, int *fds, int num); +int qemu_chr_fe_get_msgfds(CharBackend *be, int *fds, int num); /** * @qemu_chr_fe_set_msgfds: @@ -356,38 +352,9 @@ int qemu_chr_fe_get_msgfds(CharDriverState *s, int *fds, int num); * result in overwriting the fd array with the new value without being send. * Upon writing the message the fd array is freed. * - * Returns: -1 if fd passing isn't supported. - */ -int qemu_chr_fe_set_msgfds(CharDriverState *s, int *fds, int num); - -/** - * @qemu_chr_fe_claim: - * - * Claim a backend before using it, should be called before calling - * qemu_chr_add_handlers(). - * - * Returns: -1 if the backend is already in use by another frontend, 0 on - * success. - */ -int qemu_chr_fe_claim(CharDriverState *s); - -/** - * @qemu_chr_fe_claim_no_fail: - * - * Like qemu_chr_fe_claim, but will exit qemu with an error when the - * backend is already in use. + * Returns: -1 if fd passing isn't supported or no associated CharDriver. */ -void qemu_chr_fe_claim_no_fail(CharDriverState *s); - -/** - * @qemu_chr_fe_claim: - * - * Release a backend for use by another frontend. - * - * Returns: -1 if the backend is already in use by another frontend, 0 on - * success. - */ -void qemu_chr_fe_release(CharDriverState *s); +int qemu_chr_fe_set_msgfds(CharBackend *be, int *fds, int num); /** * @qemu_chr_be_can_write: @@ -432,22 +399,70 @@ void qemu_chr_be_write_impl(CharDriverState *s, uint8_t *buf, int len); */ void qemu_chr_be_event(CharDriverState *s, int event); -void qemu_chr_add_handlers(CharDriverState *s, - IOCanReadHandler *fd_can_read, - IOReadHandler *fd_read, - IOEventHandler *fd_event, - void *opaque); +/** + * @qemu_chr_fe_init: + * + * Initializes a front end for the given CharBackend and + * CharDriver. Call qemu_chr_fe_deinit() to remove the association and + * release the driver. + * + * Returns: false on error. + */ +bool qemu_chr_fe_init(CharBackend *b, CharDriverState *s, Error **errp); + +/** + * @qemu_chr_fe_get_driver: + * + * Returns the driver associated with a CharBackend or NULL if no + * associated CharDriver. + */ +CharDriverState *qemu_chr_fe_get_driver(CharBackend *be); + +/** + * @qemu_chr_fe_deinit: + * + * Dissociate the CharBackend from the CharDriver. + * + * Safe to call without associated CharDriver. + */ +void qemu_chr_fe_deinit(CharBackend *b); -/* This API can make handler run in the context what you pass to. */ -void qemu_chr_add_handlers_full(CharDriverState *s, - IOCanReadHandler *fd_can_read, - IOReadHandler *fd_read, - IOEventHandler *fd_event, - void *opaque, - GMainContext *context); +/** + * @qemu_chr_fe_set_handlers: + * @b: a CharBackend + * @fd_can_read: callback to get the amount of data the frontend may + * receive + * @fd_read: callback to receive data from char + * @fd_event: event callback + * @opaque: an opaque pointer for the callbacks + * @context: a main loop context or NULL for the default + * @set_open: whether to call qemu_chr_fe_set_open() implicitely when + * any of the handler is non-NULL + * + * Set the front end char handlers. The front end takes the focus if + * any of the handler is non-NULL. + * + * Without associated CharDriver, nothing is changed. + */ +void qemu_chr_fe_set_handlers(CharBackend *b, + IOCanReadHandler *fd_can_read, + IOReadHandler *fd_read, + IOEventHandler *fd_event, + void *opaque, + GMainContext *context, + bool set_open); + +/** + * @qemu_chr_fe_take_focus: + * + * Take the focus (if the front end is muxed). + * + * Without associated CharDriver, nothing is changed. + */ +void qemu_chr_fe_take_focus(CharBackend *b); void qemu_chr_be_generic_open(CharDriverState *s); -void qemu_chr_accept_input(CharDriverState *s); +void qemu_chr_fe_accept_input(CharBackend *be); int qemu_chr_add_client(CharDriverState *s, int fd); CharDriverState *qemu_chr_find(const char *name); bool chr_is_ringbuf(const CharDriverState *chr); @@ -458,10 +473,15 @@ void qemu_chr_set_feature(CharDriverState *chr, CharDriverFeature feature); QemuOpts *qemu_chr_parse_compat(const char *label, const char *filename); +typedef void CharDriverParse(QemuOpts *opts, ChardevBackend *backend, + Error **errp); +typedef CharDriverState *CharDriverCreate(const char *id, + ChardevBackend *backend, + ChardevReturn *ret, bool *be_opened, + Error **errp); + void register_char_driver(const char *name, ChardevBackendKind kind, - void (*parse)(QemuOpts *opts, ChardevBackend *backend, Error **errp), - CharDriverState *(*create)(const char *id, ChardevBackend *backend, - ChardevReturn *ret, Error **errp)); + CharDriverParse *parse, CharDriverCreate *create); extern int term_escape_char; diff --git a/include/sysemu/dma.h b/include/sysemu/dma.h index 34c8eaf..c228c66 100644 --- a/include/sysemu/dma.h +++ b/include/sysemu/dma.h @@ -199,14 +199,14 @@ typedef BlockAIOCB *DMAIOFunc(int64_t offset, QEMUIOVector *iov, void *opaque); BlockAIOCB *dma_blk_io(AioContext *ctx, - QEMUSGList *sg, uint64_t offset, + QEMUSGList *sg, uint64_t offset, uint32_t align, DMAIOFunc *io_func, void *io_func_opaque, BlockCompletionFunc *cb, void *opaque, DMADirection dir); BlockAIOCB *dma_blk_read(BlockBackend *blk, - QEMUSGList *sg, uint64_t offset, + QEMUSGList *sg, uint64_t offset, uint32_t align, BlockCompletionFunc *cb, void *opaque); BlockAIOCB *dma_blk_write(BlockBackend *blk, - QEMUSGList *sg, uint64_t offset, + QEMUSGList *sg, uint64_t offset, uint32_t align, BlockCompletionFunc *cb, void *opaque); uint64_t dma_buf_read(uint8_t *ptr, int32_t len, QEMUSGList *sg); uint64_t dma_buf_write(uint8_t *ptr, int32_t len, QEMUSGList *sg); diff --git a/include/sysemu/sysemu.h b/include/sysemu/sysemu.h index b668833..66c6f15 100644 --- a/include/sysemu/sysemu.h +++ b/include/sysemu/sysemu.h @@ -173,7 +173,7 @@ extern int mem_prealloc; * * Note that cpu->get_arch_id() may be larger than MAX_CPUMASK_BITS. */ -#define MAX_CPUMASK_BITS 255 +#define MAX_CPUMASK_BITS 288 #define MAX_OPTION_ROMS 16 typedef struct QEMUOptionRom { @@ -2215,15 +2215,14 @@ int kvm_sw_breakpoints_active(CPUState *cpu) struct kvm_set_guest_debug_data { struct kvm_guest_debug dbg; - CPUState *cpu; int err; }; -static void kvm_invoke_set_guest_debug(CPUState *unused_cpu, void *data) +static void kvm_invoke_set_guest_debug(CPUState *cpu, void *data) { struct kvm_set_guest_debug_data *dbg_data = data; - dbg_data->err = kvm_vcpu_ioctl(dbg_data->cpu, KVM_SET_GUEST_DEBUG, + dbg_data->err = kvm_vcpu_ioctl(cpu, KVM_SET_GUEST_DEBUG, &dbg_data->dbg); } diff --git a/linux-user/main.c b/linux-user/main.c index 0e31dad..75b199f 100644 --- a/linux-user/main.c +++ b/linux-user/main.c @@ -354,6 +354,9 @@ void cpu_loop(CPUX86State *env) } } break; + case EXCP_ATOMIC: + cpu_exec_step_atomic(cs); + break; default: pc = env->segs[R_CS].base + env->eip; EXCP_DUMP(env, "qemu: 0x%08lx: unhandled CPU exception 0x%x - aborting\n", @@ -550,94 +553,6 @@ do_kernel_trap(CPUARMState *env) return 0; } -/* Store exclusive handling for AArch32 */ -static int do_strex(CPUARMState *env) -{ - uint64_t val; - int size; - int rc = 1; - int segv = 0; - uint32_t addr; - start_exclusive(); - if (env->exclusive_addr != env->exclusive_test) { - goto fail; - } - /* We know we're always AArch32 so the address is in uint32_t range - * unless it was the -1 exclusive-monitor-lost value (which won't - * match exclusive_test above). - */ - assert(extract64(env->exclusive_addr, 32, 32) == 0); - addr = env->exclusive_addr; - size = env->exclusive_info & 0xf; - switch (size) { - case 0: - segv = get_user_u8(val, addr); - break; - case 1: - segv = get_user_data_u16(val, addr, env); - break; - case 2: - case 3: - segv = get_user_data_u32(val, addr, env); - break; - default: - abort(); - } - if (segv) { - env->exception.vaddress = addr; - goto done; - } - if (size == 3) { - uint32_t valhi; - segv = get_user_data_u32(valhi, addr + 4, env); - if (segv) { - env->exception.vaddress = addr + 4; - goto done; - } - if (arm_cpu_bswap_data(env)) { - val = deposit64((uint64_t)valhi, 32, 32, val); - } else { - val = deposit64(val, 32, 32, valhi); - } - } - if (val != env->exclusive_val) { - goto fail; - } - - val = env->regs[(env->exclusive_info >> 8) & 0xf]; - switch (size) { - case 0: - segv = put_user_u8(val, addr); - break; - case 1: - segv = put_user_data_u16(val, addr, env); - break; - case 2: - case 3: - segv = put_user_data_u32(val, addr, env); - break; - } - if (segv) { - env->exception.vaddress = addr; - goto done; - } - if (size == 3) { - val = env->regs[(env->exclusive_info >> 12) & 0xf]; - segv = put_user_data_u32(val, addr + 4, env); - if (segv) { - env->exception.vaddress = addr + 4; - goto done; - } - } - rc = 0; -fail: - env->regs[15] += 4; - env->regs[(env->exclusive_info >> 4) & 0xf] = rc; -done: - end_exclusive(); - return segv; -} - void cpu_loop(CPUARMState *env) { CPUState *cs = CPU(arm_env_get_cpu(env)); @@ -806,14 +721,12 @@ void cpu_loop(CPUARMState *env) } } break; + case EXCP_SEMIHOST: + env->regs[0] = do_arm_semihosting(env); + break; case EXCP_INTERRUPT: /* just indicate that signals should be handled asap */ break; - case EXCP_STREX: - if (!do_strex(env)) { - break; - } - /* fall through for segv */ case EXCP_PREFETCH_ABORT: case EXCP_DATA_ABORT: addr = env->exception.vaddress; @@ -848,6 +761,9 @@ void cpu_loop(CPUARMState *env) case EXCP_YIELD: /* nothing to do here for user-mode, just resume guest code */ break; + case EXCP_ATOMIC: + cpu_exec_step_atomic(cs); + break; default: error: EXCP_DUMP(env, "qemu: unhandled CPU exception 0x%x - aborting\n", trapnr); @@ -859,124 +775,6 @@ void cpu_loop(CPUARMState *env) #else -/* - * Handle AArch64 store-release exclusive - * - * rs = gets the status result of store exclusive - * rt = is the register that is stored - * rt2 = is the second register store (in STP) - * - */ -static int do_strex_a64(CPUARMState *env) -{ - uint64_t val; - int size; - bool is_pair; - int rc = 1; - int segv = 0; - uint64_t addr; - int rs, rt, rt2; - - start_exclusive(); - /* size | is_pair << 2 | (rs << 4) | (rt << 9) | (rt2 << 14)); */ - size = extract32(env->exclusive_info, 0, 2); - is_pair = extract32(env->exclusive_info, 2, 1); - rs = extract32(env->exclusive_info, 4, 5); - rt = extract32(env->exclusive_info, 9, 5); - rt2 = extract32(env->exclusive_info, 14, 5); - - addr = env->exclusive_addr; - - if (addr != env->exclusive_test) { - goto finish; - } - - switch (size) { - case 0: - segv = get_user_u8(val, addr); - break; - case 1: - segv = get_user_u16(val, addr); - break; - case 2: - segv = get_user_u32(val, addr); - break; - case 3: - segv = get_user_u64(val, addr); - break; - default: - abort(); - } - if (segv) { - env->exception.vaddress = addr; - goto error; - } - if (val != env->exclusive_val) { - goto finish; - } - if (is_pair) { - if (size == 2) { - segv = get_user_u32(val, addr + 4); - } else { - segv = get_user_u64(val, addr + 8); - } - if (segv) { - env->exception.vaddress = addr + (size == 2 ? 4 : 8); - goto error; - } - if (val != env->exclusive_high) { - goto finish; - } - } - /* handle the zero register */ - val = rt == 31 ? 0 : env->xregs[rt]; - switch (size) { - case 0: - segv = put_user_u8(val, addr); - break; - case 1: - segv = put_user_u16(val, addr); - break; - case 2: - segv = put_user_u32(val, addr); - break; - case 3: - segv = put_user_u64(val, addr); - break; - } - if (segv) { - goto error; - } - if (is_pair) { - /* handle the zero register */ - val = rt2 == 31 ? 0 : env->xregs[rt2]; - if (size == 2) { - segv = put_user_u32(val, addr + 4); - } else { - segv = put_user_u64(val, addr + 8); - } - if (segv) { - env->exception.vaddress = addr + (size == 2 ? 4 : 8); - goto error; - } - } - rc = 0; -finish: - env->pc += 4; - /* rs == 31 encodes a write to the ZR, thus throwing away - * the status return. This is rather silly but valid. - */ - if (rs < 31) { - env->xregs[rs] = rc; - } -error: - /* instruction faulted, PC does not advance */ - /* either way a strex releases any exclusive lock we have */ - env->exclusive_addr = -1; - end_exclusive(); - return segv; -} - /* AArch64 main loop */ void cpu_loop(CPUARMState *env) { @@ -1018,11 +816,6 @@ void cpu_loop(CPUARMState *env) info._sifields._sigfault._addr = env->pc; queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); break; - case EXCP_STREX: - if (!do_strex_a64(env)) { - break; - } - /* fall through for segv */ case EXCP_PREFETCH_ABORT: case EXCP_DATA_ABORT: info.si_signo = TARGET_SIGSEGV; @@ -1048,6 +841,9 @@ void cpu_loop(CPUARMState *env) case EXCP_YIELD: /* nothing to do here for user-mode, just resume guest code */ break; + case EXCP_ATOMIC: + cpu_exec_step_atomic(cs); + break; default: EXCP_DUMP(env, "qemu: unhandled CPU exception 0x%x - aborting\n", trapnr); abort(); @@ -1055,8 +851,6 @@ void cpu_loop(CPUARMState *env) process_pending_signals(env); /* Exception return on AArch64 always clears the exclusive monitor, * so any return to running guest code implies this. - * A strex (successful or otherwise) also clears the monitor, so - * we don't need to specialcase EXCP_STREX. */ env->exclusive_addr = -1; } @@ -1139,6 +933,9 @@ void cpu_loop(CPUUniCore32State *env) } } break; + case EXCP_ATOMIC: + cpu_exec_step_atomic(cs); + break; default: goto error; } @@ -1412,6 +1209,9 @@ void cpu_loop (CPUSPARCState *env) } } break; + case EXCP_ATOMIC: + cpu_exec_step_atomic(cs); + break; default: printf ("Unhandled trap: 0x%x\n", trapnr); cpu_dump_state(cs, stderr, fprintf, 0); @@ -1951,6 +1751,9 @@ void cpu_loop(CPUPPCState *env) case EXCP_INTERRUPT: /* just indicate that signals should be handled asap */ break; + case EXCP_ATOMIC: + cpu_exec_step_atomic(cs); + break; default: cpu_abort(cs, "Unknown exception 0x%x. Aborting\n", trapnr); break; @@ -2295,8 +2098,8 @@ static const uint8_t mips_syscall_args[] = { MIPS_SYS(sys_dup3, 3) MIPS_SYS(sys_pipe2, 2) MIPS_SYS(sys_inotify_init1, 1) - MIPS_SYS(sys_preadv, 6) /* 4330 */ - MIPS_SYS(sys_pwritev, 6) + MIPS_SYS(sys_preadv, 5) /* 4330 */ + MIPS_SYS(sys_pwritev, 5) MIPS_SYS(sys_rt_tgsigqueueinfo, 4) MIPS_SYS(sys_perf_event_open, 5) MIPS_SYS(sys_accept4, 4) @@ -2308,6 +2111,26 @@ static const uint8_t mips_syscall_args[] = { MIPS_SYS(sys_open_by_handle_at, 3) /* 4340 */ MIPS_SYS(sys_clock_adjtime, 2) MIPS_SYS(sys_syncfs, 1) + MIPS_SYS(sys_sendmmsg, 4) + MIPS_SYS(sys_setns, 2) + MIPS_SYS(sys_process_vm_readv, 6) /* 345 */ + MIPS_SYS(sys_process_vm_writev, 6) + MIPS_SYS(sys_kcmp, 5) + MIPS_SYS(sys_finit_module, 3) + MIPS_SYS(sys_sched_setattr, 2) + MIPS_SYS(sys_sched_getattr, 3) /* 350 */ + MIPS_SYS(sys_renameat2, 5) + MIPS_SYS(sys_seccomp, 3) + MIPS_SYS(sys_getrandom, 3) + MIPS_SYS(sys_memfd_create, 2) + MIPS_SYS(sys_bpf, 3) /* 355 */ + MIPS_SYS(sys_execveat, 5) + MIPS_SYS(sys_userfaultfd, 1) + MIPS_SYS(sys_membarrier, 2) + MIPS_SYS(sys_mlock2, 3) + MIPS_SYS(sys_copy_file_range, 6) /* 360 */ + MIPS_SYS(sys_preadv2, 6) + MIPS_SYS(sys_pwritev2, 6) }; # undef MIPS_SYS # endif /* O32 */ @@ -2626,6 +2449,9 @@ done_syscall: } } break; + case EXCP_ATOMIC: + cpu_exec_step_atomic(cs); + break; default: error: EXCP_DUMP(env, "qemu: unhandled CPU exception 0x%x - aborting\n", trapnr); @@ -2713,6 +2539,9 @@ void cpu_loop(CPUOpenRISCState *env) case EXCP_NR: qemu_log_mask(CPU_LOG_INT, "\nNR\n"); break; + case EXCP_ATOMIC: + cpu_exec_step_atomic(cs); + break; default: EXCP_DUMP(env, "\nqemu: unhandled CPU exception %#x - aborting\n", trapnr); @@ -2789,6 +2618,9 @@ void cpu_loop(CPUSH4State *env) queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); break; + case EXCP_ATOMIC: + cpu_exec_step_atomic(cs); + break; default: printf ("Unhandled trap: 0x%x\n", trapnr); cpu_dump_state(cs, stderr, fprintf, 0); @@ -2856,6 +2688,9 @@ void cpu_loop(CPUCRISState *env) } } break; + case EXCP_ATOMIC: + cpu_exec_step_atomic(cs); + break; default: printf ("Unhandled trap: 0x%x\n", trapnr); cpu_dump_state(cs, stderr, fprintf, 0); @@ -2972,6 +2807,9 @@ void cpu_loop(CPUMBState *env) } } break; + case EXCP_ATOMIC: + cpu_exec_step_atomic(cs); + break; default: printf ("Unhandled trap: 0x%x\n", trapnr); cpu_dump_state(cs, stderr, fprintf, 0); @@ -3075,6 +2913,9 @@ void cpu_loop(CPUM68KState *env) } } break; + case EXCP_ATOMIC: + cpu_exec_step_atomic(cs); + break; default: EXCP_DUMP(env, "qemu: unhandled CPU exception 0x%x - aborting\n", trapnr); abort(); @@ -3085,51 +2926,6 @@ void cpu_loop(CPUM68KState *env) #endif /* TARGET_M68K */ #ifdef TARGET_ALPHA -static void do_store_exclusive(CPUAlphaState *env, int reg, int quad) -{ - target_ulong addr, val, tmp; - target_siginfo_t info; - int ret = 0; - - addr = env->lock_addr; - tmp = env->lock_st_addr; - env->lock_addr = -1; - env->lock_st_addr = 0; - - start_exclusive(); - mmap_lock(); - - if (addr == tmp) { - if (quad ? get_user_s64(val, addr) : get_user_s32(val, addr)) { - goto do_sigsegv; - } - - if (val == env->lock_value) { - tmp = env->ir[reg]; - if (quad ? put_user_u64(tmp, addr) : put_user_u32(tmp, addr)) { - goto do_sigsegv; - } - ret = 1; - } - } - env->ir[reg] = ret; - env->pc += 4; - - mmap_unlock(); - end_exclusive(); - return; - - do_sigsegv: - mmap_unlock(); - end_exclusive(); - - info.si_signo = TARGET_SIGSEGV; - info.si_errno = 0; - info.si_code = TARGET_SEGV_MAPERR; - info._sifields._sigfault._addr = addr; - queue_signal(env, TARGET_SIGSEGV, QEMU_SI_FAULT, &info); -} - void cpu_loop(CPUAlphaState *env) { CPUState *cs = CPU(alpha_env_get_cpu(env)); @@ -3304,13 +3100,12 @@ void cpu_loop(CPUAlphaState *env) queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); } break; - case EXCP_STL_C: - case EXCP_STQ_C: - do_store_exclusive(env, env->error_code, trapnr - EXCP_STL_C); - break; case EXCP_INTERRUPT: /* Just indicate that signals should be handled asap. */ break; + case EXCP_ATOMIC: + cpu_exec_step_atomic(cs); + break; default: printf ("Unhandled trap: 0x%x\n", trapnr); cpu_dump_state(cs, stderr, fprintf, 0); @@ -3440,6 +3235,9 @@ void cpu_loop(CPUS390XState *env) queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); break; + case EXCP_ATOMIC: + cpu_exec_step_atomic(cs); + break; default: fprintf(stderr, "Unhandled trap: 0x%x\n", trapnr); cpu_dump_state(cs, stderr, fprintf, 0); @@ -3694,6 +3492,9 @@ void cpu_loop(CPUTLGState *env) case TILEGX_EXCP_REG_UDN_ACCESS: gen_sigill_reg(env); break; + case EXCP_ATOMIC: + cpu_exec_step_atomic(cs); + break; default: fprintf(stderr, "trapnr is %d[0x%x].\n", trapnr, trapnr); g_assert_not_reached(); @@ -3936,7 +3737,7 @@ static void handle_arg_strace(const char *arg) static void handle_arg_version(const char *arg) { printf("qemu-" TARGET_NAME " version " QEMU_VERSION QEMU_PKGVERSION - ", " QEMU_COPYRIGHT "\n"); + "\n" QEMU_COPYRIGHT "\n"); exit(EXIT_SUCCESS); } diff --git a/linux-user/mips/syscall_nr.h b/linux-user/mips/syscall_nr.h index 6819f86..ced3280 100644 --- a/linux-user/mips/syscall_nr.h +++ b/linux-user/mips/syscall_nr.h @@ -256,7 +256,7 @@ #define TARGET_NR_remap_file_pages (TARGET_NR_Linux + 251) #define TARGET_NR_set_tid_address (TARGET_NR_Linux + 252) #define TARGET_NR_restart_syscall (TARGET_NR_Linux + 253) -#define TARGET_NR_fadvise64 (TARGET_NR_Linux + 254) +#define TARGET_NR_fadvise64_64 (TARGET_NR_Linux + 254) #define TARGET_NR_statfs64 (TARGET_NR_Linux + 255) #define TARGET_NR_fstatfs64 (TARGET_NR_Linux + 256) #define TARGET_NR_timer_create (TARGET_NR_Linux + 257) diff --git a/linux-user/mips/termbits.h b/linux-user/mips/termbits.h index d3a6cf8..a0bcad0 100644 --- a/linux-user/mips/termbits.h +++ b/linux-user/mips/termbits.h @@ -219,8 +219,20 @@ struct target_termios { #define TARGET_TIOCSBRK 0x5427 /* BSD compatibility */ #define TARGET_TIOCCBRK 0x5428 /* BSD compatibility */ #define TARGET_TIOCGSID 0x7416 /* Return the session ID of FD */ +#define TARGET_TCGETS2 TARGET_IOR('T', 0x2A, struct termios2) +#define TARGET_TCSETS2 TARGET_IOW('T', 0x2B, struct termios2) +#define TARGET_TCSETSW2 TARGET_IOW('T', 0x2C, struct termios2) +#define TARGET_TCSETSF2 TARGET_IOW('T', 0x2D, struct termios2) +#define TARGET_TIOCGRS485 TARGET_IOR('T', 0x2E, struct serial_rs485) +#define TARGET_TIOCSRS485 TARGET_IOWR('T', 0x2F, struct serial_rs485) #define TARGET_TIOCGPTN TARGET_IOR('T',0x30, unsigned int) /* Get Pty Number (of pty-mux device) */ #define TARGET_TIOCSPTLCK TARGET_IOW('T',0x31, int) /* Lock/unlock Pty */ +#define TARGET_TIOCGDEV TARGET_IOR('T', 0x32, unsigned int) +#define TARGET_TIOCSIG TARGET_IOW('T', 0x36, int) +#define TARGET_TIOCVHANGUP 0x5437 +#define TARGET_TIOCGPKT TARGET_IOR('T', 0x38, int) +#define TARGET_TIOCGPTLCK TARGET_IOR('T', 0x39, int) +#define TARGET_TIOCGEXCL TARGET_IOR('T', 0x40, int) /* I hope the range from 0x5480 on is free ... */ #define TARGET_TIOCSCTTY 0x5480 /* become controlling tty */ diff --git a/linux-user/mips64/termbits.h b/linux-user/mips64/termbits.h index d3a6cf8..d0a271e 100644 --- a/linux-user/mips64/termbits.h +++ b/linux-user/mips64/termbits.h @@ -1,245 +1,2 @@ -/* from asm/termbits.h */ +#include "../mips/termbits.h" -#define TARGET_NCCS 23 - -struct target_termios { - unsigned int c_iflag; /* input mode flags */ - unsigned int c_oflag; /* output mode flags */ - unsigned int c_cflag; /* control mode flags */ - unsigned int c_lflag; /* local mode flags */ - unsigned char c_line; /* line discipline */ - unsigned char c_cc[TARGET_NCCS]; /* control characters */ -}; - -/* c_iflag bits */ -#define TARGET_IGNBRK 0000001 -#define TARGET_BRKINT 0000002 -#define TARGET_IGNPAR 0000004 -#define TARGET_PARMRK 0000010 -#define TARGET_INPCK 0000020 -#define TARGET_ISTRIP 0000040 -#define TARGET_INLCR 0000100 -#define TARGET_IGNCR 0000200 -#define TARGET_ICRNL 0000400 -#define TARGET_IUCLC 0001000 -#define TARGET_IXON 0002000 -#define TARGET_IXANY 0004000 -#define TARGET_IXOFF 0010000 -#define TARGET_IMAXBEL 0020000 -#define TARGET_IUTF8 0040000 - -/* c_oflag bits */ -#define TARGET_OPOST 0000001 -#define TARGET_OLCUC 0000002 -#define TARGET_ONLCR 0000004 -#define TARGET_OCRNL 0000010 -#define TARGET_ONOCR 0000020 -#define TARGET_ONLRET 0000040 -#define TARGET_OFILL 0000100 -#define TARGET_OFDEL 0000200 -#define TARGET_NLDLY 0000400 -#define TARGET_NL0 0000000 -#define TARGET_NL1 0000400 -#define TARGET_CRDLY 0003000 -#define TARGET_CR0 0000000 -#define TARGET_CR1 0001000 -#define TARGET_CR2 0002000 -#define TARGET_CR3 0003000 -#define TARGET_TABDLY 0014000 -#define TARGET_TAB0 0000000 -#define TARGET_TAB1 0004000 -#define TARGET_TAB2 0010000 -#define TARGET_TAB3 0014000 -#define TARGET_XTABS 0014000 -#define TARGET_BSDLY 0020000 -#define TARGET_BS0 0000000 -#define TARGET_BS1 0020000 -#define TARGET_VTDLY 0040000 -#define TARGET_VT0 0000000 -#define TARGET_VT1 0040000 -#define TARGET_FFDLY 0100000 -#define TARGET_FF0 0000000 -#define TARGET_FF1 0100000 - -/* c_cflag bit meaning */ -#define TARGET_CBAUD 0010017 -#define TARGET_B0 0000000 /* hang up */ -#define TARGET_B50 0000001 -#define TARGET_B75 0000002 -#define TARGET_B110 0000003 -#define TARGET_B134 0000004 -#define TARGET_B150 0000005 -#define TARGET_B200 0000006 -#define TARGET_B300 0000007 -#define TARGET_B600 0000010 -#define TARGET_B1200 0000011 -#define TARGET_B1800 0000012 -#define TARGET_B2400 0000013 -#define TARGET_B4800 0000014 -#define TARGET_B9600 0000015 -#define TARGET_B19200 0000016 -#define TARGET_B38400 0000017 -#define TARGET_EXTA B19200 -#define TARGET_EXTB B38400 -#define TARGET_CSIZE 0000060 -#define TARGET_CS5 0000000 -#define TARGET_CS6 0000020 -#define TARGET_CS7 0000040 -#define TARGET_CS8 0000060 -#define TARGET_CSTOPB 0000100 -#define TARGET_CREAD 0000200 -#define TARGET_PARENB 0000400 -#define TARGET_PARODD 0001000 -#define TARGET_HUPCL 0002000 -#define TARGET_CLOCAL 0004000 -#define TARGET_CBAUDEX 0010000 -#define TARGET_BOTHER 0010000 -#define TARGET_B57600 0010001 -#define TARGET_B115200 0010002 -#define TARGET_B230400 0010003 -#define TARGET_B460800 0010004 -#define TARGET_B500000 0010005 -#define TARGET_B576000 0010006 -#define TARGET_B921600 0010007 -#define TARGET_B1000000 0010010 -#define TARGET_B1152000 0010011 -#define TARGET_B1500000 0010012 -#define TARGET_B2000000 0010013 -#define TARGET_B2500000 0010014 -#define TARGET_B3000000 0010015 -#define TARGET_B3500000 0010016 -#define TARGET_B4000000 0010017 -#define TARGET_CIBAUD 002003600000 /* input baud rate (not used) */ -#define TARGET_CMSPAR 010000000000 /* mark or space (stick) parity */ -#define TARGET_CRTSCTS 020000000000 /* flow control */ - -/* c_lflag bits */ -#define TARGET_ISIG 0000001 -#define TARGET_ICANON 0000002 -#define TARGET_XCASE 0000004 -#define TARGET_ECHO 0000010 -#define TARGET_ECHOE 0000020 -#define TARGET_ECHOK 0000040 -#define TARGET_ECHONL 0000100 -#define TARGET_NOFLSH 0000200 -#define TARGET_IEXTEN 0000400 -#define TARGET_ECHOCTL 0001000 -#define TARGET_ECHOPRT 0002000 -#define TARGET_ECHOKE 0004000 -#define TARGET_FLUSHO 0010000 -#define TARGET_PENDIN 0040000 -#define TARGET_TOSTOP 0100000 -#define TARGET_ITOSTOP TARGET_TOSTOP - -/* c_cc character offsets */ -#define TARGET_VINTR 0 -#define TARGET_VQUIT 1 -#define TARGET_VERASE 2 -#define TARGET_VKILL 3 -#define TARGET_VMIN 4 -#define TARGET_VTIME 5 -#define TARGET_VEOL2 6 -#define TARGET_VSWTC 7 -#define TARGET_VSTART 8 -#define TARGET_VSTOP 9 -#define TARGET_VSUSP 10 -/* VDSUSP not supported */ -#define TARGET_VREPRINT 12 -#define TARGET_VDISCARD 13 -#define TARGET_VWERASE 14 -#define TARGET_VLNEXT 15 -#define TARGET_VEOF 16 -#define TARGET_VEOL 17 - -/* ioctls */ - -#define TARGET_TCGETA 0x5401 -#define TARGET_TCSETA 0x5402 /* Clashes with SNDCTL_TMR_START sound ioctl */ -#define TARGET_TCSETAW 0x5403 -#define TARGET_TCSETAF 0x5404 - -#define TARGET_TCSBRK 0x5405 -#define TARGET_TCXONC 0x5406 -#define TARGET_TCFLSH 0x5407 - -#define TARGET_TCGETS 0x540d -#define TARGET_TCSETS 0x540e -#define TARGET_TCSETSW 0x540f -#define TARGET_TCSETSF 0x5410 - -#define TARGET_TIOCEXCL 0x740d /* set exclusive use of tty */ -#define TARGET_TIOCNXCL 0x740e /* reset exclusive use of tty */ -#define TARGET_TIOCOUTQ 0x7472 /* output queue size */ -#define TARGET_TIOCSTI 0x5472 /* simulate terminal input */ -#define TARGET_TIOCMGET 0x741d /* get all modem bits */ -#define TARGET_TIOCMBIS 0x741b /* bis modem bits */ -#define TARGET_TIOCMBIC 0x741c /* bic modem bits */ -#define TARGET_TIOCMSET 0x741a /* set all modem bits */ -#define TARGET_TIOCPKT 0x5470 /* pty: set/clear packet mode */ -#define TARGET_TIOCPKT_DATA 0x00 /* data packet */ -#define TARGET_TIOCPKT_FLUSHREAD 0x01 /* flush packet */ -#define TARGET_TIOCPKT_FLUSHWRITE 0x02 /* flush packet */ -#define TARGET_TIOCPKT_STOP 0x04 /* stop output */ -#define TARGET_TIOCPKT_START 0x08 /* start output */ -#define TARGET_TIOCPKT_NOSTOP 0x10 /* no more ^S, ^Q */ -#define TARGET_TIOCPKT_DOSTOP 0x20 /* now do ^S ^Q */ -/* #define TIOCPKT_IOCTL 0x40 state change of pty driver */ -#define TARGET_TIOCSWINSZ TARGET_IOW('t', 103, struct winsize) /* set window size */ -#define TARGET_TIOCGWINSZ TARGET_IOR('t', 104, struct winsize) /* get window size */ -#define TARGET_TIOCNOTTY 0x5471 /* void tty association */ -#define TARGET_TIOCSETD 0x7401 -#define TARGET_TIOCGETD 0x7400 - -#define TARGET_FIOCLEX 0x6601 -#define TARGET_FIONCLEX 0x6602 -#define TARGET_FIOASYNC 0x667d -#define TARGET_FIONBIO 0x667e -#define TARGET_FIOQSIZE 0x667f - -#define TARGET_TIOCGLTC 0x7474 /* get special local chars */ -#define TARGET_TIOCSLTC 0x7475 /* set special local chars */ -#define TARGET_TIOCSPGRP TARGET_IOW('t', 118, int) /* set pgrp of tty */ -#define TARGET_TIOCGPGRP TARGET_IOR('t', 119, int) /* get pgrp of tty */ -#define TARGET_TIOCCONS TARGET_IOW('t', 120, int) /* become virtual console */ - -#define TARGET_FIONREAD 0x467f -#define TARGET_TIOCINQ TARGET_FIONREAD - -#define TARGET_TIOCGETP 0x7408 -#define TARGET_TIOCSETP 0x7409 -#define TARGET_TIOCSETN 0x740a /* TIOCSETP wo flush */ - -/* #define TARGET_TIOCSETA TARGET_IOW('t', 20, struct termios) set termios struct */ -/* #define TARGET_TIOCSETAW TARGET_IOW('t', 21, struct termios) drain output, set */ -/* #define TARGET_TIOCSETAF TARGET_IOW('t', 22, struct termios) drn out, fls in, set */ -/* #define TARGET_TIOCGETD TARGET_IOR('t', 26, int) get line discipline */ -/* #define TARGET_TIOCSETD TARGET_IOW('t', 27, int) set line discipline */ - /* 127-124 compat */ - -#define TARGET_TIOCSBRK 0x5427 /* BSD compatibility */ -#define TARGET_TIOCCBRK 0x5428 /* BSD compatibility */ -#define TARGET_TIOCGSID 0x7416 /* Return the session ID of FD */ -#define TARGET_TIOCGPTN TARGET_IOR('T',0x30, unsigned int) /* Get Pty Number (of pty-mux device) */ -#define TARGET_TIOCSPTLCK TARGET_IOW('T',0x31, int) /* Lock/unlock Pty */ - -/* I hope the range from 0x5480 on is free ... */ -#define TARGET_TIOCSCTTY 0x5480 /* become controlling tty */ -#define TARGET_TIOCGSOFTCAR 0x5481 -#define TARGET_TIOCSSOFTCAR 0x5482 -#define TARGET_TIOCLINUX 0x5483 -#define TARGET_TIOCGSERIAL 0x5484 -#define TARGET_TIOCSSERIAL 0x5485 -#define TARGET_TCSBRKP 0x5486 /* Needed for POSIX tcsendbreak() */ -#define TARGET_TIOCSERCONFIG 0x5488 -#define TARGET_TIOCSERGWILD 0x5489 -#define TARGET_TIOCSERSWILD 0x548a -#define TARGET_TIOCGLCKTRMIOS 0x548b -#define TARGET_TIOCSLCKTRMIOS 0x548c -#define TARGET_TIOCSERGSTRUCT 0x548d /* For debugging only */ -#define TARGET_TIOCSERGETLSR 0x548e /* Get line status register */ -#define TARGET_TIOCSERGETMULTI 0x548f /* Get multiport config */ -#define TARGET_TIOCSERSETMULTI 0x5490 /* Set multiport config */ -#define TARGET_TIOCMIWAIT 0x5491 /* wait for a change on serial input line(s) */ -#define TARGET_TIOCGICOUNT 0x5492 /* read serial port inline interrupt counts */ -#define TARGET_TIOCGHAYESESP 0x5493 /* Get Hayes ESP configuration */ -#define TARGET_TIOCSHAYESESP 0x5494 /* Set Hayes ESP configuration */ diff --git a/linux-user/mmap.c b/linux-user/mmap.c index c4371d9..ffd099d 100644 --- a/linux-user/mmap.c +++ b/linux-user/mmap.c @@ -17,8 +17,6 @@ * along with this program; if not, see <http://www.gnu.org/licenses/>. */ #include "qemu/osdep.h" -#include <linux/mman.h> -#include <linux/unistd.h> #include "qemu.h" #include "qemu-common.h" @@ -681,10 +679,8 @@ abi_long target_mremap(abi_ulong old_addr, abi_ulong old_size, mmap_lock(); if (flags & MREMAP_FIXED) { - host_addr = (void *) syscall(__NR_mremap, g2h(old_addr), - old_size, new_size, - flags, - g2h(new_addr)); + host_addr = mremap(g2h(old_addr), old_size, new_size, + flags, g2h(new_addr)); if (reserved_va && host_addr != MAP_FAILED) { /* If new and old addresses overlap then the above mremap will @@ -700,10 +696,8 @@ abi_long target_mremap(abi_ulong old_addr, abi_ulong old_size, errno = ENOMEM; host_addr = MAP_FAILED; } else { - host_addr = (void *) syscall(__NR_mremap, g2h(old_addr), - old_size, new_size, - flags | MREMAP_FIXED, - g2h(mmap_start)); + host_addr = mremap(g2h(old_addr), old_size, new_size, + flags | MREMAP_FIXED, g2h(mmap_start)); if (reserved_va) { mmap_reserve(old_addr, old_size); } diff --git a/linux-user/sparc64/target_syscall.h b/linux-user/sparc64/target_syscall.h index b7e3bf8..2cbbaae 100644 --- a/linux-user/sparc64/target_syscall.h +++ b/linux-user/sparc64/target_syscall.h @@ -23,4 +23,11 @@ struct target_pt_regs { #define TARGET_MLOCKALL_MCL_CURRENT 0x2000 #define TARGET_MLOCKALL_MCL_FUTURE 0x4000 +#define TARGET_FORCE_SHMLBA + +static inline abi_ulong target_shmlba(CPUSPARCState *env) +{ + return MAX(TARGET_PAGE_SIZE, 16 * 1024); +} + #endif /* SPARC64_TARGET_SYSCALL_H */ diff --git a/linux-user/strace.c b/linux-user/strace.c index 1e51360..489dbc9 100644 --- a/linux-user/strace.c +++ b/linux-user/strace.c @@ -435,6 +435,69 @@ print_fdset(int n, abi_ulong target_fds_addr) } #endif +#ifdef TARGET_NR_clock_adjtime +/* IDs of the various system clocks */ +#define TARGET_CLOCK_REALTIME 0 +#define TARGET_CLOCK_MONOTONIC 1 +#define TARGET_CLOCK_PROCESS_CPUTIME_ID 2 +#define TARGET_CLOCK_THREAD_CPUTIME_ID 3 +#define TARGET_CLOCK_MONOTONIC_RAW 4 +#define TARGET_CLOCK_REALTIME_COARSE 5 +#define TARGET_CLOCK_MONOTONIC_COARSE 6 +#define TARGET_CLOCK_BOOTTIME 7 +#define TARGET_CLOCK_REALTIME_ALARM 8 +#define TARGET_CLOCK_BOOTTIME_ALARM 9 +#define TARGET_CLOCK_SGI_CYCLE 10 +#define TARGET_CLOCK_TAI 11 + +static void +print_clockid(int clockid, int last) +{ + switch (clockid) { + case TARGET_CLOCK_REALTIME: + gemu_log("CLOCK_REALTIME"); + break; + case TARGET_CLOCK_MONOTONIC: + gemu_log("CLOCK_MONOTONIC"); + break; + case TARGET_CLOCK_PROCESS_CPUTIME_ID: + gemu_log("CLOCK_PROCESS_CPUTIME_ID"); + break; + case TARGET_CLOCK_THREAD_CPUTIME_ID: + gemu_log("CLOCK_THREAD_CPUTIME_ID"); + break; + case TARGET_CLOCK_MONOTONIC_RAW: + gemu_log("CLOCK_MONOTONIC_RAW"); + break; + case TARGET_CLOCK_REALTIME_COARSE: + gemu_log("CLOCK_REALTIME_COARSE"); + break; + case TARGET_CLOCK_MONOTONIC_COARSE: + gemu_log("CLOCK_MONOTONIC_COARSE"); + break; + case TARGET_CLOCK_BOOTTIME: + gemu_log("CLOCK_BOOTTIME"); + break; + case TARGET_CLOCK_REALTIME_ALARM: + gemu_log("CLOCK_REALTIME_ALARM"); + break; + case TARGET_CLOCK_BOOTTIME_ALARM: + gemu_log("CLOCK_BOOTTIME_ALARM"); + break; + case TARGET_CLOCK_SGI_CYCLE: + gemu_log("CLOCK_SGI_CYCLE"); + break; + case TARGET_CLOCK_TAI: + gemu_log("CLOCK_TAI"); + break; + default: + gemu_log("%d", clockid); + break; + } + gemu_log("%s", get_comma(last)); +} +#endif + /* * Sysycall specific output functions */ @@ -577,6 +640,52 @@ print_syscall_ret_newselect(const struct syscallname *name, abi_long ret) } #endif +/* special meanings of adjtimex()' non-negative return values */ +#define TARGET_TIME_OK 0 /* clock synchronized, no leap second */ +#define TARGET_TIME_INS 1 /* insert leap second */ +#define TARGET_TIME_DEL 2 /* delete leap second */ +#define TARGET_TIME_OOP 3 /* leap second in progress */ +#define TARGET_TIME_WAIT 4 /* leap second has occurred */ +#define TARGET_TIME_ERROR 5 /* clock not synchronized */ +static void +print_syscall_ret_adjtimex(const struct syscallname *name, abi_long ret) +{ + const char *errstr = NULL; + + gemu_log(" = "); + if (ret < 0) { + gemu_log("-1 errno=%d", errno); + errstr = target_strerror(-ret); + if (errstr) { + gemu_log(" (%s)", errstr); + } + } else { + gemu_log(TARGET_ABI_FMT_ld, ret); + switch (ret) { + case TARGET_TIME_OK: + gemu_log(" TIME_OK (clock synchronized, no leap second)"); + break; + case TARGET_TIME_INS: + gemu_log(" TIME_INS (insert leap second)"); + break; + case TARGET_TIME_DEL: + gemu_log(" TIME_DEL (delete leap second)"); + break; + case TARGET_TIME_OOP: + gemu_log(" TIME_OOP (leap second in progress)"); + break; + case TARGET_TIME_WAIT: + gemu_log(" TIME_WAIT (leap second has occurred)"); + break; + case TARGET_TIME_ERROR: + gemu_log(" TIME_ERROR (clock not synchronized)"); + break; + } + } + + gemu_log("\n"); +} + UNUSED static struct flags access_flags[] = { FLAG_GENERIC(F_OK), FLAG_GENERIC(R_OK), @@ -1050,6 +1159,19 @@ print_chmod(const struct syscallname *name, } #endif +#ifdef TARGET_NR_clock_adjtime +static void +print_clock_adjtime(const struct syscallname *name, + abi_long arg0, abi_long arg1, abi_long arg2, + abi_long arg3, abi_long arg4, abi_long arg5) +{ + print_syscall_prologue(name); + print_clockid(arg0, 0); + print_pointer(arg1, 1); + print_syscall_epilogue(name); +} +#endif + #ifdef TARGET_NR_clone static void do_print_clone(unsigned int flags, abi_ulong newsp, abi_ulong parent_tidptr, target_ulong newtls, @@ -1629,29 +1751,32 @@ print_optint: } #define PRINT_SOCKOP(name, func) \ - [SOCKOP_##name] = { #name, func } + [TARGET_SYS_##name] = { #name, func } static struct { const char *name; void (*print)(const char *, abi_long); } scall[] = { - PRINT_SOCKOP(socket, do_print_socket), - PRINT_SOCKOP(bind, do_print_sockaddr), - PRINT_SOCKOP(connect, do_print_sockaddr), - PRINT_SOCKOP(listen, do_print_listen), - PRINT_SOCKOP(accept, do_print_sockaddr), - PRINT_SOCKOP(getsockname, do_print_sockaddr), - PRINT_SOCKOP(getpeername, do_print_sockaddr), - PRINT_SOCKOP(socketpair, do_print_socketpair), - PRINT_SOCKOP(send, do_print_sendrecv), - PRINT_SOCKOP(recv, do_print_sendrecv), - PRINT_SOCKOP(sendto, do_print_msgaddr), - PRINT_SOCKOP(recvfrom, do_print_msgaddr), - PRINT_SOCKOP(shutdown, do_print_shutdown), - PRINT_SOCKOP(sendmsg, do_print_msg), - PRINT_SOCKOP(recvmsg, do_print_msg), - PRINT_SOCKOP(setsockopt, do_print_sockopt), - PRINT_SOCKOP(getsockopt, do_print_sockopt), + PRINT_SOCKOP(SOCKET, do_print_socket), + PRINT_SOCKOP(BIND, do_print_sockaddr), + PRINT_SOCKOP(CONNECT, do_print_sockaddr), + PRINT_SOCKOP(LISTEN, do_print_listen), + PRINT_SOCKOP(ACCEPT, do_print_sockaddr), + PRINT_SOCKOP(GETSOCKNAME, do_print_sockaddr), + PRINT_SOCKOP(GETPEERNAME, do_print_sockaddr), + PRINT_SOCKOP(SOCKETPAIR, do_print_socketpair), + PRINT_SOCKOP(SEND, do_print_sendrecv), + PRINT_SOCKOP(RECV, do_print_sendrecv), + PRINT_SOCKOP(SENDTO, do_print_msgaddr), + PRINT_SOCKOP(RECVFROM, do_print_msgaddr), + PRINT_SOCKOP(SHUTDOWN, do_print_shutdown), + PRINT_SOCKOP(SETSOCKOPT, do_print_sockopt), + PRINT_SOCKOP(GETSOCKOPT, do_print_sockopt), + PRINT_SOCKOP(SENDMSG, do_print_msg), + PRINT_SOCKOP(RECVMSG, do_print_msg), + PRINT_SOCKOP(ACCEPT4, NULL), + PRINT_SOCKOP(RECVMMSG, NULL), + PRINT_SOCKOP(SENDMMSG, NULL), }; static void @@ -1778,6 +1903,78 @@ print_rt_sigprocmask(const struct syscallname *name, } #endif +#ifdef TARGET_NR_syslog +static void +print_syslog_action(abi_ulong arg, int last) +{ + const char *type; + + switch (arg) { + case TARGET_SYSLOG_ACTION_CLOSE: { + type = "SYSLOG_ACTION_CLOSE"; + break; + } + case TARGET_SYSLOG_ACTION_OPEN: { + type = "SYSLOG_ACTION_OPEN"; + break; + } + case TARGET_SYSLOG_ACTION_READ: { + type = "SYSLOG_ACTION_READ"; + break; + } + case TARGET_SYSLOG_ACTION_READ_ALL: { + type = "SYSLOG_ACTION_READ_ALL"; + break; + } + case TARGET_SYSLOG_ACTION_READ_CLEAR: { + type = "SYSLOG_ACTION_READ_CLEAR"; + break; + } + case TARGET_SYSLOG_ACTION_CLEAR: { + type = "SYSLOG_ACTION_CLEAR"; + break; + } + case TARGET_SYSLOG_ACTION_CONSOLE_OFF: { + type = "SYSLOG_ACTION_CONSOLE_OFF"; + break; + } + case TARGET_SYSLOG_ACTION_CONSOLE_ON: { + type = "SYSLOG_ACTION_CONSOLE_ON"; + break; + } + case TARGET_SYSLOG_ACTION_CONSOLE_LEVEL: { + type = "SYSLOG_ACTION_CONSOLE_LEVEL"; + break; + } + case TARGET_SYSLOG_ACTION_SIZE_UNREAD: { + type = "SYSLOG_ACTION_SIZE_UNREAD"; + break; + } + case TARGET_SYSLOG_ACTION_SIZE_BUFFER: { + type = "SYSLOG_ACTION_SIZE_BUFFER"; + break; + } + default: { + print_raw_param("%ld", arg, last); + return; + } + } + gemu_log("%s%s", type, get_comma(last)); +} + +static void +print_syslog(const struct syscallname *name, + abi_long arg0, abi_long arg1, abi_long arg2, + abi_long arg3, abi_long arg4, abi_long arg5) +{ + print_syscall_prologue(name); + print_syslog_action(arg0, 0); + print_pointer(arg1, 0); + print_raw_param("%d", arg2, 1); + print_syscall_epilogue(name); +} +#endif + #ifdef TARGET_NR_mknod static void print_mknod(const struct syscallname *name, diff --git a/linux-user/strace.list b/linux-user/strace.list index 608f7e0..3b1282e 100644 --- a/linux-user/strace.list +++ b/linux-user/strace.list @@ -19,7 +19,8 @@ { TARGET_NR_add_key, "add_key" , NULL, NULL, NULL }, #endif #ifdef TARGET_NR_adjtimex -{ TARGET_NR_adjtimex, "adjtimex" , NULL, NULL, NULL }, +{ TARGET_NR_adjtimex, "adjtimex" , "%s(%p)", NULL, + print_syscall_ret_adjtimex }, #endif #ifdef TARGET_NR_afs_syscall { TARGET_NR_afs_syscall, "afs_syscall" , NULL, NULL, NULL }, @@ -78,6 +79,9 @@ #ifdef TARGET_NR_chroot { TARGET_NR_chroot, "chroot" , NULL, NULL, NULL }, #endif +#ifdef TARGET_NR_clock_adjtime +{ TARGET_NR_clock_adjtime, "clock_adjtime" , NULL, print_clock_adjtime, NULL }, +#endif #ifdef TARGET_NR_clock_getres { TARGET_NR_clock_getres, "clock_getres" , NULL, NULL, NULL }, #endif @@ -1454,11 +1458,8 @@ #ifdef TARGET_NR_sync { TARGET_NR_sync, "sync" , NULL, NULL, NULL }, #endif -#ifdef TARGET_NR_sync_file_range -{ TARGET_NR_sync_file_range, "sync_file_range" , NULL, NULL, NULL }, -#endif #ifdef TARGET_NR_syncfs -{ TARGET_NR_syncfs, "syncfs" , NULL, NULL, NULL }, +{ TARGET_NR_syncfs, "syncfs" , "%s(%d)", NULL, NULL }, #endif #ifdef TARGET_NR_syscall { TARGET_NR_syscall, "syscall" , NULL, NULL, NULL }, @@ -1485,7 +1486,7 @@ { TARGET_NR_sys_kexec_load, "sys_kexec_load" , NULL, NULL, NULL }, #endif #ifdef TARGET_NR_syslog -{ TARGET_NR_syslog, "syslog" , NULL, NULL, NULL }, +{ TARGET_NR_syslog, "syslog" , NULL, print_syslog, NULL }, #endif #ifdef TARGET_NR_sysmips { TARGET_NR_sysmips, "sysmips" , NULL, NULL, NULL }, diff --git a/linux-user/syscall.c b/linux-user/syscall.c index 03339ba..7b77503 100644 --- a/linux-user/syscall.c +++ b/linux-user/syscall.c @@ -35,6 +35,7 @@ #include <sys/swap.h> #include <linux/capability.h> #include <sched.h> +#include <sys/timex.h> #ifdef __ia64__ int __clone2(int (*fn)(void *), void *child_stack_base, size_t stack_size, int flags, void *arg, ...); @@ -47,6 +48,7 @@ int __clone2(int (*fn)(void *), void *child_stack_base, #include <sys/shm.h> #include <sys/sem.h> #include <sys/statfs.h> +#include <time.h> #include <utime.h> #include <sys/sysinfo.h> #include <sys/signalfd.h> @@ -304,6 +306,11 @@ _syscall3(int, ioprio_set, int, which, int, who, int, ioprio) _syscall3(int, getrandom, void *, buf, size_t, buflen, unsigned int, flags) #endif +#if defined(TARGET_NR_kcmp) && defined(__NR_kcmp) +_syscall5(int, kcmp, pid_t, pid1, pid_t, pid2, int, type, + unsigned long, idx1, unsigned long, idx2) +#endif + static bitmask_transtbl fcntl_flags_tbl[] = { { TARGET_O_ACCMODE, TARGET_O_WRONLY, O_ACCMODE, O_WRONLY, }, { TARGET_O_ACCMODE, TARGET_O_RDWR, O_ACCMODE, O_RDWR, }, @@ -788,6 +795,9 @@ static uint16_t host_to_target_errno_table[ERRNO_TABLE_SIZE] = { #ifdef ENOTRECOVERABLE [ENOTRECOVERABLE] = TARGET_ENOTRECOVERABLE, #endif +#ifdef ENOMSG + [ENOMSG] = TARGET_ENOMSG, +#endif }; static inline int host_to_target_errno(int err) @@ -908,6 +918,10 @@ safe_syscall2(int, tkill, int, tid, int, sig) safe_syscall3(int, tgkill, int, tgid, int, pid, int, sig) safe_syscall3(ssize_t, readv, int, fd, const struct iovec *, iov, int, iovcnt) safe_syscall3(ssize_t, writev, int, fd, const struct iovec *, iov, int, iovcnt) +safe_syscall5(ssize_t, preadv, int, fd, const struct iovec *, iov, int, iovcnt, + unsigned long, pos_l, unsigned long, pos_h) +safe_syscall5(ssize_t, pwritev, int, fd, const struct iovec *, iov, int, iovcnt, + unsigned long, pos_l, unsigned long, pos_h) safe_syscall3(int, connect, int, fd, const struct sockaddr *, addr, socklen_t, addrlen) safe_syscall6(ssize_t, sendto, int, fd, const void *, buf, size_t, len, @@ -2591,6 +2605,7 @@ static abi_long target_to_host_data_route_rtattr(struct rtattr *rtattr) case RTA_GATEWAY: break; /* u32 */ + case RTA_PRIORITY: case RTA_OIF: u32 = RTA_DATA(rtattr); *u32 = tswap32(*u32); @@ -3893,89 +3908,94 @@ fail: } #ifdef TARGET_NR_socketcall -/* do_socketcall() Must return target values and target errnos. */ +/* do_socketcall() must return target values and target errnos. */ static abi_long do_socketcall(int num, abi_ulong vptr) { - static const unsigned ac[] = { /* number of arguments per call */ - [SOCKOP_socket] = 3, /* domain, type, protocol */ - [SOCKOP_bind] = 3, /* sockfd, addr, addrlen */ - [SOCKOP_connect] = 3, /* sockfd, addr, addrlen */ - [SOCKOP_listen] = 2, /* sockfd, backlog */ - [SOCKOP_accept] = 3, /* sockfd, addr, addrlen */ - [SOCKOP_accept4] = 4, /* sockfd, addr, addrlen, flags */ - [SOCKOP_getsockname] = 3, /* sockfd, addr, addrlen */ - [SOCKOP_getpeername] = 3, /* sockfd, addr, addrlen */ - [SOCKOP_socketpair] = 4, /* domain, type, protocol, tab */ - [SOCKOP_send] = 4, /* sockfd, msg, len, flags */ - [SOCKOP_recv] = 4, /* sockfd, msg, len, flags */ - [SOCKOP_sendto] = 6, /* sockfd, msg, len, flags, addr, addrlen */ - [SOCKOP_recvfrom] = 6, /* sockfd, msg, len, flags, addr, addrlen */ - [SOCKOP_shutdown] = 2, /* sockfd, how */ - [SOCKOP_sendmsg] = 3, /* sockfd, msg, flags */ - [SOCKOP_recvmsg] = 3, /* sockfd, msg, flags */ - [SOCKOP_sendmmsg] = 4, /* sockfd, msgvec, vlen, flags */ - [SOCKOP_recvmmsg] = 4, /* sockfd, msgvec, vlen, flags */ - [SOCKOP_setsockopt] = 5, /* sockfd, level, optname, optval, optlen */ - [SOCKOP_getsockopt] = 5, /* sockfd, level, optname, optval, optlen */ + static const unsigned nargs[] = { /* number of arguments per operation */ + [TARGET_SYS_SOCKET] = 3, /* domain, type, protocol */ + [TARGET_SYS_BIND] = 3, /* fd, addr, addrlen */ + [TARGET_SYS_CONNECT] = 3, /* fd, addr, addrlen */ + [TARGET_SYS_LISTEN] = 2, /* fd, backlog */ + [TARGET_SYS_ACCEPT] = 3, /* fd, addr, addrlen */ + [TARGET_SYS_GETSOCKNAME] = 3, /* fd, addr, addrlen */ + [TARGET_SYS_GETPEERNAME] = 3, /* fd, addr, addrlen */ + [TARGET_SYS_SOCKETPAIR] = 4, /* domain, type, protocol, tab */ + [TARGET_SYS_SEND] = 4, /* fd, msg, len, flags */ + [TARGET_SYS_RECV] = 4, /* fd, msg, len, flags */ + [TARGET_SYS_SENDTO] = 6, /* fd, msg, len, flags, addr, addrlen */ + [TARGET_SYS_RECVFROM] = 6, /* fd, msg, len, flags, addr, addrlen */ + [TARGET_SYS_SHUTDOWN] = 2, /* fd, how */ + [TARGET_SYS_SETSOCKOPT] = 5, /* fd, level, optname, optval, optlen */ + [TARGET_SYS_GETSOCKOPT] = 5, /* fd, level, optname, optval, optlen */ + [TARGET_SYS_SENDMSG] = 3, /* fd, msg, flags */ + [TARGET_SYS_RECVMSG] = 3, /* fd, msg, flags */ + [TARGET_SYS_ACCEPT4] = 4, /* fd, addr, addrlen, flags */ + [TARGET_SYS_RECVMMSG] = 4, /* fd, msgvec, vlen, flags */ + [TARGET_SYS_SENDMMSG] = 4, /* fd, msgvec, vlen, flags */ }; abi_long a[6]; /* max 6 args */ + unsigned i; - /* first, collect the arguments in a[] according to ac[] */ - if (num >= 0 && num < ARRAY_SIZE(ac)) { - unsigned i; - assert(ARRAY_SIZE(a) >= ac[num]); /* ensure we have space for args */ - for (i = 0; i < ac[num]; ++i) { - if (get_user_ual(a[i], vptr + i * sizeof(abi_long)) != 0) { - return -TARGET_EFAULT; - } + /* check the range of the first argument num */ + /* (TARGET_SYS_SENDMMSG is the highest among TARGET_SYS_xxx) */ + if (num < 1 || num > TARGET_SYS_SENDMMSG) { + return -TARGET_EINVAL; + } + /* ensure we have space for args */ + if (nargs[num] > ARRAY_SIZE(a)) { + return -TARGET_EINVAL; + } + /* collect the arguments in a[] according to nargs[] */ + for (i = 0; i < nargs[num]; ++i) { + if (get_user_ual(a[i], vptr + i * sizeof(abi_long)) != 0) { + return -TARGET_EFAULT; } } - - /* now when we have the args, actually handle the call */ + /* now when we have the args, invoke the appropriate underlying function */ switch (num) { - case SOCKOP_socket: /* domain, type, protocol */ + case TARGET_SYS_SOCKET: /* domain, type, protocol */ return do_socket(a[0], a[1], a[2]); - case SOCKOP_bind: /* sockfd, addr, addrlen */ + case TARGET_SYS_BIND: /* sockfd, addr, addrlen */ return do_bind(a[0], a[1], a[2]); - case SOCKOP_connect: /* sockfd, addr, addrlen */ + case TARGET_SYS_CONNECT: /* sockfd, addr, addrlen */ return do_connect(a[0], a[1], a[2]); - case SOCKOP_listen: /* sockfd, backlog */ + case TARGET_SYS_LISTEN: /* sockfd, backlog */ return get_errno(listen(a[0], a[1])); - case SOCKOP_accept: /* sockfd, addr, addrlen */ + case TARGET_SYS_ACCEPT: /* sockfd, addr, addrlen */ return do_accept4(a[0], a[1], a[2], 0); - case SOCKOP_accept4: /* sockfd, addr, addrlen, flags */ - return do_accept4(a[0], a[1], a[2], a[3]); - case SOCKOP_getsockname: /* sockfd, addr, addrlen */ + case TARGET_SYS_GETSOCKNAME: /* sockfd, addr, addrlen */ return do_getsockname(a[0], a[1], a[2]); - case SOCKOP_getpeername: /* sockfd, addr, addrlen */ + case TARGET_SYS_GETPEERNAME: /* sockfd, addr, addrlen */ return do_getpeername(a[0], a[1], a[2]); - case SOCKOP_socketpair: /* domain, type, protocol, tab */ + case TARGET_SYS_SOCKETPAIR: /* domain, type, protocol, tab */ return do_socketpair(a[0], a[1], a[2], a[3]); - case SOCKOP_send: /* sockfd, msg, len, flags */ + case TARGET_SYS_SEND: /* sockfd, msg, len, flags */ return do_sendto(a[0], a[1], a[2], a[3], 0, 0); - case SOCKOP_recv: /* sockfd, msg, len, flags */ + case TARGET_SYS_RECV: /* sockfd, msg, len, flags */ return do_recvfrom(a[0], a[1], a[2], a[3], 0, 0); - case SOCKOP_sendto: /* sockfd, msg, len, flags, addr, addrlen */ + case TARGET_SYS_SENDTO: /* sockfd, msg, len, flags, addr, addrlen */ return do_sendto(a[0], a[1], a[2], a[3], a[4], a[5]); - case SOCKOP_recvfrom: /* sockfd, msg, len, flags, addr, addrlen */ + case TARGET_SYS_RECVFROM: /* sockfd, msg, len, flags, addr, addrlen */ return do_recvfrom(a[0], a[1], a[2], a[3], a[4], a[5]); - case SOCKOP_shutdown: /* sockfd, how */ + case TARGET_SYS_SHUTDOWN: /* sockfd, how */ return get_errno(shutdown(a[0], a[1])); - case SOCKOP_sendmsg: /* sockfd, msg, flags */ + case TARGET_SYS_SETSOCKOPT: /* sockfd, level, optname, optval, optlen */ + return do_setsockopt(a[0], a[1], a[2], a[3], a[4]); + case TARGET_SYS_GETSOCKOPT: /* sockfd, level, optname, optval, optlen */ + return do_getsockopt(a[0], a[1], a[2], a[3], a[4]); + case TARGET_SYS_SENDMSG: /* sockfd, msg, flags */ return do_sendrecvmsg(a[0], a[1], a[2], 1); - case SOCKOP_recvmsg: /* sockfd, msg, flags */ + case TARGET_SYS_RECVMSG: /* sockfd, msg, flags */ return do_sendrecvmsg(a[0], a[1], a[2], 0); - case SOCKOP_sendmmsg: /* sockfd, msgvec, vlen, flags */ - return do_sendrecvmmsg(a[0], a[1], a[2], a[3], 1); - case SOCKOP_recvmmsg: /* sockfd, msgvec, vlen, flags */ + case TARGET_SYS_ACCEPT4: /* sockfd, addr, addrlen, flags */ + return do_accept4(a[0], a[1], a[2], a[3]); + case TARGET_SYS_RECVMMSG: /* sockfd, msgvec, vlen, flags */ return do_sendrecvmmsg(a[0], a[1], a[2], a[3], 0); - case SOCKOP_setsockopt: /* sockfd, level, optname, optval, optlen */ - return do_setsockopt(a[0], a[1], a[2], a[3], a[4]); - case SOCKOP_getsockopt: /* sockfd, level, optname, optval, optlen */ - return do_getsockopt(a[0], a[1], a[2], a[3], a[4]); + case TARGET_SYS_SENDMMSG: /* sockfd, msgvec, vlen, flags */ + return do_sendrecvmmsg(a[0], a[1], a[2], a[3], 1); default: gemu_log("Unsupported socketcall: %d\n", num); - return -TARGET_ENOSYS; + return -TARGET_EINVAL; } } #endif @@ -6144,6 +6164,14 @@ static int do_fork(CPUArchState *env, unsigned int flags, abi_ulong newsp, sigfillset(&sigmask); sigprocmask(SIG_BLOCK, &sigmask, &info.sigmask); + /* If this is our first additional thread, we need to ensure we + * generate code for parallel execution and flush old translations. + */ + if (!parallel_cpus) { + parallel_cpus = true; + tb_flush(cpu); + } + ret = pthread_create(&info.thread, &attr, clone_func, &info); /* TODO: Free new CPU state if thread creation failed. */ @@ -6770,6 +6798,77 @@ static inline abi_long host_to_target_itimerspec(abi_ulong target_addr, return 0; } +static inline abi_long target_to_host_timex(struct timex *host_tx, + abi_long target_addr) +{ + struct target_timex *target_tx; + + if (!lock_user_struct(VERIFY_READ, target_tx, target_addr, 1)) { + return -TARGET_EFAULT; + } + + __get_user(host_tx->modes, &target_tx->modes); + __get_user(host_tx->offset, &target_tx->offset); + __get_user(host_tx->freq, &target_tx->freq); + __get_user(host_tx->maxerror, &target_tx->maxerror); + __get_user(host_tx->esterror, &target_tx->esterror); + __get_user(host_tx->status, &target_tx->status); + __get_user(host_tx->constant, &target_tx->constant); + __get_user(host_tx->precision, &target_tx->precision); + __get_user(host_tx->tolerance, &target_tx->tolerance); + __get_user(host_tx->time.tv_sec, &target_tx->time.tv_sec); + __get_user(host_tx->time.tv_usec, &target_tx->time.tv_usec); + __get_user(host_tx->tick, &target_tx->tick); + __get_user(host_tx->ppsfreq, &target_tx->ppsfreq); + __get_user(host_tx->jitter, &target_tx->jitter); + __get_user(host_tx->shift, &target_tx->shift); + __get_user(host_tx->stabil, &target_tx->stabil); + __get_user(host_tx->jitcnt, &target_tx->jitcnt); + __get_user(host_tx->calcnt, &target_tx->calcnt); + __get_user(host_tx->errcnt, &target_tx->errcnt); + __get_user(host_tx->stbcnt, &target_tx->stbcnt); + __get_user(host_tx->tai, &target_tx->tai); + + unlock_user_struct(target_tx, target_addr, 0); + return 0; +} + +static inline abi_long host_to_target_timex(abi_long target_addr, + struct timex *host_tx) +{ + struct target_timex *target_tx; + + if (!lock_user_struct(VERIFY_WRITE, target_tx, target_addr, 0)) { + return -TARGET_EFAULT; + } + + __put_user(host_tx->modes, &target_tx->modes); + __put_user(host_tx->offset, &target_tx->offset); + __put_user(host_tx->freq, &target_tx->freq); + __put_user(host_tx->maxerror, &target_tx->maxerror); + __put_user(host_tx->esterror, &target_tx->esterror); + __put_user(host_tx->status, &target_tx->status); + __put_user(host_tx->constant, &target_tx->constant); + __put_user(host_tx->precision, &target_tx->precision); + __put_user(host_tx->tolerance, &target_tx->tolerance); + __put_user(host_tx->time.tv_sec, &target_tx->time.tv_sec); + __put_user(host_tx->time.tv_usec, &target_tx->time.tv_usec); + __put_user(host_tx->tick, &target_tx->tick); + __put_user(host_tx->ppsfreq, &target_tx->ppsfreq); + __put_user(host_tx->jitter, &target_tx->jitter); + __put_user(host_tx->shift, &target_tx->shift); + __put_user(host_tx->stabil, &target_tx->stabil); + __put_user(host_tx->jitcnt, &target_tx->jitcnt); + __put_user(host_tx->calcnt, &target_tx->calcnt); + __put_user(host_tx->errcnt, &target_tx->errcnt); + __put_user(host_tx->stbcnt, &target_tx->stbcnt); + __put_user(host_tx->tai, &target_tx->tai); + + unlock_user_struct(target_tx, target_addr, 1); + return 0; +} + + static inline abi_long target_to_host_sigevent(struct sigevent *host_sevp, abi_ulong target_addr) { @@ -8003,6 +8102,11 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, sync(); ret = 0; break; +#if defined(TARGET_NR_syncfs) && defined(CONFIG_SYNCFS) + case TARGET_NR_syncfs: + ret = get_errno(syncfs(arg1)); + break; +#endif case TARGET_NR_kill: ret = get_errno(safe_kill(arg1, target_to_host_signal(arg2))); break; @@ -9240,14 +9344,52 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, ret = do_setsockopt(arg1, arg2, arg3, arg4, (socklen_t) arg5); break; #endif - +#if defined(TARGET_NR_syslog) case TARGET_NR_syslog: - if (!(p = lock_user_string(arg2))) - goto efault; - ret = get_errno(sys_syslog((int)arg1, p, (int)arg3)); - unlock_user(p, arg2, 0); - break; + { + int len = arg2; + switch (arg1) { + case TARGET_SYSLOG_ACTION_CLOSE: /* Close log */ + case TARGET_SYSLOG_ACTION_OPEN: /* Open log */ + case TARGET_SYSLOG_ACTION_CLEAR: /* Clear ring buffer */ + case TARGET_SYSLOG_ACTION_CONSOLE_OFF: /* Disable logging */ + case TARGET_SYSLOG_ACTION_CONSOLE_ON: /* Enable logging */ + case TARGET_SYSLOG_ACTION_CONSOLE_LEVEL: /* Set messages level */ + case TARGET_SYSLOG_ACTION_SIZE_UNREAD: /* Number of chars */ + case TARGET_SYSLOG_ACTION_SIZE_BUFFER: /* Size of the buffer */ + { + ret = get_errno(sys_syslog((int)arg1, NULL, (int)arg3)); + } + break; + case TARGET_SYSLOG_ACTION_READ: /* Read from log */ + case TARGET_SYSLOG_ACTION_READ_CLEAR: /* Read/clear msgs */ + case TARGET_SYSLOG_ACTION_READ_ALL: /* Read last messages */ + { + ret = -TARGET_EINVAL; + if (len < 0) { + goto fail; + } + ret = 0; + if (len == 0) { + break; + } + p = lock_user(VERIFY_WRITE, arg2, arg3, 0); + if (!p) { + ret = -TARGET_EFAULT; + goto fail; + } + ret = get_errno(sys_syslog((int)arg1, p, (int)arg3)); + unlock_user(p, arg2, arg3); + } + break; + default: + ret = -EINVAL; + break; + } + } + break; +#endif case TARGET_NR_setitimer: { struct itimerval value, ovalue, *pvalue; @@ -9543,7 +9685,37 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, #endif #endif case TARGET_NR_adjtimex: - goto unimplemented; + { + struct timex host_buf; + + if (target_to_host_timex(&host_buf, arg1) != 0) { + goto efault; + } + ret = get_errno(adjtimex(&host_buf)); + if (!is_error(ret)) { + if (host_to_target_timex(arg1, &host_buf) != 0) { + goto efault; + } + } + } + break; +#if defined(TARGET_NR_clock_adjtime) && defined(CONFIG_CLOCK_ADJTIME) + case TARGET_NR_clock_adjtime: + { + struct timex htx, *phtx = &htx; + + if (target_to_host_timex(phtx, arg2) != 0) { + goto efault; + } + ret = get_errno(clock_adjtime(arg1, phtx)); + if (!is_error(ret) && phtx) { + if (host_to_target_timex(arg2, phtx) != 0) { + goto efault; + } + } + } + break; +#endif #ifdef TARGET_NR_create_module case TARGET_NR_create_module: #endif @@ -9899,6 +10071,32 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, } } break; +#if defined(TARGET_NR_preadv) + case TARGET_NR_preadv: + { + struct iovec *vec = lock_iovec(VERIFY_WRITE, arg2, arg3, 0); + if (vec != NULL) { + ret = get_errno(safe_preadv(arg1, vec, arg3, arg4, arg5)); + unlock_iovec(vec, arg2, arg3, 1); + } else { + ret = -host_to_target_errno(errno); + } + } + break; +#endif +#if defined(TARGET_NR_pwritev) + case TARGET_NR_pwritev: + { + struct iovec *vec = lock_iovec(VERIFY_READ, arg2, arg3, 1); + if (vec != NULL) { + ret = get_errno(safe_pwritev(arg1, vec, arg3, arg4, arg5)); + unlock_iovec(vec, arg2, arg3, 0); + } else { + ret = -host_to_target_errno(errno); + } + } + break; +#endif case TARGET_NR_getsid: ret = get_errno(getsid(arg1)); break; @@ -11372,16 +11570,18 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, #if defined(TARGET_NR_mq_open) && defined(__NR_mq_open) case TARGET_NR_mq_open: { - struct mq_attr posix_mq_attr, *attrp; + struct mq_attr posix_mq_attr; + int host_flags; + host_flags = target_to_host_bitmask(arg2, fcntl_flags_tbl); + if (copy_from_user_mq_attr(&posix_mq_attr, arg4) != 0) { + goto efault; + } p = lock_user_string(arg1 - 1); - if (arg4 != 0) { - copy_from_user_mq_attr (&posix_mq_attr, arg4); - attrp = &posix_mq_attr; - } else { - attrp = 0; + if (!p) { + goto efault; } - ret = get_errno(mq_open(p, arg2, arg3, attrp)); + ret = get_errno(mq_open(p, host_flags, arg3, &posix_mq_attr)); unlock_user (p, arg1, 0); } break; @@ -11638,7 +11838,12 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, goto efault; } - ep = alloca(maxevents * sizeof(struct epoll_event)); + ep = g_try_new(struct epoll_event, maxevents); + if (!ep) { + unlock_user(target_ep, arg2, 0); + ret = -TARGET_ENOMEM; + break; + } switch (num) { #if defined(TARGET_NR_epoll_pwait) @@ -11656,8 +11861,8 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, target_set = lock_user(VERIFY_READ, arg5, sizeof(target_sigset_t), 1); if (!target_set) { - unlock_user(target_ep, arg2, 0); - goto efault; + ret = -TARGET_EFAULT; + break; } target_to_host_sigset(set, target_set); unlock_user(target_set, arg5, 0); @@ -11685,8 +11890,12 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, target_ep[i].events = tswap32(ep[i].events); target_ep[i].data.u64 = tswap64(ep[i].data.u64); } + unlock_user(target_ep, arg2, + ret * sizeof(struct target_epoll_event)); + } else { + unlock_user(target_ep, arg2, 0); } - unlock_user(target_ep, arg2, ret * sizeof(struct target_epoll_event)); + g_free(ep); break; } #endif @@ -11947,6 +12156,11 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, ret = get_errno(unshare(arg1)); break; #endif +#if defined(TARGET_NR_kcmp) && defined(__NR_kcmp) + case TARGET_NR_kcmp: + ret = get_errno(kcmp(arg1, arg2, arg3, arg4, arg5)); + break; +#endif default: unimplemented: diff --git a/linux-user/syscall_defs.h b/linux-user/syscall_defs.h index 9fdbe86..0b15466 100644 --- a/linux-user/syscall_defs.h +++ b/linux-user/syscall_defs.h @@ -9,26 +9,28 @@ #include "syscall_nr.h" -#define SOCKOP_socket 1 -#define SOCKOP_bind 2 -#define SOCKOP_connect 3 -#define SOCKOP_listen 4 -#define SOCKOP_accept 5 -#define SOCKOP_getsockname 6 -#define SOCKOP_getpeername 7 -#define SOCKOP_socketpair 8 -#define SOCKOP_send 9 -#define SOCKOP_recv 10 -#define SOCKOP_sendto 11 -#define SOCKOP_recvfrom 12 -#define SOCKOP_shutdown 13 -#define SOCKOP_setsockopt 14 -#define SOCKOP_getsockopt 15 -#define SOCKOP_sendmsg 16 -#define SOCKOP_recvmsg 17 -#define SOCKOP_accept4 18 -#define SOCKOP_recvmmsg 19 -#define SOCKOP_sendmmsg 20 + +/* socket operations for socketcall() */ +#define TARGET_SYS_SOCKET 1 /* socket() */ +#define TARGET_SYS_BIND 2 /* bind() */ +#define TARGET_SYS_CONNECT 3 /* connect() */ +#define TARGET_SYS_LISTEN 4 /* listen() */ +#define TARGET_SYS_ACCEPT 5 /* accept() */ +#define TARGET_SYS_GETSOCKNAME 6 /* getsockname() */ +#define TARGET_SYS_GETPEERNAME 7 /* getpeername() */ +#define TARGET_SYS_SOCKETPAIR 8 /* socketpair() */ +#define TARGET_SYS_SEND 9 /* send() */ +#define TARGET_SYS_RECV 10 /* recv() */ +#define TARGET_SYS_SENDTO 11 /* sendto() */ +#define TARGET_SYS_RECVFROM 12 /* recvfrom() */ +#define TARGET_SYS_SHUTDOWN 13 /* shutdown() */ +#define TARGET_SYS_SETSOCKOPT 14 /* setsockopt() */ +#define TARGET_SYS_GETSOCKOPT 15 /* getsockopt() */ +#define TARGET_SYS_SENDMSG 16 /* sendmsg() */ +#define TARGET_SYS_RECVMSG 17 /* recvmsg() */ +#define TARGET_SYS_ACCEPT4 18 /* accept4() */ +#define TARGET_SYS_RECVMMSG 19 /* recvmmsg() */ +#define TARGET_SYS_SENDMMSG 20 /* sendmmsg() */ #define IPCOP_semop 1 #define IPCOP_semget 2 @@ -207,6 +209,34 @@ struct target_itimerspec { struct target_timespec it_value; }; +struct target_timex { + abi_uint modes; /* Mode selector */ + abi_long offset; /* Time offset */ + abi_long freq; /* Frequency offset */ + abi_long maxerror; /* Maximum error (microseconds) */ + abi_long esterror; /* Estimated error (microseconds) */ + abi_int status; /* Clock command/status */ + abi_long constant; /* PLL (phase-locked loop) time constant */ + abi_long precision; /* Clock precision (microseconds, ro) */ + abi_long tolerance; /* Clock freq. tolerance (ppm, ro) */ + struct target_timeval time; /* Current time */ + abi_long tick; /* Microseconds between clock ticks */ + abi_long ppsfreq; /* PPS (pulse per second) frequency */ + abi_long jitter; /* PPS jitter (ro); nanoseconds */ + abi_int shift; /* PPS interval duration (seconds) */ + abi_long stabil; /* PPS stability */ + abi_long jitcnt; /* PPS jitter limit exceeded (ro) */ + abi_long calcnt; /* PPS calibration intervals */ + abi_long errcnt; /* PPS calibration errors */ + abi_long stbcnt; /* PPS stability limit exceeded */ + abi_int tai; /* TAI offset */ + + /* Further padding bytes to allow for future expansion */ + abi_int:32; abi_int:32; abi_int:32; abi_int:32; + abi_int:32; abi_int:32; abi_int:32; abi_int:32; + abi_int:32; abi_int:32; abi_int:32; +}; + typedef abi_long target_clock_t; #define TARGET_HZ 100 @@ -2628,15 +2658,19 @@ typedef int32_t target_timer_t; struct target_sigevent { target_sigval_t sigev_value; - int32_t sigev_signo; - int32_t sigev_notify; + abi_int sigev_signo; + abi_int sigev_notify; union { - int32_t _pad[TARGET_SIGEV_PAD_SIZE]; - int32_t _tid; + abi_int _pad[TARGET_SIGEV_PAD_SIZE]; + abi_int _tid; + /* The kernel (and thus QEMU) never looks at these; + * they're only used as part of the ABI between a + * userspace program and libc. + */ struct { - void (*_function)(sigval_t); - void *_attribute; + abi_ulong _function; + abi_ulong _attribute; } _sigev_thread; } _sigev_un; }; @@ -2652,4 +2686,29 @@ struct target_user_cap_data { uint32_t inheritable; }; +/* from kernel's include/linux/syslog.h */ + +/* Close the log. Currently a NOP. */ +#define TARGET_SYSLOG_ACTION_CLOSE 0 +/* Open the log. Currently a NOP. */ +#define TARGET_SYSLOG_ACTION_OPEN 1 +/* Read from the log. */ +#define TARGET_SYSLOG_ACTION_READ 2 +/* Read all messages remaining in the ring buffer. */ +#define TARGET_SYSLOG_ACTION_READ_ALL 3 +/* Read and clear all messages remaining in the ring buffer */ +#define TARGET_SYSLOG_ACTION_READ_CLEAR 4 +/* Clear ring buffer. */ +#define TARGET_SYSLOG_ACTION_CLEAR 5 +/* Disable printk's to console */ +#define TARGET_SYSLOG_ACTION_CONSOLE_OFF 6 +/* Enable printk's to console */ +#define TARGET_SYSLOG_ACTION_CONSOLE_ON 7 +/* Set level of messages printed to console */ +#define TARGET_SYSLOG_ACTION_CONSOLE_LEVEL 8 +/* Return number of unread characters in the log buffer */ +#define TARGET_SYSLOG_ACTION_SIZE_UNREAD 9 +/* Return size of the log buffer */ +#define TARGET_SYSLOG_ACTION_SIZE_BUFFER 10 + #endif @@ -97,13 +97,6 @@ static AddrRange addrrange_intersection(AddrRange r1, AddrRange r2) enum ListenerDirection { Forward, Reverse }; -static bool memory_listener_match(MemoryListener *listener, - MemoryRegionSection *section) -{ - return !listener->address_space_filter - || listener->address_space_filter == section->address_space; -} - #define MEMORY_LISTENER_CALL_GLOBAL(_callback, _direction, _args...) \ do { \ MemoryListener *_listener; \ @@ -129,24 +122,23 @@ static bool memory_listener_match(MemoryListener *listener, } \ } while (0) -#define MEMORY_LISTENER_CALL(_callback, _direction, _section, _args...) \ +#define MEMORY_LISTENER_CALL(_as, _callback, _direction, _section, _args...) \ do { \ MemoryListener *_listener; \ + struct memory_listeners_as *list = &(_as)->listeners; \ \ switch (_direction) { \ case Forward: \ - QTAILQ_FOREACH(_listener, &memory_listeners, link) { \ - if (_listener->_callback \ - && memory_listener_match(_listener, _section)) { \ + QTAILQ_FOREACH(_listener, list, link_as) { \ + if (_listener->_callback) { \ _listener->_callback(_listener, _section, ##_args); \ } \ } \ break; \ case Reverse: \ - QTAILQ_FOREACH_REVERSE(_listener, &memory_listeners, \ - memory_listeners, link) { \ - if (_listener->_callback \ - && memory_listener_match(_listener, _section)) { \ + QTAILQ_FOREACH_REVERSE(_listener, list, memory_listeners_as, \ + link_as) { \ + if (_listener->_callback) { \ _listener->_callback(_listener, _section, ##_args); \ } \ } \ @@ -160,7 +152,7 @@ static bool memory_listener_match(MemoryListener *listener, #define MEMORY_LISTENER_UPDATE_REGION(fr, as, dir, callback, _args...) \ do { \ MemoryRegionSection mrs = section_from_flat_range(fr, as); \ - MEMORY_LISTENER_CALL(callback, dir, &mrs, ##_args); \ + MEMORY_LISTENER_CALL(as, callback, dir, &mrs, ##_args); \ } while(0) struct CoalescedMemoryRange { @@ -749,7 +741,7 @@ static void address_space_add_del_ioeventfds(AddressSpace *as, .offset_within_address_space = int128_get64(fd->addr.start), .size = fd->addr.size, }; - MEMORY_LISTENER_CALL(eventfd_del, Forward, §ion, + MEMORY_LISTENER_CALL(as, eventfd_del, Forward, §ion, fd->match_data, fd->data, fd->e); ++iold; } else if (inew < fds_new_nb @@ -762,7 +754,7 @@ static void address_space_add_del_ioeventfds(AddressSpace *as, .offset_within_address_space = int128_get64(fd->addr.start), .size = fd->addr.size, }; - MEMORY_LISTENER_CALL(eventfd_add, Reverse, §ion, + MEMORY_LISTENER_CALL(as, eventfd_add, Reverse, §ion, fd->match_data, fd->data, fd->e); ++inew; } else { @@ -1507,7 +1499,7 @@ bool memory_region_is_skip_dump(MemoryRegion *mr) uint8_t memory_region_get_dirty_log_mask(MemoryRegion *mr) { uint8_t mask = mr->dirty_log_mask; - if (global_dirty_log) { + if (global_dirty_log && mr->ram_block) { mask |= (1 << DIRTY_MEMORY_MIGRATION); } return mask; @@ -1650,14 +1642,26 @@ bool memory_region_test_and_clear_dirty(MemoryRegion *mr, hwaddr addr, void memory_region_sync_dirty_bitmap(MemoryRegion *mr) { + MemoryListener *listener; AddressSpace *as; + FlatView *view; FlatRange *fr; - QTAILQ_FOREACH(as, &address_spaces, address_spaces_link) { - FlatView *view = address_space_get_flatview(as); + /* If the same address space has multiple log_sync listeners, we + * visit that address space's FlatView multiple times. But because + * log_sync listeners are rare, it's still cheaper than walking each + * address space once. + */ + QTAILQ_FOREACH(listener, &memory_listeners, link) { + if (!listener->log_sync) { + continue; + } + as = listener->address_space; + view = address_space_get_flatview(as); FOR_EACH_FLAT_RANGE(fr, view) { if (fr->mr == mr) { - MEMORY_LISTENER_UPDATE_REGION(fr, as, Forward, log_sync); + MemoryRegionSection mrs = section_from_flat_range(fr, as); + listener->log_sync(listener, &mrs); } } flatview_unref(view); @@ -1774,7 +1778,7 @@ static void memory_region_update_coalesced_range_as(MemoryRegion *mr, AddressSpa .size = fr->addr.size, }; - MEMORY_LISTENER_CALL(coalesced_mmio_del, Reverse, §ion, + MEMORY_LISTENER_CALL(as, coalesced_mmio_del, Reverse, §ion, int128_get64(fr->addr.start), int128_get64(fr->addr.size)); QTAILQ_FOREACH(cmr, &mr->coalesced, link) { @@ -1785,7 +1789,7 @@ static void memory_region_update_coalesced_range_as(MemoryRegion *mr, AddressSpa continue; } tmp = addrrange_intersection(tmp, fr->addr); - MEMORY_LISTENER_CALL(coalesced_mmio_add, Forward, §ion, + MEMORY_LISTENER_CALL(as, coalesced_mmio_add, Forward, §ion, int128_get64(tmp.start), int128_get64(tmp.size)); } @@ -2176,13 +2180,13 @@ void memory_global_dirty_log_sync(void) if (!listener->log_sync) { continue; } - /* Global listeners are being phased out. */ - assert(listener->address_space_filter); - as = listener->address_space_filter; + as = listener->address_space; view = address_space_get_flatview(as); FOR_EACH_FLAT_RANGE(fr, view) { - MemoryRegionSection mrs = section_from_flat_range(fr, as); - listener->log_sync(listener, &mrs); + if (fr->dirty_log_mask) { + MemoryRegionSection mrs = section_from_flat_range(fr, as); + listener->log_sync(listener, &mrs); + } } flatview_unref(view); } @@ -2218,11 +2222,6 @@ static void listener_add_address_space(MemoryListener *listener, FlatView *view; FlatRange *fr; - if (listener->address_space_filter - && listener->address_space_filter != as) { - return; - } - if (listener->begin) { listener->begin(listener); } @@ -2255,12 +2254,11 @@ static void listener_add_address_space(MemoryListener *listener, flatview_unref(view); } -void memory_listener_register(MemoryListener *listener, AddressSpace *filter) +void memory_listener_register(MemoryListener *listener, AddressSpace *as) { MemoryListener *other = NULL; - AddressSpace *as; - listener->address_space_filter = filter; + listener->address_space = as; if (QTAILQ_EMPTY(&memory_listeners) || listener->priority >= QTAILQ_LAST(&memory_listeners, memory_listeners)->priority) { @@ -2274,14 +2272,26 @@ void memory_listener_register(MemoryListener *listener, AddressSpace *filter) QTAILQ_INSERT_BEFORE(other, listener, link); } - QTAILQ_FOREACH(as, &address_spaces, address_spaces_link) { - listener_add_address_space(listener, as); + if (QTAILQ_EMPTY(&as->listeners) + || listener->priority >= QTAILQ_LAST(&as->listeners, + memory_listeners)->priority) { + QTAILQ_INSERT_TAIL(&as->listeners, listener, link_as); + } else { + QTAILQ_FOREACH(other, &as->listeners, link_as) { + if (listener->priority < other->priority) { + break; + } + } + QTAILQ_INSERT_BEFORE(other, listener, link_as); } + + listener_add_address_space(listener, as); } void memory_listener_unregister(MemoryListener *listener) { QTAILQ_REMOVE(&memory_listeners, listener, link); + QTAILQ_REMOVE(&listener->address_space->listeners, listener, link_as); } void address_space_init(AddressSpace *as, MemoryRegion *root, const char *name) @@ -2295,6 +2305,7 @@ void address_space_init(AddressSpace *as, MemoryRegion *root, const char *name) flatview_init(as->current_map); as->ioeventfd_nb = 0; as->ioeventfds = NULL; + QTAILQ_INIT(&as->listeners); QTAILQ_INSERT_TAIL(&address_spaces, as, address_spaces_link); as->name = g_strdup(name ? name : "anonymous"); address_space_init_dispatch(as); @@ -2304,14 +2315,10 @@ void address_space_init(AddressSpace *as, MemoryRegion *root, const char *name) static void do_address_space_destroy(AddressSpace *as) { - MemoryListener *listener; bool do_free = as->malloced; address_space_destroy_dispatch(as); - - QTAILQ_FOREACH(listener, &memory_listeners, link) { - assert(listener->address_space_filter != as); - } + assert(QTAILQ_EMPTY(&as->listeners)); flatview_unref(as->current_map); g_free(as->name); diff --git a/migration/ram.c b/migration/ram.c index bc6154f..d032d38 100644 --- a/migration/ram.c +++ b/migration/ram.c @@ -69,7 +69,7 @@ static uint64_t bitmap_sync_count; /* 0x80 is reserved in migration.h start with 0x100 next */ #define RAM_SAVE_FLAG_COMPRESS_PAGE 0x100 -static const uint8_t ZERO_TARGET_PAGE[TARGET_PAGE_SIZE]; +static uint8_t *ZERO_TARGET_PAGE; static inline bool is_zero_range(uint8_t *p, uint64_t size) { @@ -1431,6 +1431,7 @@ static void ram_migration_cleanup(void *opaque) cache_fini(XBZRLE.cache); g_free(XBZRLE.encoded_buf); g_free(XBZRLE.current_buf); + g_free(ZERO_TARGET_PAGE); XBZRLE.cache = NULL; XBZRLE.encoded_buf = NULL; XBZRLE.current_buf = NULL; @@ -1889,6 +1890,7 @@ static int ram_save_setup(QEMUFile *f, void *opaque) if (migrate_use_xbzrle()) { XBZRLE_cache_lock(); + ZERO_TARGET_PAGE = g_malloc0(TARGET_PAGE_SIZE); XBZRLE.cache = cache_init(migrate_xbzrle_cache_size() / TARGET_PAGE_SIZE, TARGET_PAGE_SIZE); diff --git a/migration/savevm.c b/migration/savevm.c index 0dede9d..0363372 100644 --- a/migration/savevm.c +++ b/migration/savevm.c @@ -265,6 +265,7 @@ typedef struct SaveState { bool skip_configuration; uint32_t len; const char *name; + uint32_t target_page_bits; } SaveState; static SaveState savevm_state = { @@ -286,6 +287,19 @@ static void configuration_pre_save(void *opaque) state->len = strlen(current_name); state->name = current_name; + state->target_page_bits = TARGET_PAGE_BITS; +} + +static int configuration_pre_load(void *opaque) +{ + SaveState *state = opaque; + + /* If there is no target-page-bits subsection it means the source + * predates the variable-target-page-bits support and is using the + * minimum possible value for this CPU. + */ + state->target_page_bits = TARGET_PAGE_BITS_MIN; + return 0; } static int configuration_post_load(void *opaque, int version_id) @@ -298,12 +312,43 @@ static int configuration_post_load(void *opaque, int version_id) (int) state->len, state->name, current_name); return -EINVAL; } + + if (state->target_page_bits != TARGET_PAGE_BITS) { + error_report("Received TARGET_PAGE_BITS is %d but local is %d", + state->target_page_bits, TARGET_PAGE_BITS); + return -EINVAL; + } + return 0; } +/* The target-page-bits subsection is present only if the + * target page size is not the same as the default (ie the + * minimum page size for a variable-page-size guest CPU). + * If it is present then it contains the actual target page + * bits for the machine, and migration will fail if the + * two ends don't agree about it. + */ +static bool vmstate_target_page_bits_needed(void *opaque) +{ + return TARGET_PAGE_BITS > TARGET_PAGE_BITS_MIN; +} + +static const VMStateDescription vmstate_target_page_bits = { + .name = "configuration/target-page-bits", + .version_id = 1, + .minimum_version_id = 1, + .needed = vmstate_target_page_bits_needed, + .fields = (VMStateField[]) { + VMSTATE_UINT32(target_page_bits, SaveState), + VMSTATE_END_OF_LIST() + } +}; + static const VMStateDescription vmstate_configuration = { .name = "configuration", .version_id = 1, + .pre_load = configuration_pre_load, .post_load = configuration_post_load, .pre_save = configuration_pre_save, .fields = (VMStateField[]) { @@ -311,6 +356,10 @@ static const VMStateDescription vmstate_configuration = { VMSTATE_VBUFFER_ALLOC_UINT32(name, SaveState, 0, NULL, 0, len), VMSTATE_END_OF_LIST() }, + .subsections = (const VMStateDescription*[]) { + &vmstate_target_page_bits, + NULL + } }; static void dump_vmstate_vmsd(FILE *out_file, @@ -186,7 +186,7 @@ typedef struct { } MonitorQAPIEventConf; struct Monitor { - CharDriverState *chr; + CharBackend chr; int reset_seen; int flags; int suspend_cnt; @@ -297,7 +297,7 @@ static void monitor_flush_locked(Monitor *mon) len = qstring_get_length(mon->outbuf); if (len && !mon->mux_out) { - rc = qemu_chr_fe_write(mon->chr, (const uint8_t *) buf, len); + rc = qemu_chr_fe_write(&mon->chr, (const uint8_t *) buf, len); if ((rc < 0 && errno != EAGAIN) || (rc == len)) { /* all flushed or error */ QDECREF(mon->outbuf); @@ -311,8 +311,9 @@ static void monitor_flush_locked(Monitor *mon) mon->outbuf = tmp; } if (mon->out_watch == 0) { - mon->out_watch = qemu_chr_fe_add_watch(mon->chr, G_IO_OUT|G_IO_HUP, - monitor_unblocked, mon); + mon->out_watch = + qemu_chr_fe_add_watch(&mon->chr, G_IO_OUT | G_IO_HUP, + monitor_unblocked, mon); } } } @@ -581,9 +582,7 @@ static void monitor_data_init(Monitor *mon) static void monitor_data_destroy(Monitor *mon) { - if (mon->chr) { - qemu_chr_add_handlers(mon->chr, NULL, NULL, NULL, NULL); - } + qemu_chr_fe_deinit(&mon->chr); if (monitor_is_qmp(mon)) { json_message_parser_destroy(&mon->qmp.parser); } @@ -951,7 +950,7 @@ EventInfoList *qmp_query_events(Error **errp) * directly into QObject instead of first parsing it with * visit_type_SchemaInfoList() into a SchemaInfoList, then marshal it * to QObject with generated output marshallers, every time. Instead, - * we do it in test-qmp-input-visitor.c, just to make sure + * we do it in test-qobject-input-visitor.c, just to make sure * qapi-introspect.py's output actually conforms to the schema. */ static void qmp_query_qmp_schema(QDict *qdict, QObject **ret_data, @@ -1745,7 +1744,7 @@ void qmp_getfd(const char *fdname, Error **errp) mon_fd_t *monfd; int fd; - fd = qemu_chr_fe_get_msgfd(cur_mon->chr); + fd = qemu_chr_fe_get_msgfd(&cur_mon->chr); if (fd == -1) { error_setg(errp, QERR_FD_NOT_SUPPLIED); return; @@ -1870,7 +1869,7 @@ AddfdInfo *qmp_add_fd(bool has_fdset_id, int64_t fdset_id, bool has_opaque, Monitor *mon = cur_mon; AddfdInfo *fdinfo; - fd = qemu_chr_fe_get_msgfd(mon->chr); + fd = qemu_chr_fe_get_msgfd(&mon->chr); if (fd == -1) { error_setg(errp, QERR_FD_NOT_SUPPLIED); goto error; @@ -3977,7 +3976,7 @@ void monitor_init(CharDriverState *chr, int flags) mon = g_malloc(sizeof(*mon)); monitor_data_init(mon); - mon->chr = chr; + qemu_chr_fe_init(&mon->chr, chr, &error_abort); mon->flags = flags; if (flags & MONITOR_USE_READLINE) { mon->rs = readline_init(monitor_readline_printf, @@ -3988,13 +3987,13 @@ void monitor_init(CharDriverState *chr, int flags) } if (monitor_is_qmp(mon)) { - qemu_chr_add_handlers(chr, monitor_can_read, monitor_qmp_read, - monitor_qmp_event, mon); - qemu_chr_fe_set_echo(chr, true); + qemu_chr_fe_set_handlers(&mon->chr, monitor_can_read, monitor_qmp_read, + monitor_qmp_event, mon, NULL, true); + qemu_chr_fe_set_echo(&mon->chr, true); json_message_parser_init(&mon->qmp.parser, handle_qmp_command); } else { - qemu_chr_add_handlers(chr, monitor_can_read, monitor_read, - monitor_event, mon); + qemu_chr_fe_set_handlers(&mon->chr, monitor_can_read, monitor_read, + monitor_event, mon, NULL, true); } qemu_mutex_lock(&monitor_lock); diff --git a/net/colo-compare.c b/net/colo-compare.c index 47703c5..f791383 100644 --- a/net/colo-compare.c +++ b/net/colo-compare.c @@ -68,9 +68,9 @@ typedef struct CompareState { char *pri_indev; char *sec_indev; char *outdev; - CharDriverState *chr_pri_in; - CharDriverState *chr_sec_in; - CharDriverState *chr_out; + CharBackend chr_pri_in; + CharBackend chr_sec_in; + CharBackend chr_out; SocketReadState pri_rs; SocketReadState sec_rs; @@ -101,7 +101,7 @@ enum { SECONDARY_IN, }; -static int compare_chr_send(CharDriverState *out, +static int compare_chr_send(CharBackend *out, const uint8_t *buf, uint32_t size); @@ -188,7 +188,6 @@ static int colo_packet_compare_tcp(Packet *spkt, Packet *ppkt) { struct tcphdr *ptcp, *stcp; int res; - char *sdebug, *ddebug; trace_colo_compare_main("compare tcp"); if (ppkt->size != spkt->size) { @@ -219,24 +218,21 @@ static int colo_packet_compare_tcp(Packet *spkt, Packet *ppkt) (spkt->size - ETH_HLEN)); if (res != 0 && trace_event_get_state(TRACE_COLO_COMPARE_MISCOMPARE)) { - sdebug = strdup(inet_ntoa(ppkt->ip->ip_src)); - ddebug = strdup(inet_ntoa(ppkt->ip->ip_dst)); - fprintf(stderr, "%s: src/dst: %s/%s p: seq/ack=%u/%u" - " s: seq/ack=%u/%u res=%d flags=%x/%x\n", - __func__, sdebug, ddebug, - (unsigned int)ntohl(ptcp->th_seq), - (unsigned int)ntohl(ptcp->th_ack), - (unsigned int)ntohl(stcp->th_seq), - (unsigned int)ntohl(stcp->th_ack), - res, ptcp->th_flags, stcp->th_flags); - - fprintf(stderr, "Primary len = %d\n", ppkt->size); - qemu_hexdump((char *)ppkt->data, stderr, "colo-compare", ppkt->size); - fprintf(stderr, "Secondary len = %d\n", spkt->size); - qemu_hexdump((char *)spkt->data, stderr, "colo-compare", spkt->size); - - g_free(sdebug); - g_free(ddebug); + trace_colo_compare_pkt_info(inet_ntoa(ppkt->ip->ip_src), + inet_ntoa(ppkt->ip->ip_dst), + ntohl(ptcp->th_seq), + ntohl(ptcp->th_ack), + ntohl(stcp->th_seq), + ntohl(stcp->th_ack), + res, ptcp->th_flags, + stcp->th_flags, + ppkt->size, + spkt->size); + + qemu_hexdump((char *)ppkt->data, stderr, + "colo-compare ppkt", ppkt->size); + qemu_hexdump((char *)spkt->data, stderr, + "colo-compare spkt", spkt->size); } return res; @@ -385,7 +381,7 @@ static void colo_compare_connection(void *opaque, void *user_data) } if (result) { - ret = compare_chr_send(s->chr_out, pkt->data, pkt->size); + ret = compare_chr_send(&s->chr_out, pkt->data, pkt->size); if (ret < 0) { error_report("colo_send_primary_packet failed"); } @@ -408,7 +404,7 @@ static void colo_compare_connection(void *opaque, void *user_data) } } -static int compare_chr_send(CharDriverState *out, +static int compare_chr_send(CharBackend *out, const uint8_t *buf, uint32_t size) { @@ -451,7 +447,8 @@ static void compare_pri_chr_in(void *opaque, const uint8_t *buf, int size) ret = net_fill_rstate(&s->pri_rs, buf, size); if (ret == -1) { - qemu_chr_add_handlers(s->chr_pri_in, NULL, NULL, NULL, NULL); + qemu_chr_fe_set_handlers(&s->chr_pri_in, NULL, NULL, NULL, + NULL, NULL, true); error_report("colo-compare primary_in error"); } } @@ -467,7 +464,8 @@ static void compare_sec_chr_in(void *opaque, const uint8_t *buf, int size) ret = net_fill_rstate(&s->sec_rs, buf, size); if (ret == -1) { - qemu_chr_add_handlers(s->chr_sec_in, NULL, NULL, NULL, NULL); + qemu_chr_fe_set_handlers(&s->chr_sec_in, NULL, NULL, NULL, + NULL, NULL, true); error_report("colo-compare secondary_in error"); } } @@ -480,10 +478,10 @@ static void *colo_compare_thread(void *opaque) worker_context = g_main_context_new(); - qemu_chr_add_handlers_full(s->chr_pri_in, compare_chr_can_read, - compare_pri_chr_in, NULL, s, worker_context); - qemu_chr_add_handlers_full(s->chr_sec_in, compare_chr_can_read, - compare_sec_chr_in, NULL, s, worker_context); + qemu_chr_fe_set_handlers(&s->chr_pri_in, compare_chr_can_read, + compare_pri_chr_in, NULL, s, worker_context, true); + qemu_chr_fe_set_handlers(&s->chr_sec_in, compare_chr_can_read, + compare_sec_chr_in, NULL, s, worker_context, true); compare_loop = g_main_loop_new(worker_context, FALSE); @@ -545,7 +543,7 @@ static void compare_pri_rs_finalize(SocketReadState *pri_rs) if (packet_enqueue(s, PRIMARY_IN)) { trace_colo_compare_main("primary: unsupported packet in"); - compare_chr_send(s->chr_out, pri_rs->buf, pri_rs->packet_len); + compare_chr_send(&s->chr_out, pri_rs->buf, pri_rs->packet_len); } else { /* compare connection */ g_queue_foreach(&s->conn_list, colo_compare_connection, s); @@ -589,6 +587,7 @@ static int find_and_check_chardev(CharDriverState **chr, chr_name); return 1; } + return 0; } @@ -619,6 +618,7 @@ static void check_old_packet_regular(void *opaque) static void colo_compare_complete(UserCreatable *uc, Error **errp) { CompareState *s = COLO_COMPARE(uc); + CharDriverState *chr; char thread_name[64]; static int compare_id; @@ -634,24 +634,21 @@ static void colo_compare_complete(UserCreatable *uc, Error **errp) return; } - if (find_and_check_chardev(&s->chr_pri_in, s->pri_indev, errp)) { + if (find_and_check_chardev(&chr, s->pri_indev, errp) || + !qemu_chr_fe_init(&s->chr_pri_in, chr, errp)) { return; } - if (find_and_check_chardev(&s->chr_sec_in, s->sec_indev, errp)) { + if (find_and_check_chardev(&chr, s->sec_indev, errp) || + !qemu_chr_fe_init(&s->chr_sec_in, chr, errp)) { return; } - if (find_and_check_chardev(&s->chr_out, s->outdev, errp)) { + if (find_and_check_chardev(&chr, s->outdev, errp) || + !qemu_chr_fe_init(&s->chr_out, chr, errp)) { return; } - qemu_chr_fe_claim_no_fail(s->chr_pri_in); - - qemu_chr_fe_claim_no_fail(s->chr_sec_in); - - qemu_chr_fe_claim_no_fail(s->chr_out); - net_socket_rs_init(&s->pri_rs, compare_pri_rs_finalize); net_socket_rs_init(&s->sec_rs, compare_sec_rs_finalize); @@ -702,17 +699,9 @@ static void colo_compare_finalize(Object *obj) { CompareState *s = COLO_COMPARE(obj); - if (s->chr_pri_in) { - qemu_chr_add_handlers(s->chr_pri_in, NULL, NULL, NULL, NULL); - qemu_chr_fe_release(s->chr_pri_in); - } - if (s->chr_sec_in) { - qemu_chr_add_handlers(s->chr_sec_in, NULL, NULL, NULL, NULL); - qemu_chr_fe_release(s->chr_sec_in); - } - if (s->chr_out) { - qemu_chr_fe_release(s->chr_out); - } + qemu_chr_fe_deinit(&s->chr_pri_in); + qemu_chr_fe_deinit(&s->chr_sec_in); + qemu_chr_fe_deinit(&s->chr_out); g_queue_free(&s->conn_list); diff --git a/net/filter-mirror.c b/net/filter-mirror.c index 0ee58d9..b7d6456 100644 --- a/net/filter-mirror.c +++ b/net/filter-mirror.c @@ -38,12 +38,12 @@ typedef struct MirrorState { NetFilterState parent_obj; char *indev; char *outdev; - CharDriverState *chr_in; - CharDriverState *chr_out; + CharBackend chr_in; + CharBackend chr_out; SocketReadState rs; } MirrorState; -static int filter_mirror_send(CharDriverState *chr_out, +static int filter_mirror_send(CharBackend *chr_out, const struct iovec *iov, int iovcnt) { @@ -110,7 +110,8 @@ static void redirector_chr_read(void *opaque, const uint8_t *buf, int size) ret = net_fill_rstate(&s->rs, buf, size); if (ret == -1) { - qemu_chr_add_handlers(s->chr_in, NULL, NULL, NULL, NULL); + qemu_chr_fe_set_handlers(&s->chr_in, NULL, NULL, NULL, + NULL, NULL, true); } } @@ -121,7 +122,8 @@ static void redirector_chr_event(void *opaque, int event) switch (event) { case CHR_EVENT_CLOSED: - qemu_chr_add_handlers(s->chr_in, NULL, NULL, NULL, NULL); + qemu_chr_fe_set_handlers(&s->chr_in, NULL, NULL, NULL, + NULL, NULL, true); break; default: break; @@ -138,7 +140,7 @@ static ssize_t filter_mirror_receive_iov(NetFilterState *nf, MirrorState *s = FILTER_MIRROR(nf); int ret; - ret = filter_mirror_send(s->chr_out, iov, iovcnt); + ret = filter_mirror_send(&s->chr_out, iov, iovcnt); if (ret) { error_report("filter_mirror_send failed(%s)", strerror(-ret)); } @@ -160,8 +162,8 @@ static ssize_t filter_redirector_receive_iov(NetFilterState *nf, MirrorState *s = FILTER_REDIRECTOR(nf); int ret; - if (s->chr_out) { - ret = filter_mirror_send(s->chr_out, iov, iovcnt); + if (qemu_chr_fe_get_driver(&s->chr_out)) { + ret = filter_mirror_send(&s->chr_out, iov, iovcnt); if (ret) { error_report("filter_mirror_send failed(%s)", strerror(-ret)); } @@ -175,27 +177,21 @@ static void filter_mirror_cleanup(NetFilterState *nf) { MirrorState *s = FILTER_MIRROR(nf); - if (s->chr_out) { - qemu_chr_fe_release(s->chr_out); - } + qemu_chr_fe_deinit(&s->chr_out); } static void filter_redirector_cleanup(NetFilterState *nf) { MirrorState *s = FILTER_REDIRECTOR(nf); - if (s->chr_in) { - qemu_chr_add_handlers(s->chr_in, NULL, NULL, NULL, NULL); - qemu_chr_fe_release(s->chr_in); - } - if (s->chr_out) { - qemu_chr_fe_release(s->chr_out); - } + qemu_chr_fe_deinit(&s->chr_in); + qemu_chr_fe_deinit(&s->chr_out); } static void filter_mirror_setup(NetFilterState *nf, Error **errp) { MirrorState *s = FILTER_MIRROR(nf); + CharDriverState *chr; if (!s->outdev) { error_setg(errp, "filter mirror needs 'outdev' " @@ -203,17 +199,14 @@ static void filter_mirror_setup(NetFilterState *nf, Error **errp) return; } - s->chr_out = qemu_chr_find(s->outdev); - if (s->chr_out == NULL) { + chr = qemu_chr_find(s->outdev); + if (chr == NULL) { error_set(errp, ERROR_CLASS_DEVICE_NOT_FOUND, "Device '%s' not found", s->outdev); return; } - if (qemu_chr_fe_claim(s->chr_out) != 0) { - error_setg(errp, QERR_DEVICE_IN_USE, s->outdev); - return; - } + qemu_chr_fe_init(&s->chr_out, chr, errp); } static void redirector_rs_finalize(SocketReadState *rs) @@ -227,6 +220,7 @@ static void redirector_rs_finalize(SocketReadState *rs) static void filter_redirector_setup(NetFilterState *nf, Error **errp) { MirrorState *s = FILTER_REDIRECTOR(nf); + CharDriverState *chr; if (!s->indev && !s->outdev) { error_setg(errp, "filter redirector needs 'indev' or " @@ -243,26 +237,32 @@ static void filter_redirector_setup(NetFilterState *nf, Error **errp) net_socket_rs_init(&s->rs, redirector_rs_finalize); if (s->indev) { - s->chr_in = qemu_chr_find(s->indev); - if (s->chr_in == NULL) { + chr = qemu_chr_find(s->indev); + if (chr == NULL) { error_set(errp, ERROR_CLASS_DEVICE_NOT_FOUND, "IN Device '%s' not found", s->indev); return; } - qemu_chr_fe_claim_no_fail(s->chr_in); - qemu_chr_add_handlers(s->chr_in, redirector_chr_can_read, - redirector_chr_read, redirector_chr_event, nf); + if (!qemu_chr_fe_init(&s->chr_in, chr, errp)) { + return; + } + + qemu_chr_fe_set_handlers(&s->chr_in, redirector_chr_can_read, + redirector_chr_read, redirector_chr_event, + nf, NULL, true); } if (s->outdev) { - s->chr_out = qemu_chr_find(s->outdev); - if (s->chr_out == NULL) { + chr = qemu_chr_find(s->outdev); + if (chr == NULL) { error_set(errp, ERROR_CLASS_DEVICE_NOT_FOUND, "OUT Device '%s' not found", s->outdev); return; } - qemu_chr_fe_claim_no_fail(s->chr_out); + if (!qemu_chr_fe_init(&s->chr_out, chr, errp)) { + return; + } } } diff --git a/net/filter-rewriter.c b/net/filter-rewriter.c index 89abe72..c4ab91c 100644 --- a/net/filter-rewriter.c +++ b/net/filter-rewriter.c @@ -68,15 +68,11 @@ static int handle_primary_tcp_pkt(NetFilterState *nf, tcp_pkt = (struct tcphdr *)pkt->transport_header; if (trace_event_get_state(TRACE_COLO_FILTER_REWRITER_DEBUG)) { - char *sdebug, *ddebug; - sdebug = strdup(inet_ntoa(pkt->ip->ip_src)); - ddebug = strdup(inet_ntoa(pkt->ip->ip_dst)); - trace_colo_filter_rewriter_pkt_info(__func__, sdebug, ddebug, + trace_colo_filter_rewriter_pkt_info(__func__, + inet_ntoa(pkt->ip->ip_src), inet_ntoa(pkt->ip->ip_dst), ntohl(tcp_pkt->th_seq), ntohl(tcp_pkt->th_ack), tcp_pkt->th_flags); trace_colo_filter_rewriter_conn_offset(conn->offset); - g_free(sdebug); - g_free(ddebug); } if (((tcp_pkt->th_flags & (TH_ACK | TH_SYN)) == TH_SYN)) { @@ -116,15 +112,11 @@ static int handle_secondary_tcp_pkt(NetFilterState *nf, tcp_pkt = (struct tcphdr *)pkt->transport_header; if (trace_event_get_state(TRACE_COLO_FILTER_REWRITER_DEBUG)) { - char *sdebug, *ddebug; - sdebug = strdup(inet_ntoa(pkt->ip->ip_src)); - ddebug = strdup(inet_ntoa(pkt->ip->ip_dst)); - trace_colo_filter_rewriter_pkt_info(__func__, sdebug, ddebug, + trace_colo_filter_rewriter_pkt_info(__func__, + inet_ntoa(pkt->ip->ip_src), inet_ntoa(pkt->ip->ip_dst), ntohl(tcp_pkt->th_seq), ntohl(tcp_pkt->th_ack), tcp_pkt->th_flags); trace_colo_filter_rewriter_conn_offset(conn->offset); - g_free(sdebug); - g_free(ddebug); } if (((tcp_pkt->th_flags & (TH_ACK | TH_SYN)) == (TH_ACK | TH_SYN))) { @@ -162,6 +154,7 @@ static ssize_t colo_rewriter_receive_iov(NetFilterState *nf, iov_to_buf(iov, iovcnt, 0, buf, size); pkt = packet_new(buf, size); + g_free(buf); /* * if we get tcp packet diff --git a/net/slirp.c b/net/slirp.c index b60893f..64dd325 100644 --- a/net/slirp.c +++ b/net/slirp.c @@ -40,6 +40,7 @@ #include "sysemu/char.h" #include "sysemu/sysemu.h" #include "qemu/cutils.h" +#include "qapi/error.h" static int get_str_sep(char *buf, int buf_size, const char **pp, int sep) { @@ -682,7 +683,7 @@ int net_slirp_smb(const char *exported_dir) #endif /* !defined(_WIN32) */ struct GuestFwd { - CharDriverState *hd; + CharBackend hd; struct in_addr server; int port; Slirp *slirp; @@ -746,15 +747,24 @@ static int slirp_guestfwd(SlirpState *s, const char *config_str, return -1; } } else { - fwd = g_new(struct GuestFwd, 1); - fwd->hd = qemu_chr_new(buf, p, NULL); - if (!fwd->hd) { + Error *err = NULL; + CharDriverState *chr = qemu_chr_new(buf, p); + + if (!chr) { error_report("could not open guest forwarding device '%s'", buf); + return -1; + } + + fwd = g_new(struct GuestFwd, 1); + qemu_chr_fe_init(&fwd->hd, chr, &err); + if (err) { + error_report_err(err); g_free(fwd); return -1; } - if (slirp_add_exec(s->slirp, 3, fwd->hd, &server, port) < 0) { + if (slirp_add_exec(s->slirp, 3, qemu_chr_fe_get_driver(&fwd->hd), + &server, port) < 0) { error_report("conflicting/invalid host:port in guest forwarding " "rule '%s'", config_str); g_free(fwd); @@ -764,9 +774,8 @@ static int slirp_guestfwd(SlirpState *s, const char *config_str, fwd->port = port; fwd->slirp = s->slirp; - qemu_chr_fe_claim_no_fail(fwd->hd); - qemu_chr_add_handlers(fwd->hd, guestfwd_can_read, guestfwd_read, - NULL, fwd); + qemu_chr_fe_set_handlers(&fwd->hd, guestfwd_can_read, guestfwd_read, + NULL, fwd, NULL, true); } return 0; diff --git a/net/tap-bsd.c b/net/tap-bsd.c index c506ac3..6c96922 100644 --- a/net/tap-bsd.c +++ b/net/tap-bsd.c @@ -35,6 +35,10 @@ #include <net/if_tap.h> #endif +#if defined(__OpenBSD__) +#include <sys/param.h> +#endif + #ifndef __FreeBSD__ int tap_open(char *ifname, int ifname_size, int *vnet_hdr, int vnet_hdr_required, int mq_required, Error **errp) @@ -55,7 +59,7 @@ int tap_open(char *ifname, int ifname_size, int *vnet_hdr, if (*ifname) { snprintf(dname, sizeof dname, "/dev/%s", ifname); } else { -#if defined(__OpenBSD__) +#if defined(__OpenBSD__) && OpenBSD < 201605 snprintf(dname, sizeof dname, "/dev/tun%d", i); #else snprintf(dname, sizeof dname, "/dev/tap%d", i); diff --git a/net/trace-events b/net/trace-events index d67f048..b1913a6 100644 --- a/net/trace-events +++ b/net/trace-events @@ -13,6 +13,7 @@ colo_compare_icmp_miscompare(const char *sta, int size) ": %s = %d" colo_compare_ip_info(int psize, const char *sta, const char *stb, int ssize, const char *stc, const char *std) "ppkt size = %d, ip_src = %s, ip_dst = %s, spkt size = %d, ip_src = %s, ip_dst = %s" colo_old_packet_check_found(int64_t old_time) "%" PRId64 colo_compare_miscompare(void) "" +colo_compare_pkt_info(const char *src, const char *dst, uint32_t pseq, uint32_t pack, uint32_t sseq, uint32_t sack, int res, uint32_t pflag, uint32_t sflag, int psize, int ssize) "src/dst: %s/%s p: seq/ack=%u/%u s: seq/ack=%u/%u res=%d flags=%x/%x ppkt_size: %d spkt_size: %d\n" # net/filter-rewriter.c colo_filter_rewriter_debug(void) "" diff --git a/net/vhost-user.c b/net/vhost-user.c index 5b94c84..7aff77e 100644 --- a/net/vhost-user.c +++ b/net/vhost-user.c @@ -20,7 +20,7 @@ typedef struct VhostUserState { NetClientState nc; - CharDriverState *chr; + CharBackend chr; /* only queue index 0 */ VHostNetState *vhost_net; guint watch; uint64_t acked_features; @@ -62,7 +62,7 @@ static void vhost_user_stop(int queues, NetClientState *ncs[]) } } -static int vhost_user_start(int queues, NetClientState *ncs[]) +static int vhost_user_start(int queues, NetClientState *ncs[], CharBackend *be) { VhostNetOptions options; struct vhost_net *net = NULL; @@ -78,7 +78,7 @@ static int vhost_user_start(int queues, NetClientState *ncs[]) s = DO_UPCAST(VhostUserState, nc, ncs[i]); options.net_backend = ncs[i]; - options.opaque = s->chr; + options.opaque = be; options.busyloop_timeout = 0; net = vhost_net_init(&options); if (!net) { @@ -150,10 +150,8 @@ static void vhost_user_cleanup(NetClientState *nc) g_free(s->vhost_net); s->vhost_net = NULL; } - if (s->chr) { - qemu_chr_add_handlers(s->chr, NULL, NULL, NULL, NULL); - qemu_chr_fe_release(s->chr); - s->chr = NULL; + if (nc->queue_index == 0) { + qemu_chr_fe_deinit(&s->chr); } qemu_purge_queued_packets(nc); @@ -187,7 +185,7 @@ static gboolean net_vhost_user_watch(GIOChannel *chan, GIOCondition cond, { VhostUserState *s = opaque; - qemu_chr_disconnect(s->chr); + qemu_chr_fe_disconnect(&s->chr); return FALSE; } @@ -197,6 +195,7 @@ static void net_vhost_user_event(void *opaque, int event) const char *name = opaque; NetClientState *ncs[MAX_QUEUE_NUM]; VhostUserState *s; + CharDriverState *chr; Error *err = NULL; int queues; @@ -206,13 +205,14 @@ static void net_vhost_user_event(void *opaque, int event) assert(queues < MAX_QUEUE_NUM); s = DO_UPCAST(VhostUserState, nc, ncs[0]); - trace_vhost_user_event(s->chr->label, event); + chr = qemu_chr_fe_get_driver(&s->chr); + trace_vhost_user_event(chr->label, event); switch (event) { case CHR_EVENT_OPENED: - s->watch = qemu_chr_fe_add_watch(s->chr, G_IO_HUP, + s->watch = qemu_chr_fe_add_watch(&s->chr, G_IO_HUP, net_vhost_user_watch, s); - if (vhost_user_start(queues, ncs) < 0) { - qemu_chr_disconnect(s->chr); + if (vhost_user_start(queues, ncs, &s->chr) < 0) { + qemu_chr_fe_disconnect(&s->chr); return; } qmp_set_link(name, true, &err); @@ -235,6 +235,7 @@ static int net_vhost_user_init(NetClientState *peer, const char *device, const char *name, CharDriverState *chr, int queues) { + Error *err = NULL; NetClientState *nc, *nc0 = NULL; VhostUserState *s; int i; @@ -244,28 +245,28 @@ static int net_vhost_user_init(NetClientState *peer, const char *device, for (i = 0; i < queues; i++) { nc = qemu_new_net_client(&net_vhost_user_info, peer, device, name); - if (!nc0) { - nc0 = nc; - } - snprintf(nc->info_str, sizeof(nc->info_str), "vhost-user%d to %s", i, chr->label); - nc->queue_index = i; + if (!nc0) { + nc0 = nc; + s = DO_UPCAST(VhostUserState, nc, nc); + if (!qemu_chr_fe_init(&s->chr, chr, &err)) { + error_report_err(err); + return -1; + } + } - s = DO_UPCAST(VhostUserState, nc, nc); - s->chr = chr; } s = DO_UPCAST(VhostUserState, nc, nc0); do { - Error *err = NULL; - if (qemu_chr_wait_connected(chr, &err) < 0) { + if (qemu_chr_fe_wait_connected(&s->chr, &err) < 0) { error_report_err(err); return -1; } - qemu_chr_add_handlers(chr, NULL, NULL, - net_vhost_user_event, nc0->name); + qemu_chr_fe_set_handlers(&s->chr, NULL, NULL, + net_vhost_user_event, nc0->name, NULL, true); } while (!s->started); assert(s->vhost_net); @@ -294,8 +295,6 @@ static CharDriverState *net_vhost_claim_chardev( return NULL; } - qemu_chr_fe_claim_no_fail(chr); - return chr; } diff --git a/pc-bios/bios-256k.bin b/pc-bios/bios-256k.bin Binary files differindex 57fb4d8..8dc4838 100644 --- a/pc-bios/bios-256k.bin +++ b/pc-bios/bios-256k.bin diff --git a/pc-bios/bios.bin b/pc-bios/bios.bin Binary files differindex 8a6869f..0b16c54 100644 --- a/pc-bios/bios.bin +++ b/pc-bios/bios.bin diff --git a/pc-bios/vgabios-cirrus.bin b/pc-bios/vgabios-cirrus.bin Binary files differindex 3f4bb30..67389ca 100644 --- a/pc-bios/vgabios-cirrus.bin +++ b/pc-bios/vgabios-cirrus.bin diff --git a/pc-bios/vgabios-qxl.bin b/pc-bios/vgabios-qxl.bin Binary files differindex 38d31b6..9d3d9b4 100644 --- a/pc-bios/vgabios-qxl.bin +++ b/pc-bios/vgabios-qxl.bin diff --git a/pc-bios/vgabios-stdvga.bin b/pc-bios/vgabios-stdvga.bin Binary files differindex e469c10..6fc42b1 100644 --- a/pc-bios/vgabios-stdvga.bin +++ b/pc-bios/vgabios-stdvga.bin diff --git a/pc-bios/vgabios-virtio.bin b/pc-bios/vgabios-virtio.bin Binary files differindex d42b028..b2e6e5a 100644 --- a/pc-bios/vgabios-virtio.bin +++ b/pc-bios/vgabios-virtio.bin diff --git a/pc-bios/vgabios-vmware.bin b/pc-bios/vgabios-vmware.bin Binary files differindex 26bc0b7..eccd87b 100644 --- a/pc-bios/vgabios-vmware.bin +++ b/pc-bios/vgabios-vmware.bin diff --git a/pc-bios/vgabios.bin b/pc-bios/vgabios.bin Binary files differindex 2d1a7c6..450230a 100644 --- a/pc-bios/vgabios.bin +++ b/pc-bios/vgabios.bin diff --git a/qapi-schema.json b/qapi-schema.json index 5a8ec38..d6a43a1 100644 --- a/qapi-schema.json +++ b/qapi-schema.json @@ -737,6 +737,7 @@ '*tls-hostname': 'str', '*max-bandwidth': 'int', '*downtime-limit': 'int'} } + ## # @query-migrate-parameters # diff --git a/qapi/Makefile.objs b/qapi/Makefile.objs index 7ea4aeb..33906ff 100644 --- a/qapi/Makefile.objs +++ b/qapi/Makefile.objs @@ -1,5 +1,5 @@ -util-obj-y = qapi-visit-core.o qapi-dealloc-visitor.o qmp-input-visitor.o -util-obj-y += qmp-output-visitor.o qmp-registry.o qmp-dispatch.o +util-obj-y = qapi-visit-core.o qapi-dealloc-visitor.o qobject-input-visitor.o +util-obj-y += qobject-output-visitor.o qmp-registry.o qmp-dispatch.o util-obj-y += string-input-visitor.o string-output-visitor.o util-obj-y += opts-visitor.o qapi-clone-visitor.o util-obj-y += qmp-event.o diff --git a/qapi/block-core.json b/qapi/block-core.json index 1b7aa1b..cd1fa7b 100644 --- a/qapi/block-core.json +++ b/qapi/block-core.json @@ -1703,15 +1703,16 @@ # # @host_device, @host_cdrom: Since 2.1 # @gluster: Since 2.7 +# @nbd: Since 2.8 # # Since: 2.0 ## { 'enum': 'BlockdevDriver', 'data': [ 'archipelago', 'blkdebug', 'blkverify', 'bochs', 'cloop', 'dmg', 'file', 'ftp', 'ftps', 'gluster', 'host_cdrom', - 'host_device', 'http', 'https', 'luks', 'null-aio', 'null-co', - 'parallels', 'qcow', 'qcow2', 'qed', 'quorum', 'raw', - 'replication', 'tftp', 'vdi', 'vhdx', 'vmdk', 'vpc', 'vvfat' ] } + 'host_device', 'http', 'https', 'luks', 'nbd', 'null-aio', + 'null-co', 'parallels', 'qcow', 'qcow2', 'qed', 'quorum', 'raw', + 'replication', 'tftp', 'vdi', 'vhdx', 'vmdk', 'vpc', 'vvfat' ] } ## # @BlockdevOptionsFile @@ -2197,7 +2198,8 @@ # @mode: the replication mode # # @top-id: #optional In secondary mode, node name or device ID of the root -# node who owns the replication node chain. Ignored in primary mode. +# node who owns the replication node chain. Must not be given in +# primary mode. # # Since: 2.8 ## @@ -2219,6 +2221,24 @@ 'data': { 'filename': 'str' } } ## +# @BlockdevOptionsNbd +# +# Driver specific block device options for NBD. +# +# @server: NBD server address +# +# @export: #optional export name +# +# @tls-creds: #optional TLS credentials ID +# +# Since: 2.8 +## +{ 'struct': 'BlockdevOptionsNbd', + 'data': { 'server': 'SocketAddress', + '*export': 'str', + '*tls-creds': 'str' } } + +## # @BlockdevOptions # # Options for creating a block device. Many options are available for all @@ -2263,7 +2283,7 @@ 'https': 'BlockdevOptionsCurl', # TODO iscsi: Wait for structured options 'luks': 'BlockdevOptionsLUKS', -# TODO nbd: Should take InetSocketAddress for 'host'? + 'nbd': 'BlockdevOptionsNbd', # TODO nfs: Wait for structured options 'null-aio': 'BlockdevOptionsNull', 'null-co': 'BlockdevOptionsNull', @@ -2312,11 +2332,11 @@ # block drivers among other things. Stay away from it unless you want # to help with its development. # -# @options: block device options for the new device +# For the arguments, see the documentation of BlockdevOptions. # # Since: 1.7 ## -{ 'command': 'blockdev-add', 'data': { 'options': 'BlockdevOptions' } } +{ 'command': 'blockdev-add', 'data': 'BlockdevOptions', 'boxed': true } ## # @x-blockdev-del: diff --git a/qapi/qapi-clone-visitor.c b/qapi/qapi-clone-visitor.c index 0bb8216..34086cb 100644 --- a/qapi/qapi-clone-visitor.c +++ b/qapi/qapi-clone-visitor.c @@ -110,7 +110,7 @@ static void qapi_clone_type_str(Visitor *v, const char *name, char **obj, assert(qcv->depth); /* * Pointer was already cloned by g_memdup; create fresh copy. - * Note that as long as qmp-output-visitor accepts NULL instead of + * Note that as long as qobject-output-visitor accepts NULL instead of * "", then we must do likewise. However, we want to obey the * input visitor semantics of never producing NULL when the empty * string is intended. diff --git a/qapi/qapi-visit-core.c b/qapi/qapi-visit-core.c index 55f5876..63bd97b 100644 --- a/qapi/qapi-visit-core.c +++ b/qapi/qapi-visit-core.c @@ -19,10 +19,12 @@ #include "qapi/qmp/qerror.h" #include "qapi/visitor.h" #include "qapi/visitor-impl.h" +#include "trace.h" void visit_complete(Visitor *v, void *opaque) { assert(v->type != VISITOR_OUTPUT || v->complete); + trace_visit_complete(v, opaque); if (v->complete) { v->complete(v, opaque); } @@ -30,6 +32,7 @@ void visit_complete(Visitor *v, void *opaque) void visit_free(Visitor *v) { + trace_visit_free(v); if (v) { v->free(v); } @@ -40,6 +43,7 @@ void visit_start_struct(Visitor *v, const char *name, void **obj, { Error *err = NULL; + trace_visit_start_struct(v, name, obj, size); if (obj) { assert(size); assert(!(v->type & VISITOR_OUTPUT) || *obj); @@ -53,6 +57,7 @@ void visit_start_struct(Visitor *v, const char *name, void **obj, void visit_check_struct(Visitor *v, Error **errp) { + trace_visit_check_struct(v); if (v->check_struct) { v->check_struct(v, errp); } @@ -60,6 +65,7 @@ void visit_check_struct(Visitor *v, Error **errp) void visit_end_struct(Visitor *v, void **obj) { + trace_visit_end_struct(v, obj); v->end_struct(v, obj); } @@ -69,6 +75,7 @@ void visit_start_list(Visitor *v, const char *name, GenericList **list, Error *err = NULL; assert(!list || size >= sizeof(GenericList)); + trace_visit_start_list(v, name, list, size); v->start_list(v, name, list, size, &err); if (list && (v->type & VISITOR_INPUT)) { assert(!(err && *list)); @@ -79,11 +86,13 @@ void visit_start_list(Visitor *v, const char *name, GenericList **list, GenericList *visit_next_list(Visitor *v, GenericList *tail, size_t size) { assert(tail && size >= sizeof(GenericList)); + trace_visit_next_list(v, tail, size); return v->next_list(v, tail, size); } void visit_end_list(Visitor *v, void **obj) { + trace_visit_end_list(v, obj); v->end_list(v, obj); } @@ -95,6 +104,7 @@ void visit_start_alternate(Visitor *v, const char *name, assert(obj && size >= sizeof(GenericAlternate)); assert(!(v->type & VISITOR_OUTPUT) || *obj); + trace_visit_start_alternate(v, name, obj, size, promote_int); if (v->start_alternate) { v->start_alternate(v, name, obj, size, promote_int, &err); } @@ -106,6 +116,7 @@ void visit_start_alternate(Visitor *v, const char *name, void visit_end_alternate(Visitor *v, void **obj) { + trace_visit_end_alternate(v, obj); if (v->end_alternate) { v->end_alternate(v, obj); } @@ -113,6 +124,7 @@ void visit_end_alternate(Visitor *v, void **obj) bool visit_optional(Visitor *v, const char *name, bool *present) { + trace_visit_optional(v, name, present); if (v->optional) { v->optional(v, name, present); } @@ -127,6 +139,7 @@ bool visit_is_input(Visitor *v) void visit_type_int(Visitor *v, const char *name, int64_t *obj, Error **errp) { assert(obj); + trace_visit_type_int(v, name, obj); v->type_int64(v, name, obj, errp); } @@ -150,7 +163,10 @@ static void visit_type_uintN(Visitor *v, uint64_t *obj, const char *name, void visit_type_uint8(Visitor *v, const char *name, uint8_t *obj, Error **errp) { - uint64_t value = *obj; + uint64_t value; + + trace_visit_type_uint8(v, name, obj); + value = *obj; visit_type_uintN(v, &value, name, UINT8_MAX, "uint8_t", errp); *obj = value; } @@ -158,7 +174,10 @@ void visit_type_uint8(Visitor *v, const char *name, uint8_t *obj, void visit_type_uint16(Visitor *v, const char *name, uint16_t *obj, Error **errp) { - uint64_t value = *obj; + uint64_t value; + + trace_visit_type_uint16(v, name, obj); + value = *obj; visit_type_uintN(v, &value, name, UINT16_MAX, "uint16_t", errp); *obj = value; } @@ -166,7 +185,10 @@ void visit_type_uint16(Visitor *v, const char *name, uint16_t *obj, void visit_type_uint32(Visitor *v, const char *name, uint32_t *obj, Error **errp) { - uint64_t value = *obj; + uint64_t value; + + trace_visit_type_uint32(v, name, obj); + value = *obj; visit_type_uintN(v, &value, name, UINT32_MAX, "uint32_t", errp); *obj = value; } @@ -175,6 +197,7 @@ void visit_type_uint64(Visitor *v, const char *name, uint64_t *obj, Error **errp) { assert(obj); + trace_visit_type_uint64(v, name, obj); v->type_uint64(v, name, obj, errp); } @@ -198,7 +221,10 @@ static void visit_type_intN(Visitor *v, int64_t *obj, const char *name, void visit_type_int8(Visitor *v, const char *name, int8_t *obj, Error **errp) { - int64_t value = *obj; + int64_t value; + + trace_visit_type_int8(v, name, obj); + value = *obj; visit_type_intN(v, &value, name, INT8_MIN, INT8_MAX, "int8_t", errp); *obj = value; } @@ -206,7 +232,10 @@ void visit_type_int8(Visitor *v, const char *name, int8_t *obj, Error **errp) void visit_type_int16(Visitor *v, const char *name, int16_t *obj, Error **errp) { - int64_t value = *obj; + int64_t value; + + trace_visit_type_int16(v, name, obj); + value = *obj; visit_type_intN(v, &value, name, INT16_MIN, INT16_MAX, "int16_t", errp); *obj = value; } @@ -214,7 +243,10 @@ void visit_type_int16(Visitor *v, const char *name, int16_t *obj, void visit_type_int32(Visitor *v, const char *name, int32_t *obj, Error **errp) { - int64_t value = *obj; + int64_t value; + + trace_visit_type_int32(v, name, obj); + value = *obj; visit_type_intN(v, &value, name, INT32_MIN, INT32_MAX, "int32_t", errp); *obj = value; } @@ -223,6 +255,7 @@ void visit_type_int64(Visitor *v, const char *name, int64_t *obj, Error **errp) { assert(obj); + trace_visit_type_int64(v, name, obj); v->type_int64(v, name, obj, errp); } @@ -230,6 +263,7 @@ void visit_type_size(Visitor *v, const char *name, uint64_t *obj, Error **errp) { assert(obj); + trace_visit_type_size(v, name, obj); if (v->type_size) { v->type_size(v, name, obj, errp); } else { @@ -240,6 +274,7 @@ void visit_type_size(Visitor *v, const char *name, uint64_t *obj, void visit_type_bool(Visitor *v, const char *name, bool *obj, Error **errp) { assert(obj); + trace_visit_type_bool(v, name, obj); v->type_bool(v, name, obj, errp); } @@ -252,6 +287,7 @@ void visit_type_str(Visitor *v, const char *name, char **obj, Error **errp) * can enable: assert(!(v->type & VISITOR_OUTPUT) || *obj); */ + trace_visit_type_str(v, name, obj); v->type_str(v, name, obj, &err); if (v->type & VISITOR_INPUT) { assert(!err != !*obj); @@ -263,6 +299,7 @@ void visit_type_number(Visitor *v, const char *name, double *obj, Error **errp) { assert(obj); + trace_visit_type_number(v, name, obj); v->type_number(v, name, obj, errp); } @@ -272,6 +309,7 @@ void visit_type_any(Visitor *v, const char *name, QObject **obj, Error **errp) assert(obj); assert(v->type != VISITOR_OUTPUT || *obj); + trace_visit_type_any(v, name, obj); v->type_any(v, name, obj, &err); if (v->type == VISITOR_INPUT) { assert(!err != !*obj); @@ -281,6 +319,7 @@ void visit_type_any(Visitor *v, const char *name, QObject **obj, Error **errp) void visit_type_null(Visitor *v, const char *name, Error **errp) { + trace_visit_type_null(v, name); v->type_null(v, name, errp); } diff --git a/qapi/qmp-output-visitor.c b/qapi/qmp-output-visitor.c deleted file mode 100644 index 9e3b67c..0000000 --- a/qapi/qmp-output-visitor.c +++ /dev/null @@ -1,256 +0,0 @@ -/* - * Core Definitions for QAPI/QMP Command Registry - * - * Copyright (C) 2012-2016 Red Hat, Inc. - * Copyright IBM, Corp. 2011 - * - * Authors: - * Anthony Liguori <aliguori@us.ibm.com> - * - * This work is licensed under the terms of the GNU LGPL, version 2.1 or later. - * See the COPYING.LIB file in the top-level directory. - * - */ - -#include "qemu/osdep.h" -#include "qapi/qmp-output-visitor.h" -#include "qapi/visitor-impl.h" -#include "qemu/queue.h" -#include "qemu-common.h" -#include "qapi/qmp/types.h" - -typedef struct QStackEntry -{ - QObject *value; - void *qapi; /* sanity check that caller uses same pointer */ - QSLIST_ENTRY(QStackEntry) node; -} QStackEntry; - -struct QmpOutputVisitor -{ - Visitor visitor; - QSLIST_HEAD(, QStackEntry) stack; /* Stack of unfinished containers */ - QObject *root; /* Root of the output visit */ - QObject **result; /* User's storage location for result */ -}; - -#define qmp_output_add(qov, name, value) \ - qmp_output_add_obj(qov, name, QOBJECT(value)) -#define qmp_output_push(qov, value, qapi) \ - qmp_output_push_obj(qov, QOBJECT(value), qapi) - -static QmpOutputVisitor *to_qov(Visitor *v) -{ - return container_of(v, QmpOutputVisitor, visitor); -} - -/* Push @value onto the stack of current QObjects being built */ -static void qmp_output_push_obj(QmpOutputVisitor *qov, QObject *value, - void *qapi) -{ - QStackEntry *e = g_malloc0(sizeof(*e)); - - assert(qov->root); - assert(value); - e->value = value; - e->qapi = qapi; - QSLIST_INSERT_HEAD(&qov->stack, e, node); -} - -/* Pop a value off the stack of QObjects being built, and return it. */ -static QObject *qmp_output_pop(QmpOutputVisitor *qov, void *qapi) -{ - QStackEntry *e = QSLIST_FIRST(&qov->stack); - QObject *value; - - assert(e); - assert(e->qapi == qapi); - QSLIST_REMOVE_HEAD(&qov->stack, node); - value = e->value; - assert(value); - g_free(e); - return value; -} - -/* Add @value to the current QObject being built. - * If the stack is visiting a dictionary or list, @value is now owned - * by that container. Otherwise, @value is now the root. */ -static void qmp_output_add_obj(QmpOutputVisitor *qov, const char *name, - QObject *value) -{ - QStackEntry *e = QSLIST_FIRST(&qov->stack); - QObject *cur = e ? e->value : NULL; - - if (!cur) { - /* Don't allow reuse of visitor on more than one root */ - assert(!qov->root); - qov->root = value; - } else { - switch (qobject_type(cur)) { - case QTYPE_QDICT: - assert(name); - qdict_put_obj(qobject_to_qdict(cur), name, value); - break; - case QTYPE_QLIST: - assert(!name); - qlist_append_obj(qobject_to_qlist(cur), value); - break; - default: - g_assert_not_reached(); - } - } -} - -static void qmp_output_start_struct(Visitor *v, const char *name, void **obj, - size_t unused, Error **errp) -{ - QmpOutputVisitor *qov = to_qov(v); - QDict *dict = qdict_new(); - - qmp_output_add(qov, name, dict); - qmp_output_push(qov, dict, obj); -} - -static void qmp_output_end_struct(Visitor *v, void **obj) -{ - QmpOutputVisitor *qov = to_qov(v); - QObject *value = qmp_output_pop(qov, obj); - assert(qobject_type(value) == QTYPE_QDICT); -} - -static void qmp_output_start_list(Visitor *v, const char *name, - GenericList **listp, size_t size, - Error **errp) -{ - QmpOutputVisitor *qov = to_qov(v); - QList *list = qlist_new(); - - qmp_output_add(qov, name, list); - qmp_output_push(qov, list, listp); -} - -static GenericList *qmp_output_next_list(Visitor *v, GenericList *tail, - size_t size) -{ - return tail->next; -} - -static void qmp_output_end_list(Visitor *v, void **obj) -{ - QmpOutputVisitor *qov = to_qov(v); - QObject *value = qmp_output_pop(qov, obj); - assert(qobject_type(value) == QTYPE_QLIST); -} - -static void qmp_output_type_int64(Visitor *v, const char *name, int64_t *obj, - Error **errp) -{ - QmpOutputVisitor *qov = to_qov(v); - qmp_output_add(qov, name, qint_from_int(*obj)); -} - -static void qmp_output_type_uint64(Visitor *v, const char *name, uint64_t *obj, - Error **errp) -{ - /* FIXME: QMP outputs values larger than INT64_MAX as negative */ - QmpOutputVisitor *qov = to_qov(v); - qmp_output_add(qov, name, qint_from_int(*obj)); -} - -static void qmp_output_type_bool(Visitor *v, const char *name, bool *obj, - Error **errp) -{ - QmpOutputVisitor *qov = to_qov(v); - qmp_output_add(qov, name, qbool_from_bool(*obj)); -} - -static void qmp_output_type_str(Visitor *v, const char *name, char **obj, - Error **errp) -{ - QmpOutputVisitor *qov = to_qov(v); - if (*obj) { - qmp_output_add(qov, name, qstring_from_str(*obj)); - } else { - qmp_output_add(qov, name, qstring_from_str("")); - } -} - -static void qmp_output_type_number(Visitor *v, const char *name, double *obj, - Error **errp) -{ - QmpOutputVisitor *qov = to_qov(v); - qmp_output_add(qov, name, qfloat_from_double(*obj)); -} - -static void qmp_output_type_any(Visitor *v, const char *name, QObject **obj, - Error **errp) -{ - QmpOutputVisitor *qov = to_qov(v); - qobject_incref(*obj); - qmp_output_add_obj(qov, name, *obj); -} - -static void qmp_output_type_null(Visitor *v, const char *name, Error **errp) -{ - QmpOutputVisitor *qov = to_qov(v); - qmp_output_add_obj(qov, name, qnull()); -} - -/* Finish building, and return the root object. - * The root object is never null. The caller becomes the object's - * owner, and should use qobject_decref() when done with it. */ -static void qmp_output_complete(Visitor *v, void *opaque) -{ - QmpOutputVisitor *qov = to_qov(v); - - /* A visit must have occurred, with each start paired with end. */ - assert(qov->root && QSLIST_EMPTY(&qov->stack)); - assert(opaque == qov->result); - - qobject_incref(qov->root); - *qov->result = qov->root; - qov->result = NULL; -} - -static void qmp_output_free(Visitor *v) -{ - QmpOutputVisitor *qov = to_qov(v); - QStackEntry *e; - - while (!QSLIST_EMPTY(&qov->stack)) { - e = QSLIST_FIRST(&qov->stack); - QSLIST_REMOVE_HEAD(&qov->stack, node); - g_free(e); - } - - qobject_decref(qov->root); - g_free(qov); -} - -Visitor *qmp_output_visitor_new(QObject **result) -{ - QmpOutputVisitor *v; - - v = g_malloc0(sizeof(*v)); - - v->visitor.type = VISITOR_OUTPUT; - v->visitor.start_struct = qmp_output_start_struct; - v->visitor.end_struct = qmp_output_end_struct; - v->visitor.start_list = qmp_output_start_list; - v->visitor.next_list = qmp_output_next_list; - v->visitor.end_list = qmp_output_end_list; - v->visitor.type_int64 = qmp_output_type_int64; - v->visitor.type_uint64 = qmp_output_type_uint64; - v->visitor.type_bool = qmp_output_type_bool; - v->visitor.type_str = qmp_output_type_str; - v->visitor.type_number = qmp_output_type_number; - v->visitor.type_any = qmp_output_type_any; - v->visitor.type_null = qmp_output_type_null; - v->visitor.complete = qmp_output_complete; - v->visitor.free = qmp_output_free; - - *result = NULL; - v->result = result; - - return &v->visitor; -} diff --git a/qapi/qmp-input-visitor.c b/qapi/qobject-input-visitor.c index 37a8e1f..0063327 100644 --- a/qapi/qmp-input-visitor.c +++ b/qapi/qobject-input-visitor.c @@ -14,7 +14,7 @@ #include "qemu/osdep.h" #include "qapi/error.h" -#include "qapi/qmp-input-visitor.h" +#include "qapi/qobject-input-visitor.h" #include "qapi/visitor-impl.h" #include "qemu/queue.h" #include "qemu-common.h" @@ -34,7 +34,7 @@ typedef struct StackObject QSLIST_ENTRY(StackObject) node; } StackObject; -struct QmpInputVisitor +struct QObjectInputVisitor { Visitor visitor; @@ -49,14 +49,14 @@ struct QmpInputVisitor bool strict; }; -static QmpInputVisitor *to_qiv(Visitor *v) +static QObjectInputVisitor *to_qiv(Visitor *v) { - return container_of(v, QmpInputVisitor, visitor); + return container_of(v, QObjectInputVisitor, visitor); } -static QObject *qmp_input_get_object(QmpInputVisitor *qiv, - const char *name, - bool consume, Error **errp) +static QObject *qobject_input_get_object(QObjectInputVisitor *qiv, + const char *name, + bool consume, Error **errp) { StackObject *tos; QObject *qobj; @@ -102,8 +102,9 @@ static void qdict_add_key(const char *key, QObject *obj, void *opaque) g_hash_table_insert(h, (gpointer) key, NULL); } -static const QListEntry *qmp_input_push(QmpInputVisitor *qiv, QObject *obj, - void *qapi, Error **errp) +static const QListEntry *qobject_input_push(QObjectInputVisitor *qiv, + QObject *obj, void *qapi, + Error **errp) { GHashTable *h; StackObject *tos = g_new0(StackObject, 1); @@ -125,9 +126,9 @@ static const QListEntry *qmp_input_push(QmpInputVisitor *qiv, QObject *obj, } -static void qmp_input_check_struct(Visitor *v, Error **errp) +static void qobject_input_check_struct(Visitor *v, Error **errp) { - QmpInputVisitor *qiv = to_qiv(v); + QObjectInputVisitor *qiv = to_qiv(v); StackObject *tos = QSLIST_FIRST(&qiv->stack); assert(tos && !tos->entry); @@ -145,7 +146,7 @@ static void qmp_input_check_struct(Visitor *v, Error **errp) } } -static void qmp_input_stack_object_free(StackObject *tos) +static void qobject_input_stack_object_free(StackObject *tos) { if (tos->h) { g_hash_table_unref(tos->h); @@ -154,21 +155,21 @@ static void qmp_input_stack_object_free(StackObject *tos) g_free(tos); } -static void qmp_input_pop(Visitor *v, void **obj) +static void qobject_input_pop(Visitor *v, void **obj) { - QmpInputVisitor *qiv = to_qiv(v); + QObjectInputVisitor *qiv = to_qiv(v); StackObject *tos = QSLIST_FIRST(&qiv->stack); assert(tos && tos->qapi == obj); QSLIST_REMOVE_HEAD(&qiv->stack, node); - qmp_input_stack_object_free(tos); + qobject_input_stack_object_free(tos); } -static void qmp_input_start_struct(Visitor *v, const char *name, void **obj, - size_t size, Error **errp) +static void qobject_input_start_struct(Visitor *v, const char *name, void **obj, + size_t size, Error **errp) { - QmpInputVisitor *qiv = to_qiv(v); - QObject *qobj = qmp_input_get_object(qiv, name, true, errp); + QObjectInputVisitor *qiv = to_qiv(v); + QObject *qobj = qobject_input_get_object(qiv, name, true, errp); Error *err = NULL; if (obj) { @@ -183,7 +184,7 @@ static void qmp_input_start_struct(Visitor *v, const char *name, void **obj, return; } - qmp_input_push(qiv, qobj, obj, &err); + qobject_input_push(qiv, qobj, obj, &err); if (err) { error_propagate(errp, err); return; @@ -195,11 +196,12 @@ static void qmp_input_start_struct(Visitor *v, const char *name, void **obj, } -static void qmp_input_start_list(Visitor *v, const char *name, - GenericList **list, size_t size, Error **errp) +static void qobject_input_start_list(Visitor *v, const char *name, + GenericList **list, size_t size, + Error **errp) { - QmpInputVisitor *qiv = to_qiv(v); - QObject *qobj = qmp_input_get_object(qiv, name, true, errp); + QObjectInputVisitor *qiv = to_qiv(v); + QObject *qobj = qobject_input_get_object(qiv, name, true, errp); const QListEntry *entry; if (!qobj) { @@ -214,7 +216,7 @@ static void qmp_input_start_list(Visitor *v, const char *name, return; } - entry = qmp_input_push(qiv, qobj, list, errp); + entry = qobject_input_push(qiv, qobj, list, errp); if (list) { if (entry) { *list = g_malloc0(size); @@ -224,10 +226,10 @@ static void qmp_input_start_list(Visitor *v, const char *name, } } -static GenericList *qmp_input_next_list(Visitor *v, GenericList *tail, - size_t size) +static GenericList *qobject_input_next_list(Visitor *v, GenericList *tail, + size_t size) { - QmpInputVisitor *qiv = to_qiv(v); + QObjectInputVisitor *qiv = to_qiv(v); StackObject *so = QSLIST_FIRST(&qiv->stack); if (!so->entry) { @@ -238,12 +240,12 @@ static GenericList *qmp_input_next_list(Visitor *v, GenericList *tail, } -static void qmp_input_start_alternate(Visitor *v, const char *name, - GenericAlternate **obj, size_t size, - bool promote_int, Error **errp) +static void qobject_input_start_alternate(Visitor *v, const char *name, + GenericAlternate **obj, size_t size, + bool promote_int, Error **errp) { - QmpInputVisitor *qiv = to_qiv(v); - QObject *qobj = qmp_input_get_object(qiv, name, false, errp); + QObjectInputVisitor *qiv = to_qiv(v); + QObject *qobj = qobject_input_get_object(qiv, name, false, errp); if (!qobj) { *obj = NULL; @@ -256,11 +258,11 @@ static void qmp_input_start_alternate(Visitor *v, const char *name, } } -static void qmp_input_type_int64(Visitor *v, const char *name, int64_t *obj, - Error **errp) +static void qobject_input_type_int64(Visitor *v, const char *name, int64_t *obj, + Error **errp) { - QmpInputVisitor *qiv = to_qiv(v); - QObject *qobj = qmp_input_get_object(qiv, name, true, errp); + QObjectInputVisitor *qiv = to_qiv(v); + QObject *qobj = qobject_input_get_object(qiv, name, true, errp); QInt *qint; if (!qobj) { @@ -276,12 +278,12 @@ static void qmp_input_type_int64(Visitor *v, const char *name, int64_t *obj, *obj = qint_get_int(qint); } -static void qmp_input_type_uint64(Visitor *v, const char *name, uint64_t *obj, - Error **errp) +static void qobject_input_type_uint64(Visitor *v, const char *name, + uint64_t *obj, Error **errp) { /* FIXME: qobject_to_qint mishandles values over INT64_MAX */ - QmpInputVisitor *qiv = to_qiv(v); - QObject *qobj = qmp_input_get_object(qiv, name, true, errp); + QObjectInputVisitor *qiv = to_qiv(v); + QObject *qobj = qobject_input_get_object(qiv, name, true, errp); QInt *qint; if (!qobj) { @@ -297,11 +299,11 @@ static void qmp_input_type_uint64(Visitor *v, const char *name, uint64_t *obj, *obj = qint_get_int(qint); } -static void qmp_input_type_bool(Visitor *v, const char *name, bool *obj, - Error **errp) +static void qobject_input_type_bool(Visitor *v, const char *name, bool *obj, + Error **errp) { - QmpInputVisitor *qiv = to_qiv(v); - QObject *qobj = qmp_input_get_object(qiv, name, true, errp); + QObjectInputVisitor *qiv = to_qiv(v); + QObject *qobj = qobject_input_get_object(qiv, name, true, errp); QBool *qbool; if (!qobj) { @@ -317,11 +319,11 @@ static void qmp_input_type_bool(Visitor *v, const char *name, bool *obj, *obj = qbool_get_bool(qbool); } -static void qmp_input_type_str(Visitor *v, const char *name, char **obj, - Error **errp) +static void qobject_input_type_str(Visitor *v, const char *name, char **obj, + Error **errp) { - QmpInputVisitor *qiv = to_qiv(v); - QObject *qobj = qmp_input_get_object(qiv, name, true, errp); + QObjectInputVisitor *qiv = to_qiv(v); + QObject *qobj = qobject_input_get_object(qiv, name, true, errp); QString *qstr; *obj = NULL; @@ -338,11 +340,11 @@ static void qmp_input_type_str(Visitor *v, const char *name, char **obj, *obj = g_strdup(qstring_get_str(qstr)); } -static void qmp_input_type_number(Visitor *v, const char *name, double *obj, - Error **errp) +static void qobject_input_type_number(Visitor *v, const char *name, double *obj, + Error **errp) { - QmpInputVisitor *qiv = to_qiv(v); - QObject *qobj = qmp_input_get_object(qiv, name, true, errp); + QObjectInputVisitor *qiv = to_qiv(v); + QObject *qobj = qobject_input_get_object(qiv, name, true, errp); QInt *qint; QFloat *qfloat; @@ -365,11 +367,11 @@ static void qmp_input_type_number(Visitor *v, const char *name, double *obj, "number"); } -static void qmp_input_type_any(Visitor *v, const char *name, QObject **obj, - Error **errp) +static void qobject_input_type_any(Visitor *v, const char *name, QObject **obj, + Error **errp) { - QmpInputVisitor *qiv = to_qiv(v); - QObject *qobj = qmp_input_get_object(qiv, name, true, errp); + QObjectInputVisitor *qiv = to_qiv(v); + QObject *qobj = qobject_input_get_object(qiv, name, true, errp); *obj = NULL; if (!qobj) { @@ -380,10 +382,10 @@ static void qmp_input_type_any(Visitor *v, const char *name, QObject **obj, *obj = qobj; } -static void qmp_input_type_null(Visitor *v, const char *name, Error **errp) +static void qobject_input_type_null(Visitor *v, const char *name, Error **errp) { - QmpInputVisitor *qiv = to_qiv(v); - QObject *qobj = qmp_input_get_object(qiv, name, true, errp); + QObjectInputVisitor *qiv = to_qiv(v); + QObject *qobj = qobject_input_get_object(qiv, name, true, errp); if (!qobj) { return; @@ -395,10 +397,10 @@ static void qmp_input_type_null(Visitor *v, const char *name, Error **errp) } } -static void qmp_input_optional(Visitor *v, const char *name, bool *present) +static void qobject_input_optional(Visitor *v, const char *name, bool *present) { - QmpInputVisitor *qiv = to_qiv(v); - QObject *qobj = qmp_input_get_object(qiv, name, false, NULL); + QObjectInputVisitor *qiv = to_qiv(v); + QObject *qobj = qobject_input_get_object(qiv, name, false, NULL); if (!qobj) { *present = false; @@ -408,44 +410,44 @@ static void qmp_input_optional(Visitor *v, const char *name, bool *present) *present = true; } -static void qmp_input_free(Visitor *v) +static void qobject_input_free(Visitor *v) { - QmpInputVisitor *qiv = to_qiv(v); + QObjectInputVisitor *qiv = to_qiv(v); while (!QSLIST_EMPTY(&qiv->stack)) { StackObject *tos = QSLIST_FIRST(&qiv->stack); QSLIST_REMOVE_HEAD(&qiv->stack, node); - qmp_input_stack_object_free(tos); + qobject_input_stack_object_free(tos); } qobject_decref(qiv->root); g_free(qiv); } -Visitor *qmp_input_visitor_new(QObject *obj, bool strict) +Visitor *qobject_input_visitor_new(QObject *obj, bool strict) { - QmpInputVisitor *v; + QObjectInputVisitor *v; assert(obj); v = g_malloc0(sizeof(*v)); v->visitor.type = VISITOR_INPUT; - v->visitor.start_struct = qmp_input_start_struct; - v->visitor.check_struct = qmp_input_check_struct; - v->visitor.end_struct = qmp_input_pop; - v->visitor.start_list = qmp_input_start_list; - v->visitor.next_list = qmp_input_next_list; - v->visitor.end_list = qmp_input_pop; - v->visitor.start_alternate = qmp_input_start_alternate; - v->visitor.type_int64 = qmp_input_type_int64; - v->visitor.type_uint64 = qmp_input_type_uint64; - v->visitor.type_bool = qmp_input_type_bool; - v->visitor.type_str = qmp_input_type_str; - v->visitor.type_number = qmp_input_type_number; - v->visitor.type_any = qmp_input_type_any; - v->visitor.type_null = qmp_input_type_null; - v->visitor.optional = qmp_input_optional; - v->visitor.free = qmp_input_free; + v->visitor.start_struct = qobject_input_start_struct; + v->visitor.check_struct = qobject_input_check_struct; + v->visitor.end_struct = qobject_input_pop; + v->visitor.start_list = qobject_input_start_list; + v->visitor.next_list = qobject_input_next_list; + v->visitor.end_list = qobject_input_pop; + v->visitor.start_alternate = qobject_input_start_alternate; + v->visitor.type_int64 = qobject_input_type_int64; + v->visitor.type_uint64 = qobject_input_type_uint64; + v->visitor.type_bool = qobject_input_type_bool; + v->visitor.type_str = qobject_input_type_str; + v->visitor.type_number = qobject_input_type_number; + v->visitor.type_any = qobject_input_type_any; + v->visitor.type_null = qobject_input_type_null; + v->visitor.optional = qobject_input_optional; + v->visitor.free = qobject_input_free; v->strict = strict; v->root = obj; diff --git a/qapi/qobject-output-visitor.c b/qapi/qobject-output-visitor.c new file mode 100644 index 0000000..8711270 --- /dev/null +++ b/qapi/qobject-output-visitor.c @@ -0,0 +1,254 @@ +/* + * Core Definitions for QAPI/QMP Command Registry + * + * Copyright (C) 2012-2016 Red Hat, Inc. + * Copyright IBM, Corp. 2011 + * + * Authors: + * Anthony Liguori <aliguori@us.ibm.com> + * + * This work is licensed under the terms of the GNU LGPL, version 2.1 or later. + * See the COPYING.LIB file in the top-level directory. + * + */ + +#include "qemu/osdep.h" +#include "qapi/qobject-output-visitor.h" +#include "qapi/visitor-impl.h" +#include "qemu/queue.h" +#include "qemu-common.h" +#include "qapi/qmp/types.h" + +typedef struct QStackEntry { + QObject *value; + void *qapi; /* sanity check that caller uses same pointer */ + QSLIST_ENTRY(QStackEntry) node; +} QStackEntry; + +struct QObjectOutputVisitor { + Visitor visitor; + QSLIST_HEAD(, QStackEntry) stack; /* Stack of unfinished containers */ + QObject *root; /* Root of the output visit */ + QObject **result; /* User's storage location for result */ +}; + +#define qobject_output_add(qov, name, value) \ + qobject_output_add_obj(qov, name, QOBJECT(value)) +#define qobject_output_push(qov, value, qapi) \ + qobject_output_push_obj(qov, QOBJECT(value), qapi) + +static QObjectOutputVisitor *to_qov(Visitor *v) +{ + return container_of(v, QObjectOutputVisitor, visitor); +} + +/* Push @value onto the stack of current QObjects being built */ +static void qobject_output_push_obj(QObjectOutputVisitor *qov, QObject *value, + void *qapi) +{ + QStackEntry *e = g_malloc0(sizeof(*e)); + + assert(qov->root); + assert(value); + e->value = value; + e->qapi = qapi; + QSLIST_INSERT_HEAD(&qov->stack, e, node); +} + +/* Pop a value off the stack of QObjects being built, and return it. */ +static QObject *qobject_output_pop(QObjectOutputVisitor *qov, void *qapi) +{ + QStackEntry *e = QSLIST_FIRST(&qov->stack); + QObject *value; + + assert(e); + assert(e->qapi == qapi); + QSLIST_REMOVE_HEAD(&qov->stack, node); + value = e->value; + assert(value); + g_free(e); + return value; +} + +/* Add @value to the current QObject being built. + * If the stack is visiting a dictionary or list, @value is now owned + * by that container. Otherwise, @value is now the root. */ +static void qobject_output_add_obj(QObjectOutputVisitor *qov, const char *name, + QObject *value) +{ + QStackEntry *e = QSLIST_FIRST(&qov->stack); + QObject *cur = e ? e->value : NULL; + + if (!cur) { + /* Don't allow reuse of visitor on more than one root */ + assert(!qov->root); + qov->root = value; + } else { + switch (qobject_type(cur)) { + case QTYPE_QDICT: + assert(name); + qdict_put_obj(qobject_to_qdict(cur), name, value); + break; + case QTYPE_QLIST: + assert(!name); + qlist_append_obj(qobject_to_qlist(cur), value); + break; + default: + g_assert_not_reached(); + } + } +} + +static void qobject_output_start_struct(Visitor *v, const char *name, + void **obj, size_t unused, Error **errp) +{ + QObjectOutputVisitor *qov = to_qov(v); + QDict *dict = qdict_new(); + + qobject_output_add(qov, name, dict); + qobject_output_push(qov, dict, obj); +} + +static void qobject_output_end_struct(Visitor *v, void **obj) +{ + QObjectOutputVisitor *qov = to_qov(v); + QObject *value = qobject_output_pop(qov, obj); + assert(qobject_type(value) == QTYPE_QDICT); +} + +static void qobject_output_start_list(Visitor *v, const char *name, + GenericList **listp, size_t size, + Error **errp) +{ + QObjectOutputVisitor *qov = to_qov(v); + QList *list = qlist_new(); + + qobject_output_add(qov, name, list); + qobject_output_push(qov, list, listp); +} + +static GenericList *qobject_output_next_list(Visitor *v, GenericList *tail, + size_t size) +{ + return tail->next; +} + +static void qobject_output_end_list(Visitor *v, void **obj) +{ + QObjectOutputVisitor *qov = to_qov(v); + QObject *value = qobject_output_pop(qov, obj); + assert(qobject_type(value) == QTYPE_QLIST); +} + +static void qobject_output_type_int64(Visitor *v, const char *name, + int64_t *obj, Error **errp) +{ + QObjectOutputVisitor *qov = to_qov(v); + qobject_output_add(qov, name, qint_from_int(*obj)); +} + +static void qobject_output_type_uint64(Visitor *v, const char *name, + uint64_t *obj, Error **errp) +{ + /* FIXME values larger than INT64_MAX become negative */ + QObjectOutputVisitor *qov = to_qov(v); + qobject_output_add(qov, name, qint_from_int(*obj)); +} + +static void qobject_output_type_bool(Visitor *v, const char *name, bool *obj, + Error **errp) +{ + QObjectOutputVisitor *qov = to_qov(v); + qobject_output_add(qov, name, qbool_from_bool(*obj)); +} + +static void qobject_output_type_str(Visitor *v, const char *name, char **obj, + Error **errp) +{ + QObjectOutputVisitor *qov = to_qov(v); + if (*obj) { + qobject_output_add(qov, name, qstring_from_str(*obj)); + } else { + qobject_output_add(qov, name, qstring_from_str("")); + } +} + +static void qobject_output_type_number(Visitor *v, const char *name, + double *obj, Error **errp) +{ + QObjectOutputVisitor *qov = to_qov(v); + qobject_output_add(qov, name, qfloat_from_double(*obj)); +} + +static void qobject_output_type_any(Visitor *v, const char *name, + QObject **obj, Error **errp) +{ + QObjectOutputVisitor *qov = to_qov(v); + qobject_incref(*obj); + qobject_output_add_obj(qov, name, *obj); +} + +static void qobject_output_type_null(Visitor *v, const char *name, Error **errp) +{ + QObjectOutputVisitor *qov = to_qov(v); + qobject_output_add_obj(qov, name, qnull()); +} + +/* Finish building, and return the root object. + * The root object is never null. The caller becomes the object's + * owner, and should use qobject_decref() when done with it. */ +static void qobject_output_complete(Visitor *v, void *opaque) +{ + QObjectOutputVisitor *qov = to_qov(v); + + /* A visit must have occurred, with each start paired with end. */ + assert(qov->root && QSLIST_EMPTY(&qov->stack)); + assert(opaque == qov->result); + + qobject_incref(qov->root); + *qov->result = qov->root; + qov->result = NULL; +} + +static void qobject_output_free(Visitor *v) +{ + QObjectOutputVisitor *qov = to_qov(v); + QStackEntry *e; + + while (!QSLIST_EMPTY(&qov->stack)) { + e = QSLIST_FIRST(&qov->stack); + QSLIST_REMOVE_HEAD(&qov->stack, node); + g_free(e); + } + + qobject_decref(qov->root); + g_free(qov); +} + +Visitor *qobject_output_visitor_new(QObject **result) +{ + QObjectOutputVisitor *v; + + v = g_malloc0(sizeof(*v)); + + v->visitor.type = VISITOR_OUTPUT; + v->visitor.start_struct = qobject_output_start_struct; + v->visitor.end_struct = qobject_output_end_struct; + v->visitor.start_list = qobject_output_start_list; + v->visitor.next_list = qobject_output_next_list; + v->visitor.end_list = qobject_output_end_list; + v->visitor.type_int64 = qobject_output_type_int64; + v->visitor.type_uint64 = qobject_output_type_uint64; + v->visitor.type_bool = qobject_output_type_bool; + v->visitor.type_str = qobject_output_type_str; + v->visitor.type_number = qobject_output_type_number; + v->visitor.type_any = qobject_output_type_any; + v->visitor.type_null = qobject_output_type_null; + v->visitor.complete = qobject_output_complete; + v->visitor.free = qobject_output_free; + + *result = NULL; + v->result = result; + + return &v->visitor; +} diff --git a/qapi/trace-events b/qapi/trace-events new file mode 100644 index 0000000..2c5d3bc --- /dev/null +++ b/qapi/trace-events @@ -0,0 +1,33 @@ +# qapi-visit-core.c +visit_free(void *v) "v=%p" +visit_complete(void *v, void *opaque) "v=%p opaque=%p" + +visit_start_struct(void *v, const char *name, void *obj, size_t size) "v=%p name=%s obj=%p size=%zu" +visit_check_struct(void *v) "v=%p" +visit_end_struct(void *v, void *obj) "v=%p obj=%p" + +visit_start_list(void *v, const char *name, void *obj, size_t size) "v=%p name=%s obj=%p size=%zu" +visit_next_list(void *v, void *tail, size_t size) "v=%p tail=%p size=%zu" +visit_end_list(void *v, void *obj) "v=%p obj=%p" + +visit_start_alternate(void *v, const char *name, void *obj, size_t size, bool promote_int) "v=%p name=%s obj=%p size=%zu promote_int=%d" +visit_end_alternate(void *v, void *obj) "v=%p obj=%p" + +visit_optional(void *v, const char *name, bool *present) "v=%p name=%s present=%p" + +visit_type_enum(void *v, const char *name, int *obj) "v=%p name=%s obj=%p" +visit_type_int(void *v, const char *name, int64_t *obj) "v=%p name=%s obj=%p" +visit_type_uint8(void *v, const char *name, uint8_t *obj) "v=%p name=%s obj=%p" +visit_type_uint16(void *v, const char *name, uint16_t *obj) "v=%p name=%s obj=%p" +visit_type_uint32(void *v, const char *name, uint32_t *obj) "v=%p name=%s obj=%p" +visit_type_uint64(void *v, const char *name, uint64_t *obj) "v=%p name=%s obj=%p" +visit_type_int8(void *v, const char *name, int8_t *obj) "v=%p name=%s obj=%p" +visit_type_int16(void *v, const char *name, int16_t *obj) "v=%p name=%s obj=%p" +visit_type_int32(void *v, const char *name, int32_t *obj) "v=%p name=%s obj=%p" +visit_type_int64(void *v, const char *name, int64_t *obj) "v=%p name=%s obj=%p" +visit_type_size(void *v, const char *name, uint64_t *obj) "v=%p name=%s obj=%p" +visit_type_bool(void *v, const char *name, bool *obj) "v=%p name=%s obj=%p" +visit_type_str(void *v, const char *name, char **obj) "v=%p name=%s obj=%p" +visit_type_number(void *v, const char *name, double *obj) "v=%p name=%s obj=%p" +visit_type_any(void *v, const char *name, void *obj) "v=%p name=%s obj=%p" +visit_type_null(void *v, const char *name) "v=%p name=%s" diff --git a/qemu-char.c b/qemu-char.c index 4d0986c..1e5a0e8 100644 --- a/qemu-char.c +++ b/qemu-char.c @@ -89,6 +89,8 @@ #define READ_RETRIES 10 #define TCP_MAX_FDS 16 +typedef struct MuxDriver MuxDriver; + /***********************************************************/ /* Socket address helpers */ @@ -190,6 +192,8 @@ CharDriverState *qemu_chr_alloc(ChardevCommon *backend, Error **errp) void qemu_chr_be_event(CharDriverState *s, int event) { + CharBackend *be = s->be; + /* Keep track if the char device is open */ switch (event) { case CHR_EVENT_OPENED: @@ -200,9 +204,11 @@ void qemu_chr_be_event(CharDriverState *s, int event) break; } - if (!s->chr_event) + if (!be || !be->chr_event) { return; - s->chr_event(s->handler_opaque, event); + } + + be->chr_event(be->opaque, event); } void qemu_chr_be_generic_open(CharDriverState *s) @@ -266,10 +272,15 @@ static int qemu_chr_fe_write_buffer(CharDriverState *s, const uint8_t *buf, int return res; } -int qemu_chr_fe_write(CharDriverState *s, const uint8_t *buf, int len) +int qemu_chr_fe_write(CharBackend *be, const uint8_t *buf, int len) { + CharDriverState *s = be->chr; int ret; + if (!s) { + return 0; + } + if (s->replay && replay_mode == REPLAY_MODE_PLAY) { int offset; replay_char_write_event_load(&ret, &offset); @@ -294,7 +305,7 @@ int qemu_chr_fe_write(CharDriverState *s, const uint8_t *buf, int len) return ret; } -int qemu_chr_fe_write_all(CharDriverState *s, const uint8_t *buf, int len) +static int qemu_chr_write_all(CharDriverState *s, const uint8_t *buf, int len) { int offset; int res; @@ -318,15 +329,27 @@ int qemu_chr_fe_write_all(CharDriverState *s, const uint8_t *buf, int len) return offset; } -int qemu_chr_fe_read_all(CharDriverState *s, uint8_t *buf, int len) +int qemu_chr_fe_write_all(CharBackend *be, const uint8_t *buf, int len) { + CharDriverState *s = be->chr; + + if (!s) { + return 0; + } + + return qemu_chr_write_all(s, buf, len); +} + +int qemu_chr_fe_read_all(CharBackend *be, uint8_t *buf, int len) +{ + CharDriverState *s = be->chr; int offset = 0, counter = 10; int res; - if (!s->chr_sync_read) { + if (!s || !s->chr_sync_read) { return 0; } - + if (s->replay && replay_mode == REPLAY_MODE_PLAY) { return replay_char_read_all_load(buf); } @@ -363,10 +386,12 @@ int qemu_chr_fe_read_all(CharDriverState *s, uint8_t *buf, int len) return offset; } -int qemu_chr_fe_ioctl(CharDriverState *s, int cmd, void *arg) +int qemu_chr_fe_ioctl(CharBackend *be, int cmd, void *arg) { + CharDriverState *s = be->chr; int res; - if (!s->chr_ioctl || s->replay) { + + if (!s || !s->chr_ioctl || s->replay) { res = -ENOTSUP; } else { res = s->chr_ioctl(s, cmd, arg); @@ -377,15 +402,21 @@ int qemu_chr_fe_ioctl(CharDriverState *s, int cmd, void *arg) int qemu_chr_be_can_write(CharDriverState *s) { - if (!s->chr_can_read) + CharBackend *be = s->be; + + if (!be || !be->chr_can_read) { return 0; - return s->chr_can_read(s->handler_opaque); + } + + return be->chr_can_read(be->opaque); } void qemu_chr_be_write_impl(CharDriverState *s, uint8_t *buf, int len) { - if (s->chr_read) { - s->chr_read(s->handler_opaque, buf, len); + CharBackend *be = s->be; + + if (be && be->chr_read) { + be->chr_read(be->opaque, buf, len); } } @@ -401,11 +432,12 @@ void qemu_chr_be_write(CharDriverState *s, uint8_t *buf, int len) } } -int qemu_chr_fe_get_msgfd(CharDriverState *s) +int qemu_chr_fe_get_msgfd(CharBackend *be) { + CharDriverState *s = be->chr; int fd; - int res = (qemu_chr_fe_get_msgfds(s, &fd, 1) == 1) ? fd : -1; - if (s->replay) { + int res = (qemu_chr_fe_get_msgfds(be, &fd, 1) == 1) ? fd : -1; + if (s && s->replay) { fprintf(stderr, "Replay: get msgfd is not supported for serial devices yet\n"); exit(1); @@ -413,13 +445,25 @@ int qemu_chr_fe_get_msgfd(CharDriverState *s) return res; } -int qemu_chr_fe_get_msgfds(CharDriverState *s, int *fds, int len) +int qemu_chr_fe_get_msgfds(CharBackend *be, int *fds, int len) { + CharDriverState *s = be->chr; + + if (!s) { + return -1; + } + return s->get_msgfds ? s->get_msgfds(s, fds, len) : -1; } -int qemu_chr_fe_set_msgfds(CharDriverState *s, int *fds, int num) +int qemu_chr_fe_set_msgfds(CharBackend *be, int *fds, int num) { + CharDriverState *s = be->chr; + + if (!s) { + return -1; + } + return s->set_msgfds ? s->set_msgfds(s, fds, num) : -1; } @@ -428,14 +472,20 @@ int qemu_chr_add_client(CharDriverState *s, int fd) return s->chr_add_client ? s->chr_add_client(s, fd) : -1; } -void qemu_chr_accept_input(CharDriverState *s) +void qemu_chr_fe_accept_input(CharBackend *be) { + CharDriverState *s = be->chr; + + if (!s) { + return; + } + if (s->chr_accept_input) s->chr_accept_input(s); qemu_notify_event(); } -void qemu_chr_fe_printf(CharDriverState *s, const char *fmt, ...) +void qemu_chr_fe_printf(CharBackend *be, const char *fmt, ...) { char buf[READ_BUF_LEN]; va_list ap; @@ -443,55 +493,13 @@ void qemu_chr_fe_printf(CharDriverState *s, const char *fmt, ...) vsnprintf(buf, sizeof(buf), fmt, ap); /* XXX this blocks entire thread. Rewrite to use * qemu_chr_fe_write and background I/O callbacks */ - qemu_chr_fe_write_all(s, (uint8_t *)buf, strlen(buf)); + qemu_chr_fe_write_all(be, (uint8_t *)buf, strlen(buf)); va_end(ap); } static void remove_fd_in_watch(CharDriverState *chr); - -void qemu_chr_add_handlers_full(CharDriverState *s, - IOCanReadHandler *fd_can_read, - IOReadHandler *fd_read, - IOEventHandler *fd_event, - void *opaque, - GMainContext *context) -{ - int fe_open; - - if (!opaque && !fd_can_read && !fd_read && !fd_event) { - fe_open = 0; - remove_fd_in_watch(s); - } else { - fe_open = 1; - } - s->chr_can_read = fd_can_read; - s->chr_read = fd_read; - s->chr_event = fd_event; - s->handler_opaque = opaque; - if (s->chr_update_read_handler) { - s->chr_update_read_handler(s, context); - } - - if (!s->explicit_fe_open) { - qemu_chr_fe_set_open(s, fe_open); - } - - /* We're connecting to an already opened device, so let's make sure we - also get the open event */ - if (fe_open && s->be_open) { - qemu_chr_be_generic_open(s); - } -} - -void qemu_chr_add_handlers(CharDriverState *s, - IOCanReadHandler *fd_can_read, - IOReadHandler *fd_read, - IOEventHandler *fd_event, - void *opaque) -{ - qemu_chr_add_handlers_full(s, fd_can_read, fd_read, - fd_event, opaque, NULL); -} +static void mux_chr_set_handlers(CharDriverState *chr, GMainContext *context); +static void mux_set_focus(MuxDriver *d, int focus); static int null_chr_write(CharDriverState *chr, const uint8_t *buf, int len) { @@ -501,6 +509,7 @@ static int null_chr_write(CharDriverState *chr, const uint8_t *buf, int len) static CharDriverState *qemu_chr_open_null(const char *id, ChardevBackend *backend, ChardevReturn *ret, + bool *be_opened, Error **errp) { CharDriverState *chr; @@ -511,7 +520,7 @@ static CharDriverState *qemu_chr_open_null(const char *id, return NULL; } chr->chr_write = null_chr_write; - chr->explicit_be_open = true; + *be_opened = false; return chr; } @@ -519,12 +528,9 @@ static CharDriverState *qemu_chr_open_null(const char *id, #define MAX_MUX 4 #define MUX_BUFFER_SIZE 32 /* Must be a power of 2. */ #define MUX_BUFFER_MASK (MUX_BUFFER_SIZE - 1) -typedef struct { - IOCanReadHandler *chr_can_read[MAX_MUX]; - IOReadHandler *chr_read[MAX_MUX]; - IOEventHandler *chr_event[MAX_MUX]; - void *ext_opaque[MAX_MUX]; - CharDriverState *drv; +struct MuxDriver { + CharBackend *backends[MAX_MUX]; + CharBackend chr; int focus; int mux_cnt; int term_got_escape; @@ -540,8 +546,7 @@ typedef struct { /* Protected by the CharDriverState chr_write_lock. */ int linestart; int64_t timestamps_start; -} MuxDriver; - +}; /* Called with chr_write_lock held. */ static int mux_chr_write(CharDriverState *chr, const uint8_t *buf, int len) @@ -549,7 +554,7 @@ static int mux_chr_write(CharDriverState *chr, const uint8_t *buf, int len) MuxDriver *d = chr->opaque; int ret; if (!d->timestamps) { - ret = qemu_chr_fe_write(d->drv, buf, len); + ret = qemu_chr_fe_write(&d->chr, buf, len); } else { int i; @@ -573,10 +578,11 @@ static int mux_chr_write(CharDriverState *chr, const uint8_t *buf, int len) (int)(ti % 1000)); /* XXX this blocks entire thread. Rewrite to use * qemu_chr_fe_write and background I/O callbacks */ - qemu_chr_fe_write_all(d->drv, (uint8_t *)buf1, strlen(buf1)); + qemu_chr_fe_write_all(&d->chr, + (uint8_t *)buf1, strlen(buf1)); d->linestart = 0; } - ret += qemu_chr_fe_write(d->drv, buf+i, 1); + ret += qemu_chr_fe_write(&d->chr, buf + i, 1); if (buf[i] == '\n') { d->linestart = 1; } @@ -613,21 +619,24 @@ static void mux_print_help(CharDriverState *chr) } /* XXX this blocks entire thread. Rewrite to use * qemu_chr_fe_write and background I/O callbacks */ - qemu_chr_fe_write_all(chr, (uint8_t *)cbuf, strlen(cbuf)); + qemu_chr_write_all(chr, (uint8_t *)cbuf, strlen(cbuf)); for (i = 0; mux_help[i] != NULL; i++) { for (j=0; mux_help[i][j] != '\0'; j++) { if (mux_help[i][j] == '%') - qemu_chr_fe_write_all(chr, (uint8_t *)ebuf, strlen(ebuf)); + qemu_chr_write_all(chr, (uint8_t *)ebuf, strlen(ebuf)); else - qemu_chr_fe_write_all(chr, (uint8_t *)&mux_help[i][j], 1); + qemu_chr_write_all(chr, (uint8_t *)&mux_help[i][j], 1); } } } static void mux_chr_send_event(MuxDriver *d, int mux_nr, int event) { - if (d->chr_event[mux_nr]) - d->chr_event[mux_nr](d->ext_opaque[mux_nr], event); + CharBackend *be = d->backends[mux_nr]; + + if (be && be->chr_event) { + be->chr_event(be->opaque, event); + } } static int mux_proc_byte(CharDriverState *chr, MuxDriver *d, int ch) @@ -644,7 +653,7 @@ static int mux_proc_byte(CharDriverState *chr, MuxDriver *d, int ch) case 'x': { const char *term = "QEMU: Terminated\n\r"; - qemu_chr_fe_write_all(chr, (uint8_t *)term, strlen(term)); + qemu_chr_write_all(chr, (uint8_t *)term, strlen(term)); exit(0); break; } @@ -655,12 +664,9 @@ static int mux_proc_byte(CharDriverState *chr, MuxDriver *d, int ch) qemu_chr_be_event(chr, CHR_EVENT_BREAK); break; case 'c': + assert(d->mux_cnt > 0); /* handler registered with first fe */ /* Switch to the next registered device */ - mux_chr_send_event(d, d->focus, CHR_EVENT_MUX_OUT); - d->focus++; - if (d->focus >= d->mux_cnt) - d->focus = 0; - mux_chr_send_event(d, d->focus, CHR_EVENT_MUX_IN); + mux_set_focus(d, (d->focus + 1) % d->mux_cnt); break; case 't': d->timestamps = !d->timestamps; @@ -681,12 +687,12 @@ static void mux_chr_accept_input(CharDriverState *chr) { MuxDriver *d = chr->opaque; int m = d->focus; + CharBackend *be = d->backends[m]; - while (d->prod[m] != d->cons[m] && - d->chr_can_read[m] && - d->chr_can_read[m](d->ext_opaque[m])) { - d->chr_read[m](d->ext_opaque[m], - &d->buffer[m][d->cons[m]++ & MUX_BUFFER_MASK], 1); + while (be && d->prod[m] != d->cons[m] && + be->chr_can_read && be->chr_can_read(be->opaque)) { + be->chr_read(be->opaque, + &d->buffer[m][d->cons[m]++ & MUX_BUFFER_MASK], 1); } } @@ -695,11 +701,16 @@ static int mux_chr_can_read(void *opaque) CharDriverState *chr = opaque; MuxDriver *d = chr->opaque; int m = d->focus; + CharBackend *be = d->backends[m]; - if ((d->prod[m] - d->cons[m]) < MUX_BUFFER_SIZE) + if ((d->prod[m] - d->cons[m]) < MUX_BUFFER_SIZE) { return 1; - if (d->chr_can_read[m]) - return d->chr_can_read[m](d->ext_opaque[m]); + } + + if (be && be->chr_can_read) { + return be->chr_can_read(be->opaque); + } + return 0; } @@ -708,16 +719,17 @@ static void mux_chr_read(void *opaque, const uint8_t *buf, int size) CharDriverState *chr = opaque; MuxDriver *d = chr->opaque; int m = d->focus; + CharBackend *be = d->backends[m]; int i; - mux_chr_accept_input (opaque); + mux_chr_accept_input(opaque); - for(i = 0; i < size; i++) + for (i = 0; i < size; i++) if (mux_proc_byte(chr, d, buf[i])) { if (d->prod[m] == d->cons[m] && - d->chr_can_read[m] && - d->chr_can_read[m](d->ext_opaque[m])) - d->chr_read[m](d->ext_opaque[m], &buf[i], 1); + be && be->chr_can_read && + be->chr_can_read(be->opaque)) + be->chr_read(be->opaque, &buf[i], 1); else d->buffer[m][d->prod[m]++ & MUX_BUFFER_MASK] = buf[i]; } @@ -734,34 +746,6 @@ static void mux_chr_event(void *opaque, int event) mux_chr_send_event(d, i, event); } -static void mux_chr_update_read_handler(CharDriverState *chr, - GMainContext *context) -{ - MuxDriver *d = chr->opaque; - - if (d->mux_cnt >= MAX_MUX) { - fprintf(stderr, "Cannot add I/O handlers, MUX array is full\n"); - return; - } - d->ext_opaque[d->mux_cnt] = chr->handler_opaque; - d->chr_can_read[d->mux_cnt] = chr->chr_can_read; - d->chr_read[d->mux_cnt] = chr->chr_read; - d->chr_event[d->mux_cnt] = chr->chr_event; - /* Fix up the real driver with mux routines */ - if (d->mux_cnt == 0) { - qemu_chr_add_handlers_full(d->drv, mux_chr_can_read, - mux_chr_read, - mux_chr_event, - chr, context); - } - if (d->focus != -1) { - mux_chr_send_event(d, d->focus, CHR_EVENT_MUX_OUT); - } - d->focus = d->mux_cnt; - d->mux_cnt++; - mux_chr_send_event(d, d->focus, CHR_EVENT_MUX_IN); -} - static bool muxes_realized; /** @@ -805,19 +789,57 @@ static Notifier muxes_realize_notify = { static GSource *mux_chr_add_watch(CharDriverState *s, GIOCondition cond) { MuxDriver *d = s->opaque; - return d->drv->chr_add_watch(d->drv, cond); + CharDriverState *chr = qemu_chr_fe_get_driver(&d->chr); + + return chr->chr_add_watch(chr, cond); } -static void mux_chr_close(struct CharDriverState *chr) +static void mux_chr_free(struct CharDriverState *chr) { MuxDriver *d = chr->opaque; + int i; + for (i = 0; i < d->mux_cnt; i++) { + CharBackend *be = d->backends[i]; + if (be) { + be->chr = NULL; + } + } + qemu_chr_fe_deinit(&d->chr); g_free(d); } +static void mux_chr_set_handlers(CharDriverState *chr, GMainContext *context) +{ + MuxDriver *d = chr->opaque; + + /* Fix up the real driver with mux routines */ + qemu_chr_fe_set_handlers(&d->chr, + mux_chr_can_read, + mux_chr_read, + mux_chr_event, + chr, + context, true); +} + +static void mux_set_focus(MuxDriver *d, int focus) +{ + assert(focus >= 0); + assert(focus < d->mux_cnt); + + if (d->focus != -1) { + mux_chr_send_event(d, d->focus, CHR_EVENT_MUX_OUT); + } + + d->focus = focus; + mux_chr_send_event(d, d->focus, CHR_EVENT_MUX_IN); +} + static CharDriverState *qemu_chr_open_mux(const char *id, ChardevBackend *backend, - ChardevReturn *ret, Error **errp) + ChardevReturn *ret, + bool *be_opened, + Error **errp) { ChardevMux *mux = backend->u.mux.data; CharDriverState *chr, *drv; @@ -837,11 +859,9 @@ static CharDriverState *qemu_chr_open_mux(const char *id, d = g_new0(MuxDriver, 1); chr->opaque = d; - d->drv = drv; d->focus = -1; - chr->chr_close = mux_chr_close; + chr->chr_free = mux_chr_free; chr->chr_write = mux_chr_write; - chr->chr_update_read_handler = mux_chr_update_read_handler; chr->chr_accept_input = mux_chr_accept_input; /* Frontend guest-open / -close notification is not support with muxes */ chr->chr_set_fe_open = NULL; @@ -851,12 +871,133 @@ static CharDriverState *qemu_chr_open_mux(const char *id, /* only default to opened state if we've realized the initial * set of muxes */ - chr->explicit_be_open = muxes_realized ? 0 : 1; + *be_opened = muxes_realized; chr->is_mux = 1; + if (!qemu_chr_fe_init(&d->chr, drv, errp)) { + qemu_chr_free(chr); + return NULL; + } return chr; } +CharDriverState *qemu_chr_fe_get_driver(CharBackend *be) +{ + return be->chr; +} + +bool qemu_chr_fe_init(CharBackend *b, CharDriverState *s, Error **errp) +{ + int tag = 0; + + if (s->is_mux) { + MuxDriver *d = s->opaque; + + if (d->mux_cnt >= MAX_MUX) { + goto unavailable; + } + + d->backends[d->mux_cnt] = b; + tag = d->mux_cnt++; + } else if (s->be) { + goto unavailable; + } else { + s->be = b; + } + + b->fe_open = false; + b->tag = tag; + b->chr = s; + return true; + +unavailable: + error_setg(errp, QERR_DEVICE_IN_USE, s->label); + return false; +} + +static bool qemu_chr_is_busy(CharDriverState *s) +{ + if (s->is_mux) { + MuxDriver *d = s->opaque; + return d->mux_cnt >= 0; + } else { + return s->be != NULL; + } +} + +void qemu_chr_fe_deinit(CharBackend *b) +{ + assert(b); + + if (b->chr) { + qemu_chr_fe_set_handlers(b, NULL, NULL, NULL, NULL, NULL, true); + b->chr->be = NULL; + if (b->chr->is_mux) { + MuxDriver *d = b->chr->opaque; + d->backends[b->tag] = NULL; + } + b->chr = NULL; + } +} + +void qemu_chr_fe_set_handlers(CharBackend *b, + IOCanReadHandler *fd_can_read, + IOReadHandler *fd_read, + IOEventHandler *fd_event, + void *opaque, + GMainContext *context, + bool set_open) +{ + CharDriverState *s; + int fe_open; + + s = b->chr; + if (!s) { + return; + } + + if (!opaque && !fd_can_read && !fd_read && !fd_event) { + fe_open = 0; + remove_fd_in_watch(s); + } else { + fe_open = 1; + } + b->chr_can_read = fd_can_read; + b->chr_read = fd_read; + b->chr_event = fd_event; + b->opaque = opaque; + if (s->chr_update_read_handler) { + s->chr_update_read_handler(s, context); + } + + if (set_open) { + qemu_chr_fe_set_open(b, fe_open); + } + + if (fe_open) { + qemu_chr_fe_take_focus(b); + /* We're connecting to an already opened device, so let's make sure we + also get the open event */ + if (s->be_open) { + qemu_chr_be_generic_open(s); + } + } + + if (s->is_mux) { + mux_chr_set_handlers(s, context); + } +} + +void qemu_chr_fe_take_focus(CharBackend *b) +{ + if (!b->chr) { + return; + } + + if (b->chr->is_mux) { + mux_set_focus(b->chr->opaque, b->tag); + } +} typedef struct IOWatchPoll { @@ -1104,7 +1245,7 @@ static void fd_chr_update_read_handler(CharDriverState *chr, } } -static void fd_chr_close(struct CharDriverState *chr) +static void fd_chr_free(struct CharDriverState *chr) { FDCharDriver *s = chr->opaque; @@ -1147,7 +1288,7 @@ static CharDriverState *qemu_chr_open_fd(int fd_in, int fd_out, chr->chr_add_watch = fd_chr_add_watch; chr->chr_write = fd_chr_write; chr->chr_update_read_handler = fd_chr_update_read_handler; - chr->chr_close = fd_chr_close; + chr->chr_free = fd_chr_free; return chr; } @@ -1155,6 +1296,7 @@ static CharDriverState *qemu_chr_open_fd(int fd_in, int fd_out, static CharDriverState *qemu_chr_open_pipe(const char *id, ChardevBackend *backend, ChardevReturn *ret, + bool *be_opened, Error **errp) { ChardevHostdev *opts = backend->u.pipe.data; @@ -1228,15 +1370,16 @@ static void qemu_chr_set_echo_stdio(CharDriverState *chr, bool echo) tcsetattr (0, TCSANOW, &tty); } -static void qemu_chr_close_stdio(struct CharDriverState *chr) +static void qemu_chr_free_stdio(struct CharDriverState *chr) { term_exit(); - fd_chr_close(chr); + fd_chr_free(chr); } static CharDriverState *qemu_chr_open_stdio(const char *id, ChardevBackend *backend, ChardevReturn *ret, + bool *be_opened, Error **errp) { ChardevStdio *opts = backend->u.stdio.data; @@ -1268,12 +1411,12 @@ static CharDriverState *qemu_chr_open_stdio(const char *id, if (!chr) { return NULL; } - chr->chr_close = qemu_chr_close_stdio; + chr->chr_free = qemu_chr_free_stdio; chr->chr_set_echo = qemu_chr_set_echo_stdio; if (opts->has_signal) { stdio_allow_signal = opts->signal; } - qemu_chr_fe_set_echo(chr, false); + qemu_chr_set_echo_stdio(chr, false); return chr; } @@ -1470,7 +1613,7 @@ static void pty_chr_state(CharDriverState *chr, int connected) } } -static void pty_chr_close(struct CharDriverState *chr) +static void pty_chr_free(struct CharDriverState *chr) { PtyCharDriver *s = chr->opaque; @@ -1489,6 +1632,7 @@ static void pty_chr_close(struct CharDriverState *chr) static CharDriverState *qemu_chr_open_pty(const char *id, ChardevBackend *backend, ChardevReturn *ret, + bool *be_opened, Error **errp) { CharDriverState *chr; @@ -1524,9 +1668,9 @@ static CharDriverState *qemu_chr_open_pty(const char *id, chr->opaque = s; chr->chr_write = pty_chr_write; chr->chr_update_read_handler = pty_chr_update_read_handler; - chr->chr_close = pty_chr_close; + chr->chr_free = pty_chr_free; chr->chr_add_watch = pty_chr_add_watch; - chr->explicit_be_open = true; + *be_opened = false; s->ioc = QIO_CHANNEL(qio_channel_file_new_fd(master_fd)); name = g_strdup_printf("chardev-pty-%s", chr->label); @@ -1722,13 +1866,14 @@ static int tty_serial_ioctl(CharDriverState *chr, int cmd, void *arg) return 0; } -static void qemu_chr_close_tty(CharDriverState *chr) +static void qemu_chr_free_tty(CharDriverState *chr) { - fd_chr_close(chr); + fd_chr_free(chr); } static CharDriverState *qemu_chr_open_tty_fd(int fd, ChardevCommon *backend, + bool *be_opened, Error **errp) { CharDriverState *chr; @@ -1739,7 +1884,7 @@ static CharDriverState *qemu_chr_open_tty_fd(int fd, return NULL; } chr->chr_ioctl = tty_serial_ioctl; - chr->chr_close = qemu_chr_close_tty; + chr->chr_free = qemu_chr_free_tty; return chr; } #endif /* __linux__ || __sun__ */ @@ -1845,7 +1990,7 @@ static int pp_ioctl(CharDriverState *chr, int cmd, void *arg) return 0; } -static void pp_close(CharDriverState *chr) +static void pp_free(CharDriverState *chr) { ParallelCharDriver *drv = chr->opaque; int fd = drv->fd; @@ -1859,6 +2004,7 @@ static void pp_close(CharDriverState *chr) static CharDriverState *qemu_chr_open_pp_fd(int fd, ChardevCommon *backend, + bool *be_opened, Error **errp) { CharDriverState *chr; @@ -1879,7 +2025,7 @@ static CharDriverState *qemu_chr_open_pp_fd(int fd, chr->opaque = drv; chr->chr_write = null_chr_write; chr->chr_ioctl = pp_ioctl; - chr->chr_close = pp_close; + chr->chr_free = pp_free; drv->fd = fd; drv->mode = IEEE1284_MODE_COMPAT; @@ -1931,6 +2077,7 @@ static int pp_ioctl(CharDriverState *chr, int cmd, void *arg) static CharDriverState *qemu_chr_open_pp_fd(int fd, ChardevCommon *backend, + bool *be_opened, Error **errp) { CharDriverState *chr; @@ -1942,7 +2089,7 @@ static CharDriverState *qemu_chr_open_pp_fd(int fd, chr->opaque = (void *)(intptr_t)fd; chr->chr_write = null_chr_write; chr->chr_ioctl = pp_ioctl; - chr->explicit_be_open = true; + *be_opened = false; return chr; } #endif @@ -1978,7 +2125,7 @@ typedef struct { static int win_chr_poll(void *opaque); static int win_chr_pipe_poll(void *opaque); -static void win_chr_close(CharDriverState *chr) +static void win_chr_free(CharDriverState *chr) { WinCharState *s = chr->opaque; @@ -2065,7 +2212,7 @@ static int win_chr_init(CharDriverState *chr, const char *filename, Error **errp return 0; fail: - win_chr_close(chr); + win_chr_free(chr); return -1; } @@ -2177,7 +2324,7 @@ static CharDriverState *qemu_chr_open_win_path(const char *filename, s = g_new0(WinCharState, 1); chr->opaque = s; chr->chr_write = win_chr_write; - chr->chr_close = win_chr_close; + chr->chr_free = win_chr_free; if (win_chr_init(chr, filename, errp) < 0) { g_free(s); @@ -2263,7 +2410,7 @@ static int win_chr_pipe_init(CharDriverState *chr, const char *filename, return 0; fail: - win_chr_close(chr); + win_chr_free(chr); return -1; } @@ -2271,6 +2418,7 @@ static int win_chr_pipe_init(CharDriverState *chr, const char *filename, static CharDriverState *qemu_chr_open_pipe(const char *id, ChardevBackend *backend, ChardevReturn *ret, + bool *be_opened, Error **errp) { ChardevHostdev *opts = backend->u.pipe.data; @@ -2286,7 +2434,7 @@ static CharDriverState *qemu_chr_open_pipe(const char *id, s = g_new0(WinCharState, 1); chr->opaque = s; chr->chr_write = win_chr_write; - chr->chr_close = win_chr_close; + chr->chr_free = win_chr_free; if (win_chr_pipe_init(chr, filename, errp) < 0) { g_free(s); @@ -2317,6 +2465,7 @@ static CharDriverState *qemu_chr_open_win_file(HANDLE fd_out, static CharDriverState *qemu_chr_open_win_con(const char *id, ChardevBackend *backend, ChardevReturn *ret, + bool *be_opened, Error **errp) { ChardevCommon *common = backend->u.console.data; @@ -2442,7 +2591,7 @@ static void qemu_chr_set_echo_win_stdio(CharDriverState *chr, bool echo) } } -static void win_stdio_close(CharDriverState *chr) +static void win_stdio_free(CharDriverState *chr) { WinStdioCharState *stdio = chr->opaque; @@ -2457,12 +2606,12 @@ static void win_stdio_close(CharDriverState *chr) } g_free(chr->opaque); - g_free(chr); } static CharDriverState *qemu_chr_open_stdio(const char *id, ChardevBackend *backend, ChardevReturn *ret, + bool *be_opened, Error **errp) { CharDriverState *chr; @@ -2487,7 +2636,7 @@ static CharDriverState *qemu_chr_open_stdio(const char *id, chr->opaque = stdio; chr->chr_write = win_stdio_write; - chr->chr_close = win_stdio_close; + chr->chr_free = win_stdio_free; if (is_console) { if (qemu_add_wait_object(stdio->hStdIn, @@ -2530,7 +2679,7 @@ static CharDriverState *qemu_chr_open_stdio(const char *id, SetConsoleMode(stdio->hStdIn, dwMode); chr->chr_set_echo = qemu_chr_set_echo_win_stdio; - qemu_chr_fe_set_echo(chr, false); + qemu_chr_set_echo_win_stdio(chr, false); return chr; @@ -2545,7 +2694,6 @@ err1: } #endif /* !_WIN32 */ - /***********************************************************/ /* UDP Net console */ @@ -2625,7 +2773,7 @@ static void udp_chr_update_read_handler(CharDriverState *chr, } } -static void udp_chr_close(CharDriverState *chr) +static void udp_chr_free(CharDriverState *chr) { NetCharDriver *s = chr->opaque; @@ -2639,6 +2787,7 @@ static void udp_chr_close(CharDriverState *chr) static CharDriverState *qemu_chr_open_udp(QIOChannelSocket *sioc, ChardevCommon *backend, + bool *be_opened, Error **errp) { CharDriverState *chr = NULL; @@ -2656,9 +2805,9 @@ static CharDriverState *qemu_chr_open_udp(QIOChannelSocket *sioc, chr->opaque = s; chr->chr_write = udp_chr_write; chr->chr_update_read_handler = udp_chr_update_read_handler; - chr->chr_close = udp_chr_close; + chr->chr_free = udp_chr_free; /* be isn't opened until we get a connection */ - chr->explicit_be_open = true; + *be_opened = false; return chr; } @@ -3289,7 +3438,7 @@ static int tcp_chr_wait_connected(CharDriverState *chr, Error **errp) return 0; } -int qemu_chr_wait_connected(CharDriverState *chr, Error **errp) +static int qemu_chr_wait_connected(CharDriverState *chr, Error **errp) { if (chr->chr_wait_connected) { return chr->chr_wait_connected(chr, errp); @@ -3298,7 +3447,17 @@ int qemu_chr_wait_connected(CharDriverState *chr, Error **errp) return 0; } -static void tcp_chr_close(CharDriverState *chr) +int qemu_chr_fe_wait_connected(CharBackend *be, Error **errp) +{ + if (!be->chr) { + error_setg(errp, "missing associated backend"); + return -1; + } + + return qemu_chr_wait_connected(be->chr, errp); +} + +static void tcp_chr_free(CharDriverState *chr) { TCPCharDriver *s = chr->opaque; @@ -3376,7 +3535,7 @@ static int ringbuf_chr_write(CharDriverState *chr, const uint8_t *buf, int len) } } - return 0; + return len; } static int ringbuf_chr_read(CharDriverState *chr, uint8_t *buf, int len) @@ -3393,7 +3552,7 @@ static int ringbuf_chr_read(CharDriverState *chr, uint8_t *buf, int len) return i; } -static void ringbuf_chr_close(struct CharDriverState *chr) +static void ringbuf_chr_free(struct CharDriverState *chr) { RingBufCharDriver *d = chr->opaque; @@ -3405,6 +3564,7 @@ static void ringbuf_chr_close(struct CharDriverState *chr) static CharDriverState *qemu_chr_open_ringbuf(const char *id, ChardevBackend *backend, ChardevReturn *ret, + bool *be_opened, Error **errp) { ChardevRingbuf *opts = backend->u.ringbuf.data; @@ -3432,7 +3592,7 @@ static CharDriverState *qemu_chr_open_ringbuf(const char *id, chr->opaque = d; chr->chr_write = ringbuf_chr_write; - chr->chr_close = ringbuf_chr_close; + chr->chr_free = ringbuf_chr_free; return chr; @@ -3933,17 +4093,14 @@ static void qemu_chr_parse_udp(QemuOpts *opts, ChardevBackend *backend, typedef struct CharDriver { const char *name; ChardevBackendKind kind; - void (*parse)(QemuOpts *opts, ChardevBackend *backend, Error **errp); - CharDriverState *(*create)(const char *id, ChardevBackend *backend, - ChardevReturn *ret, Error **errp); + CharDriverParse *parse; + CharDriverCreate *create; } CharDriver; static GSList *backends; void register_char_driver(const char *name, ChardevBackendKind kind, - void (*parse)(QemuOpts *opts, ChardevBackend *backend, Error **errp), - CharDriverState *(*create)(const char *id, ChardevBackend *backend, - ChardevReturn *ret, Error **errp)) + CharDriverParse *parse, CharDriverCreate *create) { CharDriver *s; @@ -3957,8 +4114,7 @@ void register_char_driver(const char *name, ChardevBackendKind kind, } CharDriverState *qemu_chr_new_from_opts(QemuOpts *opts, - void (*init)(struct CharDriverState *s), - Error **errp) + Error **errp) { Error *local_err = NULL; CharDriver *cd; @@ -4055,8 +4211,7 @@ err: return NULL; } -CharDriverState *qemu_chr_new_noreplay(const char *label, const char *filename, - void (*init)(struct CharDriverState *s)) +CharDriverState *qemu_chr_new_noreplay(const char *label, const char *filename) { const char *p; CharDriverState *chr; @@ -4071,22 +4226,21 @@ CharDriverState *qemu_chr_new_noreplay(const char *label, const char *filename, if (!opts) return NULL; - chr = qemu_chr_new_from_opts(opts, init, &err); + chr = qemu_chr_new_from_opts(opts, &err); if (err) { error_report_err(err); } if (chr && qemu_opt_get_bool(opts, "mux", 0)) { - qemu_chr_fe_claim_no_fail(chr); monitor_init(chr, MONITOR_USE_READLINE); } qemu_opts_del(opts); return chr; } -CharDriverState *qemu_chr_new(const char *label, const char *filename, void (*init)(struct CharDriverState *s)) +CharDriverState *qemu_chr_new(const char *label, const char *filename) { CharDriverState *chr; - chr = qemu_chr_new_noreplay(label, filename, init); + chr = qemu_chr_new_noreplay(label, filename); if (chr) { chr->replay = replay_mode != REPLAY_MODE_NONE; if (chr->replay && chr->chr_ioctl) { @@ -4098,38 +4252,40 @@ CharDriverState *qemu_chr_new(const char *label, const char *filename, void (*in return chr; } -void qemu_chr_fe_set_echo(struct CharDriverState *chr, bool echo) +void qemu_chr_fe_set_echo(CharBackend *be, bool echo) { - if (chr->chr_set_echo) { + CharDriverState *chr = be->chr; + + if (chr && chr->chr_set_echo) { chr->chr_set_echo(chr, echo); } } -void qemu_chr_fe_set_open(struct CharDriverState *chr, int fe_open) +void qemu_chr_fe_set_open(CharBackend *be, int fe_open) { - if (chr->fe_open == fe_open) { + CharDriverState *chr = be->chr; + + if (!chr) { + return; + } + + if (be->fe_open == fe_open) { return; } - chr->fe_open = fe_open; + be->fe_open = fe_open; if (chr->chr_set_fe_open) { chr->chr_set_fe_open(chr, fe_open); } } -void qemu_chr_fe_event(struct CharDriverState *chr, int event) -{ - if (chr->chr_fe_event) { - chr->chr_fe_event(chr, event); - } -} - -guint qemu_chr_fe_add_watch(CharDriverState *s, GIOCondition cond, +guint qemu_chr_fe_add_watch(CharBackend *be, GIOCondition cond, GIOFunc func, void *user_data) { + CharDriverState *s = be->chr; GSource *src; guint tag; - if (s->chr_add_watch == NULL) { + if (!s || s->chr_add_watch == NULL) { return 0; } @@ -4145,38 +4301,20 @@ guint qemu_chr_fe_add_watch(CharDriverState *s, GIOCondition cond, return tag; } -int qemu_chr_fe_claim(CharDriverState *s) -{ - if (s->avail_connections < 1) { - return -1; - } - s->avail_connections--; - return 0; -} - -void qemu_chr_fe_claim_no_fail(CharDriverState *s) -{ - if (qemu_chr_fe_claim(s) != 0) { - fprintf(stderr, "%s: error chardev \"%s\" already used\n", - __func__, s->label); - exit(1); - } -} - -void qemu_chr_fe_release(CharDriverState *s) +void qemu_chr_fe_disconnect(CharBackend *be) { - s->avail_connections++; -} + CharDriverState *chr = be->chr; -void qemu_chr_disconnect(CharDriverState *chr) -{ - if (chr->chr_disconnect) { + if (chr && chr->chr_disconnect) { chr->chr_disconnect(chr); } } static void qemu_chr_free_common(CharDriverState *chr) { + if (chr->be) { + chr->be->chr = NULL; + } g_free(chr->filename); g_free(chr->label); if (chr->logfd != -1) { @@ -4188,8 +4326,8 @@ static void qemu_chr_free_common(CharDriverState *chr) void qemu_chr_free(CharDriverState *chr) { - if (chr->chr_close) { - chr->chr_close(chr); + if (chr->chr_free) { + chr->chr_free(chr); } qemu_chr_free_common(chr); } @@ -4210,7 +4348,7 @@ ChardevInfoList *qmp_query_chardev(Error **errp) info->value = g_malloc0(sizeof(*info->value)); info->value->label = g_strdup(chr->label); info->value->filename = g_strdup(chr->filename); - info->value->frontend_open = chr->fe_open; + info->value->frontend_open = chr->be && chr->be->fe_open; info->next = chr_list; chr_list = info; @@ -4349,6 +4487,7 @@ QemuOptsList qemu_chardev_opts = { static CharDriverState *qmp_chardev_open_file(const char *id, ChardevBackend *backend, ChardevReturn *ret, + bool *be_opened, Error **errp) { ChardevFile *file = backend->u.file.data; @@ -4384,6 +4523,7 @@ static CharDriverState *qmp_chardev_open_file(const char *id, static CharDriverState *qmp_chardev_open_serial(const char *id, ChardevBackend *backend, ChardevReturn *ret, + bool *be_opened, Error **errp) { ChardevHostdev *serial = backend->u.serial.data; @@ -4408,6 +4548,7 @@ static int qmp_chardev_open_file_source(char *src, int flags, static CharDriverState *qmp_chardev_open_file(const char *id, ChardevBackend *backend, ChardevReturn *ret, + bool *be_opened, Error **errp) { ChardevFile *file = backend->u.file.data; @@ -4442,6 +4583,7 @@ static CharDriverState *qmp_chardev_open_file(const char *id, static CharDriverState *qmp_chardev_open_serial(const char *id, ChardevBackend *backend, ChardevReturn *ret, + bool *be_opened, Error **errp) { ChardevHostdev *serial = backend->u.serial.data; @@ -4453,7 +4595,7 @@ static CharDriverState *qmp_chardev_open_serial(const char *id, return NULL; } qemu_set_nonblock(fd); - return qemu_chr_open_tty_fd(fd, common, errp); + return qemu_chr_open_tty_fd(fd, common, be_opened, errp); } #endif @@ -4461,6 +4603,7 @@ static CharDriverState *qmp_chardev_open_serial(const char *id, static CharDriverState *qmp_chardev_open_parallel(const char *id, ChardevBackend *backend, ChardevReturn *ret, + bool *be_opened, Error **errp) { ChardevHostdev *parallel = backend->u.parallel.data; @@ -4471,7 +4614,7 @@ static CharDriverState *qmp_chardev_open_parallel(const char *id, if (fd < 0) { return NULL; } - return qemu_chr_open_pp_fd(fd, common, errp); + return qemu_chr_open_pp_fd(fd, common, be_opened, errp); } #endif @@ -4501,6 +4644,7 @@ static gboolean socket_reconnect_timeout(gpointer opaque) static CharDriverState *qmp_chardev_open_socket(const char *id, ChardevBackend *backend, ChardevReturn *ret, + bool *be_opened, Error **errp) { CharDriverState *chr; @@ -4569,7 +4713,7 @@ static CharDriverState *qmp_chardev_open_socket(const char *id, chr->chr_wait_connected = tcp_chr_wait_connected; chr->chr_write = tcp_chr_write; chr->chr_sync_read = tcp_chr_sync_read; - chr->chr_close = tcp_chr_close; + chr->chr_free = tcp_chr_free; chr->chr_disconnect = tcp_chr_disconnect; chr->get_msgfds = tcp_get_msgfds; chr->set_msgfds = tcp_set_msgfds; @@ -4577,7 +4721,7 @@ static CharDriverState *qmp_chardev_open_socket(const char *id, chr->chr_add_watch = tcp_chr_add_watch; chr->chr_update_read_handler = tcp_chr_update_read_handler; /* be isn't opened until we get a connection */ - chr->explicit_be_open = true; + *be_opened = false; chr->filename = SocketAddress_to_str("disconnected:", addr, is_listen, is_telnet); @@ -4640,6 +4784,7 @@ static CharDriverState *qmp_chardev_open_socket(const char *id, static CharDriverState *qmp_chardev_open_udp(const char *id, ChardevBackend *backend, ChardevReturn *ret, + bool *be_opened, Error **errp) { ChardevUdp *udp = backend->u.udp.data; @@ -4654,7 +4799,7 @@ static CharDriverState *qmp_chardev_open_udp(const char *id, object_unref(OBJECT(sioc)); return NULL; } - chr = qemu_chr_open_udp(sioc, common, errp); + chr = qemu_chr_open_udp(sioc, common, be_opened, errp); name = g_strdup_printf("chardev-udp-%s", chr->label); qio_channel_set_name(QIO_CHANNEL(sioc), name); @@ -4684,19 +4829,19 @@ ChardevReturn *qmp_chardev_add(const char *id, ChardevBackend *backend, Error *local_err = NULL; GSList *i; CharDriver *cd; + bool be_opened = true; chr = qemu_chr_find(id); if (chr) { error_setg(errp, "Chardev '%s' already exists", id); - g_free(ret); - return NULL; + goto out_error; } for (i = backends; i; i = i->next) { cd = i->data; if (cd->kind == backend->type) { - chr = cd->create(id, backend, ret, &local_err); + chr = cd->create(id, backend, ret, &be_opened, &local_err); if (local_err) { error_propagate(errp, local_err); goto out_error; @@ -4712,12 +4857,10 @@ ChardevReturn *qmp_chardev_add(const char *id, ChardevBackend *backend, } chr->label = g_strdup(id); - chr->avail_connections = - (backend->type == CHARDEV_BACKEND_KIND_MUX) ? MAX_MUX : 1; if (!chr->filename) { chr->filename = g_strdup(ChardevBackendKind_lookup[backend->type]); } - if (!chr->explicit_be_open) { + if (be_opened) { qemu_chr_be_event(chr, CHR_EVENT_OPENED); } QTAILQ_INSERT_TAIL(&chardevs, chr, next); @@ -4737,8 +4880,7 @@ void qmp_chardev_remove(const char *id, Error **errp) error_setg(errp, "Chardev '%s' not found", id); return; } - if (chr->chr_can_read || chr->chr_read || - chr->chr_event || chr->handler_opaque) { + if (qemu_chr_is_busy(chr)) { error_setg(errp, "Chardev '%s' is busy", id); return; } @@ -25,7 +25,7 @@ #include "qemu-version.h" #include "qapi/error.h" #include "qapi-visit.h" -#include "qapi/qmp-output-visitor.h" +#include "qapi/qobject-output-visitor.h" #include "qapi/qmp/qerror.h" #include "qapi/qmp/qjson.h" #include "qemu/cutils.h" @@ -44,7 +44,7 @@ #include <getopt.h> #define QEMU_IMG_VERSION "qemu-img version " QEMU_VERSION QEMU_PKGVERSION \ - ", " QEMU_COPYRIGHT "\n" + "\n" QEMU_COPYRIGHT "\n" typedef struct img_cmd_t { const char *name; @@ -500,7 +500,7 @@ static void dump_json_image_check(ImageCheck *check, bool quiet) { QString *str; QObject *obj; - Visitor *v = qmp_output_visitor_new(&obj); + Visitor *v = qobject_output_visitor_new(&obj); visit_type_ImageCheck(v, NULL, &check, &error_abort); visit_complete(v, &obj); @@ -2193,7 +2193,7 @@ static void dump_json_image_info_list(ImageInfoList *list) { QString *str; QObject *obj; - Visitor *v = qmp_output_visitor_new(&obj); + Visitor *v = qobject_output_visitor_new(&obj); visit_type_ImageInfoList(v, NULL, &list, &error_abort); visit_complete(v, &obj); @@ -2209,7 +2209,7 @@ static void dump_json_image_info(ImageInfo *info) { QString *str; QObject *obj; - Visitor *v = qmp_output_visitor_new(&obj); + Visitor *v = qobject_output_visitor_new(&obj); visit_type_ImageInfo(v, NULL, &info, &error_abort); visit_complete(v, &obj); @@ -2956,6 +2956,7 @@ static int img_rebase(int argc, char **argv) error_reportf_err(local_err, "Could not open old backing file '%s': ", backing_name); + ret = -1; goto out; } @@ -2973,6 +2974,7 @@ static int img_rebase(int argc, char **argv) error_reportf_err(local_err, "Could not open new backing file '%s': ", out_baseimg); + ret = -1; goto out; } } @@ -48,6 +48,7 @@ #define QEMU_NBD_OPT_OBJECT 260 #define QEMU_NBD_OPT_TLSCREDS 261 #define QEMU_NBD_OPT_IMAGE_OPTS 262 +#define QEMU_NBD_OPT_FORK 263 #define MBR_SIZE 512 @@ -92,6 +93,8 @@ static void usage(const char *name) " passwords and/or encryption keys\n" " -T, --trace [[enable=]<pattern>][,events=<file>][,file=<file>]\n" " specify tracing options\n" +" --fork fork off the server process and exit the parent\n" +" once the server is running\n" #ifdef __linux__ "Kernel NBD client support:\n" " -c, --connect=DEV connect FILE to the local NBD device DEV\n" @@ -503,6 +506,7 @@ int main(int argc, char **argv) { "tls-creds", required_argument, NULL, QEMU_NBD_OPT_TLSCREDS }, { "image-opts", no_argument, NULL, QEMU_NBD_OPT_IMAGE_OPTS }, { "trace", required_argument, NULL, 'T' }, + { "fork", no_argument, NULL, QEMU_NBD_OPT_FORK }, { NULL, 0, NULL, 0 } }; int ch; @@ -524,6 +528,8 @@ int main(int argc, char **argv) bool imageOpts = false; bool writethrough = true; char *trace_file = NULL; + bool fork_process = false; + int old_stderr = -1; /* The client thread uses SIGTERM to interrupt the server. A signal * handler ensures that "qemu-nbd -v -c" exits with a nice status code. @@ -715,6 +721,9 @@ int main(int argc, char **argv) g_free(trace_file); trace_file = trace_opt_parse(optarg); break; + case QEMU_NBD_OPT_FORK: + fork_process = true; + break; } } @@ -774,7 +783,7 @@ int main(int argc, char **argv) return 0; } - if (device && !verbose) { + if ((device && !verbose) || fork_process) { int stderr_fd[2]; pid_t pid; int ret; @@ -797,6 +806,7 @@ int main(int argc, char **argv) ret = qemu_daemon(1, 0); /* Temporarily redirect stderr to the parent's pipe... */ + old_stderr = dup(STDERR_FILENO); dup2(stderr_fd[1], STDERR_FILENO); if (ret < 0) { error_report("Failed to daemonize: %s", strerror(errno)); @@ -960,6 +970,11 @@ int main(int argc, char **argv) exit(EXIT_FAILURE); } + if (fork_process) { + dup2(old_stderr, STDERR_FILENO); + close(old_stderr); + } + state = RUNNING; do { main_loop_wait(false); diff --git a/qemu-nbd.texi b/qemu-nbd.texi index 91ebf04..b7a9c6d 100644 --- a/qemu-nbd.texi +++ b/qemu-nbd.texi @@ -86,6 +86,8 @@ the new style NBD protocol negotiation Enable mandatory TLS encryption for the server by setting the ID of the TLS credentials object previously created with the --object option. +@item --fork +Fork off the server process and exit the parent once the server is running. @item -v, --verbose Display extra debugging information @item -h, --help @@ -31,7 +31,7 @@ #include "qom/qom-qobject.h" #include "qapi/qmp/qerror.h" #include "qapi/qmp/qobject.h" -#include "qapi/qmp-input-visitor.h" +#include "qapi/qobject-input-visitor.h" #include "hw/boards.h" #include "qom/object_interfaces.h" #include "hw/mem/pc-dimm.h" @@ -675,7 +675,7 @@ void qmp_object_add(const char *type, const char *id, pdict = qdict_new(); } - v = qmp_input_visitor_new(QOBJECT(pdict), true); + v = qobject_input_visitor_new(QOBJECT(pdict), true); obj = user_creatable_add_type(type, id, pdict, v, errp); visit_free(v); if (obj) { diff --git a/qobject/qdict.c b/qobject/qdict.c index 60f158c..197b0fb 100644 --- a/qobject/qdict.c +++ b/qobject/qdict.c @@ -17,6 +17,7 @@ #include "qapi/qmp/qbool.h" #include "qapi/qmp/qstring.h" #include "qapi/qmp/qobject.h" +#include "qapi/error.h" #include "qemu/queue.h" #include "qemu-common.h" #include "qemu/cutils.h" @@ -684,6 +685,282 @@ void qdict_array_split(QDict *src, QList **dst) } /** + * qdict_split_flat_key: + * @key: the key string to split + * @prefix: non-NULL pointer to hold extracted prefix + * @suffix: non-NULL pointer to remaining suffix + * + * Given a flattened key such as 'foo.0.bar', split it into two parts + * at the first '.' separator. Allows double dot ('..') to escape the + * normal separator. + * + * e.g. + * 'foo.0.bar' -> prefix='foo' and suffix='0.bar' + * 'foo..0.bar' -> prefix='foo.0' and suffix='bar' + * + * The '..' sequence will be unescaped in the returned 'prefix' + * string. The 'suffix' string will be left in escaped format, so it + * can be fed back into the qdict_split_flat_key() key as the input + * later. + * + * The caller is responsible for freeing the string returned in @prefix + * using g_free(). + */ +static void qdict_split_flat_key(const char *key, char **prefix, + const char **suffix) +{ + const char *separator; + size_t i, j; + + /* Find first '.' separator, but if there is a pair '..' + * that acts as an escape, so skip over '..' */ + separator = NULL; + do { + if (separator) { + separator += 2; + } else { + separator = key; + } + separator = strchr(separator, '.'); + } while (separator && separator[1] == '.'); + + if (separator) { + *prefix = g_strndup(key, separator - key); + *suffix = separator + 1; + } else { + *prefix = g_strdup(key); + *suffix = NULL; + } + + /* Unescape the '..' sequence into '.' */ + for (i = 0, j = 0; (*prefix)[i] != '\0'; i++, j++) { + if ((*prefix)[i] == '.') { + assert((*prefix)[i + 1] == '.'); + i++; + } + (*prefix)[j] = (*prefix)[i]; + } + (*prefix)[j] = '\0'; +} + +/** + * qdict_is_list: + * @maybe_list: dict to check if keys represent list elements. + * + * Determine whether all keys in @maybe_list are valid list elements. + * If @maybe_list is non-zero in length and all the keys look like + * valid list indexes, this will return 1. If @maybe_list is zero + * length or all keys are non-numeric then it will return 0 to indicate + * it is a normal qdict. If there is a mix of numeric and non-numeric + * keys, or the list indexes are non-contiguous, an error is reported. + * + * Returns: 1 if a valid list, 0 if a dict, -1 on error + */ +static int qdict_is_list(QDict *maybe_list, Error **errp) +{ + const QDictEntry *ent; + ssize_t len = 0; + ssize_t max = -1; + int is_list = -1; + int64_t val; + + for (ent = qdict_first(maybe_list); ent != NULL; + ent = qdict_next(maybe_list, ent)) { + + if (qemu_strtoll(ent->key, NULL, 10, &val) == 0) { + if (is_list == -1) { + is_list = 1; + } else if (!is_list) { + error_setg(errp, + "Cannot mix list and non-list keys"); + return -1; + } + len++; + if (val > max) { + max = val; + } + } else { + if (is_list == -1) { + is_list = 0; + } else if (is_list) { + error_setg(errp, + "Cannot mix list and non-list keys"); + return -1; + } + } + } + + if (is_list == -1) { + assert(!qdict_size(maybe_list)); + is_list = 0; + } + + /* NB this isn't a perfect check - e.g. it won't catch + * a list containing '1', '+1', '01', '3', but that + * does not matter - we've still proved that the + * input is a list. It is up the caller to do a + * stricter check if desired */ + if (len != (max + 1)) { + error_setg(errp, "List indices are not contiguous, " + "saw %zd elements but %zd largest index", + len, max); + return -1; + } + + return is_list; +} + +/** + * qdict_crumple: + * @src: the original flat dictionary (only scalar values) to crumple + * + * Takes a flat dictionary whose keys use '.' separator to indicate + * nesting, and values are scalars, and crumples it into a nested + * structure. + * + * To include a literal '.' in a key name, it must be escaped as '..' + * + * For example, an input of: + * + * { 'foo.0.bar': 'one', 'foo.0.wizz': '1', + * 'foo.1.bar': 'two', 'foo.1.wizz': '2' } + * + * will result in an output of: + * + * { + * 'foo': [ + * { 'bar': 'one', 'wizz': '1' }, + * { 'bar': 'two', 'wizz': '2' } + * ], + * } + * + * The following scenarios in the input dict will result in an + * error being returned: + * + * - Any values in @src are non-scalar types + * - If keys in @src imply that a particular level is both a + * list and a dict. e.g., "foo.0.bar" and "foo.eek.bar". + * - If keys in @src imply that a particular level is a list, + * but the indices are non-contiguous. e.g. "foo.0.bar" and + * "foo.2.bar" without any "foo.1.bar" present. + * - If keys in @src represent list indexes, but are not in + * the "%zu" format. e.g. "foo.+0.bar" + * + * Returns: either a QDict or QList for the nested data structure, or NULL + * on error + */ +QObject *qdict_crumple(const QDict *src, Error **errp) +{ + const QDictEntry *ent; + QDict *two_level, *multi_level = NULL; + QObject *dst = NULL, *child; + size_t i; + char *prefix = NULL; + const char *suffix = NULL; + int is_list; + + two_level = qdict_new(); + + /* Step 1: split our totally flat dict into a two level dict */ + for (ent = qdict_first(src); ent != NULL; ent = qdict_next(src, ent)) { + if (qobject_type(ent->value) == QTYPE_QDICT || + qobject_type(ent->value) == QTYPE_QLIST) { + error_setg(errp, "Value %s is not a scalar", + ent->key); + goto error; + } + + qdict_split_flat_key(ent->key, &prefix, &suffix); + + child = qdict_get(two_level, prefix); + if (suffix) { + if (child) { + if (qobject_type(child) != QTYPE_QDICT) { + error_setg(errp, "Key %s prefix is already set as a scalar", + prefix); + goto error; + } + } else { + child = QOBJECT(qdict_new()); + qdict_put_obj(two_level, prefix, child); + } + qobject_incref(ent->value); + qdict_put_obj(qobject_to_qdict(child), suffix, ent->value); + } else { + if (child) { + error_setg(errp, "Key %s prefix is already set as a dict", + prefix); + goto error; + } + qobject_incref(ent->value); + qdict_put_obj(two_level, prefix, ent->value); + } + + g_free(prefix); + prefix = NULL; + } + + /* Step 2: optionally process the two level dict recursively + * into a multi-level dict */ + multi_level = qdict_new(); + for (ent = qdict_first(two_level); ent != NULL; + ent = qdict_next(two_level, ent)) { + + if (qobject_type(ent->value) == QTYPE_QDICT) { + child = qdict_crumple(qobject_to_qdict(ent->value), errp); + if (!child) { + goto error; + } + + qdict_put_obj(multi_level, ent->key, child); + } else { + qobject_incref(ent->value); + qdict_put_obj(multi_level, ent->key, ent->value); + } + } + QDECREF(two_level); + two_level = NULL; + + /* Step 3: detect if we need to turn our dict into list */ + is_list = qdict_is_list(multi_level, errp); + if (is_list < 0) { + goto error; + } + + if (is_list) { + dst = QOBJECT(qlist_new()); + + for (i = 0; i < qdict_size(multi_level); i++) { + char *key = g_strdup_printf("%zu", i); + + child = qdict_get(multi_level, key); + g_free(key); + + if (!child) { + error_setg(errp, "Missing list index %zu", i); + goto error; + } + + qobject_incref(child); + qlist_append_obj(qobject_to_qlist(dst), child); + } + QDECREF(multi_level); + multi_level = NULL; + } else { + dst = QOBJECT(multi_level); + } + + return dst; + + error: + g_free(prefix); + QDECREF(multi_level); + QDECREF(two_level); + qobject_decref(dst); + return NULL; +} + +/** * qdict_array_entries(): Returns the number of direct array entries if the * sub-QDict of src specified by the prefix in subqdict (or src itself for * prefix == "") is valid as an array, i.e. the length of the created list if @@ -345,6 +345,12 @@ static void cpu_common_realizefn(DeviceState *dev, Error **errp) trace_init_vcpu(cpu); } +static void cpu_common_unrealizefn(DeviceState *dev, Error **errp) +{ + CPUState *cpu = CPU(dev); + cpu_exec_unrealizefn(cpu); +} + static void cpu_common_initfn(Object *obj) { CPUState *cpu = CPU(obj); @@ -362,12 +368,13 @@ static void cpu_common_initfn(Object *obj) QTAILQ_INIT(&cpu->watchpoints); cpu->trace_dstate = bitmap_new(trace_get_vcpu_event_count()); + + cpu_exec_initfn(cpu); } static void cpu_common_finalize(Object *obj) { CPUState *cpu = CPU(obj); - cpu_exec_exit(cpu); g_free(cpu->trace_dstate); } @@ -401,6 +408,7 @@ static void cpu_class_init(ObjectClass *klass, void *data) k->cpu_exec_exit = cpu_common_noop; k->cpu_exec_interrupt = cpu_common_exec_interrupt; dc->realize = cpu_common_realizefn; + dc->unrealize = cpu_common_unrealizefn; /* * Reason: CPUs still need special care by board code: wiring up * IRQs, adding reset handlers, halting non-first CPUs, ... diff --git a/qom/object_interfaces.c b/qom/object_interfaces.c index bf59846..ded4d84 100644 --- a/qom/object_interfaces.c +++ b/qom/object_interfaces.c @@ -3,7 +3,7 @@ #include "qom/object_interfaces.h" #include "qemu/module.h" #include "qapi-visit.h" -#include "qapi/qmp-output-visitor.h" +#include "qapi/qobject-output-visitor.h" #include "qapi/opts-visitor.h" void user_creatable_complete(Object *obj, Error **errp) diff --git a/qom/qom-qobject.c b/qom/qom-qobject.c index c225abc..447e4a0 100644 --- a/qom/qom-qobject.c +++ b/qom/qom-qobject.c @@ -15,15 +15,15 @@ #include "qom/object.h" #include "qom/qom-qobject.h" #include "qapi/visitor.h" -#include "qapi/qmp-input-visitor.h" -#include "qapi/qmp-output-visitor.h" +#include "qapi/qobject-input-visitor.h" +#include "qapi/qobject-output-visitor.h" void object_property_set_qobject(Object *obj, QObject *value, const char *name, Error **errp) { Visitor *v; /* TODO: Should we reject, rather than ignore, excess input? */ - v = qmp_input_visitor_new(value, false); + v = qobject_input_visitor_new(value, false); object_property_set(obj, v, name, errp); visit_free(v); } @@ -35,7 +35,7 @@ QObject *object_property_get_qobject(Object *obj, const char *name, Error *local_err = NULL; Visitor *v; - v = qmp_output_visitor_new(&ret); + v = qobject_output_visitor_new(&ret); object_property_get(obj, v, name, &local_err); if (!local_err) { visit_complete(v, &ret); @@ -38,7 +38,7 @@ bool qtest_allowed; static DeviceState *irq_intercept_dev; static FILE *qtest_log_fp; -static CharDriverState *qtest_chr; +static CharBackend qtest_chr; static GString *inbuf; static int irq_levels[MAX_IRQ]; static qemu_timeval start_time; @@ -190,7 +190,7 @@ static void qtest_get_time(qemu_timeval *tv) } } -static void qtest_send_prefix(CharDriverState *chr) +static void qtest_send_prefix(CharBackend *chr) { qemu_timeval tv; @@ -218,7 +218,7 @@ static void GCC_FMT_ATTR(1, 2) qtest_log_send(const char *fmt, ...) va_end(ap); } -static void do_qtest_send(CharDriverState *chr, const char *str, size_t len) +static void do_qtest_send(CharBackend *chr, const char *str, size_t len) { qemu_chr_fe_write_all(chr, (uint8_t *)str, len); if (qtest_log_fp && qtest_opened) { @@ -226,12 +226,12 @@ static void do_qtest_send(CharDriverState *chr, const char *str, size_t len) } } -static void qtest_send(CharDriverState *chr, const char *str) +static void qtest_send(CharBackend *chr, const char *str) { do_qtest_send(chr, str, strlen(str)); } -static void GCC_FMT_ATTR(2, 3) qtest_sendf(CharDriverState *chr, +static void GCC_FMT_ATTR(2, 3) qtest_sendf(CharBackend *chr, const char *fmt, ...) { va_list ap; @@ -249,7 +249,7 @@ static void qtest_irq_handler(void *opaque, int n, int level) qemu_set_irq(old_irq, level); if (irq_levels[n] != level) { - CharDriverState *chr = qtest_chr; + CharBackend *chr = &qtest_chr; irq_levels[n] = level; qtest_send_prefix(chr); qtest_sendf(chr, "IRQ %s %d\n", @@ -257,7 +257,7 @@ static void qtest_irq_handler(void *opaque, int n, int level) } } -static void qtest_process_command(CharDriverState *chr, gchar **words) +static void qtest_process_command(CharBackend *chr, gchar **words) { const gchar *command; @@ -585,7 +585,7 @@ static void qtest_process_command(CharDriverState *chr, gchar **words) } } -static void qtest_process_inbuf(CharDriverState *chr, GString *inbuf) +static void qtest_process_inbuf(CharBackend *chr, GString *inbuf) { char *end; @@ -609,7 +609,7 @@ static void qtest_process_inbuf(CharDriverState *chr, GString *inbuf) static void qtest_read(void *opaque, const uint8_t *buf, int size) { - CharDriverState *chr = opaque; + CharBackend *chr = opaque; g_string_append_len(inbuf, (const gchar *)buf, size); qtest_process_inbuf(chr, inbuf); @@ -670,7 +670,7 @@ void qtest_init(const char *qtest_chrdev, const char *qtest_log, Error **errp) { CharDriverState *chr; - chr = qemu_chr_new("qtest", qtest_chrdev, NULL); + chr = qemu_chr_new("qtest", qtest_chrdev); if (chr == NULL) { error_setg(errp, "Failed to initialize device for qtest: \"%s\"", @@ -686,16 +686,17 @@ void qtest_init(const char *qtest_chrdev, const char *qtest_log, Error **errp) qtest_log_fp = stderr; } - qemu_chr_add_handlers(chr, qtest_can_read, qtest_read, qtest_event, chr); - qemu_chr_fe_set_echo(chr, true); + qemu_chr_fe_init(&qtest_chr, chr, errp); + qemu_chr_fe_set_handlers(&qtest_chr, qtest_can_read, qtest_read, + qtest_event, &qtest_chr, NULL, true); + qemu_chr_fe_set_echo(&qtest_chr, true); inbuf = g_string_new(""); - qtest_chr = chr; } bool qtest_driver(void) { - return qtest_chr; + return qtest_chr.chr != NULL; } static void qtest_accel_class_init(ObjectClass *oc, void *data) diff --git a/roms/seabios b/roms/seabios -Subproject e2fc41e24ee0ada60fc511d60b15a41b294538b +Subproject d7adf6044a4c772b497e97272adf97426b34a24 diff --git a/scripts/qapi-commands.py b/scripts/qapi-commands.py index 2f603b0..09e8467 100644 --- a/scripts/qapi-commands.py +++ b/scripts/qapi-commands.py @@ -68,7 +68,7 @@ static void qmp_marshal_output_%(c_name)s(%(c_type)s ret_in, QObject **ret_out, Error *err = NULL; Visitor *v; - v = qmp_output_visitor_new(ret_out); + v = qobject_output_visitor_new(ret_out); visit_type_%(c_name)s(v, "unused", &ret_in, &err); if (!err) { visit_complete(v, ret_out); @@ -130,7 +130,7 @@ def gen_marshal(name, arg_type, boxed, ret_type): push_indent() ret += mcgen(''' - v = qmp_input_visitor_new(QOBJECT(args), true); + v = qobject_input_visitor_new(QOBJECT(args), true); visit_start_struct(v, NULL, NULL, 0, &err); if (err) { goto out; @@ -293,8 +293,8 @@ fdef.write(mcgen(''' #include "qapi/qmp/types.h" #include "qapi/qmp/dispatch.h" #include "qapi/visitor.h" -#include "qapi/qmp-output-visitor.h" -#include "qapi/qmp-input-visitor.h" +#include "qapi/qobject-output-visitor.h" +#include "qapi/qobject-input-visitor.h" #include "qapi/dealloc-visitor.h" #include "%(prefix)sqapi-types.h" #include "%(prefix)sqapi-visit.h" diff --git a/scripts/qapi-event.py b/scripts/qapi-event.py index 38d8211..f4eb7f8 100644 --- a/scripts/qapi-event.py +++ b/scripts/qapi-event.py @@ -98,7 +98,7 @@ def gen_event_send(name, arg_type, boxed): if arg_type and not arg_type.is_empty(): ret += mcgen(''' - v = qmp_output_visitor_new(&obj); + v = qobject_output_visitor_new(&obj); ''') if not arg_type.is_implicit(): ret += mcgen(''' @@ -209,7 +209,7 @@ fdef.write(mcgen(''' #include "qemu-common.h" #include "%(prefix)sqapi-event.h" #include "%(prefix)sqapi-visit.h" -#include "qapi/qmp-output-visitor.h" +#include "qapi/qobject-output-visitor.h" #include "qapi/qmp-event.h" ''', diff --git a/scripts/tracetool/backend/simple.py b/scripts/tracetool/backend/simple.py index 9885e83..85f6102 100644 --- a/scripts/tracetool/backend/simple.py +++ b/scripts/tracetool/backend/simple.py @@ -21,7 +21,8 @@ PUBLIC = True def is_string(arg): strtype = ('const char*', 'char*', 'const char *', 'char *') - if arg.lstrip().startswith(strtype): + arg_strip = arg.lstrip() + if arg_strip.startswith(strtype) and arg_strip.count('*') == 1: return True else: return False diff --git a/softmmu_template.h b/softmmu_template.h index 27ed269..4a2b665 100644 --- a/softmmu_template.h +++ b/softmmu_template.h @@ -21,12 +21,6 @@ * 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/>. */ -#include "qemu/timer.h" -#include "exec/address-spaces.h" -#include "exec/memory.h" - -#define DATA_SIZE (1 << SHIFT) - #if DATA_SIZE == 8 #define SUFFIX q #define LSUFFIX q @@ -84,14 +78,6 @@ # define BSWAP(X) (X) #endif -#ifdef TARGET_WORDS_BIGENDIAN -# define TGT_BE(X) (X) -# define TGT_LE(X) BSWAP(X) -#else -# define TGT_BE(X) BSWAP(X) -# define TGT_LE(X) (X) -#endif - #if DATA_SIZE == 1 # define helper_le_ld_name glue(glue(helper_ret_ld, USUFFIX), MMUSUFFIX) # define helper_be_ld_name helper_le_ld_name @@ -108,35 +94,14 @@ # define helper_be_st_name glue(glue(helper_be_st, SUFFIX), MMUSUFFIX) #endif -#ifdef TARGET_WORDS_BIGENDIAN -# define helper_te_ld_name helper_be_ld_name -# define helper_te_st_name helper_be_st_name -#else -# define helper_te_ld_name helper_le_ld_name -# define helper_te_st_name helper_le_st_name -#endif - #ifndef SOFTMMU_CODE_ACCESS static inline DATA_TYPE glue(io_read, SUFFIX)(CPUArchState *env, - CPUIOTLBEntry *iotlbentry, + size_t mmu_idx, size_t index, target_ulong addr, uintptr_t retaddr) { - uint64_t val; - CPUState *cpu = ENV_GET_CPU(env); - hwaddr physaddr = iotlbentry->addr; - MemoryRegion *mr = iotlb_to_region(cpu, physaddr, iotlbentry->attrs); - - physaddr = (physaddr & TARGET_PAGE_MASK) + addr; - cpu->mem_io_pc = retaddr; - if (mr != &io_mem_rom && mr != &io_mem_notdirty && !cpu->can_do_io) { - cpu_io_recompile(cpu, retaddr); - } - - cpu->mem_io_vaddr = addr; - memory_region_dispatch_read(mr, physaddr, &val, 1 << SHIFT, - iotlbentry->attrs); - return val; + CPUIOTLBEntry *iotlbentry = &env->iotlb[mmu_idx][index]; + return io_readx(env, iotlbentry, addr, retaddr, DATA_SIZE); } #endif @@ -167,15 +132,13 @@ WORD_TYPE helper_le_ld_name(CPUArchState *env, target_ulong addr, /* Handle an IO access. */ if (unlikely(tlb_addr & ~TARGET_PAGE_MASK)) { - CPUIOTLBEntry *iotlbentry; if ((addr & (DATA_SIZE - 1)) != 0) { goto do_unaligned_access; } - iotlbentry = &env->iotlb[mmu_idx][index]; /* ??? Note that the io helpers always read data in the target byte ordering. We should push the LE/BE request down into io. */ - res = glue(io_read, SUFFIX)(env, iotlbentry, addr, retaddr); + res = glue(io_read, SUFFIX)(env, mmu_idx, index, addr, retaddr); res = TGT_LE(res); return res; } @@ -236,15 +199,13 @@ WORD_TYPE helper_be_ld_name(CPUArchState *env, target_ulong addr, /* Handle an IO access. */ if (unlikely(tlb_addr & ~TARGET_PAGE_MASK)) { - CPUIOTLBEntry *iotlbentry; if ((addr & (DATA_SIZE - 1)) != 0) { goto do_unaligned_access; } - iotlbentry = &env->iotlb[mmu_idx][index]; /* ??? Note that the io helpers always read data in the target byte ordering. We should push the LE/BE request down into io. */ - res = glue(io_read, SUFFIX)(env, iotlbentry, addr, retaddr); + res = glue(io_read, SUFFIX)(env, mmu_idx, index, addr, retaddr); res = TGT_BE(res); return res; } @@ -295,24 +256,13 @@ WORD_TYPE helper_be_lds_name(CPUArchState *env, target_ulong addr, #endif static inline void glue(io_write, SUFFIX)(CPUArchState *env, - CPUIOTLBEntry *iotlbentry, + size_t mmu_idx, size_t index, DATA_TYPE val, target_ulong addr, uintptr_t retaddr) { - CPUState *cpu = ENV_GET_CPU(env); - hwaddr physaddr = iotlbentry->addr; - MemoryRegion *mr = iotlb_to_region(cpu, physaddr, iotlbentry->attrs); - - physaddr = (physaddr & TARGET_PAGE_MASK) + addr; - if (mr != &io_mem_rom && mr != &io_mem_notdirty && !cpu->can_do_io) { - cpu_io_recompile(cpu, retaddr); - } - - cpu->mem_io_vaddr = addr; - cpu->mem_io_pc = retaddr; - memory_region_dispatch_write(mr, physaddr, val, 1 << SHIFT, - iotlbentry->attrs); + CPUIOTLBEntry *iotlbentry = &env->iotlb[mmu_idx][index]; + return io_writex(env, iotlbentry, val, addr, retaddr, DATA_SIZE); } void helper_le_st_name(CPUArchState *env, target_ulong addr, DATA_TYPE val, @@ -340,16 +290,14 @@ void helper_le_st_name(CPUArchState *env, target_ulong addr, DATA_TYPE val, /* Handle an IO access. */ if (unlikely(tlb_addr & ~TARGET_PAGE_MASK)) { - CPUIOTLBEntry *iotlbentry; if ((addr & (DATA_SIZE - 1)) != 0) { goto do_unaligned_access; } - iotlbentry = &env->iotlb[mmu_idx][index]; /* ??? Note that the io helpers always read data in the target byte ordering. We should push the LE/BE request down into io. */ val = TGT_LE(val); - glue(io_write, SUFFIX)(env, iotlbentry, val, addr, retaddr); + glue(io_write, SUFFIX)(env, mmu_idx, index, val, addr, retaddr); return; } @@ -418,16 +366,14 @@ void helper_be_st_name(CPUArchState *env, target_ulong addr, DATA_TYPE val, /* Handle an IO access. */ if (unlikely(tlb_addr & ~TARGET_PAGE_MASK)) { - CPUIOTLBEntry *iotlbentry; if ((addr & (DATA_SIZE - 1)) != 0) { goto do_unaligned_access; } - iotlbentry = &env->iotlb[mmu_idx][index]; /* ??? Note that the io helpers always read data in the target byte ordering. We should push the LE/BE request down into io. */ val = TGT_BE(val); - glue(io_write, SUFFIX)(env, iotlbentry, val, addr, retaddr); + glue(io_write, SUFFIX)(env, mmu_idx, index, val, addr, retaddr); return; } @@ -466,33 +412,9 @@ void helper_be_st_name(CPUArchState *env, target_ulong addr, DATA_TYPE val, glue(glue(st, SUFFIX), _be_p)((uint8_t *)haddr, val); } #endif /* DATA_SIZE > 1 */ - -#if DATA_SIZE == 1 -/* Probe for whether the specified guest write access is permitted. - * If it is not permitted then an exception will be taken in the same - * way as if this were a real write access (and we will not return). - * Otherwise the function will return, and there will be a valid - * entry in the TLB for this access. - */ -void probe_write(CPUArchState *env, target_ulong addr, int mmu_idx, - uintptr_t retaddr) -{ - int index = (addr >> TARGET_PAGE_BITS) & (CPU_TLB_SIZE - 1); - target_ulong tlb_addr = env->tlb_table[mmu_idx][index].addr_write; - - if ((addr & TARGET_PAGE_MASK) - != (tlb_addr & (TARGET_PAGE_MASK | TLB_INVALID_MASK))) { - /* TLB entry is for a different page */ - if (!VICTIM_TLB_HIT(addr_write, addr)) { - tlb_fill(ENV_GET_CPU(env), addr, MMU_DATA_STORE, mmu_idx, retaddr); - } - } -} -#endif #endif /* !defined(SOFTMMU_CODE_ACCESS) */ #undef READ_ACCESS_TYPE -#undef SHIFT #undef DATA_TYPE #undef SUFFIX #undef LSUFFIX @@ -503,15 +425,9 @@ void probe_write(CPUArchState *env, target_ulong addr, int mmu_idx, #undef USUFFIX #undef SSUFFIX #undef BSWAP -#undef TGT_BE -#undef TGT_LE -#undef CPU_BE -#undef CPU_LE #undef helper_le_ld_name #undef helper_be_ld_name #undef helper_le_lds_name #undef helper_be_lds_name #undef helper_le_st_name #undef helper_be_st_name -#undef helper_te_ld_name -#undef helper_te_st_name diff --git a/spice-qemu-char.c b/spice-qemu-char.c index 351fcaa..276c4ae 100644 --- a/spice-qemu-char.c +++ b/spice-qemu-char.c @@ -199,7 +199,7 @@ static int spice_chr_write(CharDriverState *chr, const uint8_t *buf, int len) return read_bytes; } -static void spice_chr_close(struct CharDriverState *chr) +static void spice_chr_free(struct CharDriverState *chr) { SpiceCharDriver *s = chr->opaque; @@ -236,15 +236,6 @@ static void spice_port_set_fe_open(struct CharDriverState *chr, int fe_open) #endif } -static void spice_chr_fe_event(struct CharDriverState *chr, int event) -{ -#if SPICE_SERVER_VERSION >= 0x000c02 - SpiceCharDriver *s = chr->opaque; - - spice_server_port_event(&s->sin, event); -#endif -} - static void print_allowed_subtypes(void) { const char** psubtype; @@ -289,10 +280,8 @@ static CharDriverState *chr_open(const char *subtype, chr->opaque = s; chr->chr_write = spice_chr_write; chr->chr_add_watch = spice_chr_add_watch; - chr->chr_close = spice_chr_close; + chr->chr_free = spice_chr_free; chr->chr_set_fe_open = set_fe_open; - chr->explicit_be_open = true; - chr->chr_fe_event = spice_chr_fe_event; chr->chr_accept_input = spice_chr_accept_input; QLIST_INSERT_HEAD(&spice_chars, s, next); @@ -303,6 +292,7 @@ static CharDriverState *chr_open(const char *subtype, static CharDriverState *qemu_chr_open_spice_vmc(const char *id, ChardevBackend *backend, ChardevReturn *ret, + bool *be_opened, Error **errp) { ChardevSpiceChannel *spicevmc = backend->u.spicevmc.data; @@ -321,6 +311,7 @@ static CharDriverState *qemu_chr_open_spice_vmc(const char *id, return NULL; } + *be_opened = false; return chr_open(type, spice_vmc_set_fe_open, common, errp); } @@ -328,6 +319,7 @@ static CharDriverState *qemu_chr_open_spice_vmc(const char *id, static CharDriverState *qemu_chr_open_spice_port(const char *id, ChardevBackend *backend, ChardevReturn *ret, + bool *be_opened, Error **errp) { ChardevSpicePort *spiceport = backend->u.spiceport.data; @@ -345,6 +337,7 @@ static CharDriverState *qemu_chr_open_spice_port(const char *id, if (!chr) { return NULL; } + *be_opened = false; s = chr->opaque; s->sin.portname = g_strdup(name); diff --git a/target-alpha/cpu.c b/target-alpha/cpu.c index 6d01d7f..30d77ce 100644 --- a/target-alpha/cpu.c +++ b/target-alpha/cpu.c @@ -59,6 +59,13 @@ static void alpha_cpu_realizefn(DeviceState *dev, Error **errp) { CPUState *cs = CPU(dev); AlphaCPUClass *acc = ALPHA_CPU_GET_CLASS(dev); + Error *local_err = NULL; + + cpu_exec_realizefn(cs, &local_err); + if (local_err != NULL) { + error_propagate(errp, local_err); + return; + } qemu_init_vcpu(cs); @@ -266,7 +273,6 @@ static void alpha_cpu_initfn(Object *obj) CPUAlphaState *env = &cpu->env; cs->env_ptr = env; - cpu_exec_init(cs, &error_abort); tlb_flush(cs, 1); alpha_translate_init(); @@ -309,13 +315,6 @@ static void alpha_cpu_class_init(ObjectClass *oc, void *data) cc->disas_set_info = alpha_cpu_disas_set_info; cc->gdb_num_core_regs = 67; - - /* - * Reason: alpha_cpu_initfn() calls cpu_exec_init(), which saves - * the object in cpus -> dangling pointer after final - * object_unref(). - */ - dc->cannot_destroy_with_object_finalize_yet = true; } static const TypeInfo alpha_cpu_type_info = { diff --git a/target-alpha/cpu.h b/target-alpha/cpu.h index dcdd041..b08d160 100644 --- a/target-alpha/cpu.h +++ b/target-alpha/cpu.h @@ -201,7 +201,7 @@ enum { /* MMU modes definitions */ -/* Alpha has 5 MMU modes: PALcode, kernel, executive, supervisor, and user. +/* Alpha has 5 MMU modes: PALcode, Kernel, Executive, Supervisor, and User. The Unix PALcode only exposes the kernel and user modes; presumably executive and supervisor are used by VMS. @@ -209,22 +209,18 @@ enum { there are PALmode instructions that can access data via physical mode or via an os-installed "alternate mode", which is one of the 4 above. - QEMU does not currently properly distinguish between code/data when - looking up addresses. To avoid having to address this issue, our - emulated PALcode will cheat and use the KSEG mapping for its code+data - rather than physical addresses. + That said, we're only emulating Unix PALcode, and not attempting VMS, + so we don't need to implement Executive and Supervisor. QEMU's own + PALcode cheats and usees the KSEG mapping for its code+data rather than + physical addresses. */ - Moreover, we're only emulating Unix PALcode, and not attempting VMS. - - All of which allows us to drop all but kernel and user modes. - Elide the unused MMU modes to save space. */ - -#define NB_MMU_MODES 2 +#define NB_MMU_MODES 3 #define MMU_MODE0_SUFFIX _kernel #define MMU_MODE1_SUFFIX _user #define MMU_KERNEL_IDX 0 #define MMU_USER_IDX 1 +#define MMU_PHYS_IDX 2 typedef struct CPUAlphaState CPUAlphaState; @@ -234,7 +230,6 @@ struct CPUAlphaState { uint64_t pc; uint64_t unique; uint64_t lock_addr; - uint64_t lock_st_addr; uint64_t lock_value; /* The FPCR, and disassembled portions thereof. */ @@ -350,9 +345,6 @@ enum { EXCP_ARITH, EXCP_FEN, EXCP_CALL_PAL, - /* For Usermode emulation. */ - EXCP_STL_C, - EXCP_STQ_C, }; /* Alpha-specific interrupt pending bits. */ diff --git a/target-alpha/helper.c b/target-alpha/helper.c index 85168b7..2ef6cbe 100644 --- a/target-alpha/helper.c +++ b/target-alpha/helper.c @@ -126,6 +126,14 @@ static int get_physical_address(CPUAlphaState *env, target_ulong addr, int prot = 0; int ret = MM_K_ACV; + /* Handle physical accesses. */ + if (mmu_idx == MMU_PHYS_IDX) { + phys = addr; + prot = PAGE_READ | PAGE_WRITE | PAGE_EXEC; + ret = -1; + goto exit; + } + /* Ensure that the virtual address is properly sign-extended from the last implemented virtual address bit. */ if (saddr >> TARGET_VIRT_ADDR_SPACE_BITS != saddr >> 63) { @@ -298,12 +306,6 @@ void alpha_cpu_do_interrupt(CPUState *cs) case EXCP_CALL_PAL: name = "call_pal"; break; - case EXCP_STL_C: - name = "stl_c"; - break; - case EXCP_STQ_C: - name = "stq_c"; - break; } qemu_log("INT %6d: %s(%#x) pc=%016" PRIx64 " sp=%016" PRIx64 "\n", ++count, name, env->error_code, env->pc, env->ir[IR_SP]); diff --git a/target-alpha/helper.h b/target-alpha/helper.h index c3d8a3e..004221d 100644 --- a/target-alpha/helper.h +++ b/target-alpha/helper.h @@ -92,15 +92,6 @@ DEF_HELPER_FLAGS_2(ieee_input_cmp, TCG_CALL_NO_WG, void, env, i64) DEF_HELPER_FLAGS_2(ieee_input_s, TCG_CALL_NO_WG, void, env, i64) #if !defined (CONFIG_USER_ONLY) -DEF_HELPER_2(ldl_phys, i64, env, i64) -DEF_HELPER_2(ldq_phys, i64, env, i64) -DEF_HELPER_2(ldl_l_phys, i64, env, i64) -DEF_HELPER_2(ldq_l_phys, i64, env, i64) -DEF_HELPER_3(stl_phys, void, env, i64, i64) -DEF_HELPER_3(stq_phys, void, env, i64, i64) -DEF_HELPER_3(stl_c_phys, i64, env, i64, i64) -DEF_HELPER_3(stq_c_phys, i64, env, i64, i64) - DEF_HELPER_FLAGS_1(tbia, TCG_CALL_NO_RWG, void, env) DEF_HELPER_FLAGS_2(tbis, TCG_CALL_NO_RWG, void, env, i64) DEF_HELPER_FLAGS_1(tb_flush, TCG_CALL_NO_RWG, void, env) diff --git a/target-alpha/machine.c b/target-alpha/machine.c index 710b783..b99a123 100644 --- a/target-alpha/machine.c +++ b/target-alpha/machine.c @@ -45,8 +45,6 @@ static VMStateField vmstate_env_fields[] = { VMSTATE_UINTTL(unique, CPUAlphaState), VMSTATE_UINTTL(lock_addr, CPUAlphaState), VMSTATE_UINTTL(lock_value, CPUAlphaState), - /* Note that lock_st_addr is not saved; it is a temporary - used during the execution of the st[lq]_c insns. */ VMSTATE_UINT8(ps, CPUAlphaState), VMSTATE_UINT8(intr_flag, CPUAlphaState), diff --git a/target-alpha/mem_helper.c b/target-alpha/mem_helper.c index 1b2be50..78a7d45 100644 --- a/target-alpha/mem_helper.c +++ b/target-alpha/mem_helper.c @@ -25,79 +25,6 @@ /* Softmmu support */ #ifndef CONFIG_USER_ONLY - -uint64_t helper_ldl_phys(CPUAlphaState *env, uint64_t p) -{ - CPUState *cs = CPU(alpha_env_get_cpu(env)); - return (int32_t)ldl_phys(cs->as, p); -} - -uint64_t helper_ldq_phys(CPUAlphaState *env, uint64_t p) -{ - CPUState *cs = CPU(alpha_env_get_cpu(env)); - return ldq_phys(cs->as, p); -} - -uint64_t helper_ldl_l_phys(CPUAlphaState *env, uint64_t p) -{ - CPUState *cs = CPU(alpha_env_get_cpu(env)); - env->lock_addr = p; - return env->lock_value = (int32_t)ldl_phys(cs->as, p); -} - -uint64_t helper_ldq_l_phys(CPUAlphaState *env, uint64_t p) -{ - CPUState *cs = CPU(alpha_env_get_cpu(env)); - env->lock_addr = p; - return env->lock_value = ldq_phys(cs->as, p); -} - -void helper_stl_phys(CPUAlphaState *env, uint64_t p, uint64_t v) -{ - CPUState *cs = CPU(alpha_env_get_cpu(env)); - stl_phys(cs->as, p, v); -} - -void helper_stq_phys(CPUAlphaState *env, uint64_t p, uint64_t v) -{ - CPUState *cs = CPU(alpha_env_get_cpu(env)); - stq_phys(cs->as, p, v); -} - -uint64_t helper_stl_c_phys(CPUAlphaState *env, uint64_t p, uint64_t v) -{ - CPUState *cs = CPU(alpha_env_get_cpu(env)); - uint64_t ret = 0; - - if (p == env->lock_addr) { - int32_t old = ldl_phys(cs->as, p); - if (old == (int32_t)env->lock_value) { - stl_phys(cs->as, p, v); - ret = 1; - } - } - env->lock_addr = -1; - - return ret; -} - -uint64_t helper_stq_c_phys(CPUAlphaState *env, uint64_t p, uint64_t v) -{ - CPUState *cs = CPU(alpha_env_get_cpu(env)); - uint64_t ret = 0; - - if (p == env->lock_addr) { - uint64_t old = ldq_phys(cs->as, p); - if (old == env->lock_value) { - stq_phys(cs->as, p, v); - ret = 1; - } - } - env->lock_addr = -1; - - return ret; -} - void alpha_cpu_do_unaligned_access(CPUState *cs, vaddr addr, MMUAccessType access_type, int mmu_idx, uintptr_t retaddr) diff --git a/target-alpha/translate.c b/target-alpha/translate.c index c27c7b9..03e4776 100644 --- a/target-alpha/translate.c +++ b/target-alpha/translate.c @@ -99,7 +99,6 @@ static TCGv cpu_std_ir[31]; static TCGv cpu_fir[31]; static TCGv cpu_pc; static TCGv cpu_lock_addr; -static TCGv cpu_lock_st_addr; static TCGv cpu_lock_value; #ifndef CONFIG_USER_ONLY @@ -116,7 +115,6 @@ void alpha_translate_init(void) static const GlobalVar vars[] = { DEF_VAR(pc), DEF_VAR(lock_addr), - DEF_VAR(lock_st_addr), DEF_VAR(lock_value), }; @@ -198,6 +196,23 @@ static TCGv dest_sink(DisasContext *ctx) return ctx->sink; } +static void free_context_temps(DisasContext *ctx) +{ + if (!TCGV_IS_UNUSED_I64(ctx->sink)) { + tcg_gen_discard_i64(ctx->sink); + tcg_temp_free(ctx->sink); + TCGV_UNUSED_I64(ctx->sink); + } + if (!TCGV_IS_UNUSED_I64(ctx->zero)) { + tcg_temp_free(ctx->zero); + TCGV_UNUSED_I64(ctx->zero); + } + if (!TCGV_IS_UNUSED_I64(ctx->lit)) { + tcg_temp_free(ctx->lit); + TCGV_UNUSED_I64(ctx->lit); + } +} + static TCGv load_gpr(DisasContext *ctx, unsigned reg) { if (likely(reg < 31)) { @@ -392,59 +407,40 @@ static inline void gen_store_mem(DisasContext *ctx, } static ExitStatus gen_store_conditional(DisasContext *ctx, int ra, int rb, - int32_t disp16, int quad) + int32_t disp16, int mem_idx, + TCGMemOp op) { - TCGv addr; - - if (ra == 31) { - /* ??? Don't bother storing anything. The user can't tell - the difference, since the zero register always reads zero. */ - return NO_EXIT; - } - -#if defined(CONFIG_USER_ONLY) - addr = cpu_lock_st_addr; -#else - addr = tcg_temp_local_new(); -#endif + TCGLabel *lab_fail, *lab_done; + TCGv addr, val; + addr = tcg_temp_new_i64(); tcg_gen_addi_i64(addr, load_gpr(ctx, rb), disp16); + free_context_temps(ctx); -#if defined(CONFIG_USER_ONLY) - /* ??? This is handled via a complicated version of compare-and-swap - in the cpu_loop. Hopefully one day we'll have a real CAS opcode - in TCG so that this isn't necessary. */ - return gen_excp(ctx, quad ? EXCP_STQ_C : EXCP_STL_C, ra); -#else - /* ??? In system mode we are never multi-threaded, so CAS can be - implemented via a non-atomic load-compare-store sequence. */ - { - TCGLabel *lab_fail, *lab_done; - TCGv val; - - lab_fail = gen_new_label(); - lab_done = gen_new_label(); - tcg_gen_brcond_i64(TCG_COND_NE, addr, cpu_lock_addr, lab_fail); + lab_fail = gen_new_label(); + lab_done = gen_new_label(); + tcg_gen_brcond_i64(TCG_COND_NE, addr, cpu_lock_addr, lab_fail); + tcg_temp_free_i64(addr); - val = tcg_temp_new(); - tcg_gen_qemu_ld_i64(val, addr, ctx->mem_idx, quad ? MO_LEQ : MO_LESL); - tcg_gen_brcond_i64(TCG_COND_NE, val, cpu_lock_value, lab_fail); + val = tcg_temp_new_i64(); + tcg_gen_atomic_cmpxchg_i64(val, cpu_lock_addr, cpu_lock_value, + load_gpr(ctx, ra), mem_idx, op); + free_context_temps(ctx); - tcg_gen_qemu_st_i64(ctx->ir[ra], addr, ctx->mem_idx, - quad ? MO_LEQ : MO_LEUL); - tcg_gen_movi_i64(ctx->ir[ra], 1); - tcg_gen_br(lab_done); + if (ra != 31) { + tcg_gen_setcond_i64(TCG_COND_EQ, ctx->ir[ra], val, cpu_lock_value); + } + tcg_temp_free_i64(val); + tcg_gen_br(lab_done); - gen_set_label(lab_fail); + gen_set_label(lab_fail); + if (ra != 31) { tcg_gen_movi_i64(ctx->ir[ra], 0); - - gen_set_label(lab_done); - tcg_gen_movi_i64(cpu_lock_addr, -1); - - tcg_temp_free(addr); - return NO_EXIT; } -#endif + + gen_set_label(lab_done); + tcg_gen_movi_i64(cpu_lock_addr, -1); + return NO_EXIT; } static bool in_superpage(DisasContext *ctx, int64_t addr) @@ -2423,19 +2419,19 @@ static ExitStatus translate_one(DisasContext *ctx, uint32_t insn) switch ((insn >> 12) & 0xF) { case 0x0: /* Longword physical access (hw_ldl/p) */ - gen_helper_ldl_phys(va, cpu_env, addr); + tcg_gen_qemu_ld_i64(va, addr, MMU_PHYS_IDX, MO_LESL); break; case 0x1: /* Quadword physical access (hw_ldq/p) */ - gen_helper_ldq_phys(va, cpu_env, addr); + tcg_gen_qemu_ld_i64(va, addr, MMU_PHYS_IDX, MO_LEQ); break; case 0x2: /* Longword physical access with lock (hw_ldl_l/p) */ - gen_helper_ldl_l_phys(va, cpu_env, addr); + gen_qemu_ldl_l(va, addr, MMU_PHYS_IDX); break; case 0x3: /* Quadword physical access with lock (hw_ldq_l/p) */ - gen_helper_ldq_l_phys(va, cpu_env, addr); + gen_qemu_ldq_l(va, addr, MMU_PHYS_IDX); break; case 0x4: /* Longword virtual PTE fetch (hw_ldl/v) */ @@ -2674,27 +2670,34 @@ static ExitStatus translate_one(DisasContext *ctx, uint32_t insn) #ifndef CONFIG_USER_ONLY REQUIRE_TB_FLAG(TB_FLAGS_PAL_MODE); { - TCGv addr = tcg_temp_new(); - va = load_gpr(ctx, ra); - vb = load_gpr(ctx, rb); - - tcg_gen_addi_i64(addr, vb, disp12); switch ((insn >> 12) & 0xF) { case 0x0: /* Longword physical access */ - gen_helper_stl_phys(cpu_env, addr, va); + va = load_gpr(ctx, ra); + vb = load_gpr(ctx, rb); + tmp = tcg_temp_new(); + tcg_gen_addi_i64(tmp, vb, disp12); + tcg_gen_qemu_st_i64(va, tmp, MMU_PHYS_IDX, MO_LESL); + tcg_temp_free(tmp); break; case 0x1: /* Quadword physical access */ - gen_helper_stq_phys(cpu_env, addr, va); + va = load_gpr(ctx, ra); + vb = load_gpr(ctx, rb); + tmp = tcg_temp_new(); + tcg_gen_addi_i64(tmp, vb, disp12); + tcg_gen_qemu_st_i64(va, tmp, MMU_PHYS_IDX, MO_LEQ); + tcg_temp_free(tmp); break; case 0x2: /* Longword physical access with lock */ - gen_helper_stl_c_phys(dest_gpr(ctx, ra), cpu_env, addr, va); + ret = gen_store_conditional(ctx, ra, rb, disp12, + MMU_PHYS_IDX, MO_LESL); break; case 0x3: /* Quadword physical access with lock */ - gen_helper_stq_c_phys(dest_gpr(ctx, ra), cpu_env, addr, va); + ret = gen_store_conditional(ctx, ra, rb, disp12, + MMU_PHYS_IDX, MO_LEQ); break; case 0x4: /* Longword virtual access */ @@ -2733,7 +2736,6 @@ static ExitStatus translate_one(DisasContext *ctx, uint32_t insn) /* Invalid */ goto invalid_opc; } - tcg_temp_free(addr); break; } #else @@ -2797,11 +2799,13 @@ static ExitStatus translate_one(DisasContext *ctx, uint32_t insn) break; case 0x2E: /* STL_C */ - ret = gen_store_conditional(ctx, ra, rb, disp16, 0); + ret = gen_store_conditional(ctx, ra, rb, disp16, + ctx->mem_idx, MO_LESL); break; case 0x2F: /* STQ_C */ - ret = gen_store_conditional(ctx, ra, rb, disp16, 1); + ret = gen_store_conditional(ctx, ra, rb, disp16, + ctx->mem_idx, MO_LEQ); break; case 0x30: /* BR */ @@ -2906,6 +2910,10 @@ void gen_intermediate_code(CPUAlphaState *env, struct TranslationBlock *tb) /* Similarly for flush-to-zero. */ ctx.tb_ftz = -1; + TCGV_UNUSED_I64(ctx.zero); + TCGV_UNUSED_I64(ctx.sink); + TCGV_UNUSED_I64(ctx.lit); + num_insns = 0; max_insns = tb->cflags & CF_COUNT_MASK; if (max_insns == 0) { @@ -2940,23 +2948,9 @@ void gen_intermediate_code(CPUAlphaState *env, struct TranslationBlock *tb) } insn = cpu_ldl_code(env, ctx.pc); - TCGV_UNUSED_I64(ctx.zero); - TCGV_UNUSED_I64(ctx.sink); - TCGV_UNUSED_I64(ctx.lit); - ctx.pc += 4; ret = translate_one(ctxp, insn); - - if (!TCGV_IS_UNUSED_I64(ctx.sink)) { - tcg_gen_discard_i64(ctx.sink); - tcg_temp_free(ctx.sink); - } - if (!TCGV_IS_UNUSED_I64(ctx.zero)) { - tcg_temp_free(ctx.zero); - } - if (!TCGV_IS_UNUSED_I64(ctx.lit)) { - tcg_temp_free(ctx.lit); - } + free_context_temps(ctxp); /* If we reach a page boundary, are single stepping, or exhaust instruction count, stop generation. */ diff --git a/target-arm/cpu-qom.h b/target-arm/cpu-qom.h index 3991173..a42495b 100644 --- a/target-arm/cpu-qom.h +++ b/target-arm/cpu-qom.h @@ -80,9 +80,11 @@ void arm_gt_stimer_cb(void *opaque); #define ARM_AFF2_MASK (0xFFULL << ARM_AFF2_SHIFT) #define ARM_AFF3_SHIFT 32 #define ARM_AFF3_MASK (0xFFULL << ARM_AFF3_SHIFT) +#define ARM_DEFAULT_CPUS_PER_CLUSTER 8 #define ARM32_AFFINITY_MASK (ARM_AFF0_MASK|ARM_AFF1_MASK|ARM_AFF2_MASK) #define ARM64_AFFINITY_MASK \ (ARM_AFF0_MASK|ARM_AFF1_MASK|ARM_AFF2_MASK|ARM_AFF3_MASK) +#define ARM64_AFFINITY_INVALID (~ARM64_AFFINITY_MASK) #endif diff --git a/target-arm/cpu.c b/target-arm/cpu.c index 1b9540e..2439ca5 100644 --- a/target-arm/cpu.c +++ b/target-arm/cpu.c @@ -434,29 +434,16 @@ static void arm_disas_set_info(CPUState *cpu, disassemble_info *info) } } -#define ARM_CPUS_PER_CLUSTER 8 - static void arm_cpu_initfn(Object *obj) { CPUState *cs = CPU(obj); ARMCPU *cpu = ARM_CPU(obj); static bool inited; - uint32_t Aff1, Aff0; cs->env_ptr = &cpu->env; - cpu_exec_init(cs, &error_abort); cpu->cp_regs = g_hash_table_new_full(g_int_hash, g_int_equal, g_free, g_free); - /* This cpu-id-to-MPIDR affinity is used only for TCG; KVM will override it. - * We don't support setting cluster ID ([16..23]) (known as Aff2 - * in later ARM ARM versions), or any of the higher affinity level fields, - * so these bits always RAZ. - */ - Aff1 = cs->cpu_index / ARM_CPUS_PER_CLUSTER; - Aff0 = cs->cpu_index % ARM_CPUS_PER_CLUSTER; - cpu->mp_affinity = (Aff1 << ARM_AFF1_SHIFT) | Aff0; - #ifndef CONFIG_USER_ONLY /* Our inbound IRQ and FIQ lines */ if (kvm_enabled()) { @@ -576,6 +563,14 @@ static void arm_cpu_realizefn(DeviceState *dev, Error **errp) ARMCPU *cpu = ARM_CPU(dev); ARMCPUClass *acc = ARM_CPU_GET_CLASS(dev); CPUARMState *env = &cpu->env; + int pagebits; + Error *local_err = NULL; + + cpu_exec_realizefn(cs, &local_err); + if (local_err != NULL) { + error_propagate(errp, local_err); + return; + } /* Some features automatically imply others: */ if (arm_feature(env, ARM_FEATURE_V8)) { @@ -631,6 +626,40 @@ static void arm_cpu_realizefn(DeviceState *dev, Error **errp) set_feature(env, ARM_FEATURE_THUMB_DSP); } + if (arm_feature(env, ARM_FEATURE_V7) && + !arm_feature(env, ARM_FEATURE_M) && + !arm_feature(env, ARM_FEATURE_MPU)) { + /* v7VMSA drops support for the old ARMv5 tiny pages, so we + * can use 4K pages. + */ + pagebits = 12; + } else { + /* For CPUs which might have tiny 1K pages, or which have an + * MPU and might have small region sizes, stick with 1K pages. + */ + pagebits = 10; + } + if (!set_preferred_target_page_bits(pagebits)) { + /* This can only ever happen for hotplugging a CPU, or if + * the board code incorrectly creates a CPU which it has + * promised via minimum_page_size that it will not. + */ + error_setg(errp, "This CPU requires a smaller page size than the " + "system is using"); + return; + } + + /* This cpu-id-to-MPIDR affinity is used only for TCG; KVM will override it. + * We don't support setting cluster ID ([16..23]) (known as Aff2 + * in later ARM ARM versions), or any of the higher affinity level fields, + * so these bits always RAZ. + */ + if (cpu->mp_affinity == ARM64_AFFINITY_INVALID) { + uint32_t Aff1 = cs->cpu_index / ARM_DEFAULT_CPUS_PER_CLUSTER; + uint32_t Aff0 = cs->cpu_index % ARM_DEFAULT_CPUS_PER_CLUSTER; + cpu->mp_affinity = (Aff1 << ARM_AFF1_SHIFT) | Aff0; + } + if (cpu->reset_hivecs) { cpu->reset_sctlr |= (1 << 13); } @@ -1461,7 +1490,8 @@ static Property arm_cpu_properties[] = { DEFINE_PROP_BOOL("start-powered-off", ARMCPU, start_powered_off, false), DEFINE_PROP_UINT32("psci-conduit", ARMCPU, psci_conduit, 0), DEFINE_PROP_UINT32("midr", ARMCPU, midr, 0), - DEFINE_PROP_UINT64("mp-affinity", ARMCPU, mp_affinity, 0), + DEFINE_PROP_UINT64("mp-affinity", ARMCPU, + mp_affinity, ARM64_AFFINITY_INVALID), DEFINE_PROP_END_OF_LIST() }; @@ -1533,17 +1563,6 @@ static void arm_cpu_class_init(ObjectClass *oc, void *data) cc->debug_check_watchpoint = arm_debug_check_watchpoint; cc->disas_set_info = arm_disas_set_info; - - /* - * Reason: arm_cpu_initfn() calls cpu_exec_init(), which saves - * the object in cpus -> dangling pointer after final - * object_unref(). - * - * Once this is fixed, the devices that create ARM CPUs should be - * updated not to set cannot_destroy_with_object_finalize_yet, - * unless they still screw up something else. - */ - dc->cannot_destroy_with_object_finalize_yet = true; } static void cpu_register(const ARMCPUInfo *info) diff --git a/target-arm/cpu.h b/target-arm/cpu.h index 2218c00..19d967b 100644 --- a/target-arm/cpu.h +++ b/target-arm/cpu.h @@ -46,13 +46,12 @@ #define EXCP_BKPT 7 #define EXCP_EXCEPTION_EXIT 8 /* Return from v7M exception. */ #define EXCP_KERNEL_TRAP 9 /* Jumped to kernel code page. */ -#define EXCP_STREX 10 #define EXCP_HVC 11 /* HyperVisor Call */ #define EXCP_HYP_TRAP 12 #define EXCP_SMC 13 /* Secure Monitor Call */ #define EXCP_VIRQ 14 #define EXCP_VFIQ 15 -#define EXCP_SEMIHOST 16 /* semihosting call (A64 only) */ +#define EXCP_SEMIHOST 16 /* semihosting call */ #define ARMV7M_EXCP_RESET 1 #define ARMV7M_EXCP_NMI 2 @@ -475,10 +474,6 @@ typedef struct CPUARMState { uint64_t exclusive_addr; uint64_t exclusive_val; uint64_t exclusive_high; -#if defined(CONFIG_USER_ONLY) - uint64_t exclusive_test; - uint32_t exclusive_info; -#endif /* iwMMXt coprocessor state. */ struct { @@ -1766,10 +1761,11 @@ bool write_cpustate_to_list(ARMCPU *cpu); #if defined(CONFIG_USER_ONLY) #define TARGET_PAGE_BITS 12 #else -/* The ARM MMU allows 1k pages. */ -/* ??? Linux doesn't actually use these, and they're deprecated in recent - architecture revisions. Maybe a configure option to disable them. */ -#define TARGET_PAGE_BITS 10 +/* ARMv7 and later CPUs have 4K pages minimum, but ARMv5 and v6 + * have to support 1K tiny pages. + */ +#define TARGET_PAGE_BITS_VARY +#define TARGET_PAGE_BITS_MIN 10 #endif #if defined(TARGET_AARCH64) diff --git a/target-arm/helper-a64.c b/target-arm/helper-a64.c index 41e48a4..98b97df 100644 --- a/target-arm/helper-a64.c +++ b/target-arm/helper-a64.c @@ -27,6 +27,10 @@ #include "qemu/bitops.h" #include "internals.h" #include "qemu/crc32c.h" +#include "exec/exec-all.h" +#include "exec/cpu_ldst.h" +#include "qemu/int128.h" +#include "tcg.h" #include <zlib.h> /* For crc32 */ /* C2.4.7 Multiply and divide */ @@ -444,3 +448,112 @@ uint64_t HELPER(crc32c_64)(uint64_t acc, uint64_t val, uint32_t bytes) /* Linux crc32c converts the output to one's complement. */ return crc32c(acc, buf, bytes) ^ 0xffffffff; } + +/* Returns 0 on success; 1 otherwise. */ +uint64_t HELPER(paired_cmpxchg64_le)(CPUARMState *env, uint64_t addr, + uint64_t new_lo, uint64_t new_hi) +{ + uintptr_t ra = GETPC(); + Int128 oldv, cmpv, newv; + bool success; + + cmpv = int128_make128(env->exclusive_val, env->exclusive_high); + newv = int128_make128(new_lo, new_hi); + + if (parallel_cpus) { +#ifndef CONFIG_ATOMIC128 + cpu_loop_exit_atomic(ENV_GET_CPU(env), ra); +#else + int mem_idx = cpu_mmu_index(env, false); + TCGMemOpIdx oi = make_memop_idx(MO_LEQ | MO_ALIGN_16, mem_idx); + oldv = helper_atomic_cmpxchgo_le_mmu(env, addr, cmpv, newv, oi, ra); + success = int128_eq(oldv, cmpv); +#endif + } else { + uint64_t o0, o1; + +#ifdef CONFIG_USER_ONLY + /* ??? Enforce alignment. */ + uint64_t *haddr = g2h(addr); + o0 = ldq_le_p(haddr + 0); + o1 = ldq_le_p(haddr + 1); + oldv = int128_make128(o0, o1); + + success = int128_eq(oldv, cmpv); + if (success) { + stq_le_p(haddr + 0, int128_getlo(newv)); + stq_le_p(haddr + 1, int128_gethi(newv)); + } +#else + int mem_idx = cpu_mmu_index(env, false); + TCGMemOpIdx oi0 = make_memop_idx(MO_LEQ | MO_ALIGN_16, mem_idx); + TCGMemOpIdx oi1 = make_memop_idx(MO_LEQ, mem_idx); + + o0 = helper_le_ldq_mmu(env, addr + 0, oi0, ra); + o1 = helper_le_ldq_mmu(env, addr + 8, oi1, ra); + oldv = int128_make128(o0, o1); + + success = int128_eq(oldv, cmpv); + if (success) { + helper_le_stq_mmu(env, addr + 0, int128_getlo(newv), oi1, ra); + helper_le_stq_mmu(env, addr + 8, int128_gethi(newv), oi1, ra); + } +#endif + } + + return !success; +} + +uint64_t HELPER(paired_cmpxchg64_be)(CPUARMState *env, uint64_t addr, + uint64_t new_lo, uint64_t new_hi) +{ + uintptr_t ra = GETPC(); + Int128 oldv, cmpv, newv; + bool success; + + cmpv = int128_make128(env->exclusive_val, env->exclusive_high); + newv = int128_make128(new_lo, new_hi); + + if (parallel_cpus) { +#ifndef CONFIG_ATOMIC128 + cpu_loop_exit_atomic(ENV_GET_CPU(env), ra); +#else + int mem_idx = cpu_mmu_index(env, false); + TCGMemOpIdx oi = make_memop_idx(MO_BEQ | MO_ALIGN_16, mem_idx); + oldv = helper_atomic_cmpxchgo_be_mmu(env, addr, cmpv, newv, oi, ra); + success = int128_eq(oldv, cmpv); +#endif + } else { + uint64_t o0, o1; + +#ifdef CONFIG_USER_ONLY + /* ??? Enforce alignment. */ + uint64_t *haddr = g2h(addr); + o1 = ldq_be_p(haddr + 0); + o0 = ldq_be_p(haddr + 1); + oldv = int128_make128(o0, o1); + + success = int128_eq(oldv, cmpv); + if (success) { + stq_be_p(haddr + 0, int128_gethi(newv)); + stq_be_p(haddr + 1, int128_getlo(newv)); + } +#else + int mem_idx = cpu_mmu_index(env, false); + TCGMemOpIdx oi0 = make_memop_idx(MO_BEQ | MO_ALIGN_16, mem_idx); + TCGMemOpIdx oi1 = make_memop_idx(MO_BEQ, mem_idx); + + o1 = helper_be_ldq_mmu(env, addr + 0, oi0, ra); + o0 = helper_be_ldq_mmu(env, addr + 8, oi1, ra); + oldv = int128_make128(o0, o1); + + success = int128_eq(oldv, cmpv); + if (success) { + helper_be_stq_mmu(env, addr + 0, int128_gethi(newv), oi1, ra); + helper_be_stq_mmu(env, addr + 8, int128_getlo(newv), oi1, ra); + } +#endif + } + + return !success; +} diff --git a/target-arm/helper-a64.h b/target-arm/helper-a64.h index 1d3d10f..dd32000 100644 --- a/target-arm/helper-a64.h +++ b/target-arm/helper-a64.h @@ -46,3 +46,5 @@ DEF_HELPER_FLAGS_2(frecpx_f32, TCG_CALL_NO_RWG, f32, f32, ptr) DEF_HELPER_FLAGS_2(fcvtx_f64_to_f32, TCG_CALL_NO_RWG, f32, f64, env) DEF_HELPER_FLAGS_3(crc32_64, TCG_CALL_NO_RWG_SE, i64, i64, i64, i32) DEF_HELPER_FLAGS_3(crc32c_64, TCG_CALL_NO_RWG_SE, i64, i64, i64, i32) +DEF_HELPER_FLAGS_4(paired_cmpxchg64_le, TCG_CALL_NO_WG, i64, env, i64, i64, i64) +DEF_HELPER_FLAGS_4(paired_cmpxchg64_be, TCG_CALL_NO_WG, i64, env, i64, i64, i64) diff --git a/target-arm/helper.c b/target-arm/helper.c index cb83ee2..25b15dc 100644 --- a/target-arm/helper.c +++ b/target-arm/helper.c @@ -6573,12 +6573,19 @@ static inline bool check_for_semihosting(CPUState *cs) /* Only intercept calls from privileged modes, to provide some * semblance of security. */ - if (!semihosting_enabled() || - ((env->uncached_cpsr & CPSR_M) == ARM_CPU_MODE_USR)) { + if (cs->exception_index != EXCP_SEMIHOST && + (!semihosting_enabled() || + ((env->uncached_cpsr & CPSR_M) == ARM_CPU_MODE_USR))) { return false; } switch (cs->exception_index) { + case EXCP_SEMIHOST: + /* This is always a semihosting call; the "is this usermode" + * and "is semihosting enabled" checks have been done at + * translate time. + */ + break; case EXCP_SWI: /* Check for semihosting interrupt. */ if (env->thumb) { diff --git a/target-arm/internals.h b/target-arm/internals.h index cd57401..3edccd2 100644 --- a/target-arm/internals.h +++ b/target-arm/internals.h @@ -46,8 +46,7 @@ static inline bool excp_is_internal(int excp) || excp == EXCP_HALTED || excp == EXCP_EXCEPTION_EXIT || excp == EXCP_KERNEL_TRAP - || excp == EXCP_SEMIHOST - || excp == EXCP_STREX; + || excp == EXCP_SEMIHOST; } /* Exception names for debug logging; note that not all of these @@ -63,7 +62,6 @@ static const char * const excnames[] = { [EXCP_BKPT] = "Breakpoint", [EXCP_EXCEPTION_EXIT] = "QEMU v7M exception exit", [EXCP_KERNEL_TRAP] = "QEMU intercept of kernel commpage", - [EXCP_STREX] = "QEMU intercept of STREX", [EXCP_HVC] = "Hypervisor Call", [EXCP_HYP_TRAP] = "Hypervisor Trap", [EXCP_SMC] = "Secure Monitor Call", diff --git a/target-arm/translate-a64.c b/target-arm/translate-a64.c index 96c2227..ded924a 100644 --- a/target-arm/translate-a64.c +++ b/target-arm/translate-a64.c @@ -1839,37 +1839,41 @@ static void disas_b_exc_sys(DisasContext *s, uint32_t insn) } } -/* - * Load/Store exclusive instructions are implemented by remembering - * the value/address loaded, and seeing if these are the same - * when the store is performed. This is not actually the architecturally - * mandated semantics, but it works for typical guest code sequences - * and avoids having to monitor regular stores. - * - * In system emulation mode only one CPU will be running at once, so - * this sequence is effectively atomic. In user emulation mode we - * throw an exception and handle the atomic operation elsewhere. - */ static void gen_load_exclusive(DisasContext *s, int rt, int rt2, TCGv_i64 addr, int size, bool is_pair) { TCGv_i64 tmp = tcg_temp_new_i64(); - TCGMemOp memop = s->be_data + size; + TCGMemOp be = s->be_data; g_assert(size <= 3); - tcg_gen_qemu_ld_i64(tmp, addr, get_mem_index(s), memop); - if (is_pair) { - TCGv_i64 addr2 = tcg_temp_new_i64(); TCGv_i64 hitmp = tcg_temp_new_i64(); - g_assert(size >= 2); - tcg_gen_addi_i64(addr2, addr, 1 << size); - tcg_gen_qemu_ld_i64(hitmp, addr2, get_mem_index(s), memop); - tcg_temp_free_i64(addr2); + if (size == 3) { + TCGv_i64 addr2 = tcg_temp_new_i64(); + + tcg_gen_qemu_ld_i64(tmp, addr, get_mem_index(s), + MO_64 | MO_ALIGN_16 | be); + tcg_gen_addi_i64(addr2, addr, 8); + tcg_gen_qemu_ld_i64(hitmp, addr2, get_mem_index(s), + MO_64 | MO_ALIGN | be); + tcg_temp_free_i64(addr2); + } else { + g_assert(size == 2); + tcg_gen_qemu_ld_i64(tmp, addr, get_mem_index(s), + MO_64 | MO_ALIGN | be); + if (be == MO_LE) { + tcg_gen_extr32_i64(tmp, hitmp, tmp); + } else { + tcg_gen_extr32_i64(hitmp, tmp, tmp); + } + } + tcg_gen_mov_i64(cpu_exclusive_high, hitmp); tcg_gen_mov_i64(cpu_reg(s, rt2), hitmp); tcg_temp_free_i64(hitmp); + } else { + tcg_gen_qemu_ld_i64(tmp, addr, get_mem_index(s), size | MO_ALIGN | be); } tcg_gen_mov_i64(cpu_exclusive_val, tmp); @@ -1879,16 +1883,6 @@ static void gen_load_exclusive(DisasContext *s, int rt, int rt2, tcg_gen_mov_i64(cpu_exclusive_addr, addr); } -#ifdef CONFIG_USER_ONLY -static void gen_store_exclusive(DisasContext *s, int rd, int rt, int rt2, - TCGv_i64 addr, int size, int is_pair) -{ - tcg_gen_mov_i64(cpu_exclusive_test, addr); - tcg_gen_movi_i32(cpu_exclusive_info, - size | is_pair << 2 | (rd << 4) | (rt << 9) | (rt2 << 14)); - gen_exception_internal_insn(s, 4, EXCP_STREX); -} -#else static void gen_store_exclusive(DisasContext *s, int rd, int rt, int rt2, TCGv_i64 inaddr, int size, int is_pair) { @@ -1916,46 +1910,42 @@ static void gen_store_exclusive(DisasContext *s, int rd, int rt, int rt2, tcg_gen_brcond_i64(TCG_COND_NE, addr, cpu_exclusive_addr, fail_label); tmp = tcg_temp_new_i64(); - tcg_gen_qemu_ld_i64(tmp, addr, get_mem_index(s), s->be_data + size); - tcg_gen_brcond_i64(TCG_COND_NE, tmp, cpu_exclusive_val, fail_label); - tcg_temp_free_i64(tmp); - - if (is_pair) { - TCGv_i64 addrhi = tcg_temp_new_i64(); - TCGv_i64 tmphi = tcg_temp_new_i64(); - - tcg_gen_addi_i64(addrhi, addr, 1 << size); - tcg_gen_qemu_ld_i64(tmphi, addrhi, get_mem_index(s), - s->be_data + size); - tcg_gen_brcond_i64(TCG_COND_NE, tmphi, cpu_exclusive_high, fail_label); - - tcg_temp_free_i64(tmphi); - tcg_temp_free_i64(addrhi); - } - - /* We seem to still have the exclusive monitor, so do the store */ - tcg_gen_qemu_st_i64(cpu_reg(s, rt), addr, get_mem_index(s), - s->be_data + size); if (is_pair) { - TCGv_i64 addrhi = tcg_temp_new_i64(); - - tcg_gen_addi_i64(addrhi, addr, 1 << size); - tcg_gen_qemu_st_i64(cpu_reg(s, rt2), addrhi, - get_mem_index(s), s->be_data + size); - tcg_temp_free_i64(addrhi); + if (size == 2) { + TCGv_i64 val = tcg_temp_new_i64(); + tcg_gen_concat32_i64(tmp, cpu_reg(s, rt), cpu_reg(s, rt2)); + tcg_gen_concat32_i64(val, cpu_exclusive_val, cpu_exclusive_high); + tcg_gen_atomic_cmpxchg_i64(tmp, addr, val, tmp, + get_mem_index(s), + size | MO_ALIGN | s->be_data); + tcg_gen_setcond_i64(TCG_COND_NE, tmp, tmp, val); + tcg_temp_free_i64(val); + } else if (s->be_data == MO_LE) { + gen_helper_paired_cmpxchg64_le(tmp, cpu_env, addr, cpu_reg(s, rt), + cpu_reg(s, rt2)); + } else { + gen_helper_paired_cmpxchg64_be(tmp, cpu_env, addr, cpu_reg(s, rt), + cpu_reg(s, rt2)); + } + } else { + TCGv_i64 val = cpu_reg(s, rt); + tcg_gen_atomic_cmpxchg_i64(tmp, addr, cpu_exclusive_val, val, + get_mem_index(s), + size | MO_ALIGN | s->be_data); + tcg_gen_setcond_i64(TCG_COND_NE, tmp, tmp, cpu_exclusive_val); } tcg_temp_free_i64(addr); - tcg_gen_movi_i64(cpu_reg(s, rd), 0); + tcg_gen_mov_i64(cpu_reg(s, rd), tmp); + tcg_temp_free_i64(tmp); tcg_gen_br(done_label); + gen_set_label(fail_label); tcg_gen_movi_i64(cpu_reg(s, rd), 1); gen_set_label(done_label); tcg_gen_movi_i64(cpu_exclusive_addr, -1); - } -#endif /* Update the Sixty-Four bit (SF) registersize. This logic is derived * from the ARMv8 specs for LDR (Shared decode for all encodings). diff --git a/target-arm/translate.c b/target-arm/translate.c index 164b52a..718f7d0 100644 --- a/target-arm/translate.c +++ b/target-arm/translate.c @@ -28,6 +28,7 @@ #include "qemu/log.h" #include "qemu/bitops.h" #include "arm_ldst.h" +#include "exec/semihost.h" #include "exec/helper-proto.h" #include "exec/helper-gen.h" @@ -64,10 +65,6 @@ static TCGv_i32 cpu_R[16]; TCGv_i32 cpu_CF, cpu_NF, cpu_VF, cpu_ZF; TCGv_i64 cpu_exclusive_addr; TCGv_i64 cpu_exclusive_val; -#ifdef CONFIG_USER_ONLY -TCGv_i64 cpu_exclusive_test; -TCGv_i32 cpu_exclusive_info; -#endif /* FIXME: These should be removed. */ static TCGv_i32 cpu_F0s, cpu_F1s; @@ -101,12 +98,6 @@ void arm_translate_init(void) offsetof(CPUARMState, exclusive_addr), "exclusive_addr"); cpu_exclusive_val = tcg_global_mem_new_i64(cpu_env, offsetof(CPUARMState, exclusive_val), "exclusive_val"); -#ifdef CONFIG_USER_ONLY - cpu_exclusive_test = tcg_global_mem_new_i64(cpu_env, - offsetof(CPUARMState, exclusive_test), "exclusive_test"); - cpu_exclusive_info = tcg_global_mem_new_i32(cpu_env, - offsetof(CPUARMState, exclusive_info), "exclusive_info"); -#endif a64_translate_init(); } @@ -931,145 +922,103 @@ static inline void store_reg_from_load(DisasContext *s, int reg, TCGv_i32 var) * These functions work like tcg_gen_qemu_{ld,st}* except * that the address argument is TCGv_i32 rather than TCGv. */ -#if TARGET_LONG_BITS == 32 - -#define DO_GEN_LD(SUFF, OPC, BE32_XOR) \ -static inline void gen_aa32_ld##SUFF(DisasContext *s, TCGv_i32 val, \ - TCGv_i32 addr, int index) \ -{ \ - TCGMemOp opc = (OPC) | s->be_data; \ - /* Not needed for user-mode BE32, where we use MO_BE instead. */ \ - if (!IS_USER_ONLY && s->sctlr_b && BE32_XOR) { \ - TCGv addr_be = tcg_temp_new(); \ - tcg_gen_xori_i32(addr_be, addr, BE32_XOR); \ - tcg_gen_qemu_ld_i32(val, addr_be, index, opc); \ - tcg_temp_free(addr_be); \ - return; \ - } \ - tcg_gen_qemu_ld_i32(val, addr, index, opc); \ -} - -#define DO_GEN_ST(SUFF, OPC, BE32_XOR) \ -static inline void gen_aa32_st##SUFF(DisasContext *s, TCGv_i32 val, \ - TCGv_i32 addr, int index) \ -{ \ - TCGMemOp opc = (OPC) | s->be_data; \ - /* Not needed for user-mode BE32, where we use MO_BE instead. */ \ - if (!IS_USER_ONLY && s->sctlr_b && BE32_XOR) { \ - TCGv addr_be = tcg_temp_new(); \ - tcg_gen_xori_i32(addr_be, addr, BE32_XOR); \ - tcg_gen_qemu_st_i32(val, addr_be, index, opc); \ - tcg_temp_free(addr_be); \ - return; \ - } \ - tcg_gen_qemu_st_i32(val, addr, index, opc); \ -} -static inline void gen_aa32_ld64(DisasContext *s, TCGv_i64 val, - TCGv_i32 addr, int index) +static inline TCGv gen_aa32_addr(DisasContext *s, TCGv_i32 a32, TCGMemOp op) { - TCGMemOp opc = MO_Q | s->be_data; - tcg_gen_qemu_ld_i64(val, addr, index, opc); + TCGv addr = tcg_temp_new(); + tcg_gen_extu_i32_tl(addr, a32); + /* Not needed for user-mode BE32, where we use MO_BE instead. */ - if (!IS_USER_ONLY && s->sctlr_b) { - tcg_gen_rotri_i64(val, val, 32); + if (!IS_USER_ONLY && s->sctlr_b && (op & MO_SIZE) < MO_32) { + tcg_gen_xori_tl(addr, addr, 4 - (1 << (op & MO_SIZE))); } + return addr; } -static inline void gen_aa32_st64(DisasContext *s, TCGv_i64 val, - TCGv_i32 addr, int index) +static void gen_aa32_ld_i32(DisasContext *s, TCGv_i32 val, TCGv_i32 a32, + int index, TCGMemOp opc) { - TCGMemOp opc = MO_Q | s->be_data; - /* Not needed for user-mode BE32, where we use MO_BE instead. */ - if (!IS_USER_ONLY && s->sctlr_b) { - TCGv_i64 tmp = tcg_temp_new_i64(); - tcg_gen_rotri_i64(tmp, val, 32); - tcg_gen_qemu_st_i64(tmp, addr, index, opc); - tcg_temp_free_i64(tmp); - return; - } - tcg_gen_qemu_st_i64(val, addr, index, opc); + TCGv addr = gen_aa32_addr(s, a32, opc); + tcg_gen_qemu_ld_i32(val, addr, index, opc); + tcg_temp_free(addr); } -#else +static void gen_aa32_st_i32(DisasContext *s, TCGv_i32 val, TCGv_i32 a32, + int index, TCGMemOp opc) +{ + TCGv addr = gen_aa32_addr(s, a32, opc); + tcg_gen_qemu_st_i32(val, addr, index, opc); + tcg_temp_free(addr); +} -#define DO_GEN_LD(SUFF, OPC, BE32_XOR) \ +#define DO_GEN_LD(SUFF, OPC) \ static inline void gen_aa32_ld##SUFF(DisasContext *s, TCGv_i32 val, \ - TCGv_i32 addr, int index) \ + TCGv_i32 a32, int index) \ { \ - TCGMemOp opc = (OPC) | s->be_data; \ - TCGv addr64 = tcg_temp_new(); \ - tcg_gen_extu_i32_i64(addr64, addr); \ - /* Not needed for user-mode BE32, where we use MO_BE instead. */ \ - if (!IS_USER_ONLY && s->sctlr_b && BE32_XOR) { \ - tcg_gen_xori_i64(addr64, addr64, BE32_XOR); \ - } \ - tcg_gen_qemu_ld_i32(val, addr64, index, opc); \ - tcg_temp_free(addr64); \ -} - -#define DO_GEN_ST(SUFF, OPC, BE32_XOR) \ + gen_aa32_ld_i32(s, val, a32, index, OPC | s->be_data); \ +} + +#define DO_GEN_ST(SUFF, OPC) \ static inline void gen_aa32_st##SUFF(DisasContext *s, TCGv_i32 val, \ - TCGv_i32 addr, int index) \ + TCGv_i32 a32, int index) \ { \ - TCGMemOp opc = (OPC) | s->be_data; \ - TCGv addr64 = tcg_temp_new(); \ - tcg_gen_extu_i32_i64(addr64, addr); \ - /* Not needed for user-mode BE32, where we use MO_BE instead. */ \ - if (!IS_USER_ONLY && s->sctlr_b && BE32_XOR) { \ - tcg_gen_xori_i64(addr64, addr64, BE32_XOR); \ - } \ - tcg_gen_qemu_st_i32(val, addr64, index, opc); \ - tcg_temp_free(addr64); \ + gen_aa32_st_i32(s, val, a32, index, OPC | s->be_data); \ } -static inline void gen_aa32_ld64(DisasContext *s, TCGv_i64 val, - TCGv_i32 addr, int index) +static inline void gen_aa32_frob64(DisasContext *s, TCGv_i64 val) { - TCGMemOp opc = MO_Q | s->be_data; - TCGv addr64 = tcg_temp_new(); - tcg_gen_extu_i32_i64(addr64, addr); - tcg_gen_qemu_ld_i64(val, addr64, index, opc); - /* Not needed for user-mode BE32, where we use MO_BE instead. */ if (!IS_USER_ONLY && s->sctlr_b) { tcg_gen_rotri_i64(val, val, 32); } - tcg_temp_free(addr64); } -static inline void gen_aa32_st64(DisasContext *s, TCGv_i64 val, - TCGv_i32 addr, int index) +static void gen_aa32_ld_i64(DisasContext *s, TCGv_i64 val, TCGv_i32 a32, + int index, TCGMemOp opc) +{ + TCGv addr = gen_aa32_addr(s, a32, opc); + tcg_gen_qemu_ld_i64(val, addr, index, opc); + gen_aa32_frob64(s, val); + tcg_temp_free(addr); +} + +static inline void gen_aa32_ld64(DisasContext *s, TCGv_i64 val, + TCGv_i32 a32, int index) +{ + gen_aa32_ld_i64(s, val, a32, index, MO_Q | s->be_data); +} + +static void gen_aa32_st_i64(DisasContext *s, TCGv_i64 val, TCGv_i32 a32, + int index, TCGMemOp opc) { - TCGMemOp opc = MO_Q | s->be_data; - TCGv addr64 = tcg_temp_new(); - tcg_gen_extu_i32_i64(addr64, addr); + TCGv addr = gen_aa32_addr(s, a32, opc); /* Not needed for user-mode BE32, where we use MO_BE instead. */ if (!IS_USER_ONLY && s->sctlr_b) { - TCGv tmp = tcg_temp_new(); + TCGv_i64 tmp = tcg_temp_new_i64(); tcg_gen_rotri_i64(tmp, val, 32); - tcg_gen_qemu_st_i64(tmp, addr64, index, opc); - tcg_temp_free(tmp); + tcg_gen_qemu_st_i64(tmp, addr, index, opc); + tcg_temp_free_i64(tmp); } else { - tcg_gen_qemu_st_i64(val, addr64, index, opc); + tcg_gen_qemu_st_i64(val, addr, index, opc); } - tcg_temp_free(addr64); + tcg_temp_free(addr); } -#endif +static inline void gen_aa32_st64(DisasContext *s, TCGv_i64 val, + TCGv_i32 a32, int index) +{ + gen_aa32_st_i64(s, val, a32, index, MO_Q | s->be_data); +} -DO_GEN_LD(8s, MO_SB, 3) -DO_GEN_LD(8u, MO_UB, 3) -DO_GEN_LD(16s, MO_SW, 2) -DO_GEN_LD(16u, MO_UW, 2) -DO_GEN_LD(32u, MO_UL, 0) -/* 'a' variants include an alignment check */ -DO_GEN_LD(16ua, MO_UW | MO_ALIGN, 2) -DO_GEN_LD(32ua, MO_UL | MO_ALIGN, 0) -DO_GEN_ST(8, MO_UB, 3) -DO_GEN_ST(16, MO_UW, 2) -DO_GEN_ST(32, MO_UL, 0) +DO_GEN_LD(8s, MO_SB) +DO_GEN_LD(8u, MO_UB) +DO_GEN_LD(16s, MO_SW) +DO_GEN_LD(16u, MO_UW) +DO_GEN_LD(32u, MO_UL) +DO_GEN_ST(8, MO_UB) +DO_GEN_ST(16, MO_UW) +DO_GEN_ST(32, MO_UL) static inline void gen_set_pc_im(DisasContext *s, target_ulong val) { @@ -1144,6 +1093,33 @@ static inline void gen_lookup_tb(DisasContext *s) s->is_jmp = DISAS_JUMP; } +static inline void gen_hlt(DisasContext *s, int imm) +{ + /* HLT. This has two purposes. + * Architecturally, it is an external halting debug instruction. + * Since QEMU doesn't implement external debug, we treat this as + * it is required for halting debug disabled: it will UNDEF. + * Secondly, "HLT 0x3C" is a T32 semihosting trap instruction, + * and "HLT 0xF000" is an A32 semihosting syscall. These traps + * must trigger semihosting even for ARMv7 and earlier, where + * HLT was an undefined encoding. + * In system mode, we don't allow userspace access to + * semihosting, to provide some semblance of security + * (and for consistency with our 32-bit semihosting). + */ + if (semihosting_enabled() && +#ifndef CONFIG_USER_ONLY + s->current_el != 0 && +#endif + (imm == (s->thumb ? 0x3c : 0xf000))) { + gen_exception_internal_insn(s, 0, EXCP_SEMIHOST); + return; + } + + gen_exception_insn(s, s->thumb ? 2 : 4, EXCP_UDEF, syn_uncategorized(), + default_exception_el(s)); +} + static inline void gen_add_data_offset(DisasContext *s, unsigned int insn, TCGv_i32 var) { @@ -7731,45 +7707,30 @@ static void gen_logicq_cc(TCGv_i32 lo, TCGv_i32 hi) /* Load/Store exclusive instructions are implemented by remembering the value/address loaded, and seeing if these are the same - when the store is performed. This should be sufficient to implement + when the store is performed. This should be sufficient to implement the architecturally mandated semantics, and avoids having to monitor - regular stores. - - In system emulation mode only one CPU will be running at once, so - this sequence is effectively atomic. In user emulation mode we - throw an exception and handle the atomic operation elsewhere. */ + regular stores. The compare vs the remembered value is done during + the cmpxchg operation, but we must compare the addresses manually. */ static void gen_load_exclusive(DisasContext *s, int rt, int rt2, TCGv_i32 addr, int size) { TCGv_i32 tmp = tcg_temp_new_i32(); + TCGMemOp opc = size | MO_ALIGN | s->be_data; s->is_ldex = true; - switch (size) { - case 0: - gen_aa32_ld8u(s, tmp, addr, get_mem_index(s)); - break; - case 1: - gen_aa32_ld16ua(s, tmp, addr, get_mem_index(s)); - break; - case 2: - case 3: - gen_aa32_ld32ua(s, tmp, addr, get_mem_index(s)); - break; - default: - abort(); - } - if (size == 3) { TCGv_i32 tmp2 = tcg_temp_new_i32(); - TCGv_i32 tmp3 = tcg_temp_new_i32(); + TCGv_i64 t64 = tcg_temp_new_i64(); - tcg_gen_addi_i32(tmp2, addr, 4); - gen_aa32_ld32u(s, tmp3, tmp2, get_mem_index(s)); - tcg_temp_free_i32(tmp2); - tcg_gen_concat_i32_i64(cpu_exclusive_val, tmp, tmp3); - store_reg(s, rt2, tmp3); + gen_aa32_ld_i64(s, t64, addr, get_mem_index(s), opc); + tcg_gen_mov_i64(cpu_exclusive_val, t64); + tcg_gen_extr_i64_i32(tmp, tmp2, t64); + tcg_temp_free_i64(t64); + + store_reg(s, rt2, tmp2); } else { + gen_aa32_ld_i32(s, tmp, addr, get_mem_index(s), opc); tcg_gen_extu_i32_i64(cpu_exclusive_val, tmp); } @@ -7782,23 +7743,15 @@ static void gen_clrex(DisasContext *s) tcg_gen_movi_i64(cpu_exclusive_addr, -1); } -#ifdef CONFIG_USER_ONLY static void gen_store_exclusive(DisasContext *s, int rd, int rt, int rt2, TCGv_i32 addr, int size) { - tcg_gen_extu_i32_i64(cpu_exclusive_test, addr); - tcg_gen_movi_i32(cpu_exclusive_info, - size | (rd << 4) | (rt << 8) | (rt2 << 12)); - gen_exception_internal_insn(s, 4, EXCP_STREX); -} -#else -static void gen_store_exclusive(DisasContext *s, int rd, int rt, int rt2, - TCGv_i32 addr, int size) -{ - TCGv_i32 tmp; - TCGv_i64 val64, extaddr; + TCGv_i32 t0, t1, t2; + TCGv_i64 extaddr; + TCGv taddr; TCGLabel *done_label; TCGLabel *fail_label; + TCGMemOp opc = size | MO_ALIGN | s->be_data; /* if (env->exclusive_addr == addr && env->exclusive_val == [addr]) { [addr] = {Rt}; @@ -7813,69 +7766,45 @@ static void gen_store_exclusive(DisasContext *s, int rd, int rt, int rt2, tcg_gen_brcond_i64(TCG_COND_NE, extaddr, cpu_exclusive_addr, fail_label); tcg_temp_free_i64(extaddr); - tmp = tcg_temp_new_i32(); - switch (size) { - case 0: - gen_aa32_ld8u(s, tmp, addr, get_mem_index(s)); - break; - case 1: - gen_aa32_ld16u(s, tmp, addr, get_mem_index(s)); - break; - case 2: - case 3: - gen_aa32_ld32u(s, tmp, addr, get_mem_index(s)); - break; - default: - abort(); - } - - val64 = tcg_temp_new_i64(); + taddr = gen_aa32_addr(s, addr, opc); + t0 = tcg_temp_new_i32(); + t1 = load_reg(s, rt); if (size == 3) { - TCGv_i32 tmp2 = tcg_temp_new_i32(); - TCGv_i32 tmp3 = tcg_temp_new_i32(); - tcg_gen_addi_i32(tmp2, addr, 4); - gen_aa32_ld32u(s, tmp3, tmp2, get_mem_index(s)); - tcg_temp_free_i32(tmp2); - tcg_gen_concat_i32_i64(val64, tmp, tmp3); - tcg_temp_free_i32(tmp3); - } else { - tcg_gen_extu_i32_i64(val64, tmp); - } - tcg_temp_free_i32(tmp); + TCGv_i64 o64 = tcg_temp_new_i64(); + TCGv_i64 n64 = tcg_temp_new_i64(); - tcg_gen_brcond_i64(TCG_COND_NE, val64, cpu_exclusive_val, fail_label); - tcg_temp_free_i64(val64); + t2 = load_reg(s, rt2); + tcg_gen_concat_i32_i64(n64, t1, t2); + tcg_temp_free_i32(t2); + gen_aa32_frob64(s, n64); - tmp = load_reg(s, rt); - switch (size) { - case 0: - gen_aa32_st8(s, tmp, addr, get_mem_index(s)); - break; - case 1: - gen_aa32_st16(s, tmp, addr, get_mem_index(s)); - break; - case 2: - case 3: - gen_aa32_st32(s, tmp, addr, get_mem_index(s)); - break; - default: - abort(); - } - tcg_temp_free_i32(tmp); - if (size == 3) { - tcg_gen_addi_i32(addr, addr, 4); - tmp = load_reg(s, rt2); - gen_aa32_st32(s, tmp, addr, get_mem_index(s)); - tcg_temp_free_i32(tmp); + tcg_gen_atomic_cmpxchg_i64(o64, taddr, cpu_exclusive_val, n64, + get_mem_index(s), opc); + tcg_temp_free_i64(n64); + + gen_aa32_frob64(s, o64); + tcg_gen_setcond_i64(TCG_COND_NE, o64, o64, cpu_exclusive_val); + tcg_gen_extrl_i64_i32(t0, o64); + + tcg_temp_free_i64(o64); + } else { + t2 = tcg_temp_new_i32(); + tcg_gen_extrl_i64_i32(t2, cpu_exclusive_val); + tcg_gen_atomic_cmpxchg_i32(t0, taddr, t2, t1, get_mem_index(s), opc); + tcg_gen_setcond_i32(TCG_COND_NE, t0, t0, t2); + tcg_temp_free_i32(t2); } - tcg_gen_movi_i32(cpu_R[rd], 0); + tcg_temp_free_i32(t1); + tcg_temp_free(taddr); + tcg_gen_mov_i32(cpu_R[rd], t0); + tcg_temp_free_i32(t0); tcg_gen_br(done_label); + gen_set_label(fail_label); tcg_gen_movi_i32(cpu_R[rd], 1); gen_set_label(done_label); tcg_gen_movi_i64(cpu_exclusive_addr, -1); } -#endif /* gen_srs: * @env: CPUARMState @@ -8395,6 +8324,10 @@ static void disas_arm_insn(DisasContext *s, unsigned int insn) { int imm16 = extract32(insn, 0, 4) | (extract32(insn, 8, 12) << 4); switch (op1) { + case 0: + /* HLT */ + gen_hlt(s, imm16); + break; case 1: /* bkpt */ ARCH(5); @@ -8419,7 +8352,7 @@ static void disas_arm_insn(DisasContext *s, unsigned int insn) gen_smc(s); break; default: - goto illegal_op; + g_assert_not_reached(); } break; } @@ -8846,25 +8779,27 @@ static void disas_arm_insn(DisasContext *s, unsigned int insn) } tcg_temp_free_i32(addr); } else { + TCGv taddr; + TCGMemOp opc = s->be_data; + /* SWP instruction */ rm = (insn) & 0xf; - /* ??? This is not really atomic. However we know - we never have multiple CPUs running in parallel, - so it is good enough. */ - addr = load_reg(s, rn); - tmp = load_reg(s, rm); - tmp2 = tcg_temp_new_i32(); if (insn & (1 << 22)) { - gen_aa32_ld8u(s, tmp2, addr, get_mem_index(s)); - gen_aa32_st8(s, tmp, addr, get_mem_index(s)); + opc |= MO_UB; } else { - gen_aa32_ld32u(s, tmp2, addr, get_mem_index(s)); - gen_aa32_st32(s, tmp, addr, get_mem_index(s)); + opc |= MO_UL | MO_ALIGN; } - tcg_temp_free_i32(tmp); + + addr = load_reg(s, rn); + taddr = gen_aa32_addr(s, addr, opc); tcg_temp_free_i32(addr); - store_reg(s, rd, tmp2); + + tmp = load_reg(s, rm); + tcg_gen_atomic_xchg_i32(tmp, taddr, tmp, + get_mem_index(s), opc); + tcg_temp_free(taddr); + store_reg(s, rd, tmp); } } } else { @@ -11451,19 +11386,33 @@ static void disas_thumb_insn(CPUARMState *env, DisasContext *s) break; } - case 0xa: /* rev */ + case 0xa: /* rev, and hlt */ + { + int op1 = extract32(insn, 6, 2); + + if (op1 == 2) { + /* HLT */ + int imm6 = extract32(insn, 0, 6); + + gen_hlt(s, imm6); + break; + } + + /* Otherwise this is rev */ ARCH(6); rn = (insn >> 3) & 0x7; rd = insn & 0x7; tmp = load_reg(s, rn); - switch ((insn >> 6) & 3) { + switch (op1) { case 0: tcg_gen_bswap32_i32(tmp, tmp); break; case 1: gen_rev16(tmp); break; case 3: gen_revsh(tmp); break; - default: goto illegal_op; + default: + g_assert_not_reached(); } store_reg(s, rd, tmp); break; + } case 6: switch ((insn >> 5) & 7) { diff --git a/target-arm/translate.h b/target-arm/translate.h index a53f25a..285e96f 100644 --- a/target-arm/translate.h +++ b/target-arm/translate.h @@ -79,10 +79,6 @@ extern TCGv_env cpu_env; extern TCGv_i32 cpu_NF, cpu_ZF, cpu_CF, cpu_VF; extern TCGv_i64 cpu_exclusive_addr; extern TCGv_i64 cpu_exclusive_val; -#ifdef CONFIG_USER_ONLY -extern TCGv_i64 cpu_exclusive_test; -extern TCGv_i32 cpu_exclusive_info; -#endif static inline int arm_dc_feature(DisasContext *dc, int feature) { diff --git a/target-cris/cpu.c b/target-cris/cpu.c index d680cfb..2e9ab97 100644 --- a/target-cris/cpu.c +++ b/target-cris/cpu.c @@ -142,6 +142,13 @@ static void cris_cpu_realizefn(DeviceState *dev, Error **errp) { CPUState *cs = CPU(dev); CRISCPUClass *ccc = CRIS_CPU_GET_CLASS(dev); + Error *local_err = NULL; + + cpu_exec_realizefn(cs, &local_err); + if (local_err != NULL) { + error_propagate(errp, local_err); + return; + } cpu_reset(cs); qemu_init_vcpu(cs); @@ -187,7 +194,6 @@ static void cris_cpu_initfn(Object *obj) static bool tcg_initialized; cs->env_ptr = env; - cpu_exec_init(cs, &error_abort); env->pregs[PR_VR] = ccc->vr; @@ -326,13 +332,6 @@ static void cris_cpu_class_init(ObjectClass *oc, void *data) cc->gdb_stop_before_watchpoint = true; cc->disas_set_info = cris_disas_set_info; - - /* - * Reason: cris_cpu_initfn() calls cpu_exec_init(), which saves - * the object in cpus -> dangling pointer after final - * object_unref(). - */ - dc->cannot_destroy_with_object_finalize_yet = true; } static const TypeInfo cris_cpu_type_info = { diff --git a/target-i386/cpu-qom.h b/target-i386/cpu-qom.h index e724004..7c9a07a 100644 --- a/target-i386/cpu-qom.h +++ b/target-i386/cpu-qom.h @@ -68,6 +68,7 @@ typedef struct X86CPUClass { const char *model_description; DeviceRealize parent_realize; + DeviceUnrealize parent_unrealize; void (*parent_reset)(CPUState *cpu); } X86CPUClass; diff --git a/target-i386/cpu.c b/target-i386/cpu.c index d95514c..83998a8 100644 --- a/target-i386/cpu.c +++ b/target-i386/cpu.c @@ -2945,7 +2945,7 @@ static void x86_cpu_apic_create(X86CPU *cpu, Error **errp) OBJECT(cpu->apic_state), &error_abort); object_unref(OBJECT(cpu->apic_state)); - qdev_prop_set_uint8(cpu->apic_state, "id", cpu->apic_id); + qdev_prop_set_uint32(cpu->apic_state, "id", cpu->apic_id); /* TODO: convert to link<> */ apic = APIC_COMMON(cpu->apic_state); apic->cpu = cpu; @@ -3271,7 +3271,11 @@ static void x86_cpu_realizefn(DeviceState *dev, Error **errp) cpu->phys_bits = 32; } } - cpu_exec_init(cs, &error_abort); + cpu_exec_realizefn(cs, &local_err); + if (local_err != NULL) { + error_propagate(errp, local_err); + return; + } if (tcg_enabled()) { tcg_x86_init(); @@ -3352,6 +3356,8 @@ out: static void x86_cpu_unrealizefn(DeviceState *dev, Error **errp) { X86CPU *cpu = X86_CPU(dev); + X86CPUClass *xcc = X86_CPU_GET_CLASS(dev); + Error *local_err = NULL; #ifndef CONFIG_USER_ONLY cpu_remove_sync(CPU(dev)); @@ -3362,6 +3368,12 @@ static void x86_cpu_unrealizefn(DeviceState *dev, Error **errp) object_unparent(OBJECT(cpu->apic_state)); cpu->apic_state = NULL; } + + xcc->parent_unrealize(dev, &local_err); + if (local_err != NULL) { + error_propagate(errp, local_err); + return; + } } typedef struct BitProperty { @@ -3636,6 +3648,7 @@ static void x86_cpu_common_class_init(ObjectClass *oc, void *data) DeviceClass *dc = DEVICE_CLASS(oc); xcc->parent_realize = dc->realize; + xcc->parent_unrealize = dc->unrealize; dc->realize = x86_cpu_realizefn; dc->unrealize = x86_cpu_unrealizefn; dc->props = x86_cpu_properties; diff --git a/target-i386/cpu.h b/target-i386/cpu.h index e645698..6303d65 100644 --- a/target-i386/cpu.h +++ b/target-i386/cpu.h @@ -325,6 +325,7 @@ #define MSR_IA32_APICBASE 0x1b #define MSR_IA32_APICBASE_BSP (1<<8) #define MSR_IA32_APICBASE_ENABLE (1<<11) +#define MSR_IA32_APICBASE_EXTD (1 << 10) #define MSR_IA32_APICBASE_BASE (0xfffffU<<12) #define MSR_IA32_FEATURE_CONTROL 0x0000003a #define MSR_TSC_ADJUST 0x0000003b diff --git a/target-i386/helper.h b/target-i386/helper.h index 1320edc..4e859eb 100644 --- a/target-i386/helper.h +++ b/target-i386/helper.h @@ -1,8 +1,6 @@ DEF_HELPER_FLAGS_4(cc_compute_all, TCG_CALL_NO_RWG_SE, tl, tl, tl, tl, int) DEF_HELPER_FLAGS_4(cc_compute_c, TCG_CALL_NO_RWG_SE, tl, tl, tl, tl, int) -DEF_HELPER_0(lock, void) -DEF_HELPER_0(unlock, void) DEF_HELPER_3(write_eflags, void, env, tl, i32) DEF_HELPER_1(read_eflags, tl, env) DEF_HELPER_2(divb_AL, void, env, tl) @@ -74,8 +72,10 @@ DEF_HELPER_3(boundw, void, env, tl, int) DEF_HELPER_3(boundl, void, env, tl, int) DEF_HELPER_1(rsm, void, env) DEF_HELPER_2(into, void, env, int) +DEF_HELPER_2(cmpxchg8b_unlocked, void, env, tl) DEF_HELPER_2(cmpxchg8b, void, env, tl) #ifdef TARGET_X86_64 +DEF_HELPER_2(cmpxchg16b_unlocked, void, env, tl) DEF_HELPER_2(cmpxchg16b, void, env, tl) #endif DEF_HELPER_1(single_step, void, env) diff --git a/target-i386/kvm.c b/target-i386/kvm.c index 0472f45..86b41a9 100644 --- a/target-i386/kvm.c +++ b/target-i386/kvm.c @@ -129,9 +129,8 @@ static bool kvm_x2apic_api_set_flags(uint64_t flags) return !kvm_vm_enable_cap(s, KVM_CAP_X2APIC_API, 0, flags); } -#define MEMORIZE(fn) \ +#define MEMORIZE(fn, _result) \ ({ \ - static typeof(fn) _result; \ static bool _memorized; \ \ if (_memorized) { \ @@ -141,11 +140,19 @@ static bool kvm_x2apic_api_set_flags(uint64_t flags) _result = fn; \ }) +static bool has_x2apic_api; + +bool kvm_has_x2apic_api(void) +{ + return has_x2apic_api; +} + bool kvm_enable_x2apic(void) { return MEMORIZE( kvm_x2apic_api_set_flags(KVM_X2APIC_API_USE_32BIT_IDS | - KVM_X2APIC_API_DISABLE_BROADCAST_QUIRK)); + KVM_X2APIC_API_DISABLE_BROADCAST_QUIRK), + has_x2apic_api); } static int kvm_get_tsc(CPUState *cs) diff --git a/target-i386/kvm_i386.h b/target-i386/kvm_i386.h index 5c369b1..7607929 100644 --- a/target-i386/kvm_i386.h +++ b/target-i386/kvm_i386.h @@ -44,4 +44,5 @@ int kvm_device_msix_deassign(KVMState *s, uint32_t dev_id); void kvm_put_apicbase(X86CPU *cpu, uint64_t value); bool kvm_enable_x2apic(void); +bool kvm_has_x2apic_api(void); #endif diff --git a/target-i386/mem_helper.c b/target-i386/mem_helper.c index 5bc0594..70f6766 100644 --- a/target-i386/mem_helper.c +++ b/target-i386/mem_helper.c @@ -22,87 +22,146 @@ #include "exec/helper-proto.h" #include "exec/exec-all.h" #include "exec/cpu_ldst.h" +#include "qemu/int128.h" +#include "tcg.h" -/* broken thread support */ - -#if defined(CONFIG_USER_ONLY) -QemuMutex global_cpu_lock; - -void helper_lock(void) +void helper_cmpxchg8b_unlocked(CPUX86State *env, target_ulong a0) { - qemu_mutex_lock(&global_cpu_lock); -} + uintptr_t ra = GETPC(); + uint64_t oldv, cmpv, newv; + int eflags; -void helper_unlock(void) -{ - qemu_mutex_unlock(&global_cpu_lock); -} + eflags = cpu_cc_compute_all(env, CC_OP); -void helper_lock_init(void) -{ - qemu_mutex_init(&global_cpu_lock); -} -#else -void helper_lock(void) -{ -} + cmpv = deposit64(env->regs[R_EAX], 32, 32, env->regs[R_EDX]); + newv = deposit64(env->regs[R_EBX], 32, 32, env->regs[R_ECX]); -void helper_unlock(void) -{ -} + oldv = cpu_ldq_data_ra(env, a0, ra); + newv = (cmpv == oldv ? newv : oldv); + /* always do the store */ + cpu_stq_data_ra(env, a0, newv, ra); -void helper_lock_init(void) -{ + if (oldv == cmpv) { + eflags |= CC_Z; + } else { + env->regs[R_EAX] = (uint32_t)oldv; + env->regs[R_EDX] = (uint32_t)(oldv >> 32); + eflags &= ~CC_Z; + } + CC_SRC = eflags; } -#endif void helper_cmpxchg8b(CPUX86State *env, target_ulong a0) { - uint64_t d; +#ifdef CONFIG_ATOMIC64 + uint64_t oldv, cmpv, newv; int eflags; eflags = cpu_cc_compute_all(env, CC_OP); - d = cpu_ldq_data_ra(env, a0, GETPC()); - if (d == (((uint64_t)env->regs[R_EDX] << 32) | (uint32_t)env->regs[R_EAX])) { - cpu_stq_data_ra(env, a0, ((uint64_t)env->regs[R_ECX] << 32) - | (uint32_t)env->regs[R_EBX], GETPC()); + + cmpv = deposit64(env->regs[R_EAX], 32, 32, env->regs[R_EDX]); + newv = deposit64(env->regs[R_EBX], 32, 32, env->regs[R_ECX]); + +#ifdef CONFIG_USER_ONLY + { + uint64_t *haddr = g2h(a0); + cmpv = cpu_to_le64(cmpv); + newv = cpu_to_le64(newv); + oldv = atomic_cmpxchg__nocheck(haddr, cmpv, newv); + oldv = le64_to_cpu(oldv); + } +#else + { + uintptr_t ra = GETPC(); + int mem_idx = cpu_mmu_index(env, false); + TCGMemOpIdx oi = make_memop_idx(MO_TEQ, mem_idx); + oldv = helper_atomic_cmpxchgq_le_mmu(env, a0, cmpv, newv, oi, ra); + } +#endif + + if (oldv == cmpv) { eflags |= CC_Z; } else { - /* always do the store */ - cpu_stq_data_ra(env, a0, d, GETPC()); - env->regs[R_EDX] = (uint32_t)(d >> 32); - env->regs[R_EAX] = (uint32_t)d; + env->regs[R_EAX] = (uint32_t)oldv; + env->regs[R_EDX] = (uint32_t)(oldv >> 32); eflags &= ~CC_Z; } CC_SRC = eflags; +#else + cpu_loop_exit_atomic(ENV_GET_CPU(env), GETPC()); +#endif /* CONFIG_ATOMIC64 */ } #ifdef TARGET_X86_64 -void helper_cmpxchg16b(CPUX86State *env, target_ulong a0) +void helper_cmpxchg16b_unlocked(CPUX86State *env, target_ulong a0) { - uint64_t d0, d1; + uintptr_t ra = GETPC(); + Int128 oldv, cmpv, newv; + uint64_t o0, o1; int eflags; + bool success; if ((a0 & 0xf) != 0) { raise_exception_ra(env, EXCP0D_GPF, GETPC()); } eflags = cpu_cc_compute_all(env, CC_OP); - d0 = cpu_ldq_data_ra(env, a0, GETPC()); - d1 = cpu_ldq_data_ra(env, a0 + 8, GETPC()); - if (d0 == env->regs[R_EAX] && d1 == env->regs[R_EDX]) { - cpu_stq_data_ra(env, a0, env->regs[R_EBX], GETPC()); - cpu_stq_data_ra(env, a0 + 8, env->regs[R_ECX], GETPC()); + + cmpv = int128_make128(env->regs[R_EAX], env->regs[R_EDX]); + newv = int128_make128(env->regs[R_EBX], env->regs[R_ECX]); + + o0 = cpu_ldq_data_ra(env, a0 + 0, ra); + o1 = cpu_ldq_data_ra(env, a0 + 8, ra); + + oldv = int128_make128(o0, o1); + success = int128_eq(oldv, cmpv); + if (!success) { + newv = oldv; + } + + cpu_stq_data_ra(env, a0 + 0, int128_getlo(newv), ra); + cpu_stq_data_ra(env, a0 + 8, int128_gethi(newv), ra); + + if (success) { eflags |= CC_Z; } else { - /* always do the store */ - cpu_stq_data_ra(env, a0, d0, GETPC()); - cpu_stq_data_ra(env, a0 + 8, d1, GETPC()); - env->regs[R_EDX] = d1; - env->regs[R_EAX] = d0; + env->regs[R_EAX] = int128_getlo(oldv); + env->regs[R_EDX] = int128_gethi(oldv); eflags &= ~CC_Z; } CC_SRC = eflags; } + +void helper_cmpxchg16b(CPUX86State *env, target_ulong a0) +{ + uintptr_t ra = GETPC(); + + if ((a0 & 0xf) != 0) { + raise_exception_ra(env, EXCP0D_GPF, ra); + } else { +#ifndef CONFIG_ATOMIC128 + cpu_loop_exit_atomic(ENV_GET_CPU(env), ra); +#else + int eflags = cpu_cc_compute_all(env, CC_OP); + + Int128 cmpv = int128_make128(env->regs[R_EAX], env->regs[R_EDX]); + Int128 newv = int128_make128(env->regs[R_EBX], env->regs[R_ECX]); + + int mem_idx = cpu_mmu_index(env, false); + TCGMemOpIdx oi = make_memop_idx(MO_TEQ | MO_ALIGN_16, mem_idx); + Int128 oldv = helper_atomic_cmpxchgo_le_mmu(env, a0, cmpv, + newv, oi, ra); + + if (int128_eq(oldv, cmpv)) { + eflags |= CC_Z; + } else { + env->regs[R_EAX] = int128_getlo(oldv); + env->regs[R_EDX] = int128_gethi(oldv); + eflags &= ~CC_Z; + } + CC_SRC = eflags; +#endif + } +} #endif void helper_boundw(CPUX86State *env, target_ulong a0, int v) diff --git a/target-i386/translate.c b/target-i386/translate.c index 9447557..927b366 100644 --- a/target-i386/translate.c +++ b/target-i386/translate.c @@ -457,13 +457,12 @@ static void gen_lea_v_seg(DisasContext *s, TCGMemOp aflag, TCGv a0, #endif case MO_32: /* 32 bit address */ + if (ovr_seg < 0 && s->addseg) { + ovr_seg = def_seg; + } if (ovr_seg < 0) { - if (s->addseg) { - ovr_seg = def_seg; - } else { - tcg_gen_ext32u_tl(cpu_A0, a0); - return; - } + tcg_gen_ext32u_tl(cpu_A0, a0); + return; } break; case MO_16: @@ -1258,55 +1257,95 @@ static void gen_op(DisasContext *s1, int op, TCGMemOp ot, int d) { if (d != OR_TMP0) { gen_op_mov_v_reg(ot, cpu_T0, d); - } else { + } else if (!(s1->prefix & PREFIX_LOCK)) { gen_op_ld_v(s1, ot, cpu_T0, cpu_A0); } switch(op) { case OP_ADCL: gen_compute_eflags_c(s1, cpu_tmp4); - tcg_gen_add_tl(cpu_T0, cpu_T0, cpu_T1); - tcg_gen_add_tl(cpu_T0, cpu_T0, cpu_tmp4); - gen_op_st_rm_T0_A0(s1, ot, d); + if (s1->prefix & PREFIX_LOCK) { + tcg_gen_add_tl(cpu_T0, cpu_tmp4, cpu_T1); + tcg_gen_atomic_add_fetch_tl(cpu_T0, cpu_A0, cpu_T0, + s1->mem_index, ot | MO_LE); + } else { + tcg_gen_add_tl(cpu_T0, cpu_T0, cpu_T1); + tcg_gen_add_tl(cpu_T0, cpu_T0, cpu_tmp4); + gen_op_st_rm_T0_A0(s1, ot, d); + } gen_op_update3_cc(cpu_tmp4); set_cc_op(s1, CC_OP_ADCB + ot); break; case OP_SBBL: gen_compute_eflags_c(s1, cpu_tmp4); - tcg_gen_sub_tl(cpu_T0, cpu_T0, cpu_T1); - tcg_gen_sub_tl(cpu_T0, cpu_T0, cpu_tmp4); - gen_op_st_rm_T0_A0(s1, ot, d); + if (s1->prefix & PREFIX_LOCK) { + tcg_gen_add_tl(cpu_T0, cpu_T1, cpu_tmp4); + tcg_gen_neg_tl(cpu_T0, cpu_T0); + tcg_gen_atomic_add_fetch_tl(cpu_T0, cpu_A0, cpu_T0, + s1->mem_index, ot | MO_LE); + } else { + tcg_gen_sub_tl(cpu_T0, cpu_T0, cpu_T1); + tcg_gen_sub_tl(cpu_T0, cpu_T0, cpu_tmp4); + gen_op_st_rm_T0_A0(s1, ot, d); + } gen_op_update3_cc(cpu_tmp4); set_cc_op(s1, CC_OP_SBBB + ot); break; case OP_ADDL: - tcg_gen_add_tl(cpu_T0, cpu_T0, cpu_T1); - gen_op_st_rm_T0_A0(s1, ot, d); + if (s1->prefix & PREFIX_LOCK) { + tcg_gen_atomic_add_fetch_tl(cpu_T0, cpu_A0, cpu_T1, + s1->mem_index, ot | MO_LE); + } else { + tcg_gen_add_tl(cpu_T0, cpu_T0, cpu_T1); + gen_op_st_rm_T0_A0(s1, ot, d); + } gen_op_update2_cc(); set_cc_op(s1, CC_OP_ADDB + ot); break; case OP_SUBL: - tcg_gen_mov_tl(cpu_cc_srcT, cpu_T0); - tcg_gen_sub_tl(cpu_T0, cpu_T0, cpu_T1); - gen_op_st_rm_T0_A0(s1, ot, d); + if (s1->prefix & PREFIX_LOCK) { + tcg_gen_neg_tl(cpu_T0, cpu_T1); + tcg_gen_atomic_fetch_add_tl(cpu_cc_srcT, cpu_A0, cpu_T0, + s1->mem_index, ot | MO_LE); + tcg_gen_sub_tl(cpu_T0, cpu_cc_srcT, cpu_T1); + } else { + tcg_gen_mov_tl(cpu_cc_srcT, cpu_T0); + tcg_gen_sub_tl(cpu_T0, cpu_T0, cpu_T1); + gen_op_st_rm_T0_A0(s1, ot, d); + } gen_op_update2_cc(); set_cc_op(s1, CC_OP_SUBB + ot); break; default: case OP_ANDL: - tcg_gen_and_tl(cpu_T0, cpu_T0, cpu_T1); - gen_op_st_rm_T0_A0(s1, ot, d); + if (s1->prefix & PREFIX_LOCK) { + tcg_gen_atomic_and_fetch_tl(cpu_T0, cpu_A0, cpu_T1, + s1->mem_index, ot | MO_LE); + } else { + tcg_gen_and_tl(cpu_T0, cpu_T0, cpu_T1); + gen_op_st_rm_T0_A0(s1, ot, d); + } gen_op_update1_cc(); set_cc_op(s1, CC_OP_LOGICB + ot); break; case OP_ORL: - tcg_gen_or_tl(cpu_T0, cpu_T0, cpu_T1); - gen_op_st_rm_T0_A0(s1, ot, d); + if (s1->prefix & PREFIX_LOCK) { + tcg_gen_atomic_or_fetch_tl(cpu_T0, cpu_A0, cpu_T1, + s1->mem_index, ot | MO_LE); + } else { + tcg_gen_or_tl(cpu_T0, cpu_T0, cpu_T1); + gen_op_st_rm_T0_A0(s1, ot, d); + } gen_op_update1_cc(); set_cc_op(s1, CC_OP_LOGICB + ot); break; case OP_XORL: - tcg_gen_xor_tl(cpu_T0, cpu_T0, cpu_T1); - gen_op_st_rm_T0_A0(s1, ot, d); + if (s1->prefix & PREFIX_LOCK) { + tcg_gen_atomic_xor_fetch_tl(cpu_T0, cpu_A0, cpu_T1, + s1->mem_index, ot | MO_LE); + } else { + tcg_gen_xor_tl(cpu_T0, cpu_T0, cpu_T1); + gen_op_st_rm_T0_A0(s1, ot, d); + } gen_op_update1_cc(); set_cc_op(s1, CC_OP_LOGICB + ot); break; @@ -1322,21 +1361,23 @@ static void gen_op(DisasContext *s1, int op, TCGMemOp ot, int d) /* if d == OR_TMP0, it means memory operand (address in A0) */ static void gen_inc(DisasContext *s1, TCGMemOp ot, int d, int c) { - if (d != OR_TMP0) { - gen_op_mov_v_reg(ot, cpu_T0, d); + if (s1->prefix & PREFIX_LOCK) { + tcg_gen_movi_tl(cpu_T0, c > 0 ? 1 : -1); + tcg_gen_atomic_add_fetch_tl(cpu_T0, cpu_A0, cpu_T0, + s1->mem_index, ot | MO_LE); } else { - gen_op_ld_v(s1, ot, cpu_T0, cpu_A0); + if (d != OR_TMP0) { + gen_op_mov_v_reg(ot, cpu_T0, d); + } else { + gen_op_ld_v(s1, ot, cpu_T0, cpu_A0); + } + tcg_gen_addi_tl(cpu_T0, cpu_T0, (c > 0 ? 1 : -1)); + gen_op_st_rm_T0_A0(s1, ot, d); } + gen_compute_eflags_c(s1, cpu_cc_src); - if (c > 0) { - tcg_gen_addi_tl(cpu_T0, cpu_T0, 1); - set_cc_op(s1, CC_OP_INCB + ot); - } else { - tcg_gen_addi_tl(cpu_T0, cpu_T0, -1); - set_cc_op(s1, CC_OP_DECB + ot); - } - gen_op_st_rm_T0_A0(s1, ot, d); tcg_gen_mov_tl(cpu_cc_dst, cpu_T0); + set_cc_op(s1, (c > 0 ? CC_OP_INCB : CC_OP_DECB) + ot); } static void gen_shift_flags(DisasContext *s, TCGMemOp ot, TCGv result, @@ -4495,10 +4536,6 @@ static target_ulong disas_insn(CPUX86State *env, DisasContext *s, s->aflag = aflag; s->dflag = dflag; - /* lock generation */ - if (prefixes & PREFIX_LOCK) - gen_helper_lock(); - /* now check op code */ reswitch: switch(b) { @@ -4633,10 +4670,15 @@ static target_ulong disas_insn(CPUX86State *env, DisasContext *s, rm = (modrm & 7) | REX_B(s); op = (modrm >> 3) & 7; if (mod != 3) { - if (op == 0) + if (op == 0) { s->rip_offset = insn_const_size(ot); + } gen_lea_modrm(env, s, modrm); - gen_op_ld_v(s, ot, cpu_T0, cpu_A0); + /* For those below that handle locked memory, don't load here. */ + if (!(s->prefix & PREFIX_LOCK) + || op != 2) { + gen_op_ld_v(s, ot, cpu_T0, cpu_A0); + } } else { gen_op_mov_v_reg(ot, cpu_T0, rm); } @@ -4649,19 +4691,58 @@ static target_ulong disas_insn(CPUX86State *env, DisasContext *s, set_cc_op(s, CC_OP_LOGICB + ot); break; case 2: /* not */ - tcg_gen_not_tl(cpu_T0, cpu_T0); - if (mod != 3) { - gen_op_st_v(s, ot, cpu_T0, cpu_A0); + if (s->prefix & PREFIX_LOCK) { + if (mod == 3) { + goto illegal_op; + } + tcg_gen_movi_tl(cpu_T0, ~0); + tcg_gen_atomic_xor_fetch_tl(cpu_T0, cpu_A0, cpu_T0, + s->mem_index, ot | MO_LE); } else { - gen_op_mov_reg_v(ot, rm, cpu_T0); + tcg_gen_not_tl(cpu_T0, cpu_T0); + if (mod != 3) { + gen_op_st_v(s, ot, cpu_T0, cpu_A0); + } else { + gen_op_mov_reg_v(ot, rm, cpu_T0); + } } break; case 3: /* neg */ - tcg_gen_neg_tl(cpu_T0, cpu_T0); - if (mod != 3) { - gen_op_st_v(s, ot, cpu_T0, cpu_A0); + if (s->prefix & PREFIX_LOCK) { + TCGLabel *label1; + TCGv a0, t0, t1, t2; + + if (mod == 3) { + goto illegal_op; + } + a0 = tcg_temp_local_new(); + t0 = tcg_temp_local_new(); + label1 = gen_new_label(); + + tcg_gen_mov_tl(a0, cpu_A0); + tcg_gen_mov_tl(t0, cpu_T0); + + gen_set_label(label1); + t1 = tcg_temp_new(); + t2 = tcg_temp_new(); + tcg_gen_mov_tl(t2, t0); + tcg_gen_neg_tl(t1, t0); + tcg_gen_atomic_cmpxchg_tl(t0, a0, t0, t1, + s->mem_index, ot | MO_LE); + tcg_temp_free(t1); + tcg_gen_brcond_tl(TCG_COND_NE, t0, t2, label1); + + tcg_temp_free(t2); + tcg_temp_free(a0); + tcg_gen_mov_tl(cpu_T0, t0); + tcg_temp_free(t0); } else { - gen_op_mov_reg_v(ot, rm, cpu_T0); + tcg_gen_neg_tl(cpu_T0, cpu_T0); + if (mod != 3) { + gen_op_st_v(s, ot, cpu_T0, cpu_A0); + } else { + gen_op_mov_reg_v(ot, rm, cpu_T0); + } } gen_op_update_neg_cc(); set_cc_op(s, CC_OP_SUBB + ot); @@ -5049,19 +5130,24 @@ static target_ulong disas_insn(CPUX86State *env, DisasContext *s, modrm = cpu_ldub_code(env, s->pc++); reg = ((modrm >> 3) & 7) | rex_r; mod = (modrm >> 6) & 3; + gen_op_mov_v_reg(ot, cpu_T0, reg); if (mod == 3) { rm = (modrm & 7) | REX_B(s); - gen_op_mov_v_reg(ot, cpu_T0, reg); gen_op_mov_v_reg(ot, cpu_T1, rm); tcg_gen_add_tl(cpu_T0, cpu_T0, cpu_T1); gen_op_mov_reg_v(ot, reg, cpu_T1); gen_op_mov_reg_v(ot, rm, cpu_T0); } else { gen_lea_modrm(env, s, modrm); - gen_op_mov_v_reg(ot, cpu_T0, reg); - gen_op_ld_v(s, ot, cpu_T1, cpu_A0); - tcg_gen_add_tl(cpu_T0, cpu_T0, cpu_T1); - gen_op_st_v(s, ot, cpu_T0, cpu_A0); + if (s->prefix & PREFIX_LOCK) { + tcg_gen_atomic_fetch_add_tl(cpu_T1, cpu_A0, cpu_T0, + s->mem_index, ot | MO_LE); + tcg_gen_add_tl(cpu_T0, cpu_T0, cpu_T1); + } else { + gen_op_ld_v(s, ot, cpu_T1, cpu_A0); + tcg_gen_add_tl(cpu_T0, cpu_T0, cpu_T1); + gen_op_st_v(s, ot, cpu_T0, cpu_A0); + } gen_op_mov_reg_v(ot, reg, cpu_T1); } gen_op_update2_cc(); @@ -5070,57 +5156,58 @@ static target_ulong disas_insn(CPUX86State *env, DisasContext *s, case 0x1b0: case 0x1b1: /* cmpxchg Ev, Gv */ { - TCGLabel *label1, *label2; - TCGv t0, t1, t2, a0; + TCGv oldv, newv, cmpv; ot = mo_b_d(b, dflag); modrm = cpu_ldub_code(env, s->pc++); reg = ((modrm >> 3) & 7) | rex_r; mod = (modrm >> 6) & 3; - t0 = tcg_temp_local_new(); - t1 = tcg_temp_local_new(); - t2 = tcg_temp_local_new(); - a0 = tcg_temp_local_new(); - gen_op_mov_v_reg(ot, t1, reg); - if (mod == 3) { - rm = (modrm & 7) | REX_B(s); - gen_op_mov_v_reg(ot, t0, rm); - } else { + oldv = tcg_temp_new(); + newv = tcg_temp_new(); + cmpv = tcg_temp_new(); + gen_op_mov_v_reg(ot, newv, reg); + tcg_gen_mov_tl(cmpv, cpu_regs[R_EAX]); + + if (s->prefix & PREFIX_LOCK) { + if (mod == 3) { + goto illegal_op; + } gen_lea_modrm(env, s, modrm); - tcg_gen_mov_tl(a0, cpu_A0); - gen_op_ld_v(s, ot, t0, a0); - rm = 0; /* avoid warning */ - } - label1 = gen_new_label(); - tcg_gen_mov_tl(t2, cpu_regs[R_EAX]); - gen_extu(ot, t0); - gen_extu(ot, t2); - tcg_gen_brcond_tl(TCG_COND_EQ, t2, t0, label1); - label2 = gen_new_label(); - if (mod == 3) { - gen_op_mov_reg_v(ot, R_EAX, t0); - tcg_gen_br(label2); - gen_set_label(label1); - gen_op_mov_reg_v(ot, rm, t1); + tcg_gen_atomic_cmpxchg_tl(oldv, cpu_A0, cmpv, newv, + s->mem_index, ot | MO_LE); + gen_op_mov_reg_v(ot, R_EAX, oldv); } else { - /* perform no-op store cycle like physical cpu; must be - before changing accumulator to ensure idempotency if - the store faults and the instruction is restarted */ - gen_op_st_v(s, ot, t0, a0); - gen_op_mov_reg_v(ot, R_EAX, t0); - tcg_gen_br(label2); - gen_set_label(label1); - gen_op_st_v(s, ot, t1, a0); + if (mod == 3) { + rm = (modrm & 7) | REX_B(s); + gen_op_mov_v_reg(ot, oldv, rm); + } else { + gen_lea_modrm(env, s, modrm); + gen_op_ld_v(s, ot, oldv, cpu_A0); + rm = 0; /* avoid warning */ + } + gen_extu(ot, oldv); + gen_extu(ot, cmpv); + /* store value = (old == cmp ? new : old); */ + tcg_gen_movcond_tl(TCG_COND_EQ, newv, oldv, cmpv, newv, oldv); + if (mod == 3) { + gen_op_mov_reg_v(ot, R_EAX, oldv); + gen_op_mov_reg_v(ot, rm, newv); + } else { + /* Perform an unconditional store cycle like physical cpu; + must be before changing accumulator to ensure + idempotency if the store faults and the instruction + is restarted */ + gen_op_st_v(s, ot, newv, cpu_A0); + gen_op_mov_reg_v(ot, R_EAX, oldv); + } } - gen_set_label(label2); - tcg_gen_mov_tl(cpu_cc_src, t0); - tcg_gen_mov_tl(cpu_cc_srcT, t2); - tcg_gen_sub_tl(cpu_cc_dst, t2, t0); + tcg_gen_mov_tl(cpu_cc_src, oldv); + tcg_gen_mov_tl(cpu_cc_srcT, cmpv); + tcg_gen_sub_tl(cpu_cc_dst, cmpv, oldv); set_cc_op(s, CC_OP_SUBB + ot); - tcg_temp_free(t0); - tcg_temp_free(t1); - tcg_temp_free(t2); - tcg_temp_free(a0); + tcg_temp_free(oldv); + tcg_temp_free(newv); + tcg_temp_free(cmpv); } break; case 0x1c7: /* cmpxchg8b */ @@ -5133,14 +5220,22 @@ static target_ulong disas_insn(CPUX86State *env, DisasContext *s, if (!(s->cpuid_ext_features & CPUID_EXT_CX16)) goto illegal_op; gen_lea_modrm(env, s, modrm); - gen_helper_cmpxchg16b(cpu_env, cpu_A0); + if ((s->prefix & PREFIX_LOCK) && parallel_cpus) { + gen_helper_cmpxchg16b(cpu_env, cpu_A0); + } else { + gen_helper_cmpxchg16b_unlocked(cpu_env, cpu_A0); + } } else #endif { if (!(s->cpuid_features & CPUID_CX8)) goto illegal_op; gen_lea_modrm(env, s, modrm); - gen_helper_cmpxchg8b(cpu_env, cpu_A0); + if ((s->prefix & PREFIX_LOCK) && parallel_cpus) { + gen_helper_cmpxchg8b(cpu_env, cpu_A0); + } else { + gen_helper_cmpxchg8b_unlocked(cpu_env, cpu_A0); + } } set_cc_op(s, CC_OP_EFLAGS); break; @@ -5372,7 +5467,8 @@ static target_ulong disas_insn(CPUX86State *env, DisasContext *s, { AddressParts a = gen_lea_modrm_0(env, s, modrm); TCGv ea = gen_lea_modrm_1(a); - gen_op_mov_reg_v(dflag, reg, ea); + gen_lea_v_seg(s, s->aflag, ea, -1, -1); + gen_op_mov_reg_v(dflag, reg, cpu_A0); } break; @@ -5464,12 +5560,8 @@ static target_ulong disas_insn(CPUX86State *env, DisasContext *s, gen_lea_modrm(env, s, modrm); gen_op_mov_v_reg(ot, cpu_T0, reg); /* for xchg, lock is implicit */ - if (!(prefixes & PREFIX_LOCK)) - gen_helper_lock(); - gen_op_ld_v(s, ot, cpu_T1, cpu_A0); - gen_op_st_v(s, ot, cpu_T0, cpu_A0); - if (!(prefixes & PREFIX_LOCK)) - gen_helper_unlock(); + tcg_gen_atomic_xchg_tl(cpu_T1, cpu_A0, cpu_T0, + s->mem_index, ot | MO_LE); gen_op_mov_reg_v(ot, reg, cpu_T1); } break; @@ -6555,7 +6647,9 @@ static target_ulong disas_insn(CPUX86State *env, DisasContext *s, if (mod != 3) { s->rip_offset = 1; gen_lea_modrm(env, s, modrm); - gen_op_ld_v(s, ot, cpu_T0, cpu_A0); + if (!(s->prefix & PREFIX_LOCK)) { + gen_op_ld_v(s, ot, cpu_T0, cpu_A0); + } } else { gen_op_mov_v_reg(ot, cpu_T0, rm); } @@ -6585,44 +6679,69 @@ static target_ulong disas_insn(CPUX86State *env, DisasContext *s, rm = (modrm & 7) | REX_B(s); gen_op_mov_v_reg(MO_32, cpu_T1, reg); if (mod != 3) { - gen_lea_modrm(env, s, modrm); + AddressParts a = gen_lea_modrm_0(env, s, modrm); /* specific case: we need to add a displacement */ gen_exts(ot, cpu_T1); tcg_gen_sari_tl(cpu_tmp0, cpu_T1, 3 + ot); tcg_gen_shli_tl(cpu_tmp0, cpu_tmp0, ot); - tcg_gen_add_tl(cpu_A0, cpu_A0, cpu_tmp0); - gen_op_ld_v(s, ot, cpu_T0, cpu_A0); + tcg_gen_add_tl(cpu_A0, gen_lea_modrm_1(a), cpu_tmp0); + gen_lea_v_seg(s, s->aflag, cpu_A0, a.def_seg, s->override); + if (!(s->prefix & PREFIX_LOCK)) { + gen_op_ld_v(s, ot, cpu_T0, cpu_A0); + } } else { gen_op_mov_v_reg(ot, cpu_T0, rm); } bt_op: tcg_gen_andi_tl(cpu_T1, cpu_T1, (1 << (3 + ot)) - 1); - tcg_gen_shr_tl(cpu_tmp4, cpu_T0, cpu_T1); - switch(op) { - case 0: - break; - case 1: - tcg_gen_movi_tl(cpu_tmp0, 1); - tcg_gen_shl_tl(cpu_tmp0, cpu_tmp0, cpu_T1); - tcg_gen_or_tl(cpu_T0, cpu_T0, cpu_tmp0); - break; - case 2: - tcg_gen_movi_tl(cpu_tmp0, 1); - tcg_gen_shl_tl(cpu_tmp0, cpu_tmp0, cpu_T1); - tcg_gen_andc_tl(cpu_T0, cpu_T0, cpu_tmp0); - break; - default: - case 3: - tcg_gen_movi_tl(cpu_tmp0, 1); - tcg_gen_shl_tl(cpu_tmp0, cpu_tmp0, cpu_T1); - tcg_gen_xor_tl(cpu_T0, cpu_T0, cpu_tmp0); - break; - } - if (op != 0) { - if (mod != 3) { - gen_op_st_v(s, ot, cpu_T0, cpu_A0); - } else { - gen_op_mov_reg_v(ot, rm, cpu_T0); + tcg_gen_movi_tl(cpu_tmp0, 1); + tcg_gen_shl_tl(cpu_tmp0, cpu_tmp0, cpu_T1); + if (s->prefix & PREFIX_LOCK) { + switch (op) { + case 0: /* bt */ + /* Needs no atomic ops; we surpressed the normal + memory load for LOCK above so do it now. */ + gen_op_ld_v(s, ot, cpu_T0, cpu_A0); + break; + case 1: /* bts */ + tcg_gen_atomic_fetch_or_tl(cpu_T0, cpu_A0, cpu_tmp0, + s->mem_index, ot | MO_LE); + break; + case 2: /* btr */ + tcg_gen_not_tl(cpu_tmp0, cpu_tmp0); + tcg_gen_atomic_fetch_and_tl(cpu_T0, cpu_A0, cpu_tmp0, + s->mem_index, ot | MO_LE); + break; + default: + case 3: /* btc */ + tcg_gen_atomic_fetch_xor_tl(cpu_T0, cpu_A0, cpu_tmp0, + s->mem_index, ot | MO_LE); + break; + } + tcg_gen_shr_tl(cpu_tmp4, cpu_T0, cpu_T1); + } else { + tcg_gen_shr_tl(cpu_tmp4, cpu_T0, cpu_T1); + switch (op) { + case 0: /* bt */ + /* Data already loaded; nothing to do. */ + break; + case 1: /* bts */ + tcg_gen_or_tl(cpu_T0, cpu_T0, cpu_tmp0); + break; + case 2: /* btr */ + tcg_gen_andc_tl(cpu_T0, cpu_T0, cpu_tmp0); + break; + default: + case 3: /* btc */ + tcg_gen_xor_tl(cpu_T0, cpu_T0, cpu_tmp0); + break; + } + if (op != 0) { + if (mod != 3) { + gen_op_st_v(s, ot, cpu_T0, cpu_A0); + } else { + gen_op_mov_reg_v(ot, rm, cpu_T0); + } } } @@ -8088,20 +8207,11 @@ static target_ulong disas_insn(CPUX86State *env, DisasContext *s, default: goto unknown_op; } - /* lock generation */ - if (s->prefix & PREFIX_LOCK) - gen_helper_unlock(); return s->pc; illegal_op: - if (s->prefix & PREFIX_LOCK) - gen_helper_unlock(); - /* XXX: ensure that no lock was generated */ gen_illegal_opcode(s); return s->pc; unknown_op: - if (s->prefix & PREFIX_LOCK) - gen_helper_unlock(); - /* XXX: ensure that no lock was generated */ gen_unknown_opcode(env, s); return s->pc; } @@ -8193,8 +8303,6 @@ void tcg_x86_init(void) offsetof(CPUX86State, bnd_regs[i].ub), bnd_regu_names[i]); } - - helper_lock_init(); } /* generate intermediate code for basic block 'tb'. */ diff --git a/target-lm32/cpu.c b/target-lm32/cpu.c index a783d46..8d939a7 100644 --- a/target-lm32/cpu.c +++ b/target-lm32/cpu.c @@ -144,6 +144,13 @@ static void lm32_cpu_realizefn(DeviceState *dev, Error **errp) { CPUState *cs = CPU(dev); LM32CPUClass *lcc = LM32_CPU_GET_CLASS(dev); + Error *local_err = NULL; + + cpu_exec_realizefn(cs, &local_err); + if (local_err != NULL) { + error_propagate(errp, local_err); + return; + } cpu_reset(cs); @@ -160,7 +167,6 @@ static void lm32_cpu_initfn(Object *obj) static bool tcg_initialized; cs->env_ptr = env; - cpu_exec_init(cs, &error_abort); env->flags = 0; @@ -285,13 +291,6 @@ static void lm32_cpu_class_init(ObjectClass *oc, void *data) cc->gdb_stop_before_watchpoint = true; cc->debug_excp_handler = lm32_debug_excp_handler; cc->disas_set_info = lm32_cpu_disas_set_info; - - /* - * Reason: lm32_cpu_initfn() calls cpu_exec_init(), which saves - * the object in cpus -> dangling pointer after final - * object_unref(). - */ - dc->cannot_destroy_with_object_finalize_yet = true; } static void lm32_register_cpu_type(const LM32CPUInfo *info) diff --git a/target-m68k/cpu.c b/target-m68k/cpu.c index 116b784..ba17480 100644 --- a/target-m68k/cpu.c +++ b/target-m68k/cpu.c @@ -58,15 +58,20 @@ static void m68k_cpu_reset(CPUState *s) #endif m68k_switch_sp(env); /* ??? FP regs should be initialized to NaN. */ - env->cc_op = CC_OP_FLAGS; + cpu_m68k_set_ccr(env, 0); /* TODO: We should set PC from the interrupt vector. */ env->pc = 0; tlb_flush(s, 1); } -static void m68k_cpu_disas_set_info(CPUState *cpu, disassemble_info *info) +static void m68k_cpu_disas_set_info(CPUState *s, disassemble_info *info) { + M68kCPU *cpu = M68K_CPU(s); + CPUM68KState *env = &cpu->env; info->print_insn = print_insn_m68k; + if (m68k_feature(env, M68K_FEATURE_M68000)) { + info->mach = bfd_mach_m68040; + } } /* CPU models */ @@ -98,6 +103,57 @@ static void m5206_cpu_initfn(Object *obj) m68k_set_feature(env, M68K_FEATURE_CF_ISA_A); } +static void m68000_cpu_initfn(Object *obj) +{ + M68kCPU *cpu = M68K_CPU(obj); + CPUM68KState *env = &cpu->env; + + m68k_set_feature(env, M68K_FEATURE_M68000); + m68k_set_feature(env, M68K_FEATURE_USP); + m68k_set_feature(env, M68K_FEATURE_WORD_INDEX); +} + +static void m68020_cpu_initfn(Object *obj) +{ + M68kCPU *cpu = M68K_CPU(obj); + CPUM68KState *env = &cpu->env; + + m68k_set_feature(env, M68K_FEATURE_M68000); + m68k_set_feature(env, M68K_FEATURE_USP); + m68k_set_feature(env, M68K_FEATURE_WORD_INDEX); + m68k_set_feature(env, M68K_FEATURE_QUAD_MULDIV); + m68k_set_feature(env, M68K_FEATURE_BRAL); + m68k_set_feature(env, M68K_FEATURE_BCCL); + m68k_set_feature(env, M68K_FEATURE_BITFIELD); + m68k_set_feature(env, M68K_FEATURE_EXT_FULL); + m68k_set_feature(env, M68K_FEATURE_SCALED_INDEX); + m68k_set_feature(env, M68K_FEATURE_LONG_MULDIV); + m68k_set_feature(env, M68K_FEATURE_FPU); + m68k_set_feature(env, M68K_FEATURE_CAS); + m68k_set_feature(env, M68K_FEATURE_BKPT); +} +#define m68030_cpu_initfn m68020_cpu_initfn +#define m68040_cpu_initfn m68020_cpu_initfn + +static void m68060_cpu_initfn(Object *obj) +{ + M68kCPU *cpu = M68K_CPU(obj); + CPUM68KState *env = &cpu->env; + + m68k_set_feature(env, M68K_FEATURE_M68000); + m68k_set_feature(env, M68K_FEATURE_USP); + m68k_set_feature(env, M68K_FEATURE_WORD_INDEX); + m68k_set_feature(env, M68K_FEATURE_BRAL); + m68k_set_feature(env, M68K_FEATURE_BCCL); + m68k_set_feature(env, M68K_FEATURE_BITFIELD); + m68k_set_feature(env, M68K_FEATURE_EXT_FULL); + m68k_set_feature(env, M68K_FEATURE_SCALED_INDEX); + m68k_set_feature(env, M68K_FEATURE_LONG_MULDIV); + m68k_set_feature(env, M68K_FEATURE_FPU); + m68k_set_feature(env, M68K_FEATURE_CAS); + m68k_set_feature(env, M68K_FEATURE_BKPT); +} + static void m5208_cpu_initfn(Object *obj) { M68kCPU *cpu = M68K_CPU(obj); @@ -148,6 +204,11 @@ typedef struct M68kCPUInfo { } M68kCPUInfo; static const M68kCPUInfo m68k_cpus[] = { + { .name = "m68000", .instance_init = m68000_cpu_initfn }, + { .name = "m68020", .instance_init = m68020_cpu_initfn }, + { .name = "m68030", .instance_init = m68030_cpu_initfn }, + { .name = "m68040", .instance_init = m68040_cpu_initfn }, + { .name = "m68060", .instance_init = m68060_cpu_initfn }, { .name = "m5206", .instance_init = m5206_cpu_initfn }, { .name = "m5208", .instance_init = m5208_cpu_initfn }, { .name = "cfv4e", .instance_init = cfv4e_cpu_initfn }, @@ -159,6 +220,13 @@ static void m68k_cpu_realizefn(DeviceState *dev, Error **errp) CPUState *cs = CPU(dev); M68kCPU *cpu = M68K_CPU(dev); M68kCPUClass *mcc = M68K_CPU_GET_CLASS(dev); + Error *local_err = NULL; + + cpu_exec_realizefn(cs, &local_err); + if (local_err != NULL) { + error_propagate(errp, local_err); + return; + } m68k_cpu_init_gdb(cpu); @@ -176,7 +244,6 @@ static void m68k_cpu_initfn(Object *obj) static bool inited; cs->env_ptr = env; - cpu_exec_init(cs, &error_abort); if (tcg_enabled() && !inited) { inited = true; @@ -214,21 +281,12 @@ static void m68k_cpu_class_init(ObjectClass *c, void *data) #else cc->get_phys_page_debug = m68k_cpu_get_phys_page_debug; #endif - cc->cpu_exec_enter = m68k_cpu_exec_enter; - cc->cpu_exec_exit = m68k_cpu_exec_exit; cc->disas_set_info = m68k_cpu_disas_set_info; cc->gdb_num_core_regs = 18; cc->gdb_core_xml_file = "cf-core.xml"; dc->vmsd = &vmstate_m68k_cpu; - - /* - * Reason: m68k_cpu_initfn() calls cpu_exec_init(), which saves - * the object in cpus -> dangling pointer after final - * object_unref(). - */ - dc->cannot_destroy_with_object_finalize_yet = true; } static void register_cpu_type(const M68kCPUInfo *info) diff --git a/target-m68k/cpu.h b/target-m68k/cpu.h index 471f490..48c5b81 100644 --- a/target-m68k/cpu.h +++ b/target-m68k/cpu.h @@ -30,6 +30,14 @@ #include "cpu-qom.h" #include "fpu/softfloat.h" +#define OS_BYTE 0 +#define OS_WORD 1 +#define OS_LONG 2 +#define OS_SINGLE 3 +#define OS_DOUBLE 4 +#define OS_EXTENDED 5 +#define OS_PACKED 6 + #define MAX_QREGS 32 #define EXCP_ACCESS 2 /* Access (MMU) error. */ @@ -53,6 +61,7 @@ #define EXCP_HALT_INSN 0x101 #define NB_MMU_MODES 2 +#define TARGET_INSN_START_EXTRA_WORDS 1 typedef struct CPUM68KState { uint32_t dregs[8]; @@ -66,9 +75,11 @@ typedef struct CPUM68KState { /* Condition flags. */ uint32_t cc_op; - uint32_t cc_dest; - uint32_t cc_src; - uint32_t cc_x; + uint32_t cc_x; /* always 0/1 */ + uint32_t cc_n; /* in bit 31 (i.e. negative) */ + uint32_t cc_v; /* in bit 31, unused, or computed from cc_n and cc_v */ + uint32_t cc_c; /* either 0/1, unused, or computed from cc_n and cc_v */ + uint32_t cc_z; /* == 0 or unused */ float64 fregs[8]; float64 fp_result; @@ -141,9 +152,6 @@ hwaddr m68k_cpu_get_phys_page_debug(CPUState *cpu, vaddr addr); int m68k_cpu_gdb_read_register(CPUState *cpu, uint8_t *buf, int reg); int m68k_cpu_gdb_write_register(CPUState *cpu, uint8_t *buf, int reg); -void m68k_cpu_exec_enter(CPUState *cs); -void m68k_cpu_exec_exit(CPUState *cs); - void m68k_tcg_init(void); void m68k_cpu_init_gdb(M68kCPU *cpu); M68kCPU *cpu_m68k_init(const char *cpu_model); @@ -152,7 +160,8 @@ M68kCPU *cpu_m68k_init(const char *cpu_model); is returned if the signal was handled by the virtual CPU. */ int cpu_m68k_signal_handler(int host_signum, void *pinfo, void *puc); -void cpu_m68k_flush_flags(CPUM68KState *, int); +uint32_t cpu_m68k_get_ccr(CPUM68KState *env); +void cpu_m68k_set_ccr(CPUM68KState *env, uint32_t); /* Instead of computing the condition codes after each m68k instruction, @@ -162,18 +171,25 @@ void cpu_m68k_flush_flags(CPUM68KState *, int); * using this information. Condition codes are not generated if they * are only needed for conditional branches. */ -enum { - CC_OP_DYNAMIC, /* Use env->cc_op */ - CC_OP_FLAGS, /* CC_DEST = CVZN, CC_SRC = unused */ - CC_OP_LOGIC, /* CC_DEST = result, CC_SRC = unused */ - CC_OP_ADD, /* CC_DEST = result, CC_SRC = source */ - CC_OP_SUB, /* CC_DEST = result, CC_SRC = source */ - CC_OP_CMPB, /* CC_DEST = result, CC_SRC = source */ - CC_OP_CMPW, /* CC_DEST = result, CC_SRC = source */ - CC_OP_ADDX, /* CC_DEST = result, CC_SRC = source */ - CC_OP_SUBX, /* CC_DEST = result, CC_SRC = source */ - CC_OP_SHIFT, /* CC_DEST = result, CC_SRC = carry */ -}; +typedef enum { + /* Translator only -- use env->cc_op. */ + CC_OP_DYNAMIC = -1, + + /* Each flag bit computed into cc_[xcnvz]. */ + CC_OP_FLAGS, + + /* X in cc_x, C = X, N in cc_n, Z in cc_n, V via cc_n/cc_v. */ + CC_OP_ADD, + CC_OP_SUB, + + /* X in cc_x, {N,Z,C,V} via cc_n/cc_v. */ + CC_OP_CMP, + + /* X in cc_x, C = 0, V = 0, N in cc_n, Z in cc_n. */ + CC_OP_LOGIC, + + CC_OP_NB +} CCOp; #define CCF_C 0x01 #define CCF_V 0x02 @@ -215,6 +231,7 @@ void do_m68k_semihosting(CPUM68KState *env, int nr); ISA revisions mentioned. */ enum m68k_features { + M68K_FEATURE_M68000, M68K_FEATURE_CF_ISA_A, M68K_FEATURE_CF_ISA_B, /* (ISA B or C). */ M68K_FEATURE_CF_ISA_APLUSC, /* BIT/BITREV, FF1, STRLDSR (ISA A+ or C). */ @@ -225,7 +242,15 @@ enum m68k_features { M68K_FEATURE_CF_EMAC_B, /* Revision B EMAC (dual accumulate). */ M68K_FEATURE_USP, /* User Stack Pointer. (ISA A+, B or C). */ M68K_FEATURE_EXT_FULL, /* 68020+ full extension word. */ - M68K_FEATURE_WORD_INDEX /* word sized address index registers. */ + M68K_FEATURE_WORD_INDEX, /* word sized address index registers. */ + M68K_FEATURE_SCALED_INDEX, /* scaled address index registers. */ + M68K_FEATURE_LONG_MULDIV, /* 32 bit multiply/divide. */ + M68K_FEATURE_QUAD_MULDIV, /* 64 bit multiply/divide. */ + M68K_FEATURE_BCCL, /* Long conditional branches. */ + M68K_FEATURE_BITFIELD, /* Bit field insns. */ + M68K_FEATURE_FPU, + M68K_FEATURE_CAS, + M68K_FEATURE_BKPT, }; static inline int m68k_feature(CPUM68KState *env, int feature) @@ -238,8 +263,11 @@ void m68k_cpu_list(FILE *f, fprintf_function cpu_fprintf); void register_m68k_insns (CPUM68KState *env); #ifdef CONFIG_USER_ONLY -/* Linux uses 8k pages. */ -#define TARGET_PAGE_BITS 13 +/* Coldfire Linux uses 8k pages + * and m68k linux uses 4k pages + * use the smaller one + */ +#define TARGET_PAGE_BITS 12 #else /* Smallest TLB entry size is 1k. */ #define TARGET_PAGE_BITS 10 diff --git a/target-m68k/helper.c b/target-m68k/helper.c index 89bbe6d..094a7e5 100644 --- a/target-m68k/helper.c +++ b/target-m68k/helper.c @@ -132,87 +132,6 @@ void m68k_cpu_init_gdb(M68kCPU *cpu) /* TODO: Add [E]MAC registers. */ } -void cpu_m68k_flush_flags(CPUM68KState *env, int cc_op) -{ - M68kCPU *cpu = m68k_env_get_cpu(env); - int flags; - uint32_t src; - uint32_t dest; - uint32_t tmp; - -#define HIGHBIT 0x80000000u - -#define SET_NZ(x) do { \ - if ((x) == 0) \ - flags |= CCF_Z; \ - else if ((int32_t)(x) < 0) \ - flags |= CCF_N; \ - } while (0) - -#define SET_FLAGS_SUB(type, utype) do { \ - SET_NZ((type)dest); \ - tmp = dest + src; \ - if ((utype) tmp < (utype) src) \ - flags |= CCF_C; \ - if ((1u << (sizeof(type) * 8 - 1)) & (tmp ^ dest) & (tmp ^ src)) \ - flags |= CCF_V; \ - } while (0) - - flags = 0; - src = env->cc_src; - dest = env->cc_dest; - switch (cc_op) { - case CC_OP_FLAGS: - flags = dest; - break; - case CC_OP_LOGIC: - SET_NZ(dest); - break; - case CC_OP_ADD: - SET_NZ(dest); - if (dest < src) - flags |= CCF_C; - tmp = dest - src; - if (HIGHBIT & (src ^ dest) & ~(tmp ^ src)) - flags |= CCF_V; - break; - case CC_OP_SUB: - SET_FLAGS_SUB(int32_t, uint32_t); - break; - case CC_OP_CMPB: - SET_FLAGS_SUB(int8_t, uint8_t); - break; - case CC_OP_CMPW: - SET_FLAGS_SUB(int16_t, uint16_t); - break; - case CC_OP_ADDX: - SET_NZ(dest); - if (dest <= src) - flags |= CCF_C; - tmp = dest - src - 1; - if (HIGHBIT & (src ^ dest) & ~(tmp ^ src)) - flags |= CCF_V; - break; - case CC_OP_SUBX: - SET_NZ(dest); - tmp = dest + src + 1; - if (tmp <= src) - flags |= CCF_C; - if (HIGHBIT & (tmp ^ dest) & (tmp ^ src)) - flags |= CCF_V; - break; - case CC_OP_SHIFT: - SET_NZ(dest); - if (src) - flags |= CCF_C; - break; - default: - cpu_abort(CPU(cpu), "Bad CC_OP %d", cc_op); - } - env->cc_op = CC_OP_FLAGS; - env->cc_dest = flags; -} - void HELPER(movec)(CPUM68KState *env, uint32_t reg, uint32_t val) { M68kCPU *cpu = m68k_env_get_cpu(env); @@ -349,140 +268,111 @@ uint32_t HELPER(ff1)(uint32_t x) return n; } -uint32_t HELPER(sats)(uint32_t val, uint32_t ccr) +uint32_t HELPER(sats)(uint32_t val, uint32_t v) { /* The result has the opposite sign to the original value. */ - if (ccr & CCF_V) + if ((int32_t)v < 0) { val = (((int32_t)val) >> 31) ^ SIGNBIT; + } return val; } uint32_t HELPER(subx_cc)(CPUM68KState *env, uint32_t op1, uint32_t op2) { - uint32_t res; - uint32_t old_flags; + uint32_t res, new_x; - old_flags = env->cc_dest; if (env->cc_x) { - env->cc_x = (op1 <= op2); - env->cc_op = CC_OP_SUBX; + new_x = (op1 <= op2); res = op1 - (op2 + 1); } else { - env->cc_x = (op1 < op2); - env->cc_op = CC_OP_SUB; + new_x = (op1 < op2); res = op1 - op2; } - env->cc_dest = res; - env->cc_src = op2; - cpu_m68k_flush_flags(env, env->cc_op); - /* !Z is sticky. */ - env->cc_dest &= (old_flags | ~CCF_Z); + env->cc_x = new_x; + env->cc_c = new_x; + env->cc_n = res; + env->cc_z |= res; /* !Z is sticky */ + env->cc_v = (res ^ op1) & (op1 ^ op2); + return res; } uint32_t HELPER(addx_cc)(CPUM68KState *env, uint32_t op1, uint32_t op2) { - uint32_t res; - uint32_t old_flags; + uint32_t res, new_x; - old_flags = env->cc_dest; if (env->cc_x) { res = op1 + op2 + 1; - env->cc_x = (res <= op2); - env->cc_op = CC_OP_ADDX; + new_x = (res <= op2); } else { res = op1 + op2; - env->cc_x = (res < op2); - env->cc_op = CC_OP_ADD; + new_x = (res < op2); } - env->cc_dest = res; - env->cc_src = op2; - cpu_m68k_flush_flags(env, env->cc_op); - /* !Z is sticky. */ - env->cc_dest &= (old_flags | ~CCF_Z); - return res; -} + env->cc_x = new_x; + env->cc_c = new_x; + env->cc_n = res; + env->cc_z |= res; /* !Z is sticky. */ + env->cc_v = (res ^ op1) & ~(op1 ^ op2); -uint32_t HELPER(xflag_lt)(uint32_t a, uint32_t b) -{ - return a < b; + return res; } void HELPER(set_sr)(CPUM68KState *env, uint32_t val) { - env->sr = val & 0xffff; + env->sr = val & 0xffe0; + cpu_m68k_set_ccr(env, val); m68k_switch_sp(env); } uint32_t HELPER(shl_cc)(CPUM68KState *env, uint32_t val, uint32_t shift) { - uint32_t result; - uint32_t cf; + uint64_t result; shift &= 63; - if (shift == 0) { - result = val; - cf = env->cc_src & CCF_C; - } else if (shift < 32) { - result = val << shift; - cf = (val >> (32 - shift)) & 1; - } else if (shift == 32) { - result = 0; - cf = val & 1; - } else /* shift > 32 */ { - result = 0; - cf = 0; - } - env->cc_src = cf; - env->cc_x = (cf != 0); - env->cc_dest = result; + result = (uint64_t)val << shift; + + env->cc_c = (result >> 32) & 1; + env->cc_n = result; + env->cc_z = result; + env->cc_v = 0; + env->cc_x = shift ? env->cc_c : env->cc_x; + return result; } uint32_t HELPER(shr_cc)(CPUM68KState *env, uint32_t val, uint32_t shift) { + uint64_t temp; uint32_t result; - uint32_t cf; shift &= 63; - if (shift == 0) { - result = val; - cf = env->cc_src & CCF_C; - } else if (shift < 32) { - result = val >> shift; - cf = (val >> (shift - 1)) & 1; - } else if (shift == 32) { - result = 0; - cf = val >> 31; - } else /* shift > 32 */ { - result = 0; - cf = 0; - } - env->cc_src = cf; - env->cc_x = (cf != 0); - env->cc_dest = result; + temp = (uint64_t)val << 32 >> shift; + result = temp >> 32; + + env->cc_c = (temp >> 31) & 1; + env->cc_n = result; + env->cc_z = result; + env->cc_v = 0; + env->cc_x = shift ? env->cc_c : env->cc_x; + return result; } uint32_t HELPER(sar_cc)(CPUM68KState *env, uint32_t val, uint32_t shift) { + uint64_t temp; uint32_t result; - uint32_t cf; shift &= 63; - if (shift == 0) { - result = val; - cf = (env->cc_src & CCF_C) != 0; - } else if (shift < 32) { - result = (int32_t)val >> shift; - cf = (val >> (shift - 1)) & 1; - } else /* shift >= 32 */ { - result = (int32_t)val >> 31; - cf = val >> 31; - } - env->cc_src = cf; - env->cc_x = cf; - env->cc_dest = result; + temp = (int64_t)val << 32 >> shift; + result = temp >> 32; + + env->cc_c = (temp >> 31) & 1; + env->cc_n = result; + env->cc_z = result; + env->cc_v = result ^ val; + env->cc_x = shift ? env->cc_c : env->cc_x; + return result; } @@ -734,9 +624,92 @@ void HELPER(mac_set_flags)(CPUM68KState *env, uint32_t acc) } } + +#define COMPUTE_CCR(op, x, n, z, v, c) { \ + switch (op) { \ + case CC_OP_FLAGS: \ + /* Everything in place. */ \ + break; \ + case CC_OP_ADD: \ + res = n; \ + src2 = v; \ + src1 = res - src2; \ + c = x; \ + z = n; \ + v = (res ^ src1) & ~(src1 ^ src2); \ + break; \ + case CC_OP_SUB: \ + res = n; \ + src2 = v; \ + src1 = res + src2; \ + c = x; \ + z = n; \ + v = (res ^ src1) & (src1 ^ src2); \ + break; \ + case CC_OP_CMP: \ + src1 = n; \ + src2 = v; \ + res = src1 - src2; \ + n = res; \ + z = res; \ + c = src1 < src2; \ + v = (res ^ src1) & (src1 ^ src2); \ + break; \ + case CC_OP_LOGIC: \ + c = v = 0; \ + z = n; \ + break; \ + default: \ + cpu_abort(CPU(m68k_env_get_cpu(env)), "Bad CC_OP %d", op); \ + } \ +} while (0) + +uint32_t cpu_m68k_get_ccr(CPUM68KState *env) +{ + uint32_t x, c, n, z, v; + uint32_t res, src1, src2; + + x = env->cc_x; + c = env->cc_c; + n = env->cc_n; + z = env->cc_z; + v = env->cc_v; + + COMPUTE_CCR(env->cc_op, x, n, z, v, c); + + n = n >> 31; + v = v >> 31; + z = (z == 0); + + return x * CCF_X + n * CCF_N + z * CCF_Z + v * CCF_V + c * CCF_C; +} + +uint32_t HELPER(get_ccr)(CPUM68KState *env) +{ + return cpu_m68k_get_ccr(env); +} + +void cpu_m68k_set_ccr(CPUM68KState *env, uint32_t ccr) +{ + env->cc_x = (ccr & CCF_X ? 1 : 0); + env->cc_n = (ccr & CCF_N ? -1 : 0); + env->cc_z = (ccr & CCF_Z ? 0 : 1); + env->cc_v = (ccr & CCF_V ? -1 : 0); + env->cc_c = (ccr & CCF_C ? 1 : 0); + env->cc_op = CC_OP_FLAGS; +} + +void HELPER(set_ccr)(CPUM68KState *env, uint32_t ccr) +{ + cpu_m68k_set_ccr(env, ccr); +} + void HELPER(flush_flags)(CPUM68KState *env, uint32_t cc_op) { - cpu_m68k_flush_flags(env, cc_op); + uint32_t res, src1, src2; + + COMPUTE_CCR(cc_op, env->cc_x, env->cc_n, env->cc_z, env->cc_v, env->cc_c); + env->cc_op = CC_OP_FLAGS; } uint32_t HELPER(get_macf)(CPUM68KState *env, uint64_t val) @@ -866,23 +839,3 @@ void HELPER(set_mac_extu)(CPUM68KState *env, uint32_t val, uint32_t acc) res |= (uint64_t)(val & 0xffff0000) << 16; env->macc[acc + 1] = res; } - -void m68k_cpu_exec_enter(CPUState *cs) -{ - M68kCPU *cpu = M68K_CPU(cs); - CPUM68KState *env = &cpu->env; - - env->cc_op = CC_OP_FLAGS; - env->cc_dest = env->sr & 0xf; - env->cc_x = (env->sr >> 4) & 1; -} - -void m68k_cpu_exec_exit(CPUState *cs) -{ - M68kCPU *cpu = M68K_CPU(cs); - CPUM68KState *env = &cpu->env; - - cpu_m68k_flush_flags(env, env->cc_op); - env->cc_op = CC_OP_FLAGS; - env->sr = (env->sr & 0xffe0) | env->cc_dest | (env->cc_x << 4); -} diff --git a/target-m68k/helper.h b/target-m68k/helper.h index f4e5fdf..c868148 100644 --- a/target-m68k/helper.h +++ b/target-m68k/helper.h @@ -1,6 +1,6 @@ DEF_HELPER_1(bitrev, i32, i32) DEF_HELPER_1(ff1, i32, i32) -DEF_HELPER_2(sats, i32, i32, i32) +DEF_HELPER_FLAGS_2(sats, TCG_CALL_NO_RWG_SE, i32, i32, i32) DEF_HELPER_2(divu, void, env, i32) DEF_HELPER_2(divs, void, env, i32) DEF_HELPER_3(addx_cc, i32, env, i32, i32) @@ -8,7 +8,6 @@ DEF_HELPER_3(subx_cc, i32, env, i32, i32) DEF_HELPER_3(shl_cc, i32, env, i32, i32) DEF_HELPER_3(shr_cc, i32, env, i32, i32) DEF_HELPER_3(sar_cc, i32, env, i32, i32) -DEF_HELPER_2(xflag_lt, i32, i32, i32) DEF_HELPER_2(set_sr, void, env, i32) DEF_HELPER_3(movec, void, env, i32, i32) @@ -47,4 +46,6 @@ DEF_HELPER_3(set_mac_exts, void, env, i32, i32) DEF_HELPER_3(set_mac_extu, void, env, i32, i32) DEF_HELPER_2(flush_flags, void, env, i32) +DEF_HELPER_2(set_ccr, void, env, i32) +DEF_HELPER_FLAGS_1(get_ccr, TCG_CALL_NO_WG_SE, i32, env) DEF_HELPER_2(raise_exception, void, env, i32) diff --git a/target-m68k/op_helper.c b/target-m68k/op_helper.c index e41ae46..48e02e4 100644 --- a/target-m68k/op_helper.c +++ b/target-m68k/op_helper.c @@ -63,9 +63,9 @@ static void do_rte(CPUM68KState *env) fmt = cpu_ldl_kernel(env, sp); env->pc = cpu_ldl_kernel(env, sp + 4); sp |= (fmt >> 28) & 3; - env->sr = fmt & 0xffff; env->aregs[7] = sp + 8; - m68k_switch_sp(env); + + helper_set_sr(env, fmt); } static void do_interrupt_all(CPUM68KState *env, int is_hw) @@ -112,6 +112,7 @@ static void do_interrupt_all(CPUM68KState *env, int is_hw) fmt |= 0x40000000; fmt |= vector << 16; fmt |= env->sr; + fmt |= cpu_m68k_get_ccr(env); env->sr |= SR_S; if (is_hw) { @@ -184,7 +185,6 @@ void HELPER(divu)(CPUM68KState *env, uint32_t word) uint32_t den; uint32_t quot; uint32_t rem; - uint32_t flags; num = env->div1; den = env->div2; @@ -194,16 +194,14 @@ void HELPER(divu)(CPUM68KState *env, uint32_t word) } quot = num / den; rem = num % den; - flags = 0; - if (word && quot > 0xffff) - flags |= CCF_V; - if (quot == 0) - flags |= CCF_Z; - else if ((int32_t)quot < 0) - flags |= CCF_N; + + env->cc_v = (word && quot > 0xffff ? -1 : 0); + env->cc_z = quot; + env->cc_n = quot; + env->cc_c = 0; + env->div1 = quot; env->div2 = rem; - env->cc_dest = flags; } void HELPER(divs)(CPUM68KState *env, uint32_t word) @@ -212,7 +210,6 @@ void HELPER(divs)(CPUM68KState *env, uint32_t word) int32_t den; int32_t quot; int32_t rem; - int32_t flags; num = env->div1; den = env->div2; @@ -221,14 +218,12 @@ void HELPER(divs)(CPUM68KState *env, uint32_t word) } quot = num / den; rem = num % den; - flags = 0; - if (word && quot != (int16_t)quot) - flags |= CCF_V; - if (quot == 0) - flags |= CCF_Z; - else if (quot < 0) - flags |= CCF_N; + + env->cc_v = (word && quot != (int16_t)quot ? -1 : 0); + env->cc_z = quot; + env->cc_n = quot; + env->cc_c = 0; + env->div1 = quot; env->div2 = rem; - env->cc_dest = flags; } diff --git a/target-m68k/qregs.def b/target-m68k/qregs.def index 204663e..156c0f5 100644 --- a/target-m68k/qregs.def +++ b/target-m68k/qregs.def @@ -2,9 +2,11 @@ DEFF64(FP_RESULT, fp_result) DEFO32(PC, pc) DEFO32(SR, sr) DEFO32(CC_OP, cc_op) -DEFO32(CC_DEST, cc_dest) -DEFO32(CC_SRC, cc_src) DEFO32(CC_X, cc_x) +DEFO32(CC_C, cc_c) +DEFO32(CC_N, cc_n) +DEFO32(CC_V, cc_v) +DEFO32(CC_Z, cc_z) DEFO32(DIV1, div1) DEFO32(DIV2, div2) DEFO32(MACSR, macsr) diff --git a/target-m68k/translate.c b/target-m68k/translate.c index ecd5e5c..6c6173a 100644 --- a/target-m68k/translate.c +++ b/target-m68k/translate.c @@ -59,9 +59,10 @@ static TCGv cpu_aregs[8]; static TCGv_i64 cpu_fregs[8]; static TCGv_i64 cpu_macc[4]; -#define DREG(insn, pos) cpu_dregs[((insn) >> (pos)) & 7] -#define AREG(insn, pos) cpu_aregs[((insn) >> (pos)) & 7] -#define FREG(insn, pos) cpu_fregs[((insn) >> (pos)) & 7] +#define REG(insn, pos) (((insn) >> (pos)) & 7) +#define DREG(insn, pos) cpu_dregs[REG(insn, pos)] +#define AREG(insn, pos) cpu_aregs[REG(insn, pos)] +#define FREG(insn, pos) cpu_fregs[REG(insn, pos)] #define MACREG(acc) cpu_macc[acc] #define QREG_SP cpu_aregs[7] @@ -132,7 +133,8 @@ typedef struct DisasContext { target_ulong insn_pc; /* Start of the current instruction. */ target_ulong pc; int is_jmp; - int cc_op; + CCOp cc_op; /* Current CC operation */ + int cc_op_synced; int user; uint32_t fpcr; struct TranslationBlock *tb; @@ -154,12 +156,6 @@ typedef struct DisasContext { static void *gen_throws_exception; #define gen_last_qop NULL -#define OS_BYTE 0 -#define OS_WORD 1 -#define OS_LONG 2 -#define OS_SINGLE 4 -#define OS_DOUBLE 5 - typedef void (*disas_proc)(CPUM68KState *env, DisasContext *s, uint16_t insn); #ifdef DEBUG_DISPATCH @@ -170,7 +166,7 @@ typedef void (*disas_proc)(CPUM68KState *env, DisasContext *s, uint16_t insn); uint16_t insn) \ { \ qemu_log("Dispatch " #name "\n"); \ - real_disas_##name(s, env, insn); \ + real_disas_##name(env, s, insn); \ } \ static void real_disas_##name(CPUM68KState *env, DisasContext *s, \ uint16_t insn) @@ -180,6 +176,48 @@ typedef void (*disas_proc)(CPUM68KState *env, DisasContext *s, uint16_t insn); uint16_t insn) #endif +static const uint8_t cc_op_live[CC_OP_NB] = { + [CC_OP_FLAGS] = CCF_C | CCF_V | CCF_Z | CCF_N | CCF_X, + [CC_OP_ADD] = CCF_X | CCF_N | CCF_V, + [CC_OP_SUB] = CCF_X | CCF_N | CCF_V, + [CC_OP_CMP] = CCF_X | CCF_N | CCF_V, + [CC_OP_LOGIC] = CCF_X | CCF_N +}; + +static void set_cc_op(DisasContext *s, CCOp op) +{ + CCOp old_op = s->cc_op; + int dead; + + if (old_op == op) { + return; + } + s->cc_op = op; + s->cc_op_synced = 0; + + /* Discard CC computation that will no longer be used. + Note that X and N are never dead. */ + dead = cc_op_live[old_op] & ~cc_op_live[op]; + if (dead & CCF_C) { + tcg_gen_discard_i32(QREG_CC_C); + } + if (dead & CCF_Z) { + tcg_gen_discard_i32(QREG_CC_Z); + } + if (dead & CCF_V) { + tcg_gen_discard_i32(QREG_CC_V); + } +} + +/* Update the CPU env CC_OP state. */ +static void update_cc_op(DisasContext *s) +{ + if (!s->cc_op_synced) { + s->cc_op_synced = 1; + tcg_gen_movi_i32(QREG_CC_OP, s->cc_op); + } +} + /* Generate a load from the specified address. Narrow values are sign extended to full register width. */ static inline TCGv gen_load(DisasContext * s, int opsize, TCGv addr, int sign) @@ -268,14 +306,27 @@ static TCGv gen_ldst(DisasContext *s, int opsize, TCGv addr, TCGv val, } } +/* Read a 16-bit immediate constant */ +static inline uint16_t read_im16(CPUM68KState *env, DisasContext *s) +{ + uint16_t im; + im = cpu_lduw_code(env, s->pc); + s->pc += 2; + return im; +} + +/* Read an 8-bit immediate constant */ +static inline uint8_t read_im8(CPUM68KState *env, DisasContext *s) +{ + return read_im16(env, s); +} + /* Read a 32-bit immediate constant. */ static inline uint32_t read_im32(CPUM68KState *env, DisasContext *s) { uint32_t im; - im = ((uint32_t)cpu_lduw_code(env, s->pc)) << 16; - s->pc += 2; - im |= cpu_lduw_code(env, s->pc); - s->pc += 2; + im = read_im16(env, s) << 16; + im |= 0xffff & read_im16(env, s); return im; } @@ -309,12 +360,16 @@ static TCGv gen_lea_indexed(CPUM68KState *env, DisasContext *s, TCGv base) uint32_t bd, od; offset = s->pc; - ext = cpu_lduw_code(env, s->pc); - s->pc += 2; + ext = read_im16(env, s); if ((ext & 0x800) == 0 && !m68k_feature(s->env, M68K_FEATURE_WORD_INDEX)) return NULL_QREG; + if (m68k_feature(s->env, M68K_FEATURE_M68000) && + !m68k_feature(s->env, M68K_FEATURE_SCALED_INDEX)) { + ext &= ~(3 << 9); + } + if (ext & 0x100) { /* full extension word format */ if (!m68k_feature(s->env, M68K_FEATURE_EXT_FULL)) @@ -323,8 +378,7 @@ static TCGv gen_lea_indexed(CPUM68KState *env, DisasContext *s, TCGv base) if ((ext & 0x30) > 0x10) { /* base displacement */ if ((ext & 0x30) == 0x20) { - bd = (int16_t)cpu_lduw_code(env, s->pc); - s->pc += 2; + bd = (int16_t)read_im16(env, s); } else { bd = read_im32(env, s); } @@ -372,8 +426,7 @@ static TCGv gen_lea_indexed(CPUM68KState *env, DisasContext *s, TCGv base) if ((ext & 3) > 1) { /* outer displacement */ if ((ext & 3) == 2) { - od = (int16_t)cpu_lduw_code(env, s->pc); - s->pc += 2; + od = (int16_t)read_im16(env, s); } else { od = read_im32(env, s); } @@ -401,33 +454,129 @@ static TCGv gen_lea_indexed(CPUM68KState *env, DisasContext *s, TCGv base) return add; } -/* Update the CPU env CC_OP state. */ -static inline void gen_flush_cc_op(DisasContext *s) -{ - if (s->cc_op != CC_OP_DYNAMIC) - tcg_gen_movi_i32(QREG_CC_OP, s->cc_op); -} - /* Evaluate all the CC flags. */ -static inline void gen_flush_flags(DisasContext *s) + +static void gen_flush_flags(DisasContext *s) { - if (s->cc_op == CC_OP_FLAGS) + TCGv t0, t1; + + switch (s->cc_op) { + case CC_OP_FLAGS: return; - gen_flush_cc_op(s); - gen_helper_flush_flags(cpu_env, QREG_CC_OP); + + case CC_OP_ADD: + tcg_gen_mov_i32(QREG_CC_C, QREG_CC_X); + tcg_gen_mov_i32(QREG_CC_Z, QREG_CC_N); + /* Compute signed overflow for addition. */ + t0 = tcg_temp_new(); + t1 = tcg_temp_new(); + tcg_gen_sub_i32(t0, QREG_CC_N, QREG_CC_V); + tcg_gen_xor_i32(t1, QREG_CC_N, QREG_CC_V); + tcg_gen_xor_i32(QREG_CC_V, QREG_CC_V, t0); + tcg_temp_free(t0); + tcg_gen_andc_i32(QREG_CC_V, t1, QREG_CC_V); + tcg_temp_free(t1); + break; + + case CC_OP_SUB: + tcg_gen_mov_i32(QREG_CC_C, QREG_CC_X); + tcg_gen_mov_i32(QREG_CC_Z, QREG_CC_N); + /* Compute signed overflow for subtraction. */ + t0 = tcg_temp_new(); + t1 = tcg_temp_new(); + tcg_gen_add_i32(t0, QREG_CC_N, QREG_CC_V); + tcg_gen_xor_i32(t1, QREG_CC_N, QREG_CC_V); + tcg_gen_xor_i32(QREG_CC_V, QREG_CC_V, t0); + tcg_temp_free(t0); + tcg_gen_and_i32(QREG_CC_V, QREG_CC_V, t1); + tcg_temp_free(t1); + break; + + case CC_OP_CMP: + tcg_gen_setcond_i32(TCG_COND_LTU, QREG_CC_C, QREG_CC_N, QREG_CC_V); + tcg_gen_sub_i32(QREG_CC_Z, QREG_CC_N, QREG_CC_V); + /* Compute signed overflow for subtraction. */ + t0 = tcg_temp_new(); + tcg_gen_xor_i32(t0, QREG_CC_Z, QREG_CC_N); + tcg_gen_xor_i32(QREG_CC_V, QREG_CC_V, QREG_CC_N); + tcg_gen_and_i32(QREG_CC_V, QREG_CC_V, t0); + tcg_temp_free(t0); + tcg_gen_mov_i32(QREG_CC_N, QREG_CC_Z); + break; + + case CC_OP_LOGIC: + tcg_gen_mov_i32(QREG_CC_Z, QREG_CC_N); + tcg_gen_movi_i32(QREG_CC_C, 0); + tcg_gen_movi_i32(QREG_CC_V, 0); + break; + + case CC_OP_DYNAMIC: + gen_helper_flush_flags(cpu_env, QREG_CC_OP); + break; + + default: + t0 = tcg_const_i32(s->cc_op); + gen_helper_flush_flags(cpu_env, t0); + tcg_temp_free(t0); + break; + } + + /* Note that flush_flags also assigned to env->cc_op. */ s->cc_op = CC_OP_FLAGS; + s->cc_op_synced = 1; +} + +/* Sign or zero extend a value. */ + +static inline void gen_ext(TCGv res, TCGv val, int opsize, int sign) +{ + switch (opsize) { + case OS_BYTE: + if (sign) { + tcg_gen_ext8s_i32(res, val); + } else { + tcg_gen_ext8u_i32(res, val); + } + break; + case OS_WORD: + if (sign) { + tcg_gen_ext16s_i32(res, val); + } else { + tcg_gen_ext16u_i32(res, val); + } + break; + case OS_LONG: + tcg_gen_mov_i32(res, val); + break; + default: + g_assert_not_reached(); + } +} + +static TCGv gen_extend(TCGv val, int opsize, int sign) +{ + TCGv tmp; + + if (opsize == OS_LONG) { + tmp = val; + } else { + tmp = tcg_temp_new(); + gen_ext(tmp, val, opsize, sign); + } + + return tmp; } -static void gen_logic_cc(DisasContext *s, TCGv val) +static void gen_logic_cc(DisasContext *s, TCGv val, int opsize) { - tcg_gen_mov_i32(QREG_CC_DEST, val); - s->cc_op = CC_OP_LOGIC; + gen_ext(QREG_CC_N, val, opsize, 1); + set_cc_op(s, CC_OP_LOGIC); } static void gen_update_cc_add(TCGv dest, TCGv src) { - tcg_gen_mov_i32(QREG_CC_DEST, dest); - tcg_gen_mov_i32(QREG_CC_SRC, src); + tcg_gen_mov_i32(QREG_CC_N, dest); + tcg_gen_mov_i32(QREG_CC_V, src); } static inline int opsize_bytes(int opsize) @@ -438,6 +587,19 @@ static inline int opsize_bytes(int opsize) case OS_LONG: return 4; case OS_SINGLE: return 4; case OS_DOUBLE: return 8; + case OS_EXTENDED: return 12; + case OS_PACKED: return 12; + default: + g_assert_not_reached(); + } +} + +static inline int insn_opsize(int insn) +{ + switch ((insn >> 6) & 3) { + case 0: return OS_BYTE; + case 1: return OS_WORD; + case 2: return OS_LONG; default: g_assert_not_reached(); } @@ -470,36 +632,6 @@ static void gen_partset_reg(int opsize, TCGv reg, TCGv val) } } -/* Sign or zero extend a value. */ -static inline TCGv gen_extend(TCGv val, int opsize, int sign) -{ - TCGv tmp; - - switch (opsize) { - case OS_BYTE: - tmp = tcg_temp_new(); - if (sign) - tcg_gen_ext8s_i32(tmp, val); - else - tcg_gen_ext8u_i32(tmp, val); - break; - case OS_WORD: - tmp = tcg_temp_new(); - if (sign) - tcg_gen_ext16s_i32(tmp, val); - else - tcg_gen_ext16u_i32(tmp, val); - break; - case OS_LONG: - case OS_SINGLE: - tmp = val; - break; - default: - g_assert_not_reached(); - } - return tmp; -} - /* Generate code for an "effective address". Does not adjust the base register for autoincrement addressing modes. */ static TCGv gen_lea(CPUM68KState *env, DisasContext *s, uint16_t insn, @@ -525,8 +657,7 @@ static TCGv gen_lea(CPUM68KState *env, DisasContext *s, uint16_t insn, case 5: /* Indirect displacement. */ reg = AREG(insn, 0); tmp = tcg_temp_new(); - ext = cpu_lduw_code(env, s->pc); - s->pc += 2; + ext = read_im16(env, s); tcg_gen_addi_i32(tmp, reg, (int16_t)ext); return tmp; case 6: /* Indirect index + displacement. */ @@ -535,16 +666,14 @@ static TCGv gen_lea(CPUM68KState *env, DisasContext *s, uint16_t insn, case 7: /* Other */ switch (insn & 7) { case 0: /* Absolute short. */ - offset = cpu_ldsw_code(env, s->pc); - s->pc += 2; + offset = (int16_t)read_im16(env, s); return tcg_const_i32(offset); case 1: /* Absolute long. */ offset = read_im32(env, s); return tcg_const_i32(offset); case 2: /* pc displacement */ offset = s->pc; - offset += cpu_ldsw_code(env, s->pc); - s->pc += 2; + offset += (int16_t)read_im16(env, s); return tcg_const_i32(offset); case 3: /* pc index+displacement. */ return gen_lea_indexed(env, s, NULL_QREG); @@ -651,19 +780,17 @@ static TCGv gen_ea(CPUM68KState *env, DisasContext *s, uint16_t insn, switch (opsize) { case OS_BYTE: if (what == EA_LOADS) { - offset = cpu_ldsb_code(env, s->pc + 1); + offset = (int8_t)read_im8(env, s); } else { - offset = cpu_ldub_code(env, s->pc + 1); + offset = read_im8(env, s); } - s->pc += 2; break; case OS_WORD: if (what == EA_LOADS) { - offset = cpu_ldsw_code(env, s->pc); + offset = (int16_t)read_im16(env, s); } else { - offset = cpu_lduw_code(env, s->pc); + offset = read_im16(env, s); } - s->pc += 2; break; case OS_LONG: offset = read_im32(env, s); @@ -680,131 +807,230 @@ static TCGv gen_ea(CPUM68KState *env, DisasContext *s, uint16_t insn, return NULL_QREG; } -/* This generates a conditional branch, clobbering all temporaries. */ -static void gen_jmpcc(DisasContext *s, int cond, TCGLabel *l1) -{ - TCGv tmp; +typedef struct { + TCGCond tcond; + bool g1; + bool g2; + TCGv v1; + TCGv v2; +} DisasCompare; + +static void gen_cc_cond(DisasCompare *c, DisasContext *s, int cond) +{ + TCGv tmp, tmp2; + TCGCond tcond; + CCOp op = s->cc_op; + + /* The CC_OP_CMP form can handle most normal comparisons directly. */ + if (op == CC_OP_CMP) { + c->g1 = c->g2 = 1; + c->v1 = QREG_CC_N; + c->v2 = QREG_CC_V; + switch (cond) { + case 2: /* HI */ + case 3: /* LS */ + tcond = TCG_COND_LEU; + goto done; + case 4: /* CC */ + case 5: /* CS */ + tcond = TCG_COND_LTU; + goto done; + case 6: /* NE */ + case 7: /* EQ */ + tcond = TCG_COND_EQ; + goto done; + case 10: /* PL */ + case 11: /* MI */ + c->g1 = c->g2 = 0; + c->v2 = tcg_const_i32(0); + c->v1 = tmp = tcg_temp_new(); + tcg_gen_sub_i32(tmp, QREG_CC_N, QREG_CC_V); + /* fallthru */ + case 12: /* GE */ + case 13: /* LT */ + tcond = TCG_COND_LT; + goto done; + case 14: /* GT */ + case 15: /* LE */ + tcond = TCG_COND_LE; + goto done; + } + } + + c->g1 = 1; + c->g2 = 0; + c->v2 = tcg_const_i32(0); - /* TODO: Optimize compare/branch pairs rather than always flushing - flag state to CC_OP_FLAGS. */ - gen_flush_flags(s); switch (cond) { case 0: /* T */ - tcg_gen_br(l1); - break; case 1: /* F */ + c->v1 = c->v2; + tcond = TCG_COND_NEVER; + goto done; + case 14: /* GT (!(Z || (N ^ V))) */ + case 15: /* LE (Z || (N ^ V)) */ + /* Logic operations clear V, which simplifies LE to (Z || N), + and since Z and N are co-located, this becomes a normal + comparison vs N. */ + if (op == CC_OP_LOGIC) { + c->v1 = QREG_CC_N; + tcond = TCG_COND_LE; + goto done; + } break; - case 2: /* HI (!C && !Z) */ - tmp = tcg_temp_new(); - tcg_gen_andi_i32(tmp, QREG_CC_DEST, CCF_C | CCF_Z); - tcg_gen_brcondi_i32(TCG_COND_EQ, tmp, 0, l1); + case 12: /* GE (!(N ^ V)) */ + case 13: /* LT (N ^ V) */ + /* Logic operations clear V, which simplifies this to N. */ + if (op != CC_OP_LOGIC) { + break; + } + /* fallthru */ + case 10: /* PL (!N) */ + case 11: /* MI (N) */ + /* Several cases represent N normally. */ + if (op == CC_OP_ADD || op == CC_OP_SUB || op == CC_OP_LOGIC) { + c->v1 = QREG_CC_N; + tcond = TCG_COND_LT; + goto done; + } break; - case 3: /* LS (C || Z) */ - tmp = tcg_temp_new(); - tcg_gen_andi_i32(tmp, QREG_CC_DEST, CCF_C | CCF_Z); - tcg_gen_brcondi_i32(TCG_COND_NE, tmp, 0, l1); + case 6: /* NE (!Z) */ + case 7: /* EQ (Z) */ + /* Some cases fold Z into N. */ + if (op == CC_OP_ADD || op == CC_OP_SUB || op == CC_OP_LOGIC) { + tcond = TCG_COND_EQ; + c->v1 = QREG_CC_N; + goto done; + } break; case 4: /* CC (!C) */ - tmp = tcg_temp_new(); - tcg_gen_andi_i32(tmp, QREG_CC_DEST, CCF_C); - tcg_gen_brcondi_i32(TCG_COND_EQ, tmp, 0, l1); + case 5: /* CS (C) */ + /* Some cases fold C into X. */ + if (op == CC_OP_ADD || op == CC_OP_SUB) { + tcond = TCG_COND_NE; + c->v1 = QREG_CC_X; + goto done; + } + /* fallthru */ + case 8: /* VC (!V) */ + case 9: /* VS (V) */ + /* Logic operations clear V and C. */ + if (op == CC_OP_LOGIC) { + tcond = TCG_COND_NEVER; + c->v1 = c->v2; + goto done; + } + break; + } + + /* Otherwise, flush flag state to CC_OP_FLAGS. */ + gen_flush_flags(s); + + switch (cond) { + case 0: /* T */ + case 1: /* F */ + default: + /* Invalid, or handled above. */ + abort(); + case 2: /* HI (!C && !Z) -> !(C || Z)*/ + case 3: /* LS (C || Z) */ + c->v1 = tmp = tcg_temp_new(); + c->g1 = 0; + tcg_gen_setcond_i32(TCG_COND_EQ, tmp, QREG_CC_Z, c->v2); + tcg_gen_or_i32(tmp, tmp, QREG_CC_C); + tcond = TCG_COND_NE; break; + case 4: /* CC (!C) */ case 5: /* CS (C) */ - tmp = tcg_temp_new(); - tcg_gen_andi_i32(tmp, QREG_CC_DEST, CCF_C); - tcg_gen_brcondi_i32(TCG_COND_NE, tmp, 0, l1); + c->v1 = QREG_CC_C; + tcond = TCG_COND_NE; break; case 6: /* NE (!Z) */ - tmp = tcg_temp_new(); - tcg_gen_andi_i32(tmp, QREG_CC_DEST, CCF_Z); - tcg_gen_brcondi_i32(TCG_COND_EQ, tmp, 0, l1); - break; case 7: /* EQ (Z) */ - tmp = tcg_temp_new(); - tcg_gen_andi_i32(tmp, QREG_CC_DEST, CCF_Z); - tcg_gen_brcondi_i32(TCG_COND_NE, tmp, 0, l1); + c->v1 = QREG_CC_Z; + tcond = TCG_COND_EQ; break; case 8: /* VC (!V) */ - tmp = tcg_temp_new(); - tcg_gen_andi_i32(tmp, QREG_CC_DEST, CCF_V); - tcg_gen_brcondi_i32(TCG_COND_EQ, tmp, 0, l1); - break; case 9: /* VS (V) */ - tmp = tcg_temp_new(); - tcg_gen_andi_i32(tmp, QREG_CC_DEST, CCF_V); - tcg_gen_brcondi_i32(TCG_COND_NE, tmp, 0, l1); + c->v1 = QREG_CC_V; + tcond = TCG_COND_LT; break; case 10: /* PL (!N) */ - tmp = tcg_temp_new(); - tcg_gen_andi_i32(tmp, QREG_CC_DEST, CCF_N); - tcg_gen_brcondi_i32(TCG_COND_EQ, tmp, 0, l1); - break; case 11: /* MI (N) */ - tmp = tcg_temp_new(); - tcg_gen_andi_i32(tmp, QREG_CC_DEST, CCF_N); - tcg_gen_brcondi_i32(TCG_COND_NE, tmp, 0, l1); + c->v1 = QREG_CC_N; + tcond = TCG_COND_LT; break; case 12: /* GE (!(N ^ V)) */ - tmp = tcg_temp_new(); - assert(CCF_V == (CCF_N >> 2)); - tcg_gen_shri_i32(tmp, QREG_CC_DEST, 2); - tcg_gen_xor_i32(tmp, tmp, QREG_CC_DEST); - tcg_gen_andi_i32(tmp, tmp, CCF_V); - tcg_gen_brcondi_i32(TCG_COND_EQ, tmp, 0, l1); - break; case 13: /* LT (N ^ V) */ - tmp = tcg_temp_new(); - assert(CCF_V == (CCF_N >> 2)); - tcg_gen_shri_i32(tmp, QREG_CC_DEST, 2); - tcg_gen_xor_i32(tmp, tmp, QREG_CC_DEST); - tcg_gen_andi_i32(tmp, tmp, CCF_V); - tcg_gen_brcondi_i32(TCG_COND_NE, tmp, 0, l1); + c->v1 = tmp = tcg_temp_new(); + c->g1 = 0; + tcg_gen_xor_i32(tmp, QREG_CC_N, QREG_CC_V); + tcond = TCG_COND_LT; break; case 14: /* GT (!(Z || (N ^ V))) */ - tmp = tcg_temp_new(); - assert(CCF_V == (CCF_N >> 2)); - tcg_gen_andi_i32(tmp, QREG_CC_DEST, CCF_N); - tcg_gen_shri_i32(tmp, tmp, 2); - tcg_gen_xor_i32(tmp, tmp, QREG_CC_DEST); - tcg_gen_andi_i32(tmp, tmp, CCF_V | CCF_Z); - tcg_gen_brcondi_i32(TCG_COND_EQ, tmp, 0, l1); - break; case 15: /* LE (Z || (N ^ V)) */ - tmp = tcg_temp_new(); - assert(CCF_V == (CCF_N >> 2)); - tcg_gen_andi_i32(tmp, QREG_CC_DEST, CCF_N); - tcg_gen_shri_i32(tmp, tmp, 2); - tcg_gen_xor_i32(tmp, tmp, QREG_CC_DEST); - tcg_gen_andi_i32(tmp, tmp, CCF_V | CCF_Z); - tcg_gen_brcondi_i32(TCG_COND_NE, tmp, 0, l1); + c->v1 = tmp = tcg_temp_new(); + c->g1 = 0; + tcg_gen_setcond_i32(TCG_COND_EQ, tmp, QREG_CC_Z, c->v2); + tcg_gen_neg_i32(tmp, tmp); + tmp2 = tcg_temp_new(); + tcg_gen_xor_i32(tmp2, QREG_CC_N, QREG_CC_V); + tcg_gen_or_i32(tmp, tmp, tmp2); + tcg_temp_free(tmp2); + tcond = TCG_COND_LT; break; - default: - /* Should ever happen. */ - abort(); + } + + done: + if ((cond & 1) == 0) { + tcond = tcg_invert_cond(tcond); + } + c->tcond = tcond; +} + +static void free_cond(DisasCompare *c) +{ + if (!c->g1) { + tcg_temp_free(c->v1); + } + if (!c->g2) { + tcg_temp_free(c->v2); } } +static void gen_jmpcc(DisasContext *s, int cond, TCGLabel *l1) +{ + DisasCompare c; + + gen_cc_cond(&c, s, cond); + update_cc_op(s); + tcg_gen_brcond_i32(c.tcond, c.v1, c.v2, l1); + free_cond(&c); +} + DISAS_INSN(scc) { - TCGLabel *l1; + DisasCompare c; int cond; - TCGv reg; + TCGv reg, tmp; - l1 = gen_new_label(); cond = (insn >> 8) & 0xf; + gen_cc_cond(&c, s, cond); + + tmp = tcg_temp_new(); + tcg_gen_setcond_i32(c.tcond, tmp, c.v1, c.v2); + free_cond(&c); + reg = DREG(insn, 0); - tcg_gen_andi_i32(reg, reg, 0xffffff00); - /* This is safe because we modify the reg directly, with no other values - live. */ - gen_jmpcc(s, cond ^ 1, l1); - tcg_gen_ori_i32(reg, reg, 0xff); - gen_set_label(l1); + tcg_gen_neg_i32(tmp, tmp); + tcg_gen_deposit_i32(reg, reg, tmp, 0, 8); + tcg_temp_free(tmp); } /* Force a TB lookup after an instruction that changes the CPU state. */ static void gen_lookup_tb(DisasContext *s) { - gen_flush_cc_op(s); + update_cc_op(s); tcg_gen_movi_i32(QREG_PC, s->pc); s->is_jmp = DISAS_UPDATE; } @@ -812,7 +1038,7 @@ static void gen_lookup_tb(DisasContext *s) /* Generate a jump to an immediate address. */ static void gen_jmp_im(DisasContext *s, uint32_t dest) { - gen_flush_cc_op(s); + update_cc_op(s); tcg_gen_movi_i32(QREG_PC, dest); s->is_jmp = DISAS_JUMP; } @@ -820,14 +1046,14 @@ static void gen_jmp_im(DisasContext *s, uint32_t dest) /* Generate a jump to the address in qreg DEST. */ static void gen_jmp(DisasContext *s, TCGv dest) { - gen_flush_cc_op(s); + update_cc_op(s); tcg_gen_mov_i32(QREG_PC, dest); s->is_jmp = DISAS_JUMP; } static void gen_exception(DisasContext *s, uint32_t where, int nr) { - gen_flush_cc_op(s); + update_cc_op(s); gen_jmp_im(s, where); gen_helper_raise_exception(cpu_env, tcg_const_i32(nr)); } @@ -915,8 +1141,7 @@ DISAS_INSN(mulw) SRC_EA(env, src, OS_WORD, sign, NULL); tcg_gen_mul_i32(tmp, tmp, src); tcg_gen_mov_i32(reg, tmp); - /* Unlike m68k, coldfire always clears the overflow bit. */ - gen_logic_cc(s, tmp); + gen_logic_cc(s, tmp, OS_WORD); } DISAS_INSN(divw) @@ -946,7 +1171,8 @@ DISAS_INSN(divw) tcg_gen_ext16u_i32(tmp, QREG_DIV1); tcg_gen_shli_i32(src, QREG_DIV2, 16); tcg_gen_or_i32(reg, tmp, src); - s->cc_op = CC_OP_FLAGS; + + set_cc_op(s, CC_OP_FLAGS); } DISAS_INSN(divl) @@ -956,8 +1182,7 @@ DISAS_INSN(divl) TCGv reg; uint16_t ext; - ext = cpu_lduw_code(env, s->pc); - s->pc += 2; + ext = read_im16(env, s); if (ext & 0x87f8) { gen_exception(s, s->pc - 4, EXCP_UNSUPPORTED); return; @@ -979,7 +1204,7 @@ DISAS_INSN(divl) /* rem */ tcg_gen_mov_i32 (reg, QREG_DIV2); } - s->cc_op = CC_OP_FLAGS; + set_cc_op(s, CC_OP_FLAGS); } DISAS_INSN(addsub) @@ -1003,12 +1228,12 @@ DISAS_INSN(addsub) } if (add) { tcg_gen_add_i32(dest, tmp, src); - gen_helper_xflag_lt(QREG_CC_X, dest, src); - s->cc_op = CC_OP_ADD; + tcg_gen_setcond_i32(TCG_COND_LTU, QREG_CC_X, dest, src); + set_cc_op(s, CC_OP_ADD); } else { - gen_helper_xflag_lt(QREG_CC_X, tmp, src); + tcg_gen_setcond_i32(TCG_COND_LTU, QREG_CC_X, tmp, src); tcg_gen_sub_i32(dest, tmp, src); - s->cc_op = CC_OP_SUB; + set_cc_op(s, CC_OP_SUB); } gen_update_cc_add(dest, src); if (insn & 0x100) { @@ -1042,42 +1267,43 @@ DISAS_INSN(bitop_reg) else opsize = OS_LONG; op = (insn >> 6) & 3; + + gen_flush_flags(s); + SRC_EA(env, src1, opsize, 0, op ? &addr: NULL); src2 = DREG(insn, 9); dest = tcg_temp_new(); - gen_flush_flags(s); tmp = tcg_temp_new(); if (opsize == OS_BYTE) tcg_gen_andi_i32(tmp, src2, 7); else tcg_gen_andi_i32(tmp, src2, 31); - src2 = tmp; - tmp = tcg_temp_new(); - tcg_gen_shr_i32(tmp, src1, src2); - tcg_gen_andi_i32(tmp, tmp, 1); - tcg_gen_shli_i32(tmp, tmp, 2); - /* Clear CCF_Z if bit set. */ - tcg_gen_ori_i32(QREG_CC_DEST, QREG_CC_DEST, CCF_Z); - tcg_gen_xor_i32(QREG_CC_DEST, QREG_CC_DEST, tmp); - - tcg_gen_shl_i32(tmp, tcg_const_i32(1), src2); + + src2 = tcg_const_i32(1); + tcg_gen_shl_i32(src2, src2, tmp); + tcg_temp_free(tmp); + + tcg_gen_and_i32(QREG_CC_Z, src1, src2); + switch (op) { case 1: /* bchg */ - tcg_gen_xor_i32(dest, src1, tmp); + tcg_gen_xor_i32(dest, src1, src2); break; case 2: /* bclr */ - tcg_gen_not_i32(tmp, tmp); - tcg_gen_and_i32(dest, src1, tmp); + tcg_gen_andc_i32(dest, src1, src2); break; case 3: /* bset */ - tcg_gen_or_i32(dest, src1, tmp); + tcg_gen_or_i32(dest, src1, src2); break; default: /* btst */ break; } - if (op) + tcg_temp_free(src2); + if (op) { DEST_EA(env, insn, opsize, dest, &addr); + } + tcg_temp_free(dest); } DISAS_INSN(sats) @@ -1085,8 +1311,8 @@ DISAS_INSN(sats) TCGv reg; reg = DREG(insn, 0); gen_flush_flags(s); - gen_helper_sats(reg, reg, QREG_CC_DEST); - gen_logic_cc(s, reg); + gen_helper_sats(reg, reg, QREG_CC_V); + gen_logic_cc(s, reg, OS_LONG); } static void gen_push(DisasContext *s, TCGv val) @@ -1108,8 +1334,7 @@ DISAS_INSN(movem) TCGv tmp; int is_load; - mask = cpu_lduw_code(env, s->pc); - s->pc += 2; + mask = read_im16(env, s); tmp = gen_lea(env, s, insn, OS_LONG); if (IS_NULL_QREG(tmp)) { gen_addr_fault(s); @@ -1152,35 +1377,26 @@ DISAS_INSN(bitop_im) opsize = OS_LONG; op = (insn >> 6) & 3; - bitnum = cpu_lduw_code(env, s->pc); - s->pc += 2; + bitnum = read_im16(env, s); if (bitnum & 0xff00) { disas_undef(env, s, insn); return; } + gen_flush_flags(s); + SRC_EA(env, src1, opsize, 0, op ? &addr: NULL); - gen_flush_flags(s); if (opsize == OS_BYTE) bitnum &= 7; else bitnum &= 31; mask = 1 << bitnum; - tmp = tcg_temp_new(); - assert (CCF_Z == (1 << 2)); - if (bitnum > 2) - tcg_gen_shri_i32(tmp, src1, bitnum - 2); - else if (bitnum < 2) - tcg_gen_shli_i32(tmp, src1, 2 - bitnum); - else - tcg_gen_mov_i32(tmp, src1); - tcg_gen_andi_i32(tmp, tmp, CCF_Z); - /* Clear CCF_Z if bit set. */ - tcg_gen_ori_i32(QREG_CC_DEST, QREG_CC_DEST, CCF_Z); - tcg_gen_xor_i32(QREG_CC_DEST, QREG_CC_DEST, tmp); + tcg_gen_andi_i32(QREG_CC_Z, src1, mask); + if (op) { + tmp = tcg_temp_new(); switch (op) { case 1: /* bchg */ tcg_gen_xori_i32(tmp, src1, mask); @@ -1195,6 +1411,7 @@ DISAS_INSN(bitop_im) break; } DEST_EA(env, insn, opsize, tmp, &addr); + tcg_temp_free(tmp); } } @@ -1213,35 +1430,33 @@ DISAS_INSN(arith_im) switch (op) { case 0: /* ori */ tcg_gen_ori_i32(dest, src1, im); - gen_logic_cc(s, dest); + gen_logic_cc(s, dest, OS_LONG); break; case 1: /* andi */ tcg_gen_andi_i32(dest, src1, im); - gen_logic_cc(s, dest); + gen_logic_cc(s, dest, OS_LONG); break; case 2: /* subi */ tcg_gen_mov_i32(dest, src1); - gen_helper_xflag_lt(QREG_CC_X, dest, tcg_const_i32(im)); + tcg_gen_setcondi_i32(TCG_COND_LTU, QREG_CC_X, dest, im); tcg_gen_subi_i32(dest, dest, im); gen_update_cc_add(dest, tcg_const_i32(im)); - s->cc_op = CC_OP_SUB; + set_cc_op(s, CC_OP_SUB); break; case 3: /* addi */ tcg_gen_mov_i32(dest, src1); tcg_gen_addi_i32(dest, dest, im); gen_update_cc_add(dest, tcg_const_i32(im)); - gen_helper_xflag_lt(QREG_CC_X, dest, tcg_const_i32(im)); - s->cc_op = CC_OP_ADD; + tcg_gen_setcondi_i32(TCG_COND_LTU, QREG_CC_X, dest, im); + set_cc_op(s, CC_OP_ADD); break; case 5: /* eori */ tcg_gen_xori_i32(dest, src1, im); - gen_logic_cc(s, dest); + gen_logic_cc(s, dest, OS_LONG); break; case 6: /* cmpi */ - tcg_gen_mov_i32(dest, src1); - tcg_gen_subi_i32(dest, dest, im); - gen_update_cc_add(dest, tcg_const_i32(im)); - s->cc_op = CC_OP_SUB; + gen_update_cc_add(src1, tcg_const_i32(im)); + set_cc_op(s, CC_OP_CMP); break; default: abort(); @@ -1292,7 +1507,7 @@ DISAS_INSN(move) dest_ea = ((insn >> 9) & 7) | (op << 3); DEST_EA(env, dest_ea, opsize, src, NULL); /* This will be correct because loads sign extend. */ - gen_logic_cc(s, src); + gen_logic_cc(s, src, opsize); } } @@ -1323,21 +1538,9 @@ DISAS_INSN(clr) { int opsize; - switch ((insn >> 6) & 3) { - case 0: /* clr.b */ - opsize = OS_BYTE; - break; - case 1: /* clr.w */ - opsize = OS_WORD; - break; - case 2: /* clr.l */ - opsize = OS_LONG; - break; - default: - abort(); - } + opsize = insn_opsize(insn); DEST_EA(env, insn, opsize, tcg_const_i32(0), NULL); - gen_logic_cc(s, tcg_const_i32(0)); + gen_logic_cc(s, tcg_const_i32(0), opsize); } static TCGv gen_get_ccr(DisasContext *s) @@ -1345,20 +1548,18 @@ static TCGv gen_get_ccr(DisasContext *s) TCGv dest; gen_flush_flags(s); + update_cc_op(s); dest = tcg_temp_new(); - tcg_gen_shli_i32(dest, QREG_CC_X, 4); - tcg_gen_or_i32(dest, dest, QREG_CC_DEST); + gen_helper_get_ccr(dest, cpu_env); return dest; } DISAS_INSN(move_from_ccr) { - TCGv reg; TCGv ccr; ccr = gen_get_ccr(s); - reg = DREG(insn, 0); - gen_partset_reg(OS_WORD, reg, ccr); + DEST_EA(env, insn, OS_WORD, ccr, NULL); } DISAS_INSN(neg) @@ -1370,50 +1571,45 @@ DISAS_INSN(neg) src1 = tcg_temp_new(); tcg_gen_mov_i32(src1, reg); tcg_gen_neg_i32(reg, src1); - s->cc_op = CC_OP_SUB; gen_update_cc_add(reg, src1); - gen_helper_xflag_lt(QREG_CC_X, tcg_const_i32(0), src1); - s->cc_op = CC_OP_SUB; + tcg_gen_setcondi_i32(TCG_COND_NE, QREG_CC_X, src1, 0); + set_cc_op(s, CC_OP_SUB); } static void gen_set_sr_im(DisasContext *s, uint16_t val, int ccr_only) { - tcg_gen_movi_i32(QREG_CC_DEST, val & 0xf); - tcg_gen_movi_i32(QREG_CC_X, (val & 0x10) >> 4); - if (!ccr_only) { - gen_helper_set_sr(cpu_env, tcg_const_i32(val & 0xff00)); + if (ccr_only) { + tcg_gen_movi_i32(QREG_CC_C, val & CCF_C ? 1 : 0); + tcg_gen_movi_i32(QREG_CC_V, val & CCF_V ? -1 : 0); + tcg_gen_movi_i32(QREG_CC_Z, val & CCF_Z ? 0 : 1); + tcg_gen_movi_i32(QREG_CC_N, val & CCF_N ? -1 : 0); + tcg_gen_movi_i32(QREG_CC_X, val & CCF_X ? 1 : 0); + } else { + gen_helper_set_sr(cpu_env, tcg_const_i32(val)); } + set_cc_op(s, CC_OP_FLAGS); } static void gen_set_sr(CPUM68KState *env, DisasContext *s, uint16_t insn, int ccr_only) { - TCGv tmp; - TCGv reg; - - s->cc_op = CC_OP_FLAGS; - if ((insn & 0x38) == 0) - { - tmp = tcg_temp_new(); - reg = DREG(insn, 0); - tcg_gen_andi_i32(QREG_CC_DEST, reg, 0xf); - tcg_gen_shri_i32(tmp, reg, 4); - tcg_gen_andi_i32(QREG_CC_X, tmp, 1); - if (!ccr_only) { - gen_helper_set_sr(cpu_env, reg); + if ((insn & 0x38) == 0) { + if (ccr_only) { + gen_helper_set_ccr(cpu_env, DREG(insn, 0)); + } else { + gen_helper_set_sr(cpu_env, DREG(insn, 0)); } - } - else if ((insn & 0x3f) == 0x3c) - { + set_cc_op(s, CC_OP_FLAGS); + } else if ((insn & 0x3f) == 0x3c) { uint16_t val; - val = cpu_lduw_code(env, s->pc); - s->pc += 2; + val = read_im16(env, s); gen_set_sr_im(s, val, ccr_only); - } - else + } else { disas_undef(env, s, insn); + } } + DISAS_INSN(move_to_ccr) { gen_set_sr(env, s, insn, 1); @@ -1425,7 +1621,7 @@ DISAS_INSN(not) reg = DREG(insn, 0); tcg_gen_not_i32(reg, reg); - gen_logic_cc(s, reg); + gen_logic_cc(s, reg, OS_LONG); } DISAS_INSN(swap) @@ -1440,7 +1636,7 @@ DISAS_INSN(swap) tcg_gen_shli_i32(src1, reg, 16); tcg_gen_shri_i32(src2, reg, 16); tcg_gen_or_i32(reg, src1, src2); - gen_logic_cc(s, reg); + gen_logic_cc(s, reg, OS_LONG); } DISAS_INSN(pea) @@ -1472,7 +1668,7 @@ DISAS_INSN(ext) gen_partset_reg(OS_WORD, reg, tmp); else tcg_gen_mov_i32(reg, tmp); - gen_logic_cc(s, tmp); + gen_logic_cc(s, tmp, OS_LONG); } DISAS_INSN(tst) @@ -1480,21 +1676,9 @@ DISAS_INSN(tst) int opsize; TCGv tmp; - switch ((insn >> 6) & 3) { - case 0: /* tst.b */ - opsize = OS_BYTE; - break; - case 1: /* tst.w */ - opsize = OS_WORD; - break; - case 2: /* tst.l */ - opsize = OS_LONG; - break; - default: - abort(); - } + opsize = insn_opsize(insn); SRC_EA(env, tmp, opsize, 1, NULL); - gen_logic_cc(s, tmp); + gen_logic_cc(s, tmp, opsize); } DISAS_INSN(pulse) @@ -1516,7 +1700,7 @@ DISAS_INSN(tas) dest = tcg_temp_new(); SRC_EA(env, src1, OS_BYTE, 1, &addr); - gen_logic_cc(s, src1); + gen_logic_cc(s, src1, OS_BYTE); tcg_gen_ori_i32(dest, src1, 0x80); DEST_EA(env, insn, OS_BYTE, dest, &addr); } @@ -1530,8 +1714,7 @@ DISAS_INSN(mull) /* The upper 32 bits of the product are discarded, so muls.l and mulu.l are functionally equivalent. */ - ext = cpu_lduw_code(env, s->pc); - s->pc += 2; + ext = read_im16(env, s); if (ext & 0x87ff) { gen_exception(s, s->pc - 4, EXCP_UNSUPPORTED); return; @@ -1542,7 +1725,7 @@ DISAS_INSN(mull) tcg_gen_mul_i32(dest, src1, reg); tcg_gen_mov_i32(reg, dest); /* Unlike m68k, coldfire always clears the overflow bit. */ - gen_logic_cc(s, dest); + gen_logic_cc(s, dest, OS_LONG); } DISAS_INSN(link) @@ -1632,13 +1815,13 @@ DISAS_INSN(addsubq) } else { src2 = tcg_const_i32(val); if (insn & 0x0100) { - gen_helper_xflag_lt(QREG_CC_X, dest, src2); - tcg_gen_subi_i32(dest, dest, val); - s->cc_op = CC_OP_SUB; + tcg_gen_setcond_i32(TCG_COND_LTU, QREG_CC_X, dest, src2); + tcg_gen_sub_i32(dest, dest, src2); + set_cc_op(s, CC_OP_SUB); } else { - tcg_gen_addi_i32(dest, dest, val); - gen_helper_xflag_lt(QREG_CC_X, dest, src2); - s->cc_op = CC_OP_ADD; + tcg_gen_add_i32(dest, dest, src2); + tcg_gen_setcond_i32(TCG_COND_LTU, QREG_CC_X, dest, src2); + set_cc_op(s, CC_OP_ADD); } gen_update_cc_add(dest, src2); } @@ -1672,8 +1855,7 @@ DISAS_INSN(branch) op = (insn >> 8) & 0xf; offset = (int8_t)insn; if (offset == 0) { - offset = cpu_ldsw_code(env, s->pc); - s->pc += 2; + offset = (int16_t)read_im16(env, s); } else if (offset == -1) { offset = read_im32(env, s); } @@ -1681,7 +1863,6 @@ DISAS_INSN(branch) /* bsr */ gen_push(s, tcg_const_i32(s->pc)); } - gen_flush_cc_op(s); if (op > 1) { /* Bcc */ l1 = gen_new_label(); @@ -1701,7 +1882,7 @@ DISAS_INSN(moveq) val = (int8_t)insn; tcg_gen_movi_i32(DREG(insn, 9), val); - gen_logic_cc(s, tcg_const_i32(val)); + gen_logic_cc(s, tcg_const_i32(val), OS_LONG); } DISAS_INSN(mvzs) @@ -1717,7 +1898,7 @@ DISAS_INSN(mvzs) SRC_EA(env, src, opsize, (insn & 0x80) == 0, NULL); reg = DREG(insn, 9); tcg_gen_mov_i32(reg, src); - gen_logic_cc(s, src); + gen_logic_cc(s, src, opsize); } DISAS_INSN(or) @@ -1738,7 +1919,7 @@ DISAS_INSN(or) tcg_gen_or_i32(dest, src, reg); tcg_gen_mov_i32(reg, dest); } - gen_logic_cc(s, dest); + gen_logic_cc(s, dest, OS_LONG); } DISAS_INSN(suba) @@ -1771,40 +1952,21 @@ DISAS_INSN(mov3q) if (val == 0) val = -1; src = tcg_const_i32(val); - gen_logic_cc(s, src); + gen_logic_cc(s, src, OS_LONG); DEST_EA(env, insn, OS_LONG, src, NULL); } DISAS_INSN(cmp) { - int op; TCGv src; TCGv reg; - TCGv dest; int opsize; - op = (insn >> 6) & 3; - switch (op) { - case 0: /* cmp.b */ - opsize = OS_BYTE; - s->cc_op = CC_OP_CMPB; - break; - case 1: /* cmp.w */ - opsize = OS_WORD; - s->cc_op = CC_OP_CMPW; - break; - case 2: /* cmp.l */ - opsize = OS_LONG; - s->cc_op = CC_OP_SUB; - break; - default: - abort(); - } - SRC_EA(env, src, opsize, 1, NULL); + opsize = insn_opsize(insn); + SRC_EA(env, src, opsize, -1, NULL); reg = DREG(insn, 9); - dest = tcg_temp_new(); - tcg_gen_sub_i32(dest, reg, src); - gen_update_cc_add(dest, src); + gen_update_cc_add(reg, src); + set_cc_op(s, CC_OP_CMP); } DISAS_INSN(cmpa) @@ -1812,7 +1974,6 @@ DISAS_INSN(cmpa) int opsize; TCGv src; TCGv reg; - TCGv dest; if (insn & 0x100) { opsize = OS_LONG; @@ -1821,10 +1982,8 @@ DISAS_INSN(cmpa) } SRC_EA(env, src, opsize, 1, NULL); reg = AREG(insn, 9); - dest = tcg_temp_new(); - tcg_gen_sub_i32(dest, reg, src); - gen_update_cc_add(dest, src); - s->cc_op = CC_OP_SUB; + gen_update_cc_add(reg, src); + set_cc_op(s, CC_OP_CMP); } DISAS_INSN(eor) @@ -1838,7 +1997,7 @@ DISAS_INSN(eor) reg = DREG(insn, 9); dest = tcg_temp_new(); tcg_gen_xor_i32(dest, src, reg); - gen_logic_cc(s, dest); + gen_logic_cc(s, dest, OS_LONG); DEST_EA(env, insn, OS_LONG, dest, &addr); } @@ -1860,7 +2019,7 @@ DISAS_INSN(and) tcg_gen_and_i32(dest, src, reg); tcg_gen_mov_i32(reg, dest); } - gen_logic_cc(s, dest); + gen_logic_cc(s, dest, OS_LONG); } DISAS_INSN(adda) @@ -1882,7 +2041,6 @@ DISAS_INSN(addx) reg = DREG(insn, 9); src = DREG(insn, 0); gen_helper_addx_cc(reg, cpu_env, reg, src); - s->cc_op = CC_OP_FLAGS; } /* TODO: This could be implemented without helper functions. */ @@ -1892,6 +2050,8 @@ DISAS_INSN(shift_im) int tmp; TCGv shift; + set_cc_op(s, CC_OP_FLAGS); + reg = DREG(insn, 0); tmp = (insn >> 9) & 7; if (tmp == 0) @@ -1907,7 +2067,6 @@ DISAS_INSN(shift_im) gen_helper_sar_cc(reg, cpu_env, reg, shift); } } - s->cc_op = CC_OP_SHIFT; } DISAS_INSN(shift_reg) @@ -1917,8 +2076,6 @@ DISAS_INSN(shift_reg) reg = DREG(insn, 0); shift = DREG(insn, 9); - /* Shift by zero leaves C flag unmodified. */ - gen_flush_flags(s); if (insn & 0x100) { gen_helper_shl_cc(reg, cpu_env, reg, shift); } else { @@ -1928,14 +2085,14 @@ DISAS_INSN(shift_reg) gen_helper_sar_cc(reg, cpu_env, reg, shift); } } - s->cc_op = CC_OP_SHIFT; + set_cc_op(s, CC_OP_FLAGS); } DISAS_INSN(ff1) { TCGv reg; reg = DREG(insn, 0); - gen_logic_cc(s, reg); + gen_logic_cc(s, reg, OS_LONG); gen_helper_ff1(reg, reg); } @@ -1957,14 +2114,12 @@ DISAS_INSN(strldsr) uint32_t addr; addr = s->pc - 2; - ext = cpu_lduw_code(env, s->pc); - s->pc += 2; + ext = read_im16(env, s); if (ext != 0x46FC) { gen_exception(s, addr, EXCP_UNSUPPORTED); return; } - ext = cpu_lduw_code(env, s->pc); - s->pc += 2; + ext = read_im16(env, s); if (IS_USER(s) || (ext & SR_S) == 0) { gen_exception(s, addr, EXCP_PRIVILEGE); return; @@ -1975,16 +2130,14 @@ DISAS_INSN(strldsr) DISAS_INSN(move_from_sr) { - TCGv reg; TCGv sr; - if (IS_USER(s)) { + if (IS_USER(s) && !m68k_feature(env, M68K_FEATURE_M68000)) { gen_exception(s, s->pc - 2, EXCP_PRIVILEGE); return; } sr = gen_get_sr(s); - reg = DREG(insn, 0); - gen_partset_reg(OS_WORD, reg, sr); + DEST_EA(env, insn, OS_WORD, sr, NULL); } DISAS_INSN(move_to_sr) @@ -2031,8 +2184,7 @@ DISAS_INSN(stop) return; } - ext = cpu_lduw_code(env, s->pc); - s->pc += 2; + ext = read_im16(env, s); gen_set_sr_im(s, ext, 0); tcg_gen_movi_i32(cpu_halted, 1); @@ -2058,8 +2210,7 @@ DISAS_INSN(movec) return; } - ext = cpu_lduw_code(env, s->pc); - s->pc += 2; + ext = read_im16(env, s); if (ext & 0x8000) { reg = AREG(ext, 12); @@ -2125,8 +2276,7 @@ DISAS_INSN(fpu) int set_dest; int opsize; - ext = cpu_lduw_code(env, s->pc); - s->pc += 2; + ext = read_im16(env, s); opmode = ext & 0x7f; switch ((ext >> 13) & 7) { case 0: case 2: @@ -2408,8 +2558,7 @@ DISAS_INSN(fbcc) offset = cpu_ldsw_code(env, s->pc); s->pc += 2; if (insn & (1 << 6)) { - offset = (offset << 16) | cpu_lduw_code(env, s->pc); - s->pc += 2; + offset = (offset << 16) | read_im16(env, s); } l1 = gen_new_label(); @@ -2534,8 +2683,7 @@ DISAS_INSN(mac) s->done_mac = 1; } - ext = cpu_lduw_code(env, s->pc); - s->pc += 2; + ext = read_im16(env, s); acc = ((insn >> 7) & 1) | ((ext >> 3) & 2); dual = ((insn & 0x30) != 0 && (ext & 3) != 0); @@ -2747,9 +2895,11 @@ DISAS_INSN(from_mext) DISAS_INSN(macsr_to_ccr) { - tcg_gen_movi_i32(QREG_CC_X, 0); - tcg_gen_andi_i32(QREG_CC_DEST, QREG_MACSR, 0xf); - s->cc_op = CC_OP_FLAGS; + TCGv tmp = tcg_temp_new(); + tcg_gen_andi_i32(tmp, QREG_MACSR, 0xf); + gen_helper_set_sr(cpu_env, tmp); + tcg_temp_free(tmp); + set_cc_op(s, CC_OP_FLAGS); } DISAS_INSN(to_mac) @@ -2841,90 +2991,124 @@ register_opcode (disas_proc proc, uint16_t opcode, uint16_t mask) Later insn override earlier ones. */ void register_m68k_insns (CPUM68KState *env) { + /* Build the opcode table only once to avoid + multithreading issues. */ + if (opcode_table[0] != NULL) { + return; + } + + /* use BASE() for instruction available + * for CF_ISA_A and M68000. + */ +#define BASE(name, opcode, mask) \ + register_opcode(disas_##name, 0x##opcode, 0x##mask) #define INSN(name, opcode, mask, feature) do { \ if (m68k_feature(env, M68K_FEATURE_##feature)) \ - register_opcode(disas_##name, 0x##opcode, 0x##mask); \ + BASE(name, opcode, mask); \ } while(0) - INSN(undef, 0000, 0000, CF_ISA_A); + BASE(undef, 0000, 0000); INSN(arith_im, 0080, fff8, CF_ISA_A); + INSN(arith_im, 0000, ff00, M68000); + INSN(undef, 00c0, ffc0, M68000); INSN(bitrev, 00c0, fff8, CF_ISA_APLUSC); - INSN(bitop_reg, 0100, f1c0, CF_ISA_A); - INSN(bitop_reg, 0140, f1c0, CF_ISA_A); - INSN(bitop_reg, 0180, f1c0, CF_ISA_A); - INSN(bitop_reg, 01c0, f1c0, CF_ISA_A); + BASE(bitop_reg, 0100, f1c0); + BASE(bitop_reg, 0140, f1c0); + BASE(bitop_reg, 0180, f1c0); + BASE(bitop_reg, 01c0, f1c0); INSN(arith_im, 0280, fff8, CF_ISA_A); + INSN(arith_im, 0200, ff00, M68000); + INSN(undef, 02c0, ffc0, M68000); INSN(byterev, 02c0, fff8, CF_ISA_APLUSC); INSN(arith_im, 0480, fff8, CF_ISA_A); + INSN(arith_im, 0400, ff00, M68000); + INSN(undef, 04c0, ffc0, M68000); + INSN(arith_im, 0600, ff00, M68000); + INSN(undef, 06c0, ffc0, M68000); INSN(ff1, 04c0, fff8, CF_ISA_APLUSC); INSN(arith_im, 0680, fff8, CF_ISA_A); - INSN(bitop_im, 0800, ffc0, CF_ISA_A); - INSN(bitop_im, 0840, ffc0, CF_ISA_A); - INSN(bitop_im, 0880, ffc0, CF_ISA_A); - INSN(bitop_im, 08c0, ffc0, CF_ISA_A); - INSN(arith_im, 0a80, fff8, CF_ISA_A); INSN(arith_im, 0c00, ff38, CF_ISA_A); - INSN(move, 1000, f000, CF_ISA_A); - INSN(move, 2000, f000, CF_ISA_A); - INSN(move, 3000, f000, CF_ISA_A); + INSN(arith_im, 0c00, ff00, M68000); + BASE(bitop_im, 0800, ffc0); + BASE(bitop_im, 0840, ffc0); + BASE(bitop_im, 0880, ffc0); + BASE(bitop_im, 08c0, ffc0); + INSN(arith_im, 0a80, fff8, CF_ISA_A); + INSN(arith_im, 0a00, ff00, M68000); + BASE(move, 1000, f000); + BASE(move, 2000, f000); + BASE(move, 3000, f000); INSN(strldsr, 40e7, ffff, CF_ISA_APLUSC); INSN(negx, 4080, fff8, CF_ISA_A); INSN(move_from_sr, 40c0, fff8, CF_ISA_A); - INSN(lea, 41c0, f1c0, CF_ISA_A); - INSN(clr, 4200, ff00, CF_ISA_A); - INSN(undef, 42c0, ffc0, CF_ISA_A); + INSN(move_from_sr, 40c0, ffc0, M68000); + BASE(lea, 41c0, f1c0); + BASE(clr, 4200, ff00); + BASE(undef, 42c0, ffc0); INSN(move_from_ccr, 42c0, fff8, CF_ISA_A); + INSN(move_from_ccr, 42c0, ffc0, M68000); INSN(neg, 4480, fff8, CF_ISA_A); - INSN(move_to_ccr, 44c0, ffc0, CF_ISA_A); + INSN(neg, 4400, ff00, M68000); + INSN(undef, 44c0, ffc0, M68000); + BASE(move_to_ccr, 44c0, ffc0); INSN(not, 4680, fff8, CF_ISA_A); + INSN(not, 4600, ff00, M68000); + INSN(undef, 46c0, ffc0, M68000); INSN(move_to_sr, 46c0, ffc0, CF_ISA_A); - INSN(pea, 4840, ffc0, CF_ISA_A); - INSN(swap, 4840, fff8, CF_ISA_A); - INSN(movem, 48c0, fbc0, CF_ISA_A); - INSN(ext, 4880, fff8, CF_ISA_A); - INSN(ext, 48c0, fff8, CF_ISA_A); - INSN(ext, 49c0, fff8, CF_ISA_A); - INSN(tst, 4a00, ff00, CF_ISA_A); + BASE(pea, 4840, ffc0); + BASE(swap, 4840, fff8); + BASE(movem, 48c0, fbc0); + BASE(ext, 4880, fff8); + BASE(ext, 48c0, fff8); + BASE(ext, 49c0, fff8); + BASE(tst, 4a00, ff00); INSN(tas, 4ac0, ffc0, CF_ISA_B); + INSN(tas, 4ac0, ffc0, M68000); INSN(halt, 4ac8, ffff, CF_ISA_A); INSN(pulse, 4acc, ffff, CF_ISA_A); - INSN(illegal, 4afc, ffff, CF_ISA_A); + BASE(illegal, 4afc, ffff); INSN(mull, 4c00, ffc0, CF_ISA_A); + INSN(mull, 4c00, ffc0, LONG_MULDIV); INSN(divl, 4c40, ffc0, CF_ISA_A); + INSN(divl, 4c40, ffc0, LONG_MULDIV); INSN(sats, 4c80, fff8, CF_ISA_B); - INSN(trap, 4e40, fff0, CF_ISA_A); - INSN(link, 4e50, fff8, CF_ISA_A); - INSN(unlk, 4e58, fff8, CF_ISA_A); + BASE(trap, 4e40, fff0); + BASE(link, 4e50, fff8); + BASE(unlk, 4e58, fff8); INSN(move_to_usp, 4e60, fff8, USP); INSN(move_from_usp, 4e68, fff8, USP); - INSN(nop, 4e71, ffff, CF_ISA_A); - INSN(stop, 4e72, ffff, CF_ISA_A); - INSN(rte, 4e73, ffff, CF_ISA_A); - INSN(rts, 4e75, ffff, CF_ISA_A); + BASE(nop, 4e71, ffff); + BASE(stop, 4e72, ffff); + BASE(rte, 4e73, ffff); + BASE(rts, 4e75, ffff); INSN(movec, 4e7b, ffff, CF_ISA_A); - INSN(jump, 4e80, ffc0, CF_ISA_A); + BASE(jump, 4e80, ffc0); INSN(jump, 4ec0, ffc0, CF_ISA_A); INSN(addsubq, 5180, f1c0, CF_ISA_A); + INSN(jump, 4ec0, ffc0, M68000); + INSN(addsubq, 5000, f080, M68000); + INSN(addsubq, 5080, f0c0, M68000); INSN(scc, 50c0, f0f8, CF_ISA_A); INSN(addsubq, 5080, f1c0, CF_ISA_A); INSN(tpf, 51f8, fff8, CF_ISA_A); /* Branch instructions. */ - INSN(branch, 6000, f000, CF_ISA_A); + BASE(branch, 6000, f000); /* Disable long branch instructions, then add back the ones we want. */ - INSN(undef, 60ff, f0ff, CF_ISA_A); /* All long branches. */ + BASE(undef, 60ff, f0ff); /* All long branches. */ INSN(branch, 60ff, f0ff, CF_ISA_B); INSN(undef, 60ff, ffff, CF_ISA_B); /* bra.l */ INSN(branch, 60ff, ffff, BRAL); + INSN(branch, 60ff, f0ff, BCCL); - INSN(moveq, 7000, f100, CF_ISA_A); + BASE(moveq, 7000, f100); INSN(mvzs, 7100, f100, CF_ISA_B); - INSN(or, 8000, f000, CF_ISA_A); - INSN(divw, 80c0, f0c0, CF_ISA_A); - INSN(addsub, 9000, f000, CF_ISA_A); + BASE(or, 8000, f000); + BASE(divw, 80c0, f0c0); + BASE(addsub, 9000, f000); INSN(subx, 9180, f1f8, CF_ISA_A); INSN(suba, 91c0, f1c0, CF_ISA_A); - INSN(undef_mac, a000, f000, CF_ISA_A); + BASE(undef_mac, a000, f000); INSN(mac, a000, f100, CF_EMAC); INSN(from_mac, a180, f9b0, CF_EMAC); INSN(move_mac, a110, f9fc, CF_EMAC); @@ -2943,12 +3127,16 @@ void register_m68k_insns (CPUM68KState *env) INSN(cmpa, b0c0, f1c0, CF_ISA_B); /* cmpa.w */ INSN(cmp, b080, f1c0, CF_ISA_A); INSN(cmpa, b1c0, f1c0, CF_ISA_A); + INSN(cmp, b000, f100, M68000); + INSN(eor, b100, f100, M68000); + INSN(cmpa, b0c0, f0c0, M68000); INSN(eor, b180, f1c0, CF_ISA_A); - INSN(and, c000, f000, CF_ISA_A); - INSN(mulw, c0c0, f0c0, CF_ISA_A); - INSN(addsub, d000, f000, CF_ISA_A); + BASE(and, c000, f000); + BASE(mulw, c0c0, f0c0); + BASE(addsub, d000, f000); INSN(addx, d180, f1f8, CF_ISA_A); INSN(adda, d1c0, f1c0, CF_ISA_A); + INSN(adda, d0c0, f0c0, M68000); INSN(shift_im, e080, f0f0, CF_ISA_A); INSN(shift_reg, e0a0, f0f0, CF_ISA_A); INSN(undef_fpu, f000, f000, CF_ISA_A); @@ -2969,8 +3157,7 @@ static void disas_m68k_insn(CPUM68KState * env, DisasContext *s) { uint16_t insn; - insn = cpu_lduw_code(env, s->pc); - s->pc += 2; + insn = read_im16(env, s); opcode_table[insn](env, s, insn); } @@ -2995,6 +3182,7 @@ void gen_intermediate_code(CPUM68KState *env, TranslationBlock *tb) dc->is_jmp = DISAS_NEXT; dc->pc = pc_start; dc->cc_op = CC_OP_DYNAMIC; + dc->cc_op_synced = 1; dc->singlestep_enabled = cs->singlestep_enabled; dc->fpcr = env->fpcr; dc->user = (env->sr & SR_S) == 0; @@ -3012,7 +3200,7 @@ void gen_intermediate_code(CPUM68KState *env, TranslationBlock *tb) do { pc_offset = dc->pc - pc_start; gen_throws_exception = NULL; - tcg_gen_insn_start(dc->pc); + tcg_gen_insn_start(dc->pc, dc->cc_op); num_insns++; if (unlikely(cpu_breakpoint_test(cs, dc->pc, BP_ANY))) { @@ -3043,20 +3231,20 @@ void gen_intermediate_code(CPUM68KState *env, TranslationBlock *tb) if (unlikely(cs->singlestep_enabled)) { /* Make sure the pc is updated, and raise a debug exception. */ if (!dc->is_jmp) { - gen_flush_cc_op(dc); + update_cc_op(dc); tcg_gen_movi_i32(QREG_PC, dc->pc); } gen_helper_raise_exception(cpu_env, tcg_const_i32(EXCP_DEBUG)); } else { switch(dc->is_jmp) { case DISAS_NEXT: - gen_flush_cc_op(dc); + update_cc_op(dc); gen_jmp_tb(dc, 0, dc->pc); break; default: case DISAS_JUMP: case DISAS_UPDATE: - gen_flush_cc_op(dc); + update_cc_op(dc); /* indicate that the hash table must be used to find the next TB */ tcg_gen_exit_tb(0); break; @@ -3091,20 +3279,24 @@ void m68k_cpu_dump_state(CPUState *cs, FILE *f, fprintf_function cpu_fprintf, for (i = 0; i < 8; i++) { u.d = env->fregs[i]; - cpu_fprintf (f, "D%d = %08x A%d = %08x F%d = %08x%08x (%12g)\n", - i, env->dregs[i], i, env->aregs[i], - i, u.l.upper, u.l.lower, *(double *)&u.d); + cpu_fprintf(f, "D%d = %08x A%d = %08x F%d = %08x%08x (%12g)\n", + i, env->dregs[i], i, env->aregs[i], + i, u.l.upper, u.l.lower, *(double *)&u.d); } cpu_fprintf (f, "PC = %08x ", env->pc); - sr = env->sr; - cpu_fprintf (f, "SR = %04x %c%c%c%c%c ", sr, (sr & 0x10) ? 'X' : '-', - (sr & CCF_N) ? 'N' : '-', (sr & CCF_Z) ? 'Z' : '-', - (sr & CCF_V) ? 'V' : '-', (sr & CCF_C) ? 'C' : '-'); + sr = env->sr | cpu_m68k_get_ccr(env); + cpu_fprintf(f, "SR = %04x %c%c%c%c%c ", sr, (sr & CCF_X) ? 'X' : '-', + (sr & CCF_N) ? 'N' : '-', (sr & CCF_Z) ? 'Z' : '-', + (sr & CCF_V) ? 'V' : '-', (sr & CCF_C) ? 'C' : '-'); cpu_fprintf (f, "FPRESULT = %12g\n", *(double *)&env->fp_result); } void restore_state_to_opc(CPUM68KState *env, TranslationBlock *tb, target_ulong *data) { + int cc_op = data[1]; env->pc = data[0]; + if (cc_op != CC_OP_DYNAMIC) { + env->cc_op = cc_op; + } } diff --git a/target-microblaze/cpu.c b/target-microblaze/cpu.c index 8edc00a..389c7b6 100644 --- a/target-microblaze/cpu.c +++ b/target-microblaze/cpu.c @@ -138,6 +138,13 @@ static void mb_cpu_realizefn(DeviceState *dev, Error **errp) CPUMBState *env = &cpu->env; uint8_t version_code = 0; int i = 0; + Error *local_err = NULL; + + cpu_exec_realizefn(cs, &local_err); + if (local_err != NULL) { + error_propagate(errp, local_err); + return; + } qemu_init_vcpu(cs); @@ -199,7 +206,6 @@ static void mb_cpu_initfn(Object *obj) static bool tcg_initialized; cs->env_ptr = env; - cpu_exec_init(cs, &error_abort); set_float_rounding_mode(float_round_nearest_even, &env->fp_status); @@ -267,12 +273,6 @@ static void mb_cpu_class_init(ObjectClass *oc, void *data) cc->gdb_num_core_regs = 32 + 5; cc->disas_set_info = mb_disas_set_info; - - /* - * Reason: mb_cpu_initfn() calls cpu_exec_init(), which saves the - * object in cpus -> dangling pointer after final object_unref(). - */ - dc->cannot_destroy_with_object_finalize_yet = true; } static const TypeInfo mb_cpu_type_info = { diff --git a/target-mips/cpu.c b/target-mips/cpu.c index 64ad112..65ca607 100644 --- a/target-mips/cpu.c +++ b/target-mips/cpu.c @@ -124,6 +124,13 @@ static void mips_cpu_realizefn(DeviceState *dev, Error **errp) { CPUState *cs = CPU(dev); MIPSCPUClass *mcc = MIPS_CPU_GET_CLASS(dev); + Error *local_err = NULL; + + cpu_exec_realizefn(cs, &local_err); + if (local_err != NULL) { + error_propagate(errp, local_err); + return; + } cpu_reset(cs); qemu_init_vcpu(cs); @@ -138,7 +145,6 @@ static void mips_cpu_initfn(Object *obj) CPUMIPSState *env = &cpu->env; cs->env_ptr = env; - cpu_exec_init(cs, &error_abort); if (tcg_enabled()) { mips_tcg_init(); @@ -177,13 +183,6 @@ static void mips_cpu_class_init(ObjectClass *c, void *data) cc->gdb_num_core_regs = 73; cc->gdb_stop_before_watchpoint = true; - - /* - * Reason: mips_cpu_initfn() calls cpu_exec_init(), which saves - * the object in cpus -> dangling pointer after final - * object_unref(). - */ - dc->cannot_destroy_with_object_finalize_yet = true; } static const TypeInfo mips_cpu_type_info = { diff --git a/target-moxie/cpu.c b/target-moxie/cpu.c index 50a0899..b0be4a7 100644 --- a/target-moxie/cpu.c +++ b/target-moxie/cpu.c @@ -61,6 +61,13 @@ static void moxie_cpu_realizefn(DeviceState *dev, Error **errp) { CPUState *cs = CPU(dev); MoxieCPUClass *mcc = MOXIE_CPU_GET_CLASS(dev); + Error *local_err = NULL; + + cpu_exec_realizefn(cs, &local_err); + if (local_err != NULL) { + error_propagate(errp, local_err); + return; + } qemu_init_vcpu(cs); cpu_reset(cs); @@ -75,7 +82,6 @@ static void moxie_cpu_initfn(Object *obj) static int inited; cs->env_ptr = &cpu->env; - cpu_exec_init(cs, &error_abort); if (tcg_enabled() && !inited) { inited = 1; @@ -124,13 +130,6 @@ static void moxie_cpu_class_init(ObjectClass *oc, void *data) cc->vmsd = &vmstate_moxie_cpu; #endif cc->disas_set_info = moxie_cpu_disas_set_info; - - /* - * Reason: moxie_cpu_initfn() calls cpu_exec_init(), which saves - * the object in cpus -> dangling pointer after final - * object_unref(). - */ - dc->cannot_destroy_with_object_finalize_yet = true; } static void moxielite_initfn(Object *obj) diff --git a/target-openrisc/cpu.c b/target-openrisc/cpu.c index 155913f..698e87b 100644 --- a/target-openrisc/cpu.c +++ b/target-openrisc/cpu.c @@ -81,6 +81,13 @@ static void openrisc_cpu_realizefn(DeviceState *dev, Error **errp) { CPUState *cs = CPU(dev); OpenRISCCPUClass *occ = OPENRISC_CPU_GET_CLASS(dev); + Error *local_err = NULL; + + cpu_exec_realizefn(cs, &local_err); + if (local_err != NULL) { + error_propagate(errp, local_err); + return; + } qemu_init_vcpu(cs); cpu_reset(cs); @@ -95,7 +102,6 @@ static void openrisc_cpu_initfn(Object *obj) static int inited; cs->env_ptr = &cpu->env; - cpu_exec_init(cs, &error_abort); #ifndef CONFIG_USER_ONLY cpu_openrisc_mmu_init(cpu); @@ -180,13 +186,6 @@ static void openrisc_cpu_class_init(ObjectClass *oc, void *data) dc->vmsd = &vmstate_openrisc_cpu; #endif cc->gdb_num_core_regs = 32 + 3; - - /* - * Reason: openrisc_cpu_initfn() calls cpu_exec_init(), which saves - * the object in cpus -> dangling pointer after final - * object_unref(). - */ - dc->cannot_destroy_with_object_finalize_yet = true; } static void cpu_register(const OpenRISCCPUInfo *info) diff --git a/target-ppc/cpu-qom.h b/target-ppc/cpu-qom.h index 713deef..d46c31a 100644 --- a/target-ppc/cpu-qom.h +++ b/target-ppc/cpu-qom.h @@ -174,6 +174,7 @@ typedef struct PowerPCCPUClass { /*< public >*/ DeviceRealize parent_realize; + DeviceUnrealize parent_unrealize; void (*parent_reset)(CPUState *cpu); uint32_t pvr; diff --git a/target-ppc/translate_init.c b/target-ppc/translate_init.c index b66b40b..208fa1e 100644 --- a/target-ppc/translate_init.c +++ b/target-ppc/translate_init.c @@ -9678,7 +9678,7 @@ static void ppc_cpu_realizefn(DeviceState *dev, Error **errp) } #endif - cpu_exec_init(cs, &local_err); + cpu_exec_realizefn(cs, &local_err); if (local_err != NULL) { error_propagate(errp, local_err); return; @@ -9906,11 +9906,17 @@ static void ppc_cpu_realizefn(DeviceState *dev, Error **errp) static void ppc_cpu_unrealizefn(DeviceState *dev, Error **errp) { PowerPCCPU *cpu = POWERPC_CPU(dev); + PowerPCCPUClass *pcc = POWERPC_CPU_GET_CLASS(cpu); CPUPPCState *env = &cpu->env; + Error *local_err = NULL; opc_handler_t **table, **table_2; int i, j, k; - cpu_exec_exit(CPU(dev)); + pcc->parent_unrealize(dev, &local_err); + if (local_err != NULL) { + error_propagate(errp, local_err); + return; + } for (i = 0; i < PPC_CPU_OPCODES_LEN; i++) { if (env->opcodes[i] == &invalid_handler) { @@ -10521,6 +10527,7 @@ static void ppc_cpu_class_init(ObjectClass *oc, void *data) DeviceClass *dc = DEVICE_CLASS(oc); pcc->parent_realize = dc->realize; + pcc->parent_unrealize = dc->unrealize; pcc->pvr_match = ppc_pvr_match_default; pcc->interrupts_big_endian = ppc_cpu_interrupts_big_endian_always; dc->realize = ppc_cpu_realizefn; diff --git a/target-s390x/cpu.c b/target-s390x/cpu.c index 35ae2ce..9e2f239 100644 --- a/target-s390x/cpu.c +++ b/target-s390x/cpu.c @@ -207,7 +207,7 @@ static void s390_cpu_realizefn(DeviceState *dev, Error **errp) goto out; } - cpu_exec_init(cs, &err); + cpu_exec_realizefn(cs, &err); if (err != NULL) { goto out; } @@ -440,12 +440,6 @@ static void s390_cpu_class_init(ObjectClass *oc, void *data) cc->gdb_core_xml_file = "s390x-core64.xml"; cc->gdb_arch_name = s390_gdb_arch_name; - /* - * Reason: s390_cpu_realizefn() calls cpu_exec_init(), which saves - * the object in cpus -> dangling pointer after final - * object_unref(). - */ - dc->cannot_destroy_with_object_finalize_yet = true; s390_cpu_model_class_register_props(oc); } diff --git a/target-s390x/cpu_models.c b/target-s390x/cpu_models.c index 3ff6a70..c1e729d 100644 --- a/target-s390x/cpu_models.c +++ b/target-s390x/cpu_models.c @@ -17,7 +17,7 @@ #include "qapi/visitor.h" #include "qemu/error-report.h" #include "qapi/qmp/qerror.h" -#include "qapi/qmp-input-visitor.h" +#include "qapi/qobject-input-visitor.h" #include "qapi/qmp/qbool.h" #ifndef CONFIG_USER_ONLY #include "sysemu/arch_init.h" @@ -345,7 +345,7 @@ static void cpu_model_from_info(S390CPUModel *model, const CpuModelInfo *info, } if (qdict) { - visitor = qmp_input_visitor_new(info->props, true); + visitor = qobject_input_visitor_new(info->props, true); visit_start_struct(visitor, NULL, NULL, 0, errp); if (*errp) { object_unref(obj); diff --git a/target-sh4/cpu.c b/target-sh4/cpu.c index f589532..a38f6a6 100644 --- a/target-sh4/cpu.c +++ b/target-sh4/cpu.c @@ -244,6 +244,13 @@ static void superh_cpu_realizefn(DeviceState *dev, Error **errp) { CPUState *cs = CPU(dev); SuperHCPUClass *scc = SUPERH_CPU_GET_CLASS(dev); + Error *local_err = NULL; + + cpu_exec_realizefn(cs, &local_err); + if (local_err != NULL) { + error_propagate(errp, local_err); + return; + } cpu_reset(cs); qemu_init_vcpu(cs); @@ -258,7 +265,6 @@ static void superh_cpu_initfn(Object *obj) CPUSH4State *env = &cpu->env; cs->env_ptr = env; - cpu_exec_init(cs, &error_abort); env->movcal_backup_tail = &(env->movcal_backup); @@ -303,13 +309,6 @@ static void superh_cpu_class_init(ObjectClass *oc, void *data) cc->gdb_num_core_regs = 59; dc->vmsd = &vmstate_sh_cpu; - - /* - * Reason: superh_cpu_initfn() calls cpu_exec_init(), which saves - * the object in cpus -> dangling pointer after final - * object_unref(). - */ - dc->cannot_destroy_with_object_finalize_yet = true; } static const TypeInfo superh_cpu_type_info = { diff --git a/target-sparc/cpu.c b/target-sparc/cpu.c index 800a25a..4e07b92 100644 --- a/target-sparc/cpu.c +++ b/target-sparc/cpu.c @@ -792,7 +792,9 @@ static bool sparc_cpu_has_work(CPUState *cs) static void sparc_cpu_realizefn(DeviceState *dev, Error **errp) { + CPUState *cs = CPU(dev); SPARCCPUClass *scc = SPARC_CPU_GET_CLASS(dev); + Error *local_err = NULL; #if defined(CONFIG_USER_ONLY) SPARCCPU *cpu = SPARC_CPU(dev); CPUSPARCState *env = &cpu->env; @@ -802,7 +804,13 @@ static void sparc_cpu_realizefn(DeviceState *dev, Error **errp) } #endif - qemu_init_vcpu(CPU(dev)); + cpu_exec_realizefn(cs, &local_err); + if (local_err != NULL) { + error_propagate(errp, local_err); + return; + } + + qemu_init_vcpu(cs); scc->parent_realize(dev, errp); } @@ -814,7 +822,6 @@ static void sparc_cpu_initfn(Object *obj) CPUSPARCState *env = &cpu->env; cs->env_ptr = env; - cpu_exec_init(cs, &error_abort); if (tcg_enabled()) { gen_intermediate_code_init(env); @@ -867,13 +874,6 @@ static void sparc_cpu_class_init(ObjectClass *oc, void *data) #else cc->gdb_num_core_regs = 72; #endif - - /* - * Reason: sparc_cpu_initfn() calls cpu_exec_init(), which saves - * the object in cpus -> dangling pointer after final - * object_unref(). - */ - dc->cannot_destroy_with_object_finalize_yet = true; } static const TypeInfo sparc_cpu_type_info = { diff --git a/target-tilegx/cpu.c b/target-tilegx/cpu.c index 7017cb6..454793f 100644 --- a/target-tilegx/cpu.c +++ b/target-tilegx/cpu.c @@ -92,6 +92,13 @@ static void tilegx_cpu_realizefn(DeviceState *dev, Error **errp) { CPUState *cs = CPU(dev); TileGXCPUClass *tcc = TILEGX_CPU_GET_CLASS(dev); + Error *local_err = NULL; + + cpu_exec_realizefn(cs, &local_err); + if (local_err != NULL) { + error_propagate(errp, local_err); + return; + } cpu_reset(cs); qemu_init_vcpu(cs); @@ -107,7 +114,6 @@ static void tilegx_cpu_initfn(Object *obj) static bool tcg_initialized; cs->env_ptr = env; - cpu_exec_init(cs, &error_abort); if (tcg_enabled() && !tcg_initialized) { tcg_initialized = true; @@ -162,13 +168,6 @@ static void tilegx_cpu_class_init(ObjectClass *oc, void *data) cc->set_pc = tilegx_cpu_set_pc; cc->handle_mmu_fault = tilegx_cpu_handle_mmu_fault; cc->gdb_num_core_regs = 0; - - /* - * Reason: tilegx_cpu_initfn() calls cpu_exec_init(), which saves - * the object in cpus -> dangling pointer after final - * object_unref(). - */ - dc->cannot_destroy_with_object_finalize_yet = true; } static const TypeInfo tilegx_cpu_type_info = { diff --git a/target-tricore/cpu.c b/target-tricore/cpu.c index 35d4ee4..785b76b 100644 --- a/target-tricore/cpu.c +++ b/target-tricore/cpu.c @@ -69,6 +69,13 @@ static void tricore_cpu_realizefn(DeviceState *dev, Error **errp) TriCoreCPU *cpu = TRICORE_CPU(dev); TriCoreCPUClass *tcc = TRICORE_CPU_GET_CLASS(dev); CPUTriCoreState *env = &cpu->env; + Error *local_err = NULL; + + cpu_exec_realizefn(cs, &local_err); + if (local_err != NULL) { + error_propagate(errp, local_err); + return; + } /* Some features automatically imply others */ if (tricore_feature(env, TRICORE_FEATURE_161)) { @@ -95,7 +102,6 @@ static void tricore_cpu_initfn(Object *obj) CPUTriCoreState *env = &cpu->env; cs->env_ptr = env; - cpu_exec_init(cs, &error_abort); if (tcg_enabled()) { tricore_tcg_init(); @@ -172,13 +178,6 @@ static void tricore_cpu_class_init(ObjectClass *c, void *data) cc->dump_state = tricore_cpu_dump_state; cc->set_pc = tricore_cpu_set_pc; cc->synchronize_from_tb = tricore_cpu_synchronize_from_tb; - - /* - * Reason: tricore_cpu_initfn() calls cpu_exec_init(), which saves - * the object in cpus -> dangling pointer after final - * object_unref(). - */ - dc->cannot_destroy_with_object_finalize_yet = true; } static void cpu_register(const TriCoreCPUInfo *info) diff --git a/target-unicore32/cpu.c b/target-unicore32/cpu.c index e7a4984..c169972 100644 --- a/target-unicore32/cpu.c +++ b/target-unicore32/cpu.c @@ -101,9 +101,17 @@ static const UniCore32CPUInfo uc32_cpus[] = { static void uc32_cpu_realizefn(DeviceState *dev, Error **errp) { + CPUState *cs = CPU(dev); UniCore32CPUClass *ucc = UNICORE32_CPU_GET_CLASS(dev); + Error *local_err = NULL; - qemu_init_vcpu(CPU(dev)); + cpu_exec_realizefn(cs, &local_err); + if (local_err != NULL) { + error_propagate(errp, local_err); + return; + } + + qemu_init_vcpu(cs); ucc->parent_realize(dev, errp); } @@ -116,7 +124,6 @@ static void uc32_cpu_initfn(Object *obj) static bool inited; cs->env_ptr = env; - cpu_exec_init(cs, &error_abort); #ifdef CONFIG_USER_ONLY env->uncached_asr = ASR_MODE_USER; @@ -160,13 +167,6 @@ static void uc32_cpu_class_init(ObjectClass *oc, void *data) cc->get_phys_page_debug = uc32_cpu_get_phys_page_debug; #endif dc->vmsd = &vmstate_uc32_cpu; - - /* - * Reason: uc32_cpu_initfn() calls cpu_exec_init(), which saves - * the object in cpus -> dangling pointer after final - * object_unref(). - */ - dc->cannot_destroy_with_object_finalize_yet = true; } static void uc32_register_cpu_type(const UniCore32CPUInfo *info) diff --git a/target-xtensa/cpu.c b/target-xtensa/cpu.c index 5ad08a2..e8e9f91 100644 --- a/target-xtensa/cpu.c +++ b/target-xtensa/cpu.c @@ -99,6 +99,13 @@ static void xtensa_cpu_realizefn(DeviceState *dev, Error **errp) { CPUState *cs = CPU(dev); XtensaCPUClass *xcc = XTENSA_CPU_GET_CLASS(dev); + Error *local_err = NULL; + + cpu_exec_realizefn(cs, &local_err); + if (local_err != NULL) { + error_propagate(errp, local_err); + return; + } cs->gdb_num_regs = xcc->config->gdb_regmap.num_regs; @@ -117,7 +124,6 @@ static void xtensa_cpu_initfn(Object *obj) cs->env_ptr = env; env->config = xcc->config; - cpu_exec_init(cs, &error_abort); if (tcg_enabled() && !tcg_inited) { tcg_inited = true; @@ -158,13 +164,6 @@ static void xtensa_cpu_class_init(ObjectClass *oc, void *data) #endif cc->debug_excp_handler = xtensa_breakpoint_handler; dc->vmsd = &vmstate_xtensa_cpu; - - /* - * Reason: xtensa_cpu_initfn() calls cpu_exec_init(), which saves - * the object in cpus -> dangling pointer after final - * object_unref(). - */ - dc->cannot_destroy_with_object_finalize_yet = true; } static const TypeInfo xtensa_cpu_type_info = { diff --git a/tcg-runtime.c b/tcg-runtime.c index ea2ad64..9327b6f 100644 --- a/tcg-runtime.c +++ b/tcg-runtime.c @@ -23,17 +23,10 @@ */ #include "qemu/osdep.h" #include "qemu/host-utils.h" - -/* This file is compiled once, and thus we can't include the standard - "exec/helper-proto.h", which has includes that are target specific. */ - -#include "exec/helper-head.h" - -#define DEF_HELPER_FLAGS_2(name, flags, ret, t1, t2) \ - dh_ctype(ret) HELPER(name) (dh_ctype(t1), dh_ctype(t2)); - -#include "tcg-runtime.h" - +#include "cpu.h" +#include "exec/helper-proto.h" +#include "exec/cpu_ldst.h" +#include "exec/exec-all.h" /* 32-bit helpers */ @@ -107,3 +100,62 @@ int64_t HELPER(mulsh_i64)(int64_t arg1, int64_t arg2) muls64(&l, &h, arg1, arg2); return h; } + +void HELPER(exit_atomic)(CPUArchState *env) +{ + cpu_loop_exit_atomic(ENV_GET_CPU(env), GETPC()); +} + +#ifndef CONFIG_SOFTMMU +/* The softmmu versions of these helpers are in cputlb.c. */ + +/* Do not allow unaligned operations to proceed. Return the host address. */ +static void *atomic_mmu_lookup(CPUArchState *env, target_ulong addr, + int size, uintptr_t retaddr) +{ + /* Enforce qemu required alignment. */ + if (unlikely(addr & (size - 1))) { + cpu_loop_exit_atomic(ENV_GET_CPU(env), retaddr); + } + return g2h(addr); +} + +/* Macro to call the above, with local variables from the use context. */ +#define ATOMIC_MMU_LOOKUP atomic_mmu_lookup(env, addr, DATA_SIZE, GETPC()) + +#define ATOMIC_NAME(X) HELPER(glue(glue(atomic_ ## X, SUFFIX), END)) +#define EXTRA_ARGS + +#define DATA_SIZE 1 +#include "atomic_template.h" + +#define DATA_SIZE 2 +#include "atomic_template.h" + +#define DATA_SIZE 4 +#include "atomic_template.h" + +#ifdef CONFIG_ATOMIC64 +#define DATA_SIZE 8 +#include "atomic_template.h" +#endif + +/* The following is only callable from other helpers, and matches up + with the softmmu version. */ + +#ifdef CONFIG_ATOMIC128 + +#undef EXTRA_ARGS +#undef ATOMIC_NAME +#undef ATOMIC_MMU_LOOKUP + +#define EXTRA_ARGS , TCGMemOpIdx oi, uintptr_t retaddr +#define ATOMIC_NAME(X) \ + HELPER(glue(glue(glue(atomic_ ## X, SUFFIX), END), _mmu)) +#define ATOMIC_MMU_LOOKUP atomic_mmu_lookup(env, addr, DATA_SIZE, retaddr) + +#define DATA_SIZE 16 +#include "atomic_template.h" +#endif /* CONFIG_ATOMIC128 */ + +#endif /* !CONFIG_SOFTMMU */ diff --git a/tcg/tcg-op.c b/tcg/tcg-op.c index 291d50b..bb2bfee 100644 --- a/tcg/tcg-op.c +++ b/tcg/tcg-op.c @@ -150,17 +150,7 @@ void tcg_gen_op6(TCGContext *ctx, TCGOpcode opc, TCGArg a1, TCGArg a2, void tcg_gen_mb(TCGBar mb_type) { - bool emit_barriers = true; - -#ifndef CONFIG_USER_ONLY - /* TODO: When MTTCG is available for system mode, we will check - * the following condition and enable emit_barriers - * (qemu_tcg_mttcg_enabled() && smp_cpus > 1) - */ - emit_barriers = false; -#endif - - if (emit_barriers) { + if (parallel_cpus) { tcg_gen_op1(&tcg_ctx, INDEX_op_mb, mb_type); } } @@ -1975,3 +1965,345 @@ void tcg_gen_qemu_st_i64(TCGv_i64 val, TCGv addr, TCGArg idx, TCGMemOp memop) addr, trace_mem_get_info(memop, 1)); gen_ldst_i64(INDEX_op_qemu_st_i64, val, addr, memop, idx); } + +static void tcg_gen_ext_i32(TCGv_i32 ret, TCGv_i32 val, TCGMemOp opc) +{ + switch (opc & MO_SSIZE) { + case MO_SB: + tcg_gen_ext8s_i32(ret, val); + break; + case MO_UB: + tcg_gen_ext8u_i32(ret, val); + break; + case MO_SW: + tcg_gen_ext16s_i32(ret, val); + break; + case MO_UW: + tcg_gen_ext16u_i32(ret, val); + break; + default: + tcg_gen_mov_i32(ret, val); + break; + } +} + +static void tcg_gen_ext_i64(TCGv_i64 ret, TCGv_i64 val, TCGMemOp opc) +{ + switch (opc & MO_SSIZE) { + case MO_SB: + tcg_gen_ext8s_i64(ret, val); + break; + case MO_UB: + tcg_gen_ext8u_i64(ret, val); + break; + case MO_SW: + tcg_gen_ext16s_i64(ret, val); + break; + case MO_UW: + tcg_gen_ext16u_i64(ret, val); + break; + case MO_SL: + tcg_gen_ext32s_i64(ret, val); + break; + case MO_UL: + tcg_gen_ext32u_i64(ret, val); + break; + default: + tcg_gen_mov_i64(ret, val); + break; + } +} + +#ifdef CONFIG_SOFTMMU +typedef void (*gen_atomic_cx_i32)(TCGv_i32, TCGv_env, TCGv, + TCGv_i32, TCGv_i32, TCGv_i32); +typedef void (*gen_atomic_cx_i64)(TCGv_i64, TCGv_env, TCGv, + TCGv_i64, TCGv_i64, TCGv_i32); +typedef void (*gen_atomic_op_i32)(TCGv_i32, TCGv_env, TCGv, + TCGv_i32, TCGv_i32); +typedef void (*gen_atomic_op_i64)(TCGv_i64, TCGv_env, TCGv, + TCGv_i64, TCGv_i32); +#else +typedef void (*gen_atomic_cx_i32)(TCGv_i32, TCGv_env, TCGv, TCGv_i32, TCGv_i32); +typedef void (*gen_atomic_cx_i64)(TCGv_i64, TCGv_env, TCGv, TCGv_i64, TCGv_i64); +typedef void (*gen_atomic_op_i32)(TCGv_i32, TCGv_env, TCGv, TCGv_i32); +typedef void (*gen_atomic_op_i64)(TCGv_i64, TCGv_env, TCGv, TCGv_i64); +#endif + +#ifdef CONFIG_ATOMIC64 +# define WITH_ATOMIC64(X) X, +#else +# define WITH_ATOMIC64(X) +#endif + +static void * const table_cmpxchg[16] = { + [MO_8] = gen_helper_atomic_cmpxchgb, + [MO_16 | MO_LE] = gen_helper_atomic_cmpxchgw_le, + [MO_16 | MO_BE] = gen_helper_atomic_cmpxchgw_be, + [MO_32 | MO_LE] = gen_helper_atomic_cmpxchgl_le, + [MO_32 | MO_BE] = gen_helper_atomic_cmpxchgl_be, + WITH_ATOMIC64([MO_64 | MO_LE] = gen_helper_atomic_cmpxchgq_le) + WITH_ATOMIC64([MO_64 | MO_BE] = gen_helper_atomic_cmpxchgq_be) +}; + +void tcg_gen_atomic_cmpxchg_i32(TCGv_i32 retv, TCGv addr, TCGv_i32 cmpv, + TCGv_i32 newv, TCGArg idx, TCGMemOp memop) +{ + memop = tcg_canonicalize_memop(memop, 0, 0); + + if (!parallel_cpus) { + TCGv_i32 t1 = tcg_temp_new_i32(); + TCGv_i32 t2 = tcg_temp_new_i32(); + + tcg_gen_ext_i32(t2, cmpv, memop & MO_SIZE); + + tcg_gen_qemu_ld_i32(t1, addr, idx, memop & ~MO_SIGN); + tcg_gen_movcond_i32(TCG_COND_EQ, t2, t1, t2, newv, t1); + tcg_gen_qemu_st_i32(t2, addr, idx, memop); + tcg_temp_free_i32(t2); + + if (memop & MO_SIGN) { + tcg_gen_ext_i32(retv, t1, memop); + } else { + tcg_gen_mov_i32(retv, t1); + } + tcg_temp_free_i32(t1); + } else { + gen_atomic_cx_i32 gen; + + gen = table_cmpxchg[memop & (MO_SIZE | MO_BSWAP)]; + tcg_debug_assert(gen != NULL); + +#ifdef CONFIG_SOFTMMU + { + TCGv_i32 oi = tcg_const_i32(make_memop_idx(memop & ~MO_SIGN, idx)); + gen(retv, tcg_ctx.tcg_env, addr, cmpv, newv, oi); + tcg_temp_free_i32(oi); + } +#else + gen(retv, tcg_ctx.tcg_env, addr, cmpv, newv); +#endif + + if (memop & MO_SIGN) { + tcg_gen_ext_i32(retv, retv, memop); + } + } +} + +void tcg_gen_atomic_cmpxchg_i64(TCGv_i64 retv, TCGv addr, TCGv_i64 cmpv, + TCGv_i64 newv, TCGArg idx, TCGMemOp memop) +{ + memop = tcg_canonicalize_memop(memop, 1, 0); + + if (!parallel_cpus) { + TCGv_i64 t1 = tcg_temp_new_i64(); + TCGv_i64 t2 = tcg_temp_new_i64(); + + tcg_gen_ext_i64(t2, cmpv, memop & MO_SIZE); + + tcg_gen_qemu_ld_i64(t1, addr, idx, memop & ~MO_SIGN); + tcg_gen_movcond_i64(TCG_COND_EQ, t2, t1, t2, newv, t1); + tcg_gen_qemu_st_i64(t2, addr, idx, memop); + tcg_temp_free_i64(t2); + + if (memop & MO_SIGN) { + tcg_gen_ext_i64(retv, t1, memop); + } else { + tcg_gen_mov_i64(retv, t1); + } + tcg_temp_free_i64(t1); + } else if ((memop & MO_SIZE) == MO_64) { +#ifdef CONFIG_ATOMIC64 + gen_atomic_cx_i64 gen; + + gen = table_cmpxchg[memop & (MO_SIZE | MO_BSWAP)]; + tcg_debug_assert(gen != NULL); + +#ifdef CONFIG_SOFTMMU + { + TCGv_i32 oi = tcg_const_i32(make_memop_idx(memop, idx)); + gen(retv, tcg_ctx.tcg_env, addr, cmpv, newv, oi); + tcg_temp_free_i32(oi); + } +#else + gen(retv, tcg_ctx.tcg_env, addr, cmpv, newv); +#endif +#else + gen_helper_exit_atomic(tcg_ctx.tcg_env); +#endif /* CONFIG_ATOMIC64 */ + } else { + TCGv_i32 c32 = tcg_temp_new_i32(); + TCGv_i32 n32 = tcg_temp_new_i32(); + TCGv_i32 r32 = tcg_temp_new_i32(); + + tcg_gen_extrl_i64_i32(c32, cmpv); + tcg_gen_extrl_i64_i32(n32, newv); + tcg_gen_atomic_cmpxchg_i32(r32, addr, c32, n32, idx, memop & ~MO_SIGN); + tcg_temp_free_i32(c32); + tcg_temp_free_i32(n32); + + tcg_gen_extu_i32_i64(retv, r32); + tcg_temp_free_i32(r32); + + if (memop & MO_SIGN) { + tcg_gen_ext_i64(retv, retv, memop); + } + } +} + +static void do_nonatomic_op_i32(TCGv_i32 ret, TCGv addr, TCGv_i32 val, + TCGArg idx, TCGMemOp memop, bool new_val, + void (*gen)(TCGv_i32, TCGv_i32, TCGv_i32)) +{ + TCGv_i32 t1 = tcg_temp_new_i32(); + TCGv_i32 t2 = tcg_temp_new_i32(); + + memop = tcg_canonicalize_memop(memop, 0, 0); + + tcg_gen_qemu_ld_i32(t1, addr, idx, memop & ~MO_SIGN); + gen(t2, t1, val); + tcg_gen_qemu_st_i32(t2, addr, idx, memop); + + tcg_gen_ext_i32(ret, (new_val ? t2 : t1), memop); + tcg_temp_free_i32(t1); + tcg_temp_free_i32(t2); +} + +static void do_atomic_op_i32(TCGv_i32 ret, TCGv addr, TCGv_i32 val, + TCGArg idx, TCGMemOp memop, void * const table[]) +{ + gen_atomic_op_i32 gen; + + memop = tcg_canonicalize_memop(memop, 0, 0); + + gen = table[memop & (MO_SIZE | MO_BSWAP)]; + tcg_debug_assert(gen != NULL); + +#ifdef CONFIG_SOFTMMU + { + TCGv_i32 oi = tcg_const_i32(make_memop_idx(memop & ~MO_SIGN, idx)); + gen(ret, tcg_ctx.tcg_env, addr, val, oi); + tcg_temp_free_i32(oi); + } +#else + gen(ret, tcg_ctx.tcg_env, addr, val); +#endif + + if (memop & MO_SIGN) { + tcg_gen_ext_i32(ret, ret, memop); + } +} + +static void do_nonatomic_op_i64(TCGv_i64 ret, TCGv addr, TCGv_i64 val, + TCGArg idx, TCGMemOp memop, bool new_val, + void (*gen)(TCGv_i64, TCGv_i64, TCGv_i64)) +{ + TCGv_i64 t1 = tcg_temp_new_i64(); + TCGv_i64 t2 = tcg_temp_new_i64(); + + memop = tcg_canonicalize_memop(memop, 1, 0); + + tcg_gen_qemu_ld_i64(t1, addr, idx, memop & ~MO_SIGN); + gen(t2, t1, val); + tcg_gen_qemu_st_i64(t2, addr, idx, memop); + + tcg_gen_ext_i64(ret, (new_val ? t2 : t1), memop); + tcg_temp_free_i64(t1); + tcg_temp_free_i64(t2); +} + +static void do_atomic_op_i64(TCGv_i64 ret, TCGv addr, TCGv_i64 val, + TCGArg idx, TCGMemOp memop, void * const table[]) +{ + memop = tcg_canonicalize_memop(memop, 1, 0); + + if ((memop & MO_SIZE) == MO_64) { +#ifdef CONFIG_ATOMIC64 + gen_atomic_op_i64 gen; + + gen = table[memop & (MO_SIZE | MO_BSWAP)]; + tcg_debug_assert(gen != NULL); + +#ifdef CONFIG_SOFTMMU + { + TCGv_i32 oi = tcg_const_i32(make_memop_idx(memop & ~MO_SIGN, idx)); + gen(ret, tcg_ctx.tcg_env, addr, val, oi); + tcg_temp_free_i32(oi); + } +#else + gen(ret, tcg_ctx.tcg_env, addr, val); +#endif +#else + gen_helper_exit_atomic(tcg_ctx.tcg_env); +#endif /* CONFIG_ATOMIC64 */ + } else { + TCGv_i32 v32 = tcg_temp_new_i32(); + TCGv_i32 r32 = tcg_temp_new_i32(); + + tcg_gen_extrl_i64_i32(v32, val); + do_atomic_op_i32(r32, addr, v32, idx, memop & ~MO_SIGN, table); + tcg_temp_free_i32(v32); + + tcg_gen_extu_i32_i64(ret, r32); + tcg_temp_free_i32(r32); + + if (memop & MO_SIGN) { + tcg_gen_ext_i64(ret, ret, memop); + } + } +} + +#define GEN_ATOMIC_HELPER(NAME, OP, NEW) \ +static void * const table_##NAME[16] = { \ + [MO_8] = gen_helper_atomic_##NAME##b, \ + [MO_16 | MO_LE] = gen_helper_atomic_##NAME##w_le, \ + [MO_16 | MO_BE] = gen_helper_atomic_##NAME##w_be, \ + [MO_32 | MO_LE] = gen_helper_atomic_##NAME##l_le, \ + [MO_32 | MO_BE] = gen_helper_atomic_##NAME##l_be, \ + WITH_ATOMIC64([MO_64 | MO_LE] = gen_helper_atomic_##NAME##q_le) \ + WITH_ATOMIC64([MO_64 | MO_BE] = gen_helper_atomic_##NAME##q_be) \ +}; \ +void tcg_gen_atomic_##NAME##_i32 \ + (TCGv_i32 ret, TCGv addr, TCGv_i32 val, TCGArg idx, TCGMemOp memop) \ +{ \ + if (parallel_cpus) { \ + do_atomic_op_i32(ret, addr, val, idx, memop, table_##NAME); \ + } else { \ + do_nonatomic_op_i32(ret, addr, val, idx, memop, NEW, \ + tcg_gen_##OP##_i32); \ + } \ +} \ +void tcg_gen_atomic_##NAME##_i64 \ + (TCGv_i64 ret, TCGv addr, TCGv_i64 val, TCGArg idx, TCGMemOp memop) \ +{ \ + if (parallel_cpus) { \ + do_atomic_op_i64(ret, addr, val, idx, memop, table_##NAME); \ + } else { \ + do_nonatomic_op_i64(ret, addr, val, idx, memop, NEW, \ + tcg_gen_##OP##_i64); \ + } \ +} + +GEN_ATOMIC_HELPER(fetch_add, add, 0) +GEN_ATOMIC_HELPER(fetch_and, and, 0) +GEN_ATOMIC_HELPER(fetch_or, or, 0) +GEN_ATOMIC_HELPER(fetch_xor, xor, 0) + +GEN_ATOMIC_HELPER(add_fetch, add, 1) +GEN_ATOMIC_HELPER(and_fetch, and, 1) +GEN_ATOMIC_HELPER(or_fetch, or, 1) +GEN_ATOMIC_HELPER(xor_fetch, xor, 1) + +static void tcg_gen_mov2_i32(TCGv_i32 r, TCGv_i32 a, TCGv_i32 b) +{ + tcg_gen_mov_i32(r, b); +} + +static void tcg_gen_mov2_i64(TCGv_i64 r, TCGv_i64 a, TCGv_i64 b) +{ + tcg_gen_mov_i64(r, b); +} + +GEN_ATOMIC_HELPER(xchg, mov2, 0) + +#undef GEN_ATOMIC_HELPER diff --git a/tcg/tcg-op.h b/tcg/tcg-op.h index 02cb376..89b59e8 100644 --- a/tcg/tcg-op.h +++ b/tcg/tcg-op.h @@ -854,6 +854,30 @@ static inline void tcg_gen_qemu_st64(TCGv_i64 arg, TCGv addr, int mem_index) tcg_gen_qemu_st_i64(arg, addr, mem_index, MO_TEQ); } +void tcg_gen_atomic_cmpxchg_i32(TCGv_i32, TCGv, TCGv_i32, TCGv_i32, + TCGArg, TCGMemOp); +void tcg_gen_atomic_cmpxchg_i64(TCGv_i64, TCGv, TCGv_i64, TCGv_i64, + TCGArg, TCGMemOp); + +void tcg_gen_atomic_xchg_i32(TCGv_i32, TCGv, TCGv_i32, TCGArg, TCGMemOp); +void tcg_gen_atomic_xchg_i64(TCGv_i64, TCGv, TCGv_i64, TCGArg, TCGMemOp); +void tcg_gen_atomic_fetch_add_i32(TCGv_i32, TCGv, TCGv_i32, TCGArg, TCGMemOp); +void tcg_gen_atomic_fetch_add_i64(TCGv_i64, TCGv, TCGv_i64, TCGArg, TCGMemOp); +void tcg_gen_atomic_fetch_and_i32(TCGv_i32, TCGv, TCGv_i32, TCGArg, TCGMemOp); +void tcg_gen_atomic_fetch_and_i64(TCGv_i64, TCGv, TCGv_i64, TCGArg, TCGMemOp); +void tcg_gen_atomic_fetch_or_i32(TCGv_i32, TCGv, TCGv_i32, TCGArg, TCGMemOp); +void tcg_gen_atomic_fetch_or_i64(TCGv_i64, TCGv, TCGv_i64, TCGArg, TCGMemOp); +void tcg_gen_atomic_fetch_xor_i32(TCGv_i32, TCGv, TCGv_i32, TCGArg, TCGMemOp); +void tcg_gen_atomic_fetch_xor_i64(TCGv_i64, TCGv, TCGv_i64, TCGArg, TCGMemOp); +void tcg_gen_atomic_add_fetch_i32(TCGv_i32, TCGv, TCGv_i32, TCGArg, TCGMemOp); +void tcg_gen_atomic_add_fetch_i64(TCGv_i64, TCGv, TCGv_i64, TCGArg, TCGMemOp); +void tcg_gen_atomic_and_fetch_i32(TCGv_i32, TCGv, TCGv_i32, TCGArg, TCGMemOp); +void tcg_gen_atomic_and_fetch_i64(TCGv_i64, TCGv, TCGv_i64, TCGArg, TCGMemOp); +void tcg_gen_atomic_or_fetch_i32(TCGv_i32, TCGv, TCGv_i32, TCGArg, TCGMemOp); +void tcg_gen_atomic_or_fetch_i64(TCGv_i64, TCGv, TCGv_i64, TCGArg, TCGMemOp); +void tcg_gen_atomic_xor_fetch_i32(TCGv_i32, TCGv, TCGv_i32, TCGArg, TCGMemOp); +void tcg_gen_atomic_xor_fetch_i64(TCGv_i64, TCGv, TCGv_i64, TCGArg, TCGMemOp); + #if TARGET_LONG_BITS == 64 #define tcg_gen_movi_tl tcg_gen_movi_i64 #define tcg_gen_mov_tl tcg_gen_mov_i64 @@ -932,6 +956,16 @@ static inline void tcg_gen_qemu_st64(TCGv_i64 arg, TCGv addr, int mem_index) #define tcg_gen_sub2_tl tcg_gen_sub2_i64 #define tcg_gen_mulu2_tl tcg_gen_mulu2_i64 #define tcg_gen_muls2_tl tcg_gen_muls2_i64 +#define tcg_gen_atomic_cmpxchg_tl tcg_gen_atomic_cmpxchg_i64 +#define tcg_gen_atomic_xchg_tl tcg_gen_atomic_xchg_i64 +#define tcg_gen_atomic_fetch_add_tl tcg_gen_atomic_fetch_add_i64 +#define tcg_gen_atomic_fetch_and_tl tcg_gen_atomic_fetch_and_i64 +#define tcg_gen_atomic_fetch_or_tl tcg_gen_atomic_fetch_or_i64 +#define tcg_gen_atomic_fetch_xor_tl tcg_gen_atomic_fetch_xor_i64 +#define tcg_gen_atomic_add_fetch_tl tcg_gen_atomic_add_fetch_i64 +#define tcg_gen_atomic_and_fetch_tl tcg_gen_atomic_and_fetch_i64 +#define tcg_gen_atomic_or_fetch_tl tcg_gen_atomic_or_fetch_i64 +#define tcg_gen_atomic_xor_fetch_tl tcg_gen_atomic_xor_fetch_i64 #else #define tcg_gen_movi_tl tcg_gen_movi_i32 #define tcg_gen_mov_tl tcg_gen_mov_i32 @@ -1009,6 +1043,16 @@ static inline void tcg_gen_qemu_st64(TCGv_i64 arg, TCGv addr, int mem_index) #define tcg_gen_sub2_tl tcg_gen_sub2_i32 #define tcg_gen_mulu2_tl tcg_gen_mulu2_i32 #define tcg_gen_muls2_tl tcg_gen_muls2_i32 +#define tcg_gen_atomic_cmpxchg_tl tcg_gen_atomic_cmpxchg_i32 +#define tcg_gen_atomic_xchg_tl tcg_gen_atomic_xchg_i32 +#define tcg_gen_atomic_fetch_add_tl tcg_gen_atomic_fetch_add_i32 +#define tcg_gen_atomic_fetch_and_tl tcg_gen_atomic_fetch_and_i32 +#define tcg_gen_atomic_fetch_or_tl tcg_gen_atomic_fetch_or_i32 +#define tcg_gen_atomic_fetch_xor_tl tcg_gen_atomic_fetch_xor_i32 +#define tcg_gen_atomic_add_fetch_tl tcg_gen_atomic_add_fetch_i32 +#define tcg_gen_atomic_and_fetch_tl tcg_gen_atomic_and_fetch_i32 +#define tcg_gen_atomic_or_fetch_tl tcg_gen_atomic_or_fetch_i32 +#define tcg_gen_atomic_xor_fetch_tl tcg_gen_atomic_xor_fetch_i32 #endif #if UINTPTR_MAX == UINT32_MAX diff --git a/tcg/tcg-runtime.h b/tcg/tcg-runtime.h index 23a0c37..1deb86a 100644 --- a/tcg/tcg-runtime.h +++ b/tcg/tcg-runtime.h @@ -14,3 +14,112 @@ DEF_HELPER_FLAGS_2(sar_i64, TCG_CALL_NO_RWG_SE, s64, s64, s64) DEF_HELPER_FLAGS_2(mulsh_i64, TCG_CALL_NO_RWG_SE, s64, s64, s64) DEF_HELPER_FLAGS_2(muluh_i64, TCG_CALL_NO_RWG_SE, i64, i64, i64) + +DEF_HELPER_FLAGS_1(exit_atomic, TCG_CALL_NO_WG, noreturn, env) + +#ifdef CONFIG_SOFTMMU + +DEF_HELPER_FLAGS_5(atomic_cmpxchgb, TCG_CALL_NO_WG, + i32, env, tl, i32, i32, i32) +DEF_HELPER_FLAGS_5(atomic_cmpxchgw_be, TCG_CALL_NO_WG, + i32, env, tl, i32, i32, i32) +DEF_HELPER_FLAGS_5(atomic_cmpxchgw_le, TCG_CALL_NO_WG, + i32, env, tl, i32, i32, i32) +DEF_HELPER_FLAGS_5(atomic_cmpxchgl_be, TCG_CALL_NO_WG, + i32, env, tl, i32, i32, i32) +DEF_HELPER_FLAGS_5(atomic_cmpxchgl_le, TCG_CALL_NO_WG, + i32, env, tl, i32, i32, i32) +#ifdef CONFIG_ATOMIC64 +DEF_HELPER_FLAGS_5(atomic_cmpxchgq_be, TCG_CALL_NO_WG, + i64, env, tl, i64, i64, i32) +DEF_HELPER_FLAGS_5(atomic_cmpxchgq_le, TCG_CALL_NO_WG, + i64, env, tl, i64, i64, i32) +#endif + +#ifdef CONFIG_ATOMIC64 +#define GEN_ATOMIC_HELPERS(NAME) \ + DEF_HELPER_FLAGS_4(glue(glue(atomic_, NAME), b), \ + TCG_CALL_NO_WG, i32, env, tl, i32, i32) \ + DEF_HELPER_FLAGS_4(glue(glue(atomic_, NAME), w_le), \ + TCG_CALL_NO_WG, i32, env, tl, i32, i32) \ + DEF_HELPER_FLAGS_4(glue(glue(atomic_, NAME), w_be), \ + TCG_CALL_NO_WG, i32, env, tl, i32, i32) \ + DEF_HELPER_FLAGS_4(glue(glue(atomic_, NAME), l_le), \ + TCG_CALL_NO_WG, i32, env, tl, i32, i32) \ + DEF_HELPER_FLAGS_4(glue(glue(atomic_, NAME), l_be), \ + TCG_CALL_NO_WG, i32, env, tl, i32, i32) \ + DEF_HELPER_FLAGS_4(glue(glue(atomic_, NAME), q_le), \ + TCG_CALL_NO_WG, i64, env, tl, i64, i32) \ + DEF_HELPER_FLAGS_4(glue(glue(atomic_, NAME), q_be), \ + TCG_CALL_NO_WG, i64, env, tl, i64, i32) +#else +#define GEN_ATOMIC_HELPERS(NAME) \ + DEF_HELPER_FLAGS_4(glue(glue(atomic_, NAME), b), \ + TCG_CALL_NO_WG, i32, env, tl, i32, i32) \ + DEF_HELPER_FLAGS_4(glue(glue(atomic_, NAME), w_le), \ + TCG_CALL_NO_WG, i32, env, tl, i32, i32) \ + DEF_HELPER_FLAGS_4(glue(glue(atomic_, NAME), w_be), \ + TCG_CALL_NO_WG, i32, env, tl, i32, i32) \ + DEF_HELPER_FLAGS_4(glue(glue(atomic_, NAME), l_le), \ + TCG_CALL_NO_WG, i32, env, tl, i32, i32) \ + DEF_HELPER_FLAGS_4(glue(glue(atomic_, NAME), l_be), \ + TCG_CALL_NO_WG, i32, env, tl, i32, i32) +#endif /* CONFIG_ATOMIC64 */ + +#else + +DEF_HELPER_FLAGS_4(atomic_cmpxchgb, TCG_CALL_NO_WG, i32, env, tl, i32, i32) +DEF_HELPER_FLAGS_4(atomic_cmpxchgw_be, TCG_CALL_NO_WG, i32, env, tl, i32, i32) +DEF_HELPER_FLAGS_4(atomic_cmpxchgw_le, TCG_CALL_NO_WG, i32, env, tl, i32, i32) +DEF_HELPER_FLAGS_4(atomic_cmpxchgl_be, TCG_CALL_NO_WG, i32, env, tl, i32, i32) +DEF_HELPER_FLAGS_4(atomic_cmpxchgl_le, TCG_CALL_NO_WG, i32, env, tl, i32, i32) +#ifdef CONFIG_ATOMIC64 +DEF_HELPER_FLAGS_4(atomic_cmpxchgq_be, TCG_CALL_NO_WG, i64, env, tl, i64, i64) +DEF_HELPER_FLAGS_4(atomic_cmpxchgq_le, TCG_CALL_NO_WG, i64, env, tl, i64, i64) +#endif + +#ifdef CONFIG_ATOMIC64 +#define GEN_ATOMIC_HELPERS(NAME) \ + DEF_HELPER_FLAGS_3(glue(glue(atomic_, NAME), b), \ + TCG_CALL_NO_WG, i32, env, tl, i32) \ + DEF_HELPER_FLAGS_3(glue(glue(atomic_, NAME), w_le), \ + TCG_CALL_NO_WG, i32, env, tl, i32) \ + DEF_HELPER_FLAGS_3(glue(glue(atomic_, NAME), w_be), \ + TCG_CALL_NO_WG, i32, env, tl, i32) \ + DEF_HELPER_FLAGS_3(glue(glue(atomic_, NAME), l_le), \ + TCG_CALL_NO_WG, i32, env, tl, i32) \ + DEF_HELPER_FLAGS_3(glue(glue(atomic_, NAME), l_be), \ + TCG_CALL_NO_WG, i32, env, tl, i32) \ + DEF_HELPER_FLAGS_3(glue(glue(atomic_, NAME), q_le), \ + TCG_CALL_NO_WG, i64, env, tl, i64) \ + DEF_HELPER_FLAGS_3(glue(glue(atomic_, NAME), q_be), \ + TCG_CALL_NO_WG, i64, env, tl, i64) +#else +#define GEN_ATOMIC_HELPERS(NAME) \ + DEF_HELPER_FLAGS_3(glue(glue(atomic_, NAME), b), \ + TCG_CALL_NO_WG, i32, env, tl, i32) \ + DEF_HELPER_FLAGS_3(glue(glue(atomic_, NAME), w_le), \ + TCG_CALL_NO_WG, i32, env, tl, i32) \ + DEF_HELPER_FLAGS_3(glue(glue(atomic_, NAME), w_be), \ + TCG_CALL_NO_WG, i32, env, tl, i32) \ + DEF_HELPER_FLAGS_3(glue(glue(atomic_, NAME), l_le), \ + TCG_CALL_NO_WG, i32, env, tl, i32) \ + DEF_HELPER_FLAGS_3(glue(glue(atomic_, NAME), l_be), \ + TCG_CALL_NO_WG, i32, env, tl, i32) +#endif /* CONFIG_ATOMIC64 */ + +#endif /* CONFIG_SOFTMMU */ + +GEN_ATOMIC_HELPERS(fetch_add) +GEN_ATOMIC_HELPERS(fetch_and) +GEN_ATOMIC_HELPERS(fetch_or) +GEN_ATOMIC_HELPERS(fetch_xor) + +GEN_ATOMIC_HELPERS(add_fetch) +GEN_ATOMIC_HELPERS(and_fetch) +GEN_ATOMIC_HELPERS(or_fetch) +GEN_ATOMIC_HELPERS(xor_fetch) + +GEN_ATOMIC_HELPERS(xchg) + +#undef GEN_ATOMIC_HELPERS @@ -2097,15 +2097,9 @@ static void tcg_reg_alloc_bb_end(TCGContext *s, TCGRegSet allocated_regs) save_globals(s, allocated_regs); } -static void tcg_reg_alloc_movi(TCGContext *s, const TCGArg *args, - TCGLifeData arg_life) +static void tcg_reg_alloc_do_movi(TCGContext *s, TCGTemp *ots, + tcg_target_ulong val, TCGLifeData arg_life) { - TCGTemp *ots; - tcg_target_ulong val; - - ots = &s->temps[args[0]]; - val = args[1]; - if (ots->fixed_reg) { /* For fixed registers, we do not do any constant propagation. */ tcg_out_movi(s, ots->type, ots->reg, val); @@ -2126,6 +2120,15 @@ static void tcg_reg_alloc_movi(TCGContext *s, const TCGArg *args, } } +static void tcg_reg_alloc_movi(TCGContext *s, const TCGArg *args, + TCGLifeData arg_life) +{ + TCGTemp *ots = &s->temps[args[0]]; + tcg_target_ulong val = args[1]; + + tcg_reg_alloc_do_movi(s, ots, val, arg_life); +} + static void tcg_reg_alloc_mov(TCGContext *s, const TCGOpDef *def, const TCGArg *args, TCGLifeData arg_life) { @@ -2141,21 +2144,29 @@ static void tcg_reg_alloc_mov(TCGContext *s, const TCGOpDef *def, otype = ots->type; itype = ts->type; - /* If the source value is not in a register, and we're going to be - forced to have it in a register in order to perform the copy, - then copy the SOURCE value into its own register first. That way - we don't have to reload SOURCE the next time it is used. */ - if (((NEED_SYNC_ARG(0) || ots->fixed_reg) && ts->val_type != TEMP_VAL_REG) - || ts->val_type == TEMP_VAL_MEM) { + if (ts->val_type == TEMP_VAL_CONST) { + /* propagate constant or generate sti */ + tcg_target_ulong val = ts->val; + if (IS_DEAD_ARG(1)) { + temp_dead(s, ts); + } + tcg_reg_alloc_do_movi(s, ots, val, arg_life); + return; + } + + /* If the source value is in memory we're going to be forced + to have it in a register in order to perform the copy. Copy + the SOURCE value into its own register first, that way we + don't have to reload SOURCE the next time it is used. */ + if (ts->val_type == TEMP_VAL_MEM) { temp_load(s, ts, tcg_target_available_regs[itype], allocated_regs); } + tcg_debug_assert(ts->val_type == TEMP_VAL_REG); if (IS_DEAD_ARG(0) && !ots->fixed_reg) { /* mov to a non-saved dead register makes no sense (even with liveness analysis disabled). */ tcg_debug_assert(NEED_SYNC_ARG(0)); - /* The code above should have moved the temp to a register. */ - tcg_debug_assert(ts->val_type == TEMP_VAL_REG); if (!ots->mem_allocated) { temp_allocate_frame(s, args[0]); } @@ -2164,20 +2175,7 @@ static void tcg_reg_alloc_mov(TCGContext *s, const TCGOpDef *def, temp_dead(s, ts); } temp_dead(s, ots); - } else if (ts->val_type == TEMP_VAL_CONST) { - /* propagate constant */ - if (ots->val_type == TEMP_VAL_REG) { - s->reg_to_temp[ots->reg] = NULL; - } - ots->val_type = TEMP_VAL_CONST; - ots->val = ts->val; - if (IS_DEAD_ARG(1)) { - temp_dead(s, ts); - } } else { - /* The code in the first if block should have moved the - temp to a register. */ - tcg_debug_assert(ts->val_type == TEMP_VAL_REG); if (IS_DEAD_ARG(1) && !ts->fixed_reg && !ots->fixed_reg) { /* the mov can be suppressed */ if (ots->val_type == TEMP_VAL_REG) { @@ -704,6 +704,7 @@ struct TCGContext { }; extern TCGContext tcg_ctx; +extern bool parallel_cpus; static inline void tcg_set_insn_param(int op_idx, int arg, TCGArg v) { @@ -1176,6 +1177,90 @@ uint64_t helper_be_ldq_cmmu(CPUArchState *env, target_ulong addr, # define helper_ret_ldq_cmmu helper_le_ldq_cmmu #endif +uint32_t helper_atomic_cmpxchgb_mmu(CPUArchState *env, target_ulong addr, + uint32_t cmpv, uint32_t newv, + TCGMemOpIdx oi, uintptr_t retaddr); +uint32_t helper_atomic_cmpxchgw_le_mmu(CPUArchState *env, target_ulong addr, + uint32_t cmpv, uint32_t newv, + TCGMemOpIdx oi, uintptr_t retaddr); +uint32_t helper_atomic_cmpxchgl_le_mmu(CPUArchState *env, target_ulong addr, + uint32_t cmpv, uint32_t newv, + TCGMemOpIdx oi, uintptr_t retaddr); +uint64_t helper_atomic_cmpxchgq_le_mmu(CPUArchState *env, target_ulong addr, + uint64_t cmpv, uint64_t newv, + TCGMemOpIdx oi, uintptr_t retaddr); +uint32_t helper_atomic_cmpxchgw_be_mmu(CPUArchState *env, target_ulong addr, + uint32_t cmpv, uint32_t newv, + TCGMemOpIdx oi, uintptr_t retaddr); +uint32_t helper_atomic_cmpxchgl_be_mmu(CPUArchState *env, target_ulong addr, + uint32_t cmpv, uint32_t newv, + TCGMemOpIdx oi, uintptr_t retaddr); +uint64_t helper_atomic_cmpxchgq_be_mmu(CPUArchState *env, target_ulong addr, + uint64_t cmpv, uint64_t newv, + TCGMemOpIdx oi, uintptr_t retaddr); + +#define GEN_ATOMIC_HELPER(NAME, TYPE, SUFFIX) \ +TYPE helper_atomic_ ## NAME ## SUFFIX ## _mmu \ + (CPUArchState *env, target_ulong addr, TYPE val, \ + TCGMemOpIdx oi, uintptr_t retaddr); + +#ifdef CONFIG_ATOMIC64 +#define GEN_ATOMIC_HELPER_ALL(NAME) \ + GEN_ATOMIC_HELPER(NAME, uint32_t, b) \ + GEN_ATOMIC_HELPER(NAME, uint32_t, w_le) \ + GEN_ATOMIC_HELPER(NAME, uint32_t, w_be) \ + GEN_ATOMIC_HELPER(NAME, uint32_t, l_le) \ + GEN_ATOMIC_HELPER(NAME, uint32_t, l_be) \ + GEN_ATOMIC_HELPER(NAME, uint64_t, q_le) \ + GEN_ATOMIC_HELPER(NAME, uint64_t, q_be) +#else +#define GEN_ATOMIC_HELPER_ALL(NAME) \ + GEN_ATOMIC_HELPER(NAME, uint32_t, b) \ + GEN_ATOMIC_HELPER(NAME, uint32_t, w_le) \ + GEN_ATOMIC_HELPER(NAME, uint32_t, w_be) \ + GEN_ATOMIC_HELPER(NAME, uint32_t, l_le) \ + GEN_ATOMIC_HELPER(NAME, uint32_t, l_be) +#endif + +GEN_ATOMIC_HELPER_ALL(fetch_add) +GEN_ATOMIC_HELPER_ALL(fetch_sub) +GEN_ATOMIC_HELPER_ALL(fetch_and) +GEN_ATOMIC_HELPER_ALL(fetch_or) +GEN_ATOMIC_HELPER_ALL(fetch_xor) + +GEN_ATOMIC_HELPER_ALL(add_fetch) +GEN_ATOMIC_HELPER_ALL(sub_fetch) +GEN_ATOMIC_HELPER_ALL(and_fetch) +GEN_ATOMIC_HELPER_ALL(or_fetch) +GEN_ATOMIC_HELPER_ALL(xor_fetch) + +GEN_ATOMIC_HELPER_ALL(xchg) + +#undef GEN_ATOMIC_HELPER_ALL +#undef GEN_ATOMIC_HELPER #endif /* CONFIG_SOFTMMU */ +#ifdef CONFIG_ATOMIC128 +#include "qemu/int128.h" + +/* These aren't really a "proper" helpers because TCG cannot manage Int128. + However, use the same format as the others, for use by the backends. */ +Int128 helper_atomic_cmpxchgo_le_mmu(CPUArchState *env, target_ulong addr, + Int128 cmpv, Int128 newv, + TCGMemOpIdx oi, uintptr_t retaddr); +Int128 helper_atomic_cmpxchgo_be_mmu(CPUArchState *env, target_ulong addr, + Int128 cmpv, Int128 newv, + TCGMemOpIdx oi, uintptr_t retaddr); + +Int128 helper_atomic_ldo_le_mmu(CPUArchState *env, target_ulong addr, + TCGMemOpIdx oi, uintptr_t retaddr); +Int128 helper_atomic_ldo_be_mmu(CPUArchState *env, target_ulong addr, + TCGMemOpIdx oi, uintptr_t retaddr); +void helper_atomic_sto_le_mmu(CPUArchState *env, target_ulong addr, Int128 val, + TCGMemOpIdx oi, uintptr_t retaddr); +void helper_atomic_sto_be_mmu(CPUArchState *env, target_ulong addr, Int128 val, + TCGMemOpIdx oi, uintptr_t retaddr); + +#endif /* CONFIG_ATOMIC128 */ + #endif /* TCG_H */ diff --git a/tests/.gitignore b/tests/.gitignore index 9f3d2ee..64e050e 100644 --- a/tests/.gitignore +++ b/tests/.gitignore @@ -1,3 +1,4 @@ +atomic_add-bench check-qdict check-qfloat check-qint @@ -59,11 +60,11 @@ test-qht-par test-qmp-commands test-qmp-commands.h test-qmp-event -test-qmp-input-strict -test-qmp-input-visitor +test-qobject-input-strict +test-qobject-input-visitor test-qmp-introspect.[ch] test-qmp-marshal.c -test-qmp-output-visitor +test-qobject-output-visitor test-rcu-list test-replication test-rfifolock diff --git a/tests/Makefile.include b/tests/Makefile.include index cbe38ad..22656ea 100644 --- a/tests/Makefile.include +++ b/tests/Makefile.include @@ -8,6 +8,8 @@ SYSEMU_TARGET_LIST := $(subst -softmmu.mak,,$(notdir \ check-unit-y = tests/check-qdict$(EXESUF) gcov-files-check-qdict-y = qobject/qdict.c +check-unit-y += tests/test-char$(EXESUF) +gcov-files-check-qdict-y = qemu-char.c check-unit-y += tests/check-qfloat$(EXESUF) gcov-files-check-qfloat-y = qobject/qfloat.c check-unit-y += tests/check-qint$(EXESUF) @@ -20,13 +22,13 @@ check-unit-y += tests/check-qnull$(EXESUF) gcov-files-check-qnull-y = qobject/qnull.c check-unit-y += tests/check-qjson$(EXESUF) gcov-files-check-qjson-y = qobject/qjson.c -check-unit-y += tests/test-qmp-output-visitor$(EXESUF) -gcov-files-test-qmp-output-visitor-y = qapi/qmp-output-visitor.c +check-unit-y += tests/test-qobject-output-visitor$(EXESUF) +gcov-files-test-qobject-output-visitor-y = qapi/qobject-output-visitor.c check-unit-y += tests/test-clone-visitor$(EXESUF) gcov-files-test-clone-visitor-y = qapi/qapi-clone-visitor.c -check-unit-y += tests/test-qmp-input-visitor$(EXESUF) -gcov-files-test-qmp-input-visitor-y = qapi/qmp-input-visitor.c -check-unit-y += tests/test-qmp-input-strict$(EXESUF) +check-unit-y += tests/test-qobject-input-visitor$(EXESUF) +gcov-files-test-qobject-input-visitor-y = qapi/qobject-input-visitor.c +check-unit-y += tests/test-qobject-input-strict$(EXESUF) check-unit-y += tests/test-qmp-commands$(EXESUF) gcov-files-test-qmp-commands-y = qapi/qmp-dispatch.c check-unit-y += tests/test-string-input-visitor$(EXESUF) @@ -301,6 +303,8 @@ check-qtest-arm-y += tests/m25p80-test$(EXESUF) gcov-files-arm-y += hw/misc/tmp105.c check-qtest-arm-y += tests/virtio-blk-test$(EXESUF) gcov-files-arm-y += arm-softmmu/hw/block/virtio-blk.c +check-qtest-arm-y += tests/test-arm-mptimer$(EXESUF) +gcov-files-arm-y += hw/timer/arm_mptimer.c check-qtest-microblazeel-y = $(check-qtest-microblaze-y) @@ -448,15 +452,16 @@ test-obj-y = tests/check-qint.o tests/check-qstring.o tests/check-qdict.o \ tests/check-qlist.o tests/check-qfloat.o tests/check-qnull.o \ tests/check-qjson.o \ tests/test-coroutine.o tests/test-string-output-visitor.o \ - tests/test-string-input-visitor.o tests/test-qmp-output-visitor.o \ + tests/test-string-input-visitor.o tests/test-qobject-output-visitor.o \ tests/test-clone-visitor.o \ - tests/test-qmp-input-visitor.o tests/test-qmp-input-strict.o \ + tests/test-qobject-input-visitor.o tests/test-qobject-input-strict.o \ tests/test-qmp-commands.o tests/test-visitor-serialization.o \ tests/test-x86-cpuid.o tests/test-mul64.o tests/test-int128.o \ tests/test-opts-visitor.o tests/test-qmp-event.o \ tests/rcutorture.o tests/test-rcu-list.o \ tests/test-qdist.o \ - tests/test-qht.o tests/qht-bench.o tests/test-qht-par.o + tests/test-qht.o tests/qht-bench.o tests/test-qht-par.o \ + tests/atomic_add-bench.o $(test-obj-y): QEMU_INCLUDES += -Itests QEMU_CFLAGS += -I$(SRC_PATH)/tests @@ -481,6 +486,8 @@ tests/check-qnull$(EXESUF): tests/check-qnull.o $(test-util-obj-y) tests/check-qjson$(EXESUF): tests/check-qjson.o $(test-util-obj-y) tests/check-qom-interface$(EXESUF): tests/check-qom-interface.o $(test-qom-obj-y) tests/check-qom-proplist$(EXESUF): tests/check-qom-proplist.o $(test-qom-obj-y) + +tests/test-char$(EXESUF): tests/test-char.o qemu-char.o qemu-timer.o $(test-util-obj-y) $(qtest-obj-y) $(test-io-obj-y) tests/test-coroutine$(EXESUF): tests/test-coroutine.o $(test-block-obj-y) tests/test-aio$(EXESUF): tests/test-aio.o $(test-block-obj-y) tests/test-rfifolock$(EXESUF): tests/test-rfifolock.o $(test-util-obj-y) @@ -501,6 +508,7 @@ tests/test-qht$(EXESUF): tests/test-qht.o $(test-util-obj-y) tests/test-qht-par$(EXESUF): tests/test-qht-par.o tests/qht-bench$(EXESUF) $(test-util-obj-y) tests/qht-bench$(EXESUF): tests/qht-bench.o $(test-util-obj-y) tests/test-bufferiszero$(EXESUF): tests/test-bufferiszero.o $(test-util-obj-y) +tests/atomic_add-bench$(EXESUF): tests/atomic_add-bench.o $(test-util-obj-y) tests/test-qdev-global-props$(EXESUF): tests/test-qdev-global-props.o \ hw/core/qdev.o hw/core/qdev-properties.o hw/core/hotplug.o\ @@ -552,10 +560,10 @@ $(SRC_PATH)/tests/qapi-schema/qapi-schema-test.json $(SRC_PATH)/scripts/qapi-int tests/test-string-output-visitor$(EXESUF): tests/test-string-output-visitor.o $(test-qapi-obj-y) tests/test-string-input-visitor$(EXESUF): tests/test-string-input-visitor.o $(test-qapi-obj-y) tests/test-qmp-event$(EXESUF): tests/test-qmp-event.o $(test-qapi-obj-y) -tests/test-qmp-output-visitor$(EXESUF): tests/test-qmp-output-visitor.o $(test-qapi-obj-y) +tests/test-qobject-output-visitor$(EXESUF): tests/test-qobject-output-visitor.o $(test-qapi-obj-y) tests/test-clone-visitor$(EXESUF): tests/test-clone-visitor.o $(test-qapi-obj-y) -tests/test-qmp-input-visitor$(EXESUF): tests/test-qmp-input-visitor.o $(test-qapi-obj-y) -tests/test-qmp-input-strict$(EXESUF): tests/test-qmp-input-strict.o $(test-qapi-obj-y) +tests/test-qobject-input-visitor$(EXESUF): tests/test-qobject-input-visitor.o $(test-qapi-obj-y) +tests/test-qobject-input-strict$(EXESUF): tests/test-qobject-input-strict.o $(test-qapi-obj-y) tests/test-qmp-commands$(EXESUF): tests/test-qmp-commands.o tests/test-qmp-marshal.o $(test-qapi-obj-y) tests/test-visitor-serialization$(EXESUF): tests/test-visitor-serialization.o $(test-qapi-obj-y) tests/test-opts-visitor$(EXESUF): tests/test-opts-visitor.o $(test-qapi-obj-y) @@ -680,6 +688,7 @@ tests/test-x86-cpuid-compat$(EXESUF): tests/test-x86-cpuid-compat.o $(qtest-obj- tests/ivshmem-test$(EXESUF): tests/ivshmem-test.o contrib/ivshmem-server/ivshmem-server.o $(libqos-pc-obj-y) tests/vhost-user-bridge$(EXESUF): tests/vhost-user-bridge.o tests/test-uuid$(EXESUF): tests/test-uuid.o $(test-util-obj-y) +tests/test-arm-mptimer$(EXESUF): tests/test-arm-mptimer.o tests/migration/stress$(EXESUF): tests/migration/stress.o $(call quiet-command, $(LINKPROG) -static -O3 $(PTHREAD_LIB) -o $@ $< ,"LINK","$(TARGET_DIR)$@") diff --git a/tests/atomic_add-bench.c b/tests/atomic_add-bench.c new file mode 100644 index 0000000..caa1e8e --- /dev/null +++ b/tests/atomic_add-bench.c @@ -0,0 +1,163 @@ +#include "qemu/osdep.h" +#include "qemu/thread.h" +#include "qemu/host-utils.h" +#include "qemu/processor.h" + +struct thread_info { + uint64_t r; +} QEMU_ALIGNED(64); + +struct count { + unsigned long val; +} QEMU_ALIGNED(64); + +static QemuThread *threads; +static struct thread_info *th_info; +static unsigned int n_threads = 1; +static unsigned int n_ready_threads; +static struct count *counts; +static unsigned int duration = 1; +static unsigned int range = 1024; +static bool test_start; +static bool test_stop; + +static const char commands_string[] = + " -n = number of threads\n" + " -d = duration in seconds\n" + " -r = range (will be rounded up to pow2)"; + +static void usage_complete(char *argv[]) +{ + fprintf(stderr, "Usage: %s [options]\n", argv[0]); + fprintf(stderr, "options:\n%s\n", commands_string); +} + +/* + * From: https://en.wikipedia.org/wiki/Xorshift + * This is faster than rand_r(), and gives us a wider range (RAND_MAX is only + * guaranteed to be >= INT_MAX). + */ +static uint64_t xorshift64star(uint64_t x) +{ + x ^= x >> 12; /* a */ + x ^= x << 25; /* b */ + x ^= x >> 27; /* c */ + return x * UINT64_C(2685821657736338717); +} + +static void *thread_func(void *arg) +{ + struct thread_info *info = arg; + + atomic_inc(&n_ready_threads); + while (!atomic_read(&test_start)) { + cpu_relax(); + } + + while (!atomic_read(&test_stop)) { + unsigned int index; + + info->r = xorshift64star(info->r); + index = info->r & (range - 1); + atomic_inc(&counts[index].val); + } + return NULL; +} + +static void run_test(void) +{ + unsigned int remaining; + unsigned int i; + + while (atomic_read(&n_ready_threads) != n_threads) { + cpu_relax(); + } + atomic_set(&test_start, true); + do { + remaining = sleep(duration); + } while (remaining); + atomic_set(&test_stop, true); + + for (i = 0; i < n_threads; i++) { + qemu_thread_join(&threads[i]); + } +} + +static void create_threads(void) +{ + unsigned int i; + + threads = g_new(QemuThread, n_threads); + th_info = g_new(struct thread_info, n_threads); + counts = qemu_memalign(64, sizeof(*counts) * range); + memset(counts, 0, sizeof(*counts) * range); + + for (i = 0; i < n_threads; i++) { + struct thread_info *info = &th_info[i]; + + info->r = (i + 1) ^ time(NULL); + qemu_thread_create(&threads[i], NULL, thread_func, info, + QEMU_THREAD_JOINABLE); + } +} + +static void pr_params(void) +{ + printf("Parameters:\n"); + printf(" # of threads: %u\n", n_threads); + printf(" duration: %u\n", duration); + printf(" ops' range: %u\n", range); +} + +static void pr_stats(void) +{ + unsigned long long val = 0; + unsigned int i; + double tx; + + for (i = 0; i < range; i++) { + val += counts[i].val; + } + tx = val / duration / 1e6; + + printf("Results:\n"); + printf("Duration: %u s\n", duration); + printf(" Throughput: %.2f Mops/s\n", tx); + printf(" Throughput/thread: %.2f Mops/s/thread\n", tx / n_threads); +} + +static void parse_args(int argc, char *argv[]) +{ + int c; + + for (;;) { + c = getopt(argc, argv, "hd:n:r:"); + if (c < 0) { + break; + } + switch (c) { + case 'h': + usage_complete(argv); + exit(0); + case 'd': + duration = atoi(optarg); + break; + case 'n': + n_threads = atoi(optarg); + break; + case 'r': + range = pow2ceil(atoi(optarg)); + break; + } + } +} + +int main(int argc, char *argv[]) +{ + parse_args(argc, argv); + pr_params(); + create_threads(); + run_test(); + pr_stats(); + return 0; +} diff --git a/tests/check-qdict.c b/tests/check-qdict.c index 42da1e6..07b1c79 100644 --- a/tests/check-qdict.c +++ b/tests/check-qdict.c @@ -14,6 +14,7 @@ #include "qapi/qmp/qint.h" #include "qapi/qmp/qdict.h" #include "qapi/qmp/qstring.h" +#include "qapi/error.h" #include "qemu-common.h" /* @@ -412,7 +413,6 @@ static void qdict_array_split_test(void) QDECREF(test_dict); - /* * Test the split of * @@ -518,7 +518,6 @@ static void qdict_join_test(void) dict1 = qdict_new(); dict2 = qdict_new(); - /* Test everything once without overwrite and once with */ do { @@ -528,7 +527,6 @@ static void qdict_join_test(void) g_assert(qdict_size(dict1) == 0); g_assert(qdict_size(dict2) == 0); - /* First iteration: Test movement */ /* Second iteration: Test empty source and non-empty destination */ qdict_put(dict2, "foo", qint_from_int(42)); @@ -542,7 +540,6 @@ static void qdict_join_test(void) g_assert(qdict_get_int(dict1, "foo") == 42); } - /* Test non-empty source and destination without conflict */ qdict_put(dict2, "bar", qint_from_int(23)); @@ -554,7 +551,6 @@ static void qdict_join_test(void) g_assert(qdict_get_int(dict1, "foo") == 42); g_assert(qdict_get_int(dict1, "bar") == 23); - /* Test conflict */ qdict_put(dict2, "foo", qint_from_int(84)); @@ -570,7 +566,6 @@ static void qdict_join_test(void) g_assert(qdict_get_int(dict2, "foo") == 84); } - /* Check the references */ g_assert(qdict_get(dict1, "foo")->refcnt == 1); g_assert(qdict_get(dict1, "bar")->refcnt == 1); @@ -579,7 +574,6 @@ static void qdict_join_test(void) g_assert(qdict_get(dict2, "foo")->refcnt == 1); } - /* Clean up */ qdict_del(dict1, "foo"); qdict_del(dict1, "bar"); @@ -590,11 +584,152 @@ static void qdict_join_test(void) } while (overwrite ^= true); - QDECREF(dict1); QDECREF(dict2); } +static void qdict_crumple_test_recursive(void) +{ + QDict *src, *dst, *rule, *vnc, *acl, *listen; + QObject *child, *res; + QList *rules; + + src = qdict_new(); + qdict_put(src, "vnc.listen.addr", qstring_from_str("127.0.0.1")); + qdict_put(src, "vnc.listen.port", qstring_from_str("5901")); + qdict_put(src, "vnc.acl.rules.0.match", qstring_from_str("fred")); + qdict_put(src, "vnc.acl.rules.0.policy", qstring_from_str("allow")); + qdict_put(src, "vnc.acl.rules.1.match", qstring_from_str("bob")); + qdict_put(src, "vnc.acl.rules.1.policy", qstring_from_str("deny")); + qdict_put(src, "vnc.acl.default", qstring_from_str("deny")); + qdict_put(src, "vnc.acl..name", qstring_from_str("acl0")); + qdict_put(src, "vnc.acl.rule..name", qstring_from_str("acl0")); + + res = qdict_crumple(src, &error_abort); + + g_assert_cmpint(qobject_type(res), ==, QTYPE_QDICT); + + dst = qobject_to_qdict(res); + + g_assert_cmpint(qdict_size(dst), ==, 1); + + child = qdict_get(dst, "vnc"); + g_assert_cmpint(qobject_type(child), ==, QTYPE_QDICT); + vnc = qobject_to_qdict(child); + + child = qdict_get(vnc, "listen"); + g_assert_cmpint(qobject_type(child), ==, QTYPE_QDICT); + listen = qobject_to_qdict(child); + g_assert_cmpstr("127.0.0.1", ==, qdict_get_str(listen, "addr")); + g_assert_cmpstr("5901", ==, qdict_get_str(listen, "port")); + + child = qdict_get(vnc, "acl"); + g_assert_cmpint(qobject_type(child), ==, QTYPE_QDICT); + acl = qobject_to_qdict(child); + + child = qdict_get(acl, "rules"); + g_assert_cmpint(qobject_type(child), ==, QTYPE_QLIST); + rules = qobject_to_qlist(child); + g_assert_cmpint(qlist_size(rules), ==, 2); + + rule = qobject_to_qdict(qlist_pop(rules)); + g_assert_cmpint(qdict_size(rule), ==, 2); + g_assert_cmpstr("fred", ==, qdict_get_str(rule, "match")); + g_assert_cmpstr("allow", ==, qdict_get_str(rule, "policy")); + QDECREF(rule); + + rule = qobject_to_qdict(qlist_pop(rules)); + g_assert_cmpint(qdict_size(rule), ==, 2); + g_assert_cmpstr("bob", ==, qdict_get_str(rule, "match")); + g_assert_cmpstr("deny", ==, qdict_get_str(rule, "policy")); + QDECREF(rule); + + /* With recursive crumpling, we should see all names unescaped */ + g_assert_cmpstr("acl0", ==, qdict_get_str(vnc, "acl.name")); + child = qdict_get(vnc, "acl"); + g_assert_cmpint(qobject_type(child), ==, QTYPE_QDICT); + acl = qdict_get_qdict(vnc, "acl"); + g_assert_cmpstr("acl0", ==, qdict_get_str(acl, "rule.name")); + + QDECREF(src); + QDECREF(dst); +} + +static void qdict_crumple_test_empty(void) +{ + QDict *src, *dst; + + src = qdict_new(); + + dst = (QDict *)qdict_crumple(src, &error_abort); + + g_assert_cmpint(qdict_size(dst), ==, 0); + + QDECREF(src); + QDECREF(dst); +} + +static void qdict_crumple_test_bad_inputs(void) +{ + QDict *src; + Error *error = NULL; + + src = qdict_new(); + /* rule.0 can't be both a string and a dict */ + qdict_put(src, "rule.0", qstring_from_str("fred")); + qdict_put(src, "rule.0.policy", qstring_from_str("allow")); + + g_assert(qdict_crumple(src, &error) == NULL); + g_assert(error != NULL); + error_free(error); + error = NULL; + QDECREF(src); + + src = qdict_new(); + /* rule can't be both a list and a dict */ + qdict_put(src, "rule.0", qstring_from_str("fred")); + qdict_put(src, "rule.a", qstring_from_str("allow")); + + g_assert(qdict_crumple(src, &error) == NULL); + g_assert(error != NULL); + error_free(error); + error = NULL; + QDECREF(src); + + src = qdict_new(); + /* The input should be flat, ie no dicts or lists */ + qdict_put(src, "rule.a", qdict_new()); + qdict_put(src, "rule.b", qstring_from_str("allow")); + + g_assert(qdict_crumple(src, &error) == NULL); + g_assert(error != NULL); + error_free(error); + error = NULL; + QDECREF(src); + + src = qdict_new(); + /* List indexes must not have gaps */ + qdict_put(src, "rule.0", qstring_from_str("deny")); + qdict_put(src, "rule.3", qstring_from_str("allow")); + + g_assert(qdict_crumple(src, &error) == NULL); + g_assert(error != NULL); + error_free(error); + error = NULL; + QDECREF(src); + + src = qdict_new(); + /* List indexes must be in %zu format */ + qdict_put(src, "rule.0", qstring_from_str("deny")); + qdict_put(src, "rule.+1", qstring_from_str("allow")); + + g_assert(qdict_crumple(src, &error) == NULL); + g_assert(error != NULL); + error_free(error); + error = NULL; + QDECREF(src); +} + /* * Errors test-cases */ @@ -742,6 +877,13 @@ int main(int argc, char **argv) g_test_add_func("/errors/put_exists", qdict_put_exists_test); g_test_add_func("/errors/get_not_exists", qdict_get_not_exists_test); + g_test_add_func("/public/crumple/recursive", + qdict_crumple_test_recursive); + g_test_add_func("/public/crumple/empty", + qdict_crumple_test_empty); + g_test_add_func("/public/crumple/bad_inputs", + qdict_crumple_test_bad_inputs); + /* The Big one */ if (g_test_slow()) { g_test_add_func("/stress/test", qdict_stress_test); diff --git a/tests/check-qnull.c b/tests/check-qnull.c index dc906b1..b50bb8a 100644 --- a/tests/check-qnull.c +++ b/tests/check-qnull.c @@ -10,8 +10,8 @@ #include "qapi/qmp/qobject.h" #include "qemu-common.h" -#include "qapi/qmp-input-visitor.h" -#include "qapi/qmp-output-visitor.h" +#include "qapi/qobject-input-visitor.h" +#include "qapi/qobject-output-visitor.h" #include "qapi/error.h" /* @@ -47,12 +47,12 @@ static void qnull_visit_test(void) g_assert(qnull_.refcnt == 1); obj = qnull(); - v = qmp_input_visitor_new(obj, true); + v = qobject_input_visitor_new(obj, true); qobject_decref(obj); visit_type_null(v, NULL, &error_abort); visit_free(v); - v = qmp_output_visitor_new(&obj); + v = qobject_output_visitor_new(&obj); visit_type_null(v, NULL, &error_abort); visit_complete(v, &obj); g_assert(obj == &qnull_); diff --git a/tests/ptimer-test-stubs.c b/tests/ptimer-test-stubs.c index e028a81..21d4ebb 100644 --- a/tests/ptimer-test-stubs.c +++ b/tests/ptimer-test-stubs.c @@ -1,7 +1,7 @@ /* * Stubs for the ptimer-test * - * Author: Dmitry Osipenko <digetx@gmail.com> + * Copyright (c) 2016 Dmitry Osipenko <digetx@gmail.com> * * This work is licensed under the terms of the GNU GPL, version 2 or later. * See the COPYING file in the top-level directory. diff --git a/tests/ptimer-test.c b/tests/ptimer-test.c index 7b0ddf6..b36a476 100644 --- a/tests/ptimer-test.c +++ b/tests/ptimer-test.c @@ -1,7 +1,7 @@ /* * QTest testcase for the ptimer * - * Author: Dmitry Osipenko <digetx@gmail.com> + * Copyright (c) 2016 Dmitry Osipenko <digetx@gmail.com> * * This work is licensed under the terms of the GNU GPL, version 2 or later. * See the COPYING file in the top-level directory. @@ -99,6 +99,7 @@ static void check_oneshot(gconstpointer arg) const uint8_t *policy = arg; QEMUBH *bh = qemu_bh_new(ptimer_trigger, NULL); ptimer_state *ptimer = ptimer_init(bh, *policy); + bool no_round_down = (*policy & PTIMER_POLICY_NO_COUNTER_ROUND_DOWN); triggered = false; @@ -106,34 +107,46 @@ static void check_oneshot(gconstpointer arg) ptimer_set_count(ptimer, 10); ptimer_run(ptimer, 1); - qemu_clock_step(2000000 * 2 + 100000); + qemu_clock_step(2000000 * 2 + 1); - g_assert_cmpuint(ptimer_get_count(ptimer), ==, 7); + g_assert_cmpuint(ptimer_get_count(ptimer), ==, no_round_down ? 8 : 7); g_assert_false(triggered); ptimer_stop(ptimer); - g_assert_cmpuint(ptimer_get_count(ptimer), ==, 7); + g_assert_cmpuint(ptimer_get_count(ptimer), ==, no_round_down ? 8 : 7); g_assert_false(triggered); qemu_clock_step(2000000 * 11); - g_assert_cmpuint(ptimer_get_count(ptimer), ==, 7); + g_assert_cmpuint(ptimer_get_count(ptimer), ==, no_round_down ? 8 : 7); g_assert_false(triggered); ptimer_run(ptimer, 1); - qemu_clock_step(2000000 * 7 + 100000); + qemu_clock_step(2000000 * 7 + 1); - g_assert_cmpuint(ptimer_get_count(ptimer), ==, 0); - g_assert_true(triggered); + g_assert_cmpuint(ptimer_get_count(ptimer), ==, no_round_down ? 1 : 0); - triggered = false; + if (no_round_down) { + g_assert_false(triggered); + } else { + g_assert_true(triggered); + + triggered = false; + } qemu_clock_step(2000000); g_assert_cmpuint(ptimer_get_count(ptimer), ==, 0); - g_assert_false(triggered); + + if (no_round_down) { + g_assert_true(triggered); + + triggered = false; + } else { + g_assert_false(triggered); + } qemu_clock_step(4000000); @@ -142,30 +155,30 @@ static void check_oneshot(gconstpointer arg) ptimer_set_count(ptimer, 10); - qemu_clock_step(20000000 + 100000); + qemu_clock_step(20000000 + 1); g_assert_cmpuint(ptimer_get_count(ptimer), ==, 10); g_assert_false(triggered); ptimer_set_limit(ptimer, 9, 1); - qemu_clock_step(20000000 + 100000); + qemu_clock_step(20000000 + 1); g_assert_cmpuint(ptimer_get_count(ptimer), ==, 9); g_assert_false(triggered); ptimer_run(ptimer, 1); - qemu_clock_step(2000000 + 100000); + qemu_clock_step(2000000 + 1); - g_assert_cmpuint(ptimer_get_count(ptimer), ==, 7); + g_assert_cmpuint(ptimer_get_count(ptimer), ==, no_round_down ? 8 : 7); g_assert_false(triggered); ptimer_set_count(ptimer, 20); - qemu_clock_step(2000000 * 19 + 100000); + qemu_clock_step(2000000 * 19 + 1); - g_assert_cmpuint(ptimer_get_count(ptimer), ==, 0); + g_assert_cmpuint(ptimer_get_count(ptimer), ==, no_round_down ? 1 : 0); g_assert_false(triggered); qemu_clock_step(2000000); @@ -177,7 +190,7 @@ static void check_oneshot(gconstpointer arg) triggered = false; - qemu_clock_step(2000000 * 12 + 100000); + qemu_clock_step(2000000 * 12 + 1); g_assert_cmpuint(ptimer_get_count(ptimer), ==, 0); g_assert_false(triggered); @@ -188,6 +201,10 @@ static void check_periodic(gconstpointer arg) const uint8_t *policy = arg; QEMUBH *bh = qemu_bh_new(ptimer_trigger, NULL); ptimer_state *ptimer = ptimer_init(bh, *policy); + bool wrap_policy = (*policy & PTIMER_POLICY_WRAP_AFTER_ONE_PERIOD); + bool no_immediate_trigger = (*policy & PTIMER_POLICY_NO_IMMEDIATE_TRIGGER); + bool no_immediate_reload = (*policy & PTIMER_POLICY_NO_IMMEDIATE_RELOAD); + bool no_round_down = (*policy & PTIMER_POLICY_NO_COUNTER_ROUND_DOWN); triggered = false; @@ -195,28 +212,70 @@ static void check_periodic(gconstpointer arg) ptimer_set_limit(ptimer, 10, 1); ptimer_run(ptimer, 0); - qemu_clock_step(2000000 * 10 + 100000); + g_assert_cmpuint(ptimer_get_count(ptimer), ==, 10); + g_assert_false(triggered); - g_assert_cmpuint(ptimer_get_count(ptimer), ==, 9); + qemu_clock_step(1); + + g_assert_cmpuint(ptimer_get_count(ptimer), ==, no_round_down ? 10 : 9); + g_assert_false(triggered); + + qemu_clock_step(2000000 * 10 - 1); + + g_assert_cmpuint(ptimer_get_count(ptimer), ==, wrap_policy ? 0 : 10); + g_assert_true(triggered); + + qemu_clock_step(1); + + g_assert_cmpuint(ptimer_get_count(ptimer), ==, + wrap_policy ? 0 : (no_round_down ? 10 : 9)); g_assert_true(triggered); triggered = false; qemu_clock_step(2000000); - g_assert_cmpuint(ptimer_get_count(ptimer), ==, 8); + g_assert_cmpuint(ptimer_get_count(ptimer), ==, + (no_round_down ? 9 : 8) + (wrap_policy ? 1 : 0)); g_assert_false(triggered); ptimer_set_count(ptimer, 20); - qemu_clock_step(2000000 * 11 + 100000); + g_assert_cmpuint(ptimer_get_count(ptimer), ==, 20); + g_assert_false(triggered); + + qemu_clock_step(1); - g_assert_cmpuint(ptimer_get_count(ptimer), ==, 8); + g_assert_cmpuint(ptimer_get_count(ptimer), ==, no_round_down ? 20 : 19); + g_assert_false(triggered); + + qemu_clock_step(2000000 * 11 + 1); + + g_assert_cmpuint(ptimer_get_count(ptimer), ==, no_round_down ? 9 : 8); g_assert_false(triggered); qemu_clock_step(2000000 * 10); - g_assert_cmpuint(ptimer_get_count(ptimer), ==, 8); + g_assert_cmpuint(ptimer_get_count(ptimer), ==, + (no_round_down ? 9 : 8) + (wrap_policy ? 1 : 0)); + g_assert_true(triggered); + + triggered = false; + + ptimer_set_count(ptimer, 3); + + g_assert_cmpuint(ptimer_get_count(ptimer), ==, 3); + g_assert_false(triggered); + + qemu_clock_step(1); + + g_assert_cmpuint(ptimer_get_count(ptimer), ==, no_round_down ? 3 : 2); + g_assert_false(triggered); + + qemu_clock_step(2000000 * 4); + + g_assert_cmpuint(ptimer_get_count(ptimer), ==, + (no_round_down ? 9 : 8) + (wrap_policy ? 1 : 0)); g_assert_true(triggered); ptimer_stop(ptimer); @@ -224,50 +283,82 @@ static void check_periodic(gconstpointer arg) qemu_clock_step(2000000); - g_assert_cmpuint(ptimer_get_count(ptimer), ==, 8); + g_assert_cmpuint(ptimer_get_count(ptimer), ==, + (no_round_down ? 9 : 8) + (wrap_policy ? 1 : 0)); g_assert_false(triggered); ptimer_set_count(ptimer, 3); ptimer_run(ptimer, 0); - qemu_clock_step(2000000 * 3 + 100000); + qemu_clock_step(2000000 * 3 + 1); - g_assert_cmpuint(ptimer_get_count(ptimer), ==, 9); + g_assert_cmpuint(ptimer_get_count(ptimer), ==, + wrap_policy ? 0 : (no_round_down ? 10 : 9)); g_assert_true(triggered); triggered = false; qemu_clock_step(2000000); - g_assert_cmpuint(ptimer_get_count(ptimer), ==, 8); + g_assert_cmpuint(ptimer_get_count(ptimer), ==, + (no_round_down ? 9 : 8) + (wrap_policy ? 1 : 0)); g_assert_false(triggered); ptimer_set_count(ptimer, 0); - g_assert_cmpuint(ptimer_get_count(ptimer), ==, 10); - g_assert_true(triggered); + g_assert_cmpuint(ptimer_get_count(ptimer), ==, + no_immediate_reload ? 0 : 10); + + if (no_immediate_trigger) { + g_assert_false(triggered); + } else { + g_assert_true(triggered); + } triggered = false; - qemu_clock_step(2000000 * 12 + 100000); + qemu_clock_step(1); + + if (no_immediate_reload) { + g_assert_cmpuint(ptimer_get_count(ptimer), ==, 0); + g_assert_false(triggered); + + qemu_clock_step(2000000); + + if (no_immediate_trigger) { + g_assert_true(triggered); + } else { + g_assert_false(triggered); + } + + triggered = false; + } - g_assert_cmpuint(ptimer_get_count(ptimer), ==, 7); + g_assert_cmpuint(ptimer_get_count(ptimer), ==, no_round_down ? 10 : 9); + g_assert_false(triggered); + + qemu_clock_step(2000000 * 12); + + g_assert_cmpuint(ptimer_get_count(ptimer), ==, + (no_round_down ? 8 : 7) + (wrap_policy ? 1 : 0)); g_assert_true(triggered); ptimer_stop(ptimer); triggered = false; - qemu_clock_step(2000000 * 12 + 100000); + qemu_clock_step(2000000 * 10); - g_assert_cmpuint(ptimer_get_count(ptimer), ==, 7); + g_assert_cmpuint(ptimer_get_count(ptimer), ==, + (no_round_down ? 8 : 7) + (wrap_policy ? 1 : 0)); g_assert_false(triggered); ptimer_run(ptimer, 0); ptimer_set_period(ptimer, 0); - qemu_clock_step(2000000 + 100000); + qemu_clock_step(2000000 + 1); - g_assert_cmpuint(ptimer_get_count(ptimer), ==, 7); + g_assert_cmpuint(ptimer_get_count(ptimer), ==, + (no_round_down ? 8 : 7) + (wrap_policy ? 1 : 0)); g_assert_false(triggered); } @@ -276,6 +367,8 @@ static void check_on_the_fly_mode_change(gconstpointer arg) const uint8_t *policy = arg; QEMUBH *bh = qemu_bh_new(ptimer_trigger, NULL); ptimer_state *ptimer = ptimer_init(bh, *policy); + bool wrap_policy = (*policy & PTIMER_POLICY_WRAP_AFTER_ONE_PERIOD); + bool no_round_down = (*policy & PTIMER_POLICY_NO_COUNTER_ROUND_DOWN); triggered = false; @@ -283,16 +376,20 @@ static void check_on_the_fly_mode_change(gconstpointer arg) ptimer_set_limit(ptimer, 10, 1); ptimer_run(ptimer, 1); - qemu_clock_step(2000000 * 9 + 100000); + qemu_clock_step(2000000 * 9 + 1); + + g_assert_cmpuint(ptimer_get_count(ptimer), ==, no_round_down ? 1 : 0); + g_assert_false(triggered); ptimer_run(ptimer, 0); - g_assert_cmpuint(ptimer_get_count(ptimer), ==, 0); + g_assert_cmpuint(ptimer_get_count(ptimer), ==, no_round_down ? 1 : 0); g_assert_false(triggered); qemu_clock_step(2000000); - g_assert_cmpuint(ptimer_get_count(ptimer), ==, 9); + g_assert_cmpuint(ptimer_get_count(ptimer), ==, + wrap_policy ? 0 : (no_round_down ? 10 : 9)); g_assert_true(triggered); triggered = false; @@ -301,7 +398,8 @@ static void check_on_the_fly_mode_change(gconstpointer arg) ptimer_run(ptimer, 1); - g_assert_cmpuint(ptimer_get_count(ptimer), ==, 0); + g_assert_cmpuint(ptimer_get_count(ptimer), ==, + (no_round_down ? 1 : 0) + (wrap_policy ? 1 : 0)); g_assert_false(triggered); qemu_clock_step(2000000 * 3); @@ -315,6 +413,7 @@ static void check_on_the_fly_period_change(gconstpointer arg) const uint8_t *policy = arg; QEMUBH *bh = qemu_bh_new(ptimer_trigger, NULL); ptimer_state *ptimer = ptimer_init(bh, *policy); + bool no_round_down = (*policy & PTIMER_POLICY_NO_COUNTER_ROUND_DOWN); triggered = false; @@ -322,17 +421,17 @@ static void check_on_the_fly_period_change(gconstpointer arg) ptimer_set_limit(ptimer, 8, 1); ptimer_run(ptimer, 1); - qemu_clock_step(2000000 * 4 + 100000); + qemu_clock_step(2000000 * 4 + 1); - g_assert_cmpuint(ptimer_get_count(ptimer), ==, 3); + g_assert_cmpuint(ptimer_get_count(ptimer), ==, no_round_down ? 4 : 3); g_assert_false(triggered); ptimer_set_period(ptimer, 4000000); - g_assert_cmpuint(ptimer_get_count(ptimer), ==, 3); + g_assert_cmpuint(ptimer_get_count(ptimer), ==, no_round_down ? 4 : 3); - qemu_clock_step(4000000 * 2 + 100000); + qemu_clock_step(4000000 * 2 + 1); - g_assert_cmpuint(ptimer_get_count(ptimer), ==, 0); + g_assert_cmpuint(ptimer_get_count(ptimer), ==, no_round_down ? 2 : 0); g_assert_false(triggered); qemu_clock_step(4000000 * 2); @@ -346,6 +445,7 @@ static void check_on_the_fly_freq_change(gconstpointer arg) const uint8_t *policy = arg; QEMUBH *bh = qemu_bh_new(ptimer_trigger, NULL); ptimer_state *ptimer = ptimer_init(bh, *policy); + bool no_round_down = (*policy & PTIMER_POLICY_NO_COUNTER_ROUND_DOWN); triggered = false; @@ -353,17 +453,17 @@ static void check_on_the_fly_freq_change(gconstpointer arg) ptimer_set_limit(ptimer, 8, 1); ptimer_run(ptimer, 1); - qemu_clock_step(2000000 * 4 + 100000); + qemu_clock_step(2000000 * 4 + 1); - g_assert_cmpuint(ptimer_get_count(ptimer), ==, 3); + g_assert_cmpuint(ptimer_get_count(ptimer), ==, no_round_down ? 4 : 3); g_assert_false(triggered); ptimer_set_freq(ptimer, 250); - g_assert_cmpuint(ptimer_get_count(ptimer), ==, 3); + g_assert_cmpuint(ptimer_get_count(ptimer), ==, no_round_down ? 4 : 3); - qemu_clock_step(2000000 * 4 + 100000); + qemu_clock_step(2000000 * 4 + 1); - g_assert_cmpuint(ptimer_get_count(ptimer), ==, 0); + g_assert_cmpuint(ptimer_get_count(ptimer), ==, no_round_down ? 2 : 0); g_assert_false(triggered); qemu_clock_step(2000000 * 4); @@ -394,25 +494,53 @@ static void check_run_with_delta_0(gconstpointer arg) const uint8_t *policy = arg; QEMUBH *bh = qemu_bh_new(ptimer_trigger, NULL); ptimer_state *ptimer = ptimer_init(bh, *policy); + bool wrap_policy = (*policy & PTIMER_POLICY_WRAP_AFTER_ONE_PERIOD); + bool no_immediate_trigger = (*policy & PTIMER_POLICY_NO_IMMEDIATE_TRIGGER); + bool no_immediate_reload = (*policy & PTIMER_POLICY_NO_IMMEDIATE_RELOAD); + bool no_round_down = (*policy & PTIMER_POLICY_NO_COUNTER_ROUND_DOWN); triggered = false; ptimer_set_period(ptimer, 2000000); ptimer_set_limit(ptimer, 99, 0); ptimer_run(ptimer, 1); - g_assert_cmpuint(ptimer_get_count(ptimer), ==, 99); - g_assert_true(triggered); + g_assert_cmpuint(ptimer_get_count(ptimer), ==, + no_immediate_reload ? 0 : 99); + + if (no_immediate_trigger) { + g_assert_false(triggered); + } else { + g_assert_true(triggered); + } triggered = false; - qemu_clock_step(2000000 + 100000); + if (no_immediate_trigger || no_immediate_reload) { + qemu_clock_step(2000000 + 1); + + g_assert_cmpuint(ptimer_get_count(ptimer), ==, + no_immediate_reload ? 0 : (no_round_down ? 98 : 97)); + + if (no_immediate_trigger && no_immediate_reload) { + g_assert_true(triggered); + + triggered = false; + } else { + g_assert_false(triggered); + } + + ptimer_set_count(ptimer, 99); + ptimer_run(ptimer, 1); + } + + qemu_clock_step(2000000 + 1); - g_assert_cmpuint(ptimer_get_count(ptimer), ==, 97); + g_assert_cmpuint(ptimer_get_count(ptimer), ==, no_round_down ? 98 : 97); g_assert_false(triggered); qemu_clock_step(2000000 * 97); - g_assert_cmpuint(ptimer_get_count(ptimer), ==, 0); + g_assert_cmpuint(ptimer_get_count(ptimer), ==, no_round_down ? 1 : 0); g_assert_false(triggered); qemu_clock_step(2000000 * 2); @@ -424,19 +552,42 @@ static void check_run_with_delta_0(gconstpointer arg) ptimer_set_count(ptimer, 0); ptimer_run(ptimer, 0); - g_assert_cmpuint(ptimer_get_count(ptimer), ==, 99); - g_assert_true(triggered); + g_assert_cmpuint(ptimer_get_count(ptimer), ==, + no_immediate_reload ? 0 : 99); + + if (no_immediate_trigger) { + g_assert_false(triggered); + } else { + g_assert_true(triggered); + } + + triggered = false; + + qemu_clock_step(1); + + if (no_immediate_reload) { + qemu_clock_step(2000000); + } + + g_assert_cmpuint(ptimer_get_count(ptimer), ==, no_round_down ? 99 : 98); + + if (no_immediate_reload && no_immediate_trigger) { + g_assert_true(triggered); + } else { + g_assert_false(triggered); + } triggered = false; - qemu_clock_step(2000000 + 100000); + qemu_clock_step(2000000); - g_assert_cmpuint(ptimer_get_count(ptimer), ==, 97); + g_assert_cmpuint(ptimer_get_count(ptimer), ==, no_round_down ? 98 : 97); g_assert_false(triggered); qemu_clock_step(2000000 * 98); - g_assert_cmpuint(ptimer_get_count(ptimer), ==, 98); + g_assert_cmpuint(ptimer_get_count(ptimer), ==, + wrap_policy ? 0 : (no_round_down ? 99 : 98)); g_assert_true(triggered); ptimer_stop(ptimer); @@ -447,6 +598,8 @@ static void check_periodic_with_load_0(gconstpointer arg) const uint8_t *policy = arg; QEMUBH *bh = qemu_bh_new(ptimer_trigger, NULL); ptimer_state *ptimer = ptimer_init(bh, *policy); + bool continuous_trigger = (*policy & PTIMER_POLICY_CONTINUOUS_TRIGGER); + bool no_immediate_trigger = (*policy & PTIMER_POLICY_NO_IMMEDIATE_TRIGGER); triggered = false; @@ -454,14 +607,46 @@ static void check_periodic_with_load_0(gconstpointer arg) ptimer_run(ptimer, 0); g_assert_cmpuint(ptimer_get_count(ptimer), ==, 0); + + if (no_immediate_trigger) { + g_assert_false(triggered); + } else { + g_assert_true(triggered); + } + + triggered = false; + + qemu_clock_step(2000000 + 1); + + g_assert_cmpuint(ptimer_get_count(ptimer), ==, 0); + + if (continuous_trigger || no_immediate_trigger) { + g_assert_true(triggered); + } else { + g_assert_false(triggered); + } + + triggered = false; + + ptimer_set_count(ptimer, 10); + ptimer_run(ptimer, 0); + + qemu_clock_step(2000000 * 10 + 1); + + g_assert_cmpuint(ptimer_get_count(ptimer), ==, 0); g_assert_true(triggered); triggered = false; - qemu_clock_step(2000000 + 100000); + qemu_clock_step(2000000 + 1); g_assert_cmpuint(ptimer_get_count(ptimer), ==, 0); - g_assert_false(triggered); + + if (continuous_trigger) { + g_assert_true(triggered); + } else { + g_assert_false(triggered); + } ptimer_stop(ptimer); } @@ -471,6 +656,7 @@ static void check_oneshot_with_load_0(gconstpointer arg) const uint8_t *policy = arg; QEMUBH *bh = qemu_bh_new(ptimer_trigger, NULL); ptimer_state *ptimer = ptimer_init(bh, *policy); + bool no_immediate_trigger = (*policy & PTIMER_POLICY_NO_IMMEDIATE_TRIGGER); triggered = false; @@ -478,26 +664,30 @@ static void check_oneshot_with_load_0(gconstpointer arg) ptimer_run(ptimer, 1); g_assert_cmpuint(ptimer_get_count(ptimer), ==, 0); - g_assert_true(triggered); + + if (no_immediate_trigger) { + g_assert_false(triggered); + } else { + g_assert_true(triggered); + } triggered = false; - qemu_clock_step(2000000 + 100000); + qemu_clock_step(2000000 + 1); g_assert_cmpuint(ptimer_get_count(ptimer), ==, 0); - g_assert_false(triggered); - triggered = false; - - qemu_clock_step(2000000 + 100000); - - g_assert_false(triggered); + if (no_immediate_trigger) { + g_assert_true(triggered); + } else { + g_assert_false(triggered); + } } static void add_ptimer_tests(uint8_t policy) { uint8_t *ppolicy = g_malloc(1); - char *policy_name = g_malloc(64); + char *policy_name = g_malloc0(256); *ppolicy = policy; @@ -505,6 +695,26 @@ static void add_ptimer_tests(uint8_t policy) g_sprintf(policy_name, "default"); } + if (policy & PTIMER_POLICY_WRAP_AFTER_ONE_PERIOD) { + g_strlcat(policy_name, "wrap_after_one_period,", 256); + } + + if (policy & PTIMER_POLICY_CONTINUOUS_TRIGGER) { + g_strlcat(policy_name, "continuous_trigger,", 256); + } + + if (policy & PTIMER_POLICY_NO_IMMEDIATE_TRIGGER) { + g_strlcat(policy_name, "no_immediate_trigger,", 256); + } + + if (policy & PTIMER_POLICY_NO_IMMEDIATE_RELOAD) { + g_strlcat(policy_name, "no_immediate_reload,", 256); + } + + if (policy & PTIMER_POLICY_NO_COUNTER_ROUND_DOWN) { + g_strlcat(policy_name, "no_counter_rounddown,", 256); + } + g_test_add_data_func( g_strdup_printf("/ptimer/set_count policy=%s", policy_name), ppolicy, check_set_count); @@ -550,6 +760,16 @@ static void add_ptimer_tests(uint8_t policy) ppolicy, check_oneshot_with_load_0); } +static void add_all_ptimer_policies_comb_tests(void) +{ + int last_policy = PTIMER_POLICY_NO_COUNTER_ROUND_DOWN; + int policy = PTIMER_POLICY_DEFAULT; + + for (; policy < (last_policy << 1); policy++) { + add_ptimer_tests(policy); + } +} + int main(int argc, char **argv) { int i; @@ -560,7 +780,7 @@ int main(int argc, char **argv) main_loop_tlg.tl[i] = g_new0(QEMUTimerList, 1); } - add_ptimer_tests(PTIMER_POLICY_DEFAULT); + add_all_ptimer_policies_comb_tests(); qtest_allowed = true; diff --git a/tests/ptimer-test.h b/tests/ptimer-test.h index 98d9b8f..09ac56d 100644 --- a/tests/ptimer-test.h +++ b/tests/ptimer-test.h @@ -1,7 +1,7 @@ /* * QTest testcase for the ptimer * - * Author: Dmitry Osipenko <digetx@gmail.com> + * Copyright (c) 2016 Dmitry Osipenko <digetx@gmail.com> * * This work is licensed under the terms of the GNU GPL, version 2 or later. * See the COPYING file in the top-level directory. diff --git a/tests/qemu-iotests/041 b/tests/qemu-iotests/041 index d1e1ad8..30e628f 100755 --- a/tests/qemu-iotests/041 +++ b/tests/qemu-iotests/041 @@ -194,10 +194,9 @@ class TestSingleBlockdev(TestSingleDrive): def setUp(self): TestSingleDrive.setUp(self) qemu_img('create', '-f', iotests.imgfmt, '-o', 'backing_file=%s' % backing_img, target_img) - args = {'options': - {'driver': iotests.imgfmt, - 'node-name': self.qmp_target, - 'file': { 'filename': target_img, 'driver': 'file' } } } + args = {'driver': iotests.imgfmt, + 'node-name': self.qmp_target, + 'file': { 'filename': target_img, 'driver': 'file' } } result = self.vm.qmp("blockdev-add", **args) self.assert_qmp(result, 'return', {}) @@ -782,8 +781,8 @@ class TestRepairQuorum(iotests.QMPTestCase): self.vm.launch() #assemble the quorum block device from the individual files - args = { "options" : { "driver": "quorum", "node-name": "quorum0", - "vote-threshold": 2, "children": [ "img0", "img1", "img2" ] } } + args = { "driver": "quorum", "node-name": "quorum0", + "vote-threshold": 2, "children": [ "img0", "img1", "img2" ] } if self.has_quorum(): result = self.vm.qmp("blockdev-add", **args) self.assert_qmp(result, 'return', {}) diff --git a/tests/qemu-iotests/051.out b/tests/qemu-iotests/051.out index 408d613..42bf416 100644 --- a/tests/qemu-iotests/051.out +++ b/tests/qemu-iotests/051.out @@ -222,7 +222,7 @@ Testing: -drive driver=file QEMU_PROG: -drive driver=file: The 'file' block driver requires a file name Testing: -drive driver=nbd -QEMU_PROG: -drive driver=nbd: one of path and host must be specified. +QEMU_PROG: -drive driver=nbd: NBD server address missing Testing: -drive driver=raw QEMU_PROG: -drive driver=raw: Can't use 'raw' as a block driver for the protocol level @@ -231,7 +231,7 @@ Testing: -drive file.driver=file QEMU_PROG: -drive file.driver=file: The 'file' block driver requires a file name Testing: -drive file.driver=nbd -QEMU_PROG: -drive file.driver=nbd: one of path and host must be specified. +QEMU_PROG: -drive file.driver=nbd: NBD server address missing Testing: -drive file.driver=raw QEMU_PROG: -drive file.driver=raw: Can't use 'raw' as a block driver for the protocol level diff --git a/tests/qemu-iotests/051.pc.out b/tests/qemu-iotests/051.pc.out index ec6d222..603bb76 100644 --- a/tests/qemu-iotests/051.pc.out +++ b/tests/qemu-iotests/051.pc.out @@ -316,7 +316,7 @@ Testing: -drive driver=file QEMU_PROG: -drive driver=file: The 'file' block driver requires a file name Testing: -drive driver=nbd -QEMU_PROG: -drive driver=nbd: one of path and host must be specified. +QEMU_PROG: -drive driver=nbd: NBD server address missing Testing: -drive driver=raw QEMU_PROG: -drive driver=raw: Can't use 'raw' as a block driver for the protocol level @@ -325,7 +325,7 @@ Testing: -drive file.driver=file QEMU_PROG: -drive file.driver=file: The 'file' block driver requires a file name Testing: -drive file.driver=nbd -QEMU_PROG: -drive file.driver=nbd: one of path and host must be specified. +QEMU_PROG: -drive file.driver=nbd: NBD server address missing Testing: -drive file.driver=raw QEMU_PROG: -drive file.driver=raw: Can't use 'raw' as a block driver for the protocol level diff --git a/tests/qemu-iotests/067 b/tests/qemu-iotests/067 index a12125b..38d23fc 100755 --- a/tests/qemu-iotests/067 +++ b/tests/qemu-iotests/067 @@ -119,13 +119,11 @@ run_qemu <<EOF { "execute": "qmp_capabilities" } { "execute": "blockdev-add", "arguments": { - "options": { - "driver": "$IMGFMT", - "node-name": "disk", - "file": { - "driver": "file", - "filename": "$TEST_IMG" - } + "driver": "$IMGFMT", + "node-name": "disk", + "file": { + "driver": "file", + "filename": "$TEST_IMG" } } } diff --git a/tests/qemu-iotests/071 b/tests/qemu-iotests/071 index 6d0864c..48b4955 100755 --- a/tests/qemu-iotests/071 +++ b/tests/qemu-iotests/071 @@ -107,25 +107,21 @@ run_qemu <<EOF { "execute": "qmp_capabilities" } { "execute": "blockdev-add", "arguments": { - "options": { - "node-name": "drive0", - "driver": "file", - "filename": "$TEST_IMG" - } + "node-name": "drive0", + "driver": "file", + "filename": "$TEST_IMG" } } { "execute": "blockdev-add", "arguments": { - "options": { - "driver": "$IMGFMT", - "node-name": "drive0-debug", - "file": { - "driver": "blkdebug", - "image": "drive0", - "inject-error": [{ - "event": "l2_load" - }] - } + "driver": "$IMGFMT", + "node-name": "drive0-debug", + "file": { + "driver": "blkdebug", + "image": "drive0", + "inject-error": [{ + "event": "l2_load" + }] } } } @@ -145,26 +141,22 @@ run_qemu <<EOF { "execute": "qmp_capabilities" } { "execute": "blockdev-add", "arguments": { - "options": { - "node-name": "drive0", - "driver": "$IMGFMT", - "file": { - "driver": "file", - "filename": "$TEST_IMG" - } + "node-name": "drive0", + "driver": "$IMGFMT", + "file": { + "driver": "file", + "filename": "$TEST_IMG" } } } { "execute": "blockdev-add", "arguments": { - "options": { - "driver": "blkverify", - "node-name": "drive0-verify", - "test": "drive0", - "raw": { - "driver": "file", - "filename": "$TEST_IMG.base" - } + "driver": "blkverify", + "node-name": "drive0-verify", + "test": "drive0", + "raw": { + "driver": "file", + "filename": "$TEST_IMG.base" } } } @@ -184,27 +176,23 @@ run_qemu <<EOF { "execute": "qmp_capabilities" } { "execute": "blockdev-add", "arguments": { - "options": { - "node-name": "drive0", - "driver": "file", - "filename": "$TEST_IMG.base" - } + "node-name": "drive0", + "driver": "file", + "filename": "$TEST_IMG.base" } } { "execute": "blockdev-add", "arguments": { - "options": { - "driver": "blkverify", - "node-name": "drive0-verify", - "test": { - "driver": "$IMGFMT", - "file": { - "driver": "file", - "filename": "$TEST_IMG" - } - }, - "raw": "drive0" - } + "driver": "blkverify", + "node-name": "drive0-verify", + "test": { + "driver": "$IMGFMT", + "file": { + "driver": "file", + "filename": "$TEST_IMG" + } + }, + "raw": "drive0" } } { "execute": "human-monitor-command", @@ -223,30 +211,26 @@ run_qemu <<EOF { "execute": "qmp_capabilities" } { "execute": "blockdev-add", "arguments": { - "options": { - "node-name": "drive0", - "driver": "file", - "filename": "$TEST_IMG" - } + "node-name": "drive0", + "driver": "file", + "filename": "$TEST_IMG" } } { "execute": "blockdev-add", "arguments": { - "options": { - "driver": "$IMGFMT", - "node-name": "drive0-debug", - "file": { - "driver": "blkdebug", - "image": "drive0", - "inject-error": [{ - "event": "read_aio", - "state": 42 - }], - "set-state": [{ - "event": "write_aio", - "new_state": 42 - }] - } + "driver": "$IMGFMT", + "node-name": "drive0-debug", + "file": { + "driver": "blkdebug", + "image": "drive0", + "inject-error": [{ + "event": "read_aio", + "state": 42 + }], + "set-state": [{ + "event": "write_aio", + "new_state": 42 + }] } } } diff --git a/tests/qemu-iotests/081 b/tests/qemu-iotests/081 index 0a809f3..da3fb09 100755 --- a/tests/qemu-iotests/081 +++ b/tests/qemu-iotests/081 @@ -105,40 +105,36 @@ run_qemu <<EOF { "execute": "qmp_capabilities" } { "execute": "blockdev-add", "arguments": { - "options": { - "node-name": "drive2", - "driver": "$IMGFMT", - "file": { - "driver": "file", - "filename": "$TEST_DIR/2.raw" - } + "node-name": "drive2", + "driver": "$IMGFMT", + "file": { + "driver": "file", + "filename": "$TEST_DIR/2.raw" } } } { "execute": "blockdev-add", "arguments": { - "options": { - "driver": "quorum", - "node-name": "drive0-quorum", - "vote-threshold": 2, - "children": [ - { - "driver": "$IMGFMT", - "file": { - "driver": "file", - "filename": "$TEST_DIR/1.raw" - } - }, - "drive2", - { - "driver": "$IMGFMT", - "file": { - "driver": "file", - "filename": "$TEST_DIR/3.raw" - } + "driver": "quorum", + "node-name": "drive0-quorum", + "vote-threshold": 2, + "children": [ + { + "driver": "$IMGFMT", + "file": { + "driver": "file", + "filename": "$TEST_DIR/1.raw" } - ] - } + }, + "drive2", + { + "driver": "$IMGFMT", + "file": { + "driver": "file", + "filename": "$TEST_DIR/3.raw" + } + } + ] } } { "execute": "human-monitor-command", diff --git a/tests/qemu-iotests/085 b/tests/qemu-iotests/085 index aa77eca..c53e97f 100755 --- a/tests/qemu-iotests/085 +++ b/tests/qemu-iotests/085 @@ -100,11 +100,10 @@ function add_snapshot_image() _make_test_img -b "${base_image}" "$size" mv "${TEST_IMG}" "${snapshot_file}" cmd="{ 'execute': 'blockdev-add', 'arguments': - { 'options': - { 'driver': 'qcow2', 'node-name': 'snap_${1}', ${extra_params} - 'file': - { 'driver': 'file', 'filename': '${snapshot_file}', - 'node-name': 'file_${1}' } } } }" + { 'driver': 'qcow2', 'node-name': 'snap_${1}', ${extra_params} + 'file': + { 'driver': 'file', 'filename': '${snapshot_file}', + 'node-name': 'file_${1}' } } }" _send_qemu_cmd $h "${cmd}" "return" } diff --git a/tests/qemu-iotests/087 b/tests/qemu-iotests/087 index b1ac71f..9de57dd 100755 --- a/tests/qemu-iotests/087 +++ b/tests/qemu-iotests/087 @@ -61,12 +61,10 @@ run_qemu <<EOF { "execute": "qmp_capabilities" } { "execute": "blockdev-add", "arguments": { - "options": { - "driver": "$IMGFMT", - "file": { - "driver": "file", - "filename": "$TEST_IMG" - } + "driver": "$IMGFMT", + "file": { + "driver": "file", + "filename": "$TEST_IMG" } } } @@ -81,25 +79,21 @@ run_qemu -drive driver=$IMGFMT,id=disk,node-name=test-node,file="$TEST_IMG" <<EO { "execute": "qmp_capabilities" } { "execute": "blockdev-add", "arguments": { - "options": { - "driver": "$IMGFMT", - "node-name": "disk", - "file": { - "driver": "file", - "filename": "$TEST_IMG" - } + "driver": "$IMGFMT", + "node-name": "disk", + "file": { + "driver": "file", + "filename": "$TEST_IMG" } } } { "execute": "blockdev-add", "arguments": { - "options": { - "driver": "$IMGFMT", - "node-name": "test-node", - "file": { - "driver": "file", - "filename": "$TEST_IMG" - } + "driver": "$IMGFMT", + "node-name": "test-node", + "file": { + "driver": "file", + "filename": "$TEST_IMG" } } } @@ -114,14 +108,12 @@ run_qemu <<EOF { "execute": "qmp_capabilities" } { "execute": "blockdev-add", "arguments": { - "options": { - "driver": "$IMGFMT", - "node-name": "disk", - "file": { - "driver": "file", - "filename": "$TEST_IMG", - "aio": "native" - } + "driver": "$IMGFMT", + "node-name": "disk", + "file": { + "driver": "file", + "filename": "$TEST_IMG", + "aio": "native" } } } @@ -137,13 +129,11 @@ run_qemu -S <<EOF { "execute": "qmp_capabilities" } { "execute": "blockdev-add", "arguments": { - "options": { - "driver": "$IMGFMT", - "node-name": "disk", - "file": { - "driver": "file", - "filename": "$TEST_IMG" - } + "driver": "$IMGFMT", + "node-name": "disk", + "file": { + "driver": "file", + "filename": "$TEST_IMG" } } } @@ -154,13 +144,11 @@ run_qemu <<EOF { "execute": "qmp_capabilities" } { "execute": "blockdev-add", "arguments": { - "options": { - "driver": "$IMGFMT", - "node-name": "disk", - "file": { - "driver": "file", - "filename": "$TEST_IMG" - } + "driver": "$IMGFMT", + "node-name": "disk", + "file": { + "driver": "file", + "filename": "$TEST_IMG" } } } @@ -176,9 +164,7 @@ run_qemu -S <<EOF { "execute": "qmp_capabilities" } { "execute": "blockdev-add", "arguments": { - "options": { - "node-name": "disk" - } + "node-name": "disk" } } { "execute": "quit" } diff --git a/tests/qemu-iotests/093 b/tests/qemu-iotests/093 index ffcb271..2ed393a 100755 --- a/tests/qemu-iotests/093 +++ b/tests/qemu-iotests/093 @@ -53,7 +53,7 @@ class ThrottleTestCase(iotests.QMPTestCase): result = self.vm.qmp("block_set_io_throttle", conv_keys=False, **params) self.assert_qmp(result, 'return', {}) - def do_test_throttle(self, ndrives, seconds, params): + def do_test_throttle(self, ndrives, seconds, params, first_drive = 0): def check_limit(limit, num): # IO throttling algorithm is discrete, allow 10% error so the test # is more robust @@ -85,12 +85,14 @@ class ThrottleTestCase(iotests.QMPTestCase): # Send I/O requests to all drives for i in range(rd_nr): for drive in range(0, ndrives): - self.vm.hmp_qemu_io("drive%d" % drive, "aio_read %d %d" % + idx = first_drive + drive + self.vm.hmp_qemu_io("drive%d" % idx, "aio_read %d %d" % (i * rq_size, rq_size)) for i in range(wr_nr): for drive in range(0, ndrives): - self.vm.hmp_qemu_io("drive%d" % drive, "aio_write %d %d" % + idx = first_drive + drive + self.vm.hmp_qemu_io("drive%d" % idx, "aio_write %d %d" % (i * rq_size, rq_size)) # We'll store the I/O stats for each drive in these arrays @@ -105,15 +107,17 @@ class ThrottleTestCase(iotests.QMPTestCase): # Read the stats before advancing the clock for i in range(0, ndrives): + idx = first_drive + i start_rd_bytes[i], start_rd_iops[i], start_wr_bytes[i], \ - start_wr_iops[i] = self.blockstats('drive%d' % i) + start_wr_iops[i] = self.blockstats('drive%d' % idx) self.vm.qtest("clock_step %d" % ns) # Read the stats after advancing the clock for i in range(0, ndrives): + idx = first_drive + i end_rd_bytes[i], end_rd_iops[i], end_wr_bytes[i], \ - end_wr_iops[i] = self.blockstats('drive%d' % i) + end_wr_iops[i] = self.blockstats('drive%d' % idx) # Check that the I/O is within the limits and evenly distributed for i in range(0, ndrives): @@ -129,6 +133,7 @@ class ThrottleTestCase(iotests.QMPTestCase): self.assertTrue(check_limit(params['iops_rd'], rd_iops)) self.assertTrue(check_limit(params['iops_wr'], wr_iops)) + # Connect N drives to a VM and test I/O in all of them def test_all(self): params = {"bps": 4096, "bps_rd": 4096, @@ -146,6 +151,24 @@ class ThrottleTestCase(iotests.QMPTestCase): self.configure_throttle(ndrives, limits) self.do_test_throttle(ndrives, 5, limits) + # Connect N drives to a VM and test I/O in just one of them a time + def test_one(self): + params = {"bps": 4096, + "bps_rd": 4096, + "bps_wr": 4096, + "iops": 10, + "iops_rd": 10, + "iops_wr": 10, + } + # Repeat the test for each one of the drives + for drive in range(0, self.max_drives): + # Pick each out of all possible params and test + for tk in params: + limits = dict([(k, 0) for k in params]) + limits[tk] = params[tk] * self.max_drives + self.configure_throttle(self.max_drives, limits) + self.do_test_throttle(1, 5, limits, drive) + def test_burst(self): params = {"bps": 4096, "bps_rd": 4096, diff --git a/tests/qemu-iotests/093.out b/tests/qemu-iotests/093.out index 914e373..2f7d390 100644 --- a/tests/qemu-iotests/093.out +++ b/tests/qemu-iotests/093.out @@ -1,5 +1,5 @@ -..... +....... ---------------------------------------------------------------------- -Ran 5 tests +Ran 7 tests OK diff --git a/tests/qemu-iotests/117 b/tests/qemu-iotests/117 index 5b28039..e955d52 100755 --- a/tests/qemu-iotests/117 +++ b/tests/qemu-iotests/117 @@ -52,16 +52,16 @@ _send_qemu_cmd $QEMU_HANDLE \ _send_qemu_cmd $QEMU_HANDLE \ "{ 'execute': 'blockdev-add', - 'arguments': { 'options': { 'node-name': 'protocol', - 'driver': 'file', - 'filename': '$TEST_IMG' } } }" \ + 'arguments': { 'node-name': 'protocol', + 'driver': 'file', + 'filename': '$TEST_IMG' } }" \ 'return' _send_qemu_cmd $QEMU_HANDLE \ "{ 'execute': 'blockdev-add', - 'arguments': { 'options': { 'node-name': 'format', - 'driver': '$IMGFMT', - 'file': 'protocol' } } }" \ + 'arguments': { 'node-name': 'format', + 'driver': '$IMGFMT', + 'file': 'protocol' } }" \ 'return' _send_qemu_cmd $QEMU_HANDLE \ diff --git a/tests/qemu-iotests/118 b/tests/qemu-iotests/118 index e63a40f..8a9e838 100755 --- a/tests/qemu-iotests/118 +++ b/tests/qemu-iotests/118 @@ -229,10 +229,10 @@ class GeneralChangeTestsBaseClass(ChangeBaseClass): def test_cycle(self): result = self.vm.qmp('blockdev-add', - options={'node-name': 'new', - 'driver': iotests.imgfmt, - 'file': {'filename': new_img, - 'driver': 'file'}}) + node_name='new', + driver=iotests.imgfmt, + file={'filename': new_img, + 'driver': 'file'}) self.assert_qmp(result, 'return', {}) if self.device_name is not None: @@ -309,10 +309,10 @@ class GeneralChangeTestsBaseClass(ChangeBaseClass): return result = self.vm.qmp('blockdev-add', - options={'node-name': 'new', - 'driver': iotests.imgfmt, - 'file': {'filename': new_img, - 'driver': 'file'}}) + node_name='new', + driver=iotests.imgfmt, + file={'filename': new_img, + 'driver': 'file'}) self.assert_qmp(result, 'return', {}) result = self.vm.qmp('x-blockdev-insert-medium', device='drive0', @@ -341,10 +341,10 @@ class TestInitiallyFilled(GeneralChangeTestsBaseClass): def test_insert_on_filled(self): result = self.vm.qmp('blockdev-add', - options={'node-name': 'new', - 'driver': iotests.imgfmt, - 'file': {'filename': new_img, - 'driver': 'file'}}) + node_name='new', + driver=iotests.imgfmt, + file={'filename': new_img, + 'driver': 'file'}) self.assert_qmp(result, 'return', {}) result = self.vm.qmp('blockdev-open-tray', device='drive0') @@ -609,11 +609,11 @@ class TestChangeReadOnly(ChangeBaseClass): self.assert_qmp(result, 'return[0]/inserted/image/filename', old_img) result = self.vm.qmp('blockdev-add', - options={'node-name': 'new', - 'driver': iotests.imgfmt, - 'read-only': True, - 'file': {'filename': new_img, - 'driver': 'file'}}) + node_name='new', + driver=iotests.imgfmt, + read_only=True, + file={'filename': new_img, + 'driver': 'file'}) self.assert_qmp(result, 'return', {}) result = self.vm.qmp('query-block') @@ -663,10 +663,10 @@ class TestBlockJobsAfterCycle(ChangeBaseClass): self.assert_qmp_absent(result, 'return[0]/inserted') result = self.vm.qmp('blockdev-add', - options={'node-name': 'node0', - 'driver': iotests.imgfmt, - 'file': {'filename': old_img, - 'driver': 'file'}}) + node_name='node0', + driver=iotests.imgfmt, + file={'filename': old_img, + 'driver': 'file'}) self.assert_qmp(result, 'return', {}) result = self.vm.qmp('x-blockdev-insert-medium', device='drive0', diff --git a/tests/qemu-iotests/124 b/tests/qemu-iotests/124 index 2f0bc24..f06938e 100644 --- a/tests/qemu-iotests/124 +++ b/tests/qemu-iotests/124 @@ -416,10 +416,10 @@ class TestIncrementalBackup(TestIncrementalBackupBase): ('0xcd', '32M', '124k'))) # Create a blkdebug interface to this img as 'drive1' - result = self.vm.qmp('blockdev-add', options={ - 'node-name': drive1['id'], - 'driver': drive1['fmt'], - 'file': { + result = self.vm.qmp('blockdev-add', + node_name=drive1['id'], + driver=drive1['fmt'], + file={ 'driver': 'blkdebug', 'image': { 'driver': 'file', @@ -438,7 +438,7 @@ class TestIncrementalBackup(TestIncrementalBackupBase): 'once': True }], } - }) + ) self.assert_qmp(result, 'return', {}) # Create bitmaps and full backups for both drives @@ -560,10 +560,10 @@ class TestIncrementalBackupBlkdebug(TestIncrementalBackupBase): ''' drive0 = self.drives[0] - result = self.vm.qmp('blockdev-add', options={ - 'node-name': drive0['id'], - 'driver': drive0['fmt'], - 'file': { + result = self.vm.qmp('blockdev-add', + node_name=drive0['id'], + driver=drive0['fmt'], + file={ 'driver': 'blkdebug', 'image': { 'driver': 'file', @@ -582,7 +582,7 @@ class TestIncrementalBackupBlkdebug(TestIncrementalBackupBase): 'once': True }], } - }) + ) self.assert_qmp(result, 'return', {}) self.create_anchor_backup(drive0) diff --git a/tests/qemu-iotests/139 b/tests/qemu-iotests/139 index 47a4c26..6a0f6ca 100644 --- a/tests/qemu-iotests/139 +++ b/tests/qemu-iotests/139 @@ -57,7 +57,7 @@ class TestBlockdevDel(iotests.QMPTestCase): 'file': {'driver': 'file', 'node-name': file_node, 'filename': base_img}} - result = self.vm.qmp('blockdev-add', conv_keys = False, options = opts) + result = self.vm.qmp('blockdev-add', conv_keys = False, **opts) self.assert_qmp(result, 'return', {}) self.checkBlockDriverState(node) self.checkBlockDriverState(file_node) @@ -72,7 +72,7 @@ class TestBlockdevDel(iotests.QMPTestCase): 'backing': '', 'file': {'driver': 'file', 'filename': new_img}} - result = self.vm.qmp('blockdev-add', conv_keys = False, options = opts) + result = self.vm.qmp('blockdev-add', conv_keys = False, **opts) self.assert_qmp(result, 'return', {}) self.checkBlockDriverState(node) @@ -185,7 +185,7 @@ class TestBlockdevDel(iotests.QMPTestCase): opts = {'driver': 'blkdebug', 'node-name': debug, 'image': image} - result = self.vm.qmp('blockdev-add', conv_keys = False, options = opts) + result = self.vm.qmp('blockdev-add', conv_keys = False, **opts) self.assert_qmp(result, 'return', {}) self.checkBlockDriverState(node) self.checkBlockDriverState(debug) @@ -210,7 +210,7 @@ class TestBlockdevDel(iotests.QMPTestCase): 'node-name': blkverify, 'test': node_0, 'raw': node_1} - result = self.vm.qmp('blockdev-add', conv_keys = False, options = opts) + result = self.vm.qmp('blockdev-add', conv_keys = False, **opts) self.assert_qmp(result, 'return', {}) self.checkBlockDriverState(test) self.checkBlockDriverState(raw) @@ -234,7 +234,7 @@ class TestBlockdevDel(iotests.QMPTestCase): 'node-name': quorum, 'vote-threshold': 1, 'children': [ child_0, child_1 ]} - result = self.vm.qmp('blockdev-add', conv_keys = False, options = opts) + result = self.vm.qmp('blockdev-add', conv_keys = False, **opts) self.assert_qmp(result, 'return', {}) self.checkBlockDriverState(child0) self.checkBlockDriverState(child1) diff --git a/tests/qemu-iotests/141 b/tests/qemu-iotests/141 index c092d87..3ba79f0 100755 --- a/tests/qemu-iotests/141 +++ b/tests/qemu-iotests/141 @@ -50,13 +50,12 @@ test_blockjob() _send_qemu_cmd $QEMU_HANDLE \ "{'execute': 'blockdev-add', 'arguments': { - 'options': { - 'node-name': 'drv0', - 'driver': '$IMGFMT', - 'file': { - 'driver': 'file', - 'filename': '$TEST_IMG' - }}}}" \ + 'node-name': 'drv0', + 'driver': '$IMGFMT', + 'file': { + 'driver': 'file', + 'filename': '$TEST_IMG' + }}}" \ 'return' _send_qemu_cmd $QEMU_HANDLE \ diff --git a/tests/qemu-iotests/147 b/tests/qemu-iotests/147 new file mode 100755 index 0000000..45469c9 --- /dev/null +++ b/tests/qemu-iotests/147 @@ -0,0 +1,195 @@ +#!/usr/bin/env python +# +# Test case for NBD's blockdev-add interface +# +# Copyright (C) 2016 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/>. +# + +import os +import socket +import stat +import time +import iotests +from iotests import cachemode, imgfmt, qemu_img, qemu_nbd + +NBD_PORT = 10811 + +test_img = os.path.join(iotests.test_dir, 'test.img') +unix_socket = os.path.join(iotests.test_dir, 'nbd.socket') + +class NBDBlockdevAddBase(iotests.QMPTestCase): + def blockdev_add_options(self, address, export=None): + options = { 'node-name': 'nbd-blockdev', + 'driver': 'raw', + 'file': { + 'driver': 'nbd', + 'server': address + } } + if export is not None: + options['file']['export'] = export + return options + + def client_test(self, filename, address, export=None): + bao = self.blockdev_add_options(address, export) + result = self.vm.qmp('blockdev-add', **bao) + self.assert_qmp(result, 'return', {}) + + result = self.vm.qmp('query-named-block-nodes') + for node in result['return']: + if node['node-name'] == 'nbd-blockdev': + if isinstance(filename, str): + self.assert_qmp(node, 'image/filename', filename) + else: + self.assert_json_filename_equal(node['image']['filename'], + filename) + break + + result = self.vm.qmp('x-blockdev-del', node_name='nbd-blockdev') + self.assert_qmp(result, 'return', {}) + + +class QemuNBD(NBDBlockdevAddBase): + def setUp(self): + qemu_img('create', '-f', iotests.imgfmt, test_img, '64k') + self.vm = iotests.VM() + self.vm.launch() + + def tearDown(self): + self.vm.shutdown() + os.remove(test_img) + try: + os.remove(unix_socket) + except OSError: + pass + + def _server_up(self, *args): + self.assertEqual(qemu_nbd('-f', imgfmt, test_img, *args), 0) + + def test_inet(self): + self._server_up('-p', str(NBD_PORT)) + address = { 'type': 'inet', + 'data': { + 'host': 'localhost', + 'port': str(NBD_PORT) + } } + self.client_test('nbd://localhost:%i' % NBD_PORT, address) + + def test_unix(self): + self._server_up('-k', unix_socket) + address = { 'type': 'unix', + 'data': { 'path': unix_socket } } + self.client_test('nbd+unix://?socket=' + unix_socket, address) + + +class BuiltinNBD(NBDBlockdevAddBase): + def setUp(self): + qemu_img('create', '-f', iotests.imgfmt, test_img, '64k') + self.vm = iotests.VM() + self.vm.launch() + self.server = iotests.VM('.server') + self.server.add_drive_raw('if=none,id=nbd-export,' + + 'file=%s,' % test_img + + 'format=%s,' % imgfmt + + 'cache=%s' % cachemode) + self.server.launch() + + def tearDown(self): + self.vm.shutdown() + self.server.shutdown() + os.remove(test_img) + try: + os.remove(unix_socket) + except OSError: + pass + + def _server_up(self, address): + result = self.server.qmp('nbd-server-start', addr=address) + self.assert_qmp(result, 'return', {}) + + result = self.server.qmp('nbd-server-add', device='nbd-export') + self.assert_qmp(result, 'return', {}) + + def _server_down(self): + result = self.server.qmp('nbd-server-stop') + self.assert_qmp(result, 'return', {}) + + def test_inet(self): + address = { 'type': 'inet', + 'data': { + 'host': 'localhost', + 'port': str(NBD_PORT) + } } + self._server_up(address) + self.client_test('nbd://localhost:%i/nbd-export' % NBD_PORT, + address, 'nbd-export') + self._server_down() + + def test_inet6(self): + address = { 'type': 'inet', + 'data': { + 'host': '::1', + 'port': str(NBD_PORT), + 'ipv4': False, + 'ipv6': True + } } + filename = { 'driver': 'raw', + 'file': { + 'driver': 'nbd', + 'export': 'nbd-export', + 'server': address + } } + self._server_up(address) + self.client_test(filename, address, 'nbd-export') + self._server_down() + + def test_unix(self): + address = { 'type': 'unix', + 'data': { 'path': unix_socket } } + self._server_up(address) + self.client_test('nbd+unix:///nbd-export?socket=' + unix_socket, + address, 'nbd-export') + self._server_down() + + def test_fd(self): + self._server_up({ 'type': 'unix', + 'data': { 'path': unix_socket } }) + + sockfd = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) + sockfd.connect(unix_socket) + + result = self.vm.send_fd_scm(str(sockfd.fileno())) + self.assertEqual(result, 0, 'Failed to send socket FD') + + result = self.vm.qmp('getfd', fdname='nbd-fifo') + self.assert_qmp(result, 'return', {}) + + address = { 'type': 'fd', + 'data': { 'str': 'nbd-fifo' } } + filename = { 'driver': 'raw', + 'file': { + 'driver': 'nbd', + 'export': 'nbd-export', + 'server': address + } } + self.client_test(filename, address, 'nbd-export') + + self._server_down() + + +if __name__ == '__main__': + # Need to support image creation + iotests.main(supported_fmts=['vpc', 'parallels', 'qcow', 'vdi', 'qcow2', + 'vmdk', 'raw', 'vhdx', 'qed']) diff --git a/tests/qemu-iotests/147.out b/tests/qemu-iotests/147.out new file mode 100644 index 0000000..3f8a935 --- /dev/null +++ b/tests/qemu-iotests/147.out @@ -0,0 +1,5 @@ +...... +---------------------------------------------------------------------- +Ran 6 tests + +OK diff --git a/tests/qemu-iotests/155 b/tests/qemu-iotests/155 index 4057b5e..0b86ea4 100755 --- a/tests/qemu-iotests/155 +++ b/tests/qemu-iotests/155 @@ -63,10 +63,10 @@ class BaseClass(iotests.QMPTestCase): # Add the BDS via blockdev-add so it stays around after the mirror block # job has been completed result = self.vm.qmp('blockdev-add', - options={'node-name': 'source', - 'driver': iotests.imgfmt, - 'file': {'driver': 'file', - 'filename': source_img}}) + node_name='source', + driver=iotests.imgfmt, + file={'driver': 'file', + 'filename': source_img}) self.assert_qmp(result, 'return', {}) result = self.vm.qmp('x-blockdev-insert-medium', @@ -90,7 +90,7 @@ class BaseClass(iotests.QMPTestCase): if self.target_blockdev_backing: options['backing'] = self.target_blockdev_backing - result = self.vm.qmp('blockdev-add', options=options) + result = self.vm.qmp('blockdev-add', **options) self.assert_qmp(result, 'return', {}) def tearDown(self): diff --git a/tests/qemu-iotests/162 b/tests/qemu-iotests/162 index 0b43ea3..f8eecb3 100755 --- a/tests/qemu-iotests/162 +++ b/tests/qemu-iotests/162 @@ -43,16 +43,26 @@ echo '=== NBD ===' $QEMU_IMG info 'json:{"driver": "nbd", "host": 42}' # And this should not treat @port as if it had not been specified -# (We cannot use localhost with an invalid port here, but we need to use a -# non-existing domain, because otherwise the error message will not contain -# the port) -$QEMU_IMG info 'json:{"driver": "nbd", "host": "does.not.exist.example.com", "port": 42}' +# (We need to set up a server here, because the error message for "Connection +# refused" does not contain the destination port) + +# Launching qemu-nbd is done in a loop: We try to set up an NBD server on some +# random port and continue until success, i.e. until we have found a port that +# is not in use yet. +while true; do + port=$((RANDOM + 32768)) + if $QEMU_NBD -p $port -f raw --fork null-co:// 2> /dev/null; then + break + fi +done + +$QEMU_IMG info "json:{'driver': 'nbd', 'host': 'localhost', 'port': $port}" \ + | grep '^image' | sed -e "s/$port/PORT/" # This is a test for NBD's bdrv_refresh_filename() implementation: It expects # either host or path to be set, but it must not assume that they are set to # strings in the options QDict -$QEMU_NBD -k "$PWD/42" -f raw null-co:// & -sleep 0.5 +$QEMU_NBD -k "$PWD/42" -f raw --fork null-co:// $QEMU_IMG info 'json:{"driver": "nbd", "path": 42}' | grep '^image' rm -f 42 diff --git a/tests/qemu-iotests/162.out b/tests/qemu-iotests/162.out index 9bba723..3c5be2c 100644 --- a/tests/qemu-iotests/162.out +++ b/tests/qemu-iotests/162.out @@ -2,7 +2,7 @@ QA output created by 162 === NBD === qemu-img: Could not open 'json:{"driver": "nbd", "host": 42}': Failed to connect socket: Invalid argument -qemu-img: Could not open 'json:{"driver": "nbd", "host": "does.not.exist.example.com", "port": 42}': address resolution failed for does.not.exist.example.com:42: Name or service not known +image: nbd://localhost:PORT image: nbd+unix://?socket=42 === SSH === diff --git a/tests/qemu-iotests/172 b/tests/qemu-iotests/172 new file mode 100755 index 0000000..1b7d3a1 --- /dev/null +++ b/tests/qemu-iotests/172 @@ -0,0 +1,246 @@ +#!/bin/bash +# +# Test floppy configuration +# +# Copyright (C) 2016 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` +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 + +if [ "$QEMU_DEFAULT_MACHINE" != "pc" ]; then + _notrun "Requires a PC machine" +fi + +function do_run_qemu() +{ + ( + if ! test -t 0; then + while read cmd; do + echo $cmd + done + fi + echo quit + ) | $QEMU -nographic -monitor stdio -serial none "$@" + echo +} + +function check_floppy_qtree() +{ + echo + echo Testing: "$@" | _filter_testdir + + # QEMU_OPTIONS contains -nodefaults, we don't want that here because the + # defaults are part of what should be checked here. + # + # Apply the sed filter to stdout only, but keep the stderr output and + # filter the qemu program name in it. + echo "info qtree" | + (QEMU_OPTIONS="" do_run_qemu "$@" | + sed -ne '/^ dev: isa-fdc/,/^ dev:/{x;p}' ) 2>&1 | + _filter_win32 | _filter_qemu +} + +function check_cache_mode() +{ + echo "info block none0" | + QEMU_OPTIONS="" do_run_qemu -drive if=none,file="$TEST_IMG" "$@" | + _filter_win32 | _filter_qemu | grep "Cache mode" +} + + +size=720k + +_make_test_img $size + +# Default drive semantics: +# +# By default you get a single empty floppy drive. You can override it with +# -drive and using the same index, but if you use -drive to add a floppy to a +# different index, you get both of them. However, as soon as you use any +# '-device floppy', even to a different slot, the default drive is disabled. + +echo +echo +echo === Default === + +check_floppy_qtree + +echo +echo +echo === Using -fda/-fdb options === + +check_floppy_qtree -fda "$TEST_IMG" +check_floppy_qtree -fdb "$TEST_IMG" +check_floppy_qtree -fda "$TEST_IMG" -fdb "$TEST_IMG" + + +echo +echo +echo === Using -drive options === + +check_floppy_qtree -drive if=floppy,file="$TEST_IMG" +check_floppy_qtree -drive if=floppy,file="$TEST_IMG",index=1 +check_floppy_qtree -drive if=floppy,file="$TEST_IMG" -drive if=floppy,file="$TEST_IMG",index=1 + +echo +echo +echo === Using -drive if=none and -global === + +check_floppy_qtree -drive if=none,file="$TEST_IMG" -global isa-fdc.driveA=none0 +check_floppy_qtree -drive if=none,file="$TEST_IMG" -global isa-fdc.driveB=none0 +check_floppy_qtree -drive if=none,file="$TEST_IMG" -drive if=none,file="$TEST_IMG" \ + -global isa-fdc.driveA=none0 -global isa-fdc.driveB=none1 + +echo +echo +echo === Using -drive if=none and -device === + +check_floppy_qtree -drive if=none,file="$TEST_IMG" -device floppy,drive=none0 +check_floppy_qtree -drive if=none,file="$TEST_IMG" -device floppy,drive=none0,unit=1 +check_floppy_qtree -drive if=none,file="$TEST_IMG" -drive if=none,file="$TEST_IMG" \ + -device floppy,drive=none0 -device floppy,drive=none1,unit=1 + +echo +echo +echo === Mixing -fdX and -global === + +# Working +check_floppy_qtree -fda "$TEST_IMG" -drive if=none,file="$TEST_IMG" -global isa-fdc.driveB=none0 +check_floppy_qtree -fdb "$TEST_IMG" -drive if=none,file="$TEST_IMG" -global isa-fdc.driveA=none0 + +# Conflicting (-fdX wins) +check_floppy_qtree -fda "$TEST_IMG" -drive if=none,file="$TEST_IMG" -global isa-fdc.driveA=none0 +check_floppy_qtree -fdb "$TEST_IMG" -drive if=none,file="$TEST_IMG" -global isa-fdc.driveB=none0 + +echo +echo +echo === Mixing -fdX and -device === + +# Working +check_floppy_qtree -fda "$TEST_IMG" -drive if=none,file="$TEST_IMG" -device floppy,drive=none0 +check_floppy_qtree -fda "$TEST_IMG" -drive if=none,file="$TEST_IMG" -device floppy,drive=none0,unit=1 + +check_floppy_qtree -fdb "$TEST_IMG" -drive if=none,file="$TEST_IMG" -device floppy,drive=none0 +check_floppy_qtree -fdb "$TEST_IMG" -drive if=none,file="$TEST_IMG" -device floppy,drive=none0,unit=0 + +# Conflicting +check_floppy_qtree -fda "$TEST_IMG" -drive if=none,file="$TEST_IMG" -device floppy,drive=none0,unit=0 +check_floppy_qtree -fdb "$TEST_IMG" -drive if=none,file="$TEST_IMG" -device floppy,drive=none0,unit=1 + +echo +echo +echo === Mixing -drive and -device === + +# Working +check_floppy_qtree -drive if=floppy,file="$TEST_IMG" -drive if=none,file="$TEST_IMG" -device floppy,drive=none0 +check_floppy_qtree -drive if=floppy,file="$TEST_IMG" -drive if=none,file="$TEST_IMG" -device floppy,drive=none0,unit=1 + +# Conflicting +check_floppy_qtree -drive if=floppy,file="$TEST_IMG" -drive if=none,file="$TEST_IMG" -device floppy,drive=none0,unit=0 + +echo +echo +echo === Mixing -global and -device === + +# Working +check_floppy_qtree -drive if=none,file="$TEST_IMG" -drive if=none,file="$TEST_IMG" \ + -global isa-fdc.driveA=none0 -device floppy,drive=none1 +check_floppy_qtree -drive if=none,file="$TEST_IMG" -drive if=none,file="$TEST_IMG" \ + -global isa-fdc.driveA=none0 -device floppy,drive=none1,unit=1 + +check_floppy_qtree -drive if=none,file="$TEST_IMG" -drive if=none,file="$TEST_IMG" \ + -global isa-fdc.driveB=none0 -device floppy,drive=none1 +check_floppy_qtree -drive if=none,file="$TEST_IMG" -drive if=none,file="$TEST_IMG" \ + -global isa-fdc.driveB=none0 -device floppy,drive=none1,unit=0 + +# Conflicting +check_floppy_qtree -drive if=none,file="$TEST_IMG" -drive if=none,file="$TEST_IMG" \ + -global isa-fdc.driveA=none0 -device floppy,drive=none1,unit=0 +check_floppy_qtree -drive if=none,file="$TEST_IMG" -drive if=none,file="$TEST_IMG" \ + -global isa-fdc.driveB=none0 -device floppy,drive=none1,unit=1 + +echo +echo +echo === Too many floppy drives === + +# Working +check_floppy_qtree -drive if=floppy,file="$TEST_IMG" \ + -drive if=none,file="$TEST_IMG" \ + -drive if=none,file="$TEST_IMG" \ + -global isa-fdc.driveB=none0 \ + -device floppy,drive=none1 + +echo +echo +echo === Creating an empty drive with anonymous BB === + +check_floppy_qtree -device floppy +check_floppy_qtree -device floppy,drive-type=120 +check_floppy_qtree -device floppy,drive-type=144 +check_floppy_qtree -device floppy,drive-type=288 + +echo +echo +echo === Try passing different drive size with image === + +check_floppy_qtree -drive if=none,file="$TEST_IMG" -device floppy,drive=none0,drive-type=120 +check_floppy_qtree -drive if=none,file="$TEST_IMG" -device floppy,drive=none0,drive-type=288 + +echo +echo +echo === Try passing different block sizes === + +# Explicitly setting the default is okay +check_floppy_qtree -drive if=none,file="$TEST_IMG" -device floppy,drive=none0,logical_block_size=512 +check_floppy_qtree -drive if=none,file="$TEST_IMG" -device floppy,drive=none0,physical_block_size=512 + +# Changing it is not +check_floppy_qtree -drive if=none,file="$TEST_IMG" -device floppy,drive=none0,logical_block_size=4096 +check_floppy_qtree -drive if=none,file="$TEST_IMG" -device floppy,drive=none0,physical_block_size=1024 + +echo +echo +echo === Writethrough caching === + +check_cache_mode -device floppy,drive=none0 +check_cache_mode -device floppy,drive=none0,write-cache=on +check_cache_mode -device floppy,drive=none0,write-cache=off + +# success, all done +echo "*** done" +rm -f $seq.full +status=0 diff --git a/tests/qemu-iotests/172.out b/tests/qemu-iotests/172.out new file mode 100644 index 0000000..6b7edaf --- /dev/null +++ b/tests/qemu-iotests/172.out @@ -0,0 +1,1170 @@ +QA output created by 172 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=737280 + + +=== Default === + +Testing: + + dev: isa-fdc, id "" + iobase = 1008 (0x3f0) + irq = 6 (0x6) + dma = 2 (0x2) + driveA = "" + driveB = "" + check_media_rate = true + fdtypeA = "auto" + fdtypeB = "auto" + fallback = "288" + isa irq 6 + bus: floppy-bus.0 + type floppy-bus + dev: floppy, id "" + unit = 0 (0x0) + drive = "floppy0" + logical_block_size = 512 (0x200) + physical_block_size = 512 (0x200) + min_io_size = 0 (0x0) + opt_io_size = 0 (0x0) + discard_granularity = 4294967295 (0xffffffff) + write-cache = "auto" + drive-type = "288" + + +=== Using -fda/-fdb options === + +Testing: -fda TEST_DIR/t.qcow2 + + dev: isa-fdc, id "" + iobase = 1008 (0x3f0) + irq = 6 (0x6) + dma = 2 (0x2) + driveA = "" + driveB = "" + check_media_rate = true + fdtypeA = "auto" + fdtypeB = "auto" + fallback = "288" + isa irq 6 + bus: floppy-bus.0 + type floppy-bus + dev: floppy, id "" + unit = 0 (0x0) + drive = "floppy0" + logical_block_size = 512 (0x200) + physical_block_size = 512 (0x200) + min_io_size = 0 (0x0) + opt_io_size = 0 (0x0) + discard_granularity = 4294967295 (0xffffffff) + write-cache = "auto" + drive-type = "144" + +Testing: -fdb TEST_DIR/t.qcow2 + + dev: isa-fdc, id "" + iobase = 1008 (0x3f0) + irq = 6 (0x6) + dma = 2 (0x2) + driveA = "" + driveB = "" + check_media_rate = true + fdtypeA = "auto" + fdtypeB = "auto" + fallback = "288" + isa irq 6 + bus: floppy-bus.0 + type floppy-bus + dev: floppy, id "" + unit = 1 (0x1) + drive = "floppy1" + logical_block_size = 512 (0x200) + physical_block_size = 512 (0x200) + min_io_size = 0 (0x0) + opt_io_size = 0 (0x0) + discard_granularity = 4294967295 (0xffffffff) + write-cache = "auto" + drive-type = "144" + dev: floppy, id "" + unit = 0 (0x0) + drive = "floppy0" + logical_block_size = 512 (0x200) + physical_block_size = 512 (0x200) + min_io_size = 0 (0x0) + opt_io_size = 0 (0x0) + discard_granularity = 4294967295 (0xffffffff) + write-cache = "auto" + drive-type = "288" + +Testing: -fda TEST_DIR/t.qcow2 -fdb TEST_DIR/t.qcow2 + + dev: isa-fdc, id "" + iobase = 1008 (0x3f0) + irq = 6 (0x6) + dma = 2 (0x2) + driveA = "" + driveB = "" + check_media_rate = true + fdtypeA = "auto" + fdtypeB = "auto" + fallback = "288" + isa irq 6 + bus: floppy-bus.0 + type floppy-bus + dev: floppy, id "" + unit = 1 (0x1) + drive = "floppy1" + logical_block_size = 512 (0x200) + physical_block_size = 512 (0x200) + min_io_size = 0 (0x0) + opt_io_size = 0 (0x0) + discard_granularity = 4294967295 (0xffffffff) + write-cache = "auto" + drive-type = "144" + dev: floppy, id "" + unit = 0 (0x0) + drive = "floppy0" + logical_block_size = 512 (0x200) + physical_block_size = 512 (0x200) + min_io_size = 0 (0x0) + opt_io_size = 0 (0x0) + discard_granularity = 4294967295 (0xffffffff) + write-cache = "auto" + drive-type = "144" + + +=== Using -drive options === + +Testing: -drive if=floppy,file=TEST_DIR/t.qcow2 + + dev: isa-fdc, id "" + iobase = 1008 (0x3f0) + irq = 6 (0x6) + dma = 2 (0x2) + driveA = "" + driveB = "" + check_media_rate = true + fdtypeA = "auto" + fdtypeB = "auto" + fallback = "288" + isa irq 6 + bus: floppy-bus.0 + type floppy-bus + dev: floppy, id "" + unit = 0 (0x0) + drive = "floppy0" + logical_block_size = 512 (0x200) + physical_block_size = 512 (0x200) + min_io_size = 0 (0x0) + opt_io_size = 0 (0x0) + discard_granularity = 4294967295 (0xffffffff) + write-cache = "auto" + drive-type = "144" + +Testing: -drive if=floppy,file=TEST_DIR/t.qcow2,index=1 + + dev: isa-fdc, id "" + iobase = 1008 (0x3f0) + irq = 6 (0x6) + dma = 2 (0x2) + driveA = "" + driveB = "" + check_media_rate = true + fdtypeA = "auto" + fdtypeB = "auto" + fallback = "288" + isa irq 6 + bus: floppy-bus.0 + type floppy-bus + dev: floppy, id "" + unit = 1 (0x1) + drive = "floppy1" + logical_block_size = 512 (0x200) + physical_block_size = 512 (0x200) + min_io_size = 0 (0x0) + opt_io_size = 0 (0x0) + discard_granularity = 4294967295 (0xffffffff) + write-cache = "auto" + drive-type = "144" + dev: floppy, id "" + unit = 0 (0x0) + drive = "floppy0" + logical_block_size = 512 (0x200) + physical_block_size = 512 (0x200) + min_io_size = 0 (0x0) + opt_io_size = 0 (0x0) + discard_granularity = 4294967295 (0xffffffff) + write-cache = "auto" + drive-type = "288" + +Testing: -drive if=floppy,file=TEST_DIR/t.qcow2 -drive if=floppy,file=TEST_DIR/t.qcow2,index=1 + + dev: isa-fdc, id "" + iobase = 1008 (0x3f0) + irq = 6 (0x6) + dma = 2 (0x2) + driveA = "" + driveB = "" + check_media_rate = true + fdtypeA = "auto" + fdtypeB = "auto" + fallback = "288" + isa irq 6 + bus: floppy-bus.0 + type floppy-bus + dev: floppy, id "" + unit = 1 (0x1) + drive = "floppy1" + logical_block_size = 512 (0x200) + physical_block_size = 512 (0x200) + min_io_size = 0 (0x0) + opt_io_size = 0 (0x0) + discard_granularity = 4294967295 (0xffffffff) + write-cache = "auto" + drive-type = "144" + dev: floppy, id "" + unit = 0 (0x0) + drive = "floppy0" + logical_block_size = 512 (0x200) + physical_block_size = 512 (0x200) + min_io_size = 0 (0x0) + opt_io_size = 0 (0x0) + discard_granularity = 4294967295 (0xffffffff) + write-cache = "auto" + drive-type = "144" + + +=== Using -drive if=none and -global === + +Testing: -drive if=none,file=TEST_DIR/t.qcow2 -global isa-fdc.driveA=none0 + + dev: isa-fdc, id "" + iobase = 1008 (0x3f0) + irq = 6 (0x6) + dma = 2 (0x2) + driveA = "" + driveB = "" + check_media_rate = true + fdtypeA = "auto" + fdtypeB = "auto" + fallback = "288" + isa irq 6 + bus: floppy-bus.0 + type floppy-bus + dev: floppy, id "" + unit = 0 (0x0) + drive = "none0" + logical_block_size = 512 (0x200) + physical_block_size = 512 (0x200) + min_io_size = 0 (0x0) + opt_io_size = 0 (0x0) + discard_granularity = 4294967295 (0xffffffff) + write-cache = "auto" + drive-type = "144" + +Testing: -drive if=none,file=TEST_DIR/t.qcow2 -global isa-fdc.driveB=none0 + + dev: isa-fdc, id "" + iobase = 1008 (0x3f0) + irq = 6 (0x6) + dma = 2 (0x2) + driveA = "" + driveB = "" + check_media_rate = true + fdtypeA = "auto" + fdtypeB = "auto" + fallback = "288" + isa irq 6 + bus: floppy-bus.0 + type floppy-bus + dev: floppy, id "" + unit = 1 (0x1) + drive = "none0" + logical_block_size = 512 (0x200) + physical_block_size = 512 (0x200) + min_io_size = 0 (0x0) + opt_io_size = 0 (0x0) + discard_granularity = 4294967295 (0xffffffff) + write-cache = "auto" + drive-type = "144" + +Testing: -drive if=none,file=TEST_DIR/t.qcow2 -drive if=none,file=TEST_DIR/t.qcow2 -global isa-fdc.driveA=none0 -global isa-fdc.driveB=none1 + + dev: isa-fdc, id "" + iobase = 1008 (0x3f0) + irq = 6 (0x6) + dma = 2 (0x2) + driveA = "" + driveB = "" + check_media_rate = true + fdtypeA = "auto" + fdtypeB = "auto" + fallback = "288" + isa irq 6 + bus: floppy-bus.0 + type floppy-bus + dev: floppy, id "" + unit = 1 (0x1) + drive = "none1" + logical_block_size = 512 (0x200) + physical_block_size = 512 (0x200) + min_io_size = 0 (0x0) + opt_io_size = 0 (0x0) + discard_granularity = 4294967295 (0xffffffff) + write-cache = "auto" + drive-type = "144" + dev: floppy, id "" + unit = 0 (0x0) + drive = "none0" + logical_block_size = 512 (0x200) + physical_block_size = 512 (0x200) + min_io_size = 0 (0x0) + opt_io_size = 0 (0x0) + discard_granularity = 4294967295 (0xffffffff) + write-cache = "auto" + drive-type = "144" + + +=== Using -drive if=none and -device === + +Testing: -drive if=none,file=TEST_DIR/t.qcow2 -device floppy,drive=none0 + + dev: isa-fdc, id "" + iobase = 1008 (0x3f0) + irq = 6 (0x6) + dma = 2 (0x2) + driveA = "" + driveB = "" + check_media_rate = true + fdtypeA = "auto" + fdtypeB = "auto" + fallback = "288" + isa irq 6 + bus: floppy-bus.0 + type floppy-bus + dev: floppy, id "" + unit = 0 (0x0) + drive = "none0" + logical_block_size = 512 (0x200) + physical_block_size = 512 (0x200) + min_io_size = 0 (0x0) + opt_io_size = 0 (0x0) + discard_granularity = 4294967295 (0xffffffff) + write-cache = "auto" + drive-type = "144" + +Testing: -drive if=none,file=TEST_DIR/t.qcow2 -device floppy,drive=none0,unit=1 + + dev: isa-fdc, id "" + iobase = 1008 (0x3f0) + irq = 6 (0x6) + dma = 2 (0x2) + driveA = "" + driveB = "" + check_media_rate = true + fdtypeA = "auto" + fdtypeB = "auto" + fallback = "288" + isa irq 6 + bus: floppy-bus.0 + type floppy-bus + dev: floppy, id "" + unit = 1 (0x1) + drive = "none0" + logical_block_size = 512 (0x200) + physical_block_size = 512 (0x200) + min_io_size = 0 (0x0) + opt_io_size = 0 (0x0) + discard_granularity = 4294967295 (0xffffffff) + write-cache = "auto" + drive-type = "144" + +Testing: -drive if=none,file=TEST_DIR/t.qcow2 -drive if=none,file=TEST_DIR/t.qcow2 -device floppy,drive=none0 -device floppy,drive=none1,unit=1 + + dev: isa-fdc, id "" + iobase = 1008 (0x3f0) + irq = 6 (0x6) + dma = 2 (0x2) + driveA = "" + driveB = "" + check_media_rate = true + fdtypeA = "auto" + fdtypeB = "auto" + fallback = "288" + isa irq 6 + bus: floppy-bus.0 + type floppy-bus + dev: floppy, id "" + unit = 1 (0x1) + drive = "none1" + logical_block_size = 512 (0x200) + physical_block_size = 512 (0x200) + min_io_size = 0 (0x0) + opt_io_size = 0 (0x0) + discard_granularity = 4294967295 (0xffffffff) + write-cache = "auto" + drive-type = "144" + dev: floppy, id "" + unit = 0 (0x0) + drive = "none0" + logical_block_size = 512 (0x200) + physical_block_size = 512 (0x200) + min_io_size = 0 (0x0) + opt_io_size = 0 (0x0) + discard_granularity = 4294967295 (0xffffffff) + write-cache = "auto" + drive-type = "144" + + +=== Mixing -fdX and -global === + +Testing: -fda TEST_DIR/t.qcow2 -drive if=none,file=TEST_DIR/t.qcow2 -global isa-fdc.driveB=none0 + + dev: isa-fdc, id "" + iobase = 1008 (0x3f0) + irq = 6 (0x6) + dma = 2 (0x2) + driveA = "" + driveB = "" + check_media_rate = true + fdtypeA = "auto" + fdtypeB = "auto" + fallback = "288" + isa irq 6 + bus: floppy-bus.0 + type floppy-bus + dev: floppy, id "" + unit = 1 (0x1) + drive = "none0" + logical_block_size = 512 (0x200) + physical_block_size = 512 (0x200) + min_io_size = 0 (0x0) + opt_io_size = 0 (0x0) + discard_granularity = 4294967295 (0xffffffff) + write-cache = "auto" + drive-type = "144" + dev: floppy, id "" + unit = 0 (0x0) + drive = "floppy0" + logical_block_size = 512 (0x200) + physical_block_size = 512 (0x200) + min_io_size = 0 (0x0) + opt_io_size = 0 (0x0) + discard_granularity = 4294967295 (0xffffffff) + write-cache = "auto" + drive-type = "144" + +Testing: -fdb TEST_DIR/t.qcow2 -drive if=none,file=TEST_DIR/t.qcow2 -global isa-fdc.driveA=none0 + + dev: isa-fdc, id "" + iobase = 1008 (0x3f0) + irq = 6 (0x6) + dma = 2 (0x2) + driveA = "" + driveB = "" + check_media_rate = true + fdtypeA = "auto" + fdtypeB = "auto" + fallback = "288" + isa irq 6 + bus: floppy-bus.0 + type floppy-bus + dev: floppy, id "" + unit = 1 (0x1) + drive = "floppy1" + logical_block_size = 512 (0x200) + physical_block_size = 512 (0x200) + min_io_size = 0 (0x0) + opt_io_size = 0 (0x0) + discard_granularity = 4294967295 (0xffffffff) + write-cache = "auto" + drive-type = "144" + dev: floppy, id "" + unit = 0 (0x0) + drive = "none0" + logical_block_size = 512 (0x200) + physical_block_size = 512 (0x200) + min_io_size = 0 (0x0) + opt_io_size = 0 (0x0) + discard_granularity = 4294967295 (0xffffffff) + write-cache = "auto" + drive-type = "144" + +Testing: -fda TEST_DIR/t.qcow2 -drive if=none,file=TEST_DIR/t.qcow2 -global isa-fdc.driveA=none0 + + dev: isa-fdc, id "" + iobase = 1008 (0x3f0) + irq = 6 (0x6) + dma = 2 (0x2) + driveA = "" + driveB = "" + check_media_rate = true + fdtypeA = "auto" + fdtypeB = "auto" + fallback = "288" + isa irq 6 + bus: floppy-bus.0 + type floppy-bus + dev: floppy, id "" + unit = 0 (0x0) + drive = "floppy0" + logical_block_size = 512 (0x200) + physical_block_size = 512 (0x200) + min_io_size = 0 (0x0) + opt_io_size = 0 (0x0) + discard_granularity = 4294967295 (0xffffffff) + write-cache = "auto" + drive-type = "144" + +Testing: -fdb TEST_DIR/t.qcow2 -drive if=none,file=TEST_DIR/t.qcow2 -global isa-fdc.driveB=none0 + + dev: isa-fdc, id "" + iobase = 1008 (0x3f0) + irq = 6 (0x6) + dma = 2 (0x2) + driveA = "" + driveB = "" + check_media_rate = true + fdtypeA = "auto" + fdtypeB = "auto" + fallback = "288" + isa irq 6 + bus: floppy-bus.0 + type floppy-bus + dev: floppy, id "" + unit = 1 (0x1) + drive = "floppy1" + logical_block_size = 512 (0x200) + physical_block_size = 512 (0x200) + min_io_size = 0 (0x0) + opt_io_size = 0 (0x0) + discard_granularity = 4294967295 (0xffffffff) + write-cache = "auto" + drive-type = "144" + + +=== Mixing -fdX and -device === + +Testing: -fda TEST_DIR/t.qcow2 -drive if=none,file=TEST_DIR/t.qcow2 -device floppy,drive=none0 + + dev: isa-fdc, id "" + iobase = 1008 (0x3f0) + irq = 6 (0x6) + dma = 2 (0x2) + driveA = "" + driveB = "" + check_media_rate = true + fdtypeA = "auto" + fdtypeB = "auto" + fallback = "288" + isa irq 6 + bus: floppy-bus.0 + type floppy-bus + dev: floppy, id "" + unit = 1 (0x1) + drive = "none0" + logical_block_size = 512 (0x200) + physical_block_size = 512 (0x200) + min_io_size = 0 (0x0) + opt_io_size = 0 (0x0) + discard_granularity = 4294967295 (0xffffffff) + write-cache = "auto" + drive-type = "144" + dev: floppy, id "" + unit = 0 (0x0) + drive = "floppy0" + logical_block_size = 512 (0x200) + physical_block_size = 512 (0x200) + min_io_size = 0 (0x0) + opt_io_size = 0 (0x0) + discard_granularity = 4294967295 (0xffffffff) + write-cache = "auto" + drive-type = "144" + +Testing: -fda TEST_DIR/t.qcow2 -drive if=none,file=TEST_DIR/t.qcow2 -device floppy,drive=none0,unit=1 + + dev: isa-fdc, id "" + iobase = 1008 (0x3f0) + irq = 6 (0x6) + dma = 2 (0x2) + driveA = "" + driveB = "" + check_media_rate = true + fdtypeA = "auto" + fdtypeB = "auto" + fallback = "288" + isa irq 6 + bus: floppy-bus.0 + type floppy-bus + dev: floppy, id "" + unit = 1 (0x1) + drive = "none0" + logical_block_size = 512 (0x200) + physical_block_size = 512 (0x200) + min_io_size = 0 (0x0) + opt_io_size = 0 (0x0) + discard_granularity = 4294967295 (0xffffffff) + write-cache = "auto" + drive-type = "144" + dev: floppy, id "" + unit = 0 (0x0) + drive = "floppy0" + logical_block_size = 512 (0x200) + physical_block_size = 512 (0x200) + min_io_size = 0 (0x0) + opt_io_size = 0 (0x0) + discard_granularity = 4294967295 (0xffffffff) + write-cache = "auto" + drive-type = "144" + +Testing: -fdb TEST_DIR/t.qcow2 -drive if=none,file=TEST_DIR/t.qcow2 -device floppy,drive=none0 + + dev: isa-fdc, id "" + iobase = 1008 (0x3f0) + irq = 6 (0x6) + dma = 2 (0x2) + driveA = "" + driveB = "" + check_media_rate = true + fdtypeA = "auto" + fdtypeB = "auto" + fallback = "288" + isa irq 6 + bus: floppy-bus.0 + type floppy-bus + dev: floppy, id "" + unit = 0 (0x0) + drive = "none0" + logical_block_size = 512 (0x200) + physical_block_size = 512 (0x200) + min_io_size = 0 (0x0) + opt_io_size = 0 (0x0) + discard_granularity = 4294967295 (0xffffffff) + write-cache = "auto" + drive-type = "144" + dev: floppy, id "" + unit = 1 (0x1) + drive = "floppy1" + logical_block_size = 512 (0x200) + physical_block_size = 512 (0x200) + min_io_size = 0 (0x0) + opt_io_size = 0 (0x0) + discard_granularity = 4294967295 (0xffffffff) + write-cache = "auto" + drive-type = "144" + +Testing: -fdb TEST_DIR/t.qcow2 -drive if=none,file=TEST_DIR/t.qcow2 -device floppy,drive=none0,unit=0 + + dev: isa-fdc, id "" + iobase = 1008 (0x3f0) + irq = 6 (0x6) + dma = 2 (0x2) + driveA = "" + driveB = "" + check_media_rate = true + fdtypeA = "auto" + fdtypeB = "auto" + fallback = "288" + isa irq 6 + bus: floppy-bus.0 + type floppy-bus + dev: floppy, id "" + unit = 0 (0x0) + drive = "none0" + logical_block_size = 512 (0x200) + physical_block_size = 512 (0x200) + min_io_size = 0 (0x0) + opt_io_size = 0 (0x0) + discard_granularity = 4294967295 (0xffffffff) + write-cache = "auto" + drive-type = "144" + dev: floppy, id "" + unit = 1 (0x1) + drive = "floppy1" + logical_block_size = 512 (0x200) + physical_block_size = 512 (0x200) + min_io_size = 0 (0x0) + opt_io_size = 0 (0x0) + discard_granularity = 4294967295 (0xffffffff) + write-cache = "auto" + drive-type = "144" + +Testing: -fda TEST_DIR/t.qcow2 -drive if=none,file=TEST_DIR/t.qcow2 -device floppy,drive=none0,unit=0 +QEMU_PROG: -device floppy,drive=none0,unit=0: Floppy unit 0 is in use +QEMU_PROG: -device floppy,drive=none0,unit=0: Device initialization failed. + +Testing: -fdb TEST_DIR/t.qcow2 -drive if=none,file=TEST_DIR/t.qcow2 -device floppy,drive=none0,unit=1 +QEMU_PROG: -device floppy,drive=none0,unit=1: Floppy unit 1 is in use +QEMU_PROG: -device floppy,drive=none0,unit=1: Device initialization failed. + + +=== Mixing -drive and -device === + +Testing: -drive if=floppy,file=TEST_DIR/t.qcow2 -drive if=none,file=TEST_DIR/t.qcow2 -device floppy,drive=none0 + + dev: isa-fdc, id "" + iobase = 1008 (0x3f0) + irq = 6 (0x6) + dma = 2 (0x2) + driveA = "" + driveB = "" + check_media_rate = true + fdtypeA = "auto" + fdtypeB = "auto" + fallback = "288" + isa irq 6 + bus: floppy-bus.0 + type floppy-bus + dev: floppy, id "" + unit = 1 (0x1) + drive = "none0" + logical_block_size = 512 (0x200) + physical_block_size = 512 (0x200) + min_io_size = 0 (0x0) + opt_io_size = 0 (0x0) + discard_granularity = 4294967295 (0xffffffff) + write-cache = "auto" + drive-type = "144" + dev: floppy, id "" + unit = 0 (0x0) + drive = "floppy0" + logical_block_size = 512 (0x200) + physical_block_size = 512 (0x200) + min_io_size = 0 (0x0) + opt_io_size = 0 (0x0) + discard_granularity = 4294967295 (0xffffffff) + write-cache = "auto" + drive-type = "144" + +Testing: -drive if=floppy,file=TEST_DIR/t.qcow2 -drive if=none,file=TEST_DIR/t.qcow2 -device floppy,drive=none0,unit=1 + + dev: isa-fdc, id "" + iobase = 1008 (0x3f0) + irq = 6 (0x6) + dma = 2 (0x2) + driveA = "" + driveB = "" + check_media_rate = true + fdtypeA = "auto" + fdtypeB = "auto" + fallback = "288" + isa irq 6 + bus: floppy-bus.0 + type floppy-bus + dev: floppy, id "" + unit = 1 (0x1) + drive = "none0" + logical_block_size = 512 (0x200) + physical_block_size = 512 (0x200) + min_io_size = 0 (0x0) + opt_io_size = 0 (0x0) + discard_granularity = 4294967295 (0xffffffff) + write-cache = "auto" + drive-type = "144" + dev: floppy, id "" + unit = 0 (0x0) + drive = "floppy0" + logical_block_size = 512 (0x200) + physical_block_size = 512 (0x200) + min_io_size = 0 (0x0) + opt_io_size = 0 (0x0) + discard_granularity = 4294967295 (0xffffffff) + write-cache = "auto" + drive-type = "144" + +Testing: -drive if=floppy,file=TEST_DIR/t.qcow2 -drive if=none,file=TEST_DIR/t.qcow2 -device floppy,drive=none0,unit=0 +QEMU_PROG: -device floppy,drive=none0,unit=0: Floppy unit 0 is in use +QEMU_PROG: -device floppy,drive=none0,unit=0: Device initialization failed. + + +=== Mixing -global and -device === + +Testing: -drive if=none,file=TEST_DIR/t.qcow2 -drive if=none,file=TEST_DIR/t.qcow2 -global isa-fdc.driveA=none0 -device floppy,drive=none1 + + dev: isa-fdc, id "" + iobase = 1008 (0x3f0) + irq = 6 (0x6) + dma = 2 (0x2) + driveA = "" + driveB = "" + check_media_rate = true + fdtypeA = "auto" + fdtypeB = "auto" + fallback = "288" + isa irq 6 + bus: floppy-bus.0 + type floppy-bus + dev: floppy, id "" + unit = 1 (0x1) + drive = "none1" + logical_block_size = 512 (0x200) + physical_block_size = 512 (0x200) + min_io_size = 0 (0x0) + opt_io_size = 0 (0x0) + discard_granularity = 4294967295 (0xffffffff) + write-cache = "auto" + drive-type = "144" + dev: floppy, id "" + unit = 0 (0x0) + drive = "none0" + logical_block_size = 512 (0x200) + physical_block_size = 512 (0x200) + min_io_size = 0 (0x0) + opt_io_size = 0 (0x0) + discard_granularity = 4294967295 (0xffffffff) + write-cache = "auto" + drive-type = "144" + +Testing: -drive if=none,file=TEST_DIR/t.qcow2 -drive if=none,file=TEST_DIR/t.qcow2 -global isa-fdc.driveA=none0 -device floppy,drive=none1,unit=1 + + dev: isa-fdc, id "" + iobase = 1008 (0x3f0) + irq = 6 (0x6) + dma = 2 (0x2) + driveA = "" + driveB = "" + check_media_rate = true + fdtypeA = "auto" + fdtypeB = "auto" + fallback = "288" + isa irq 6 + bus: floppy-bus.0 + type floppy-bus + dev: floppy, id "" + unit = 1 (0x1) + drive = "none1" + logical_block_size = 512 (0x200) + physical_block_size = 512 (0x200) + min_io_size = 0 (0x0) + opt_io_size = 0 (0x0) + discard_granularity = 4294967295 (0xffffffff) + write-cache = "auto" + drive-type = "144" + dev: floppy, id "" + unit = 0 (0x0) + drive = "none0" + logical_block_size = 512 (0x200) + physical_block_size = 512 (0x200) + min_io_size = 0 (0x0) + opt_io_size = 0 (0x0) + discard_granularity = 4294967295 (0xffffffff) + write-cache = "auto" + drive-type = "144" + +Testing: -drive if=none,file=TEST_DIR/t.qcow2 -drive if=none,file=TEST_DIR/t.qcow2 -global isa-fdc.driveB=none0 -device floppy,drive=none1 + + dev: isa-fdc, id "" + iobase = 1008 (0x3f0) + irq = 6 (0x6) + dma = 2 (0x2) + driveA = "" + driveB = "" + check_media_rate = true + fdtypeA = "auto" + fdtypeB = "auto" + fallback = "288" + isa irq 6 + bus: floppy-bus.0 + type floppy-bus + dev: floppy, id "" + unit = 0 (0x0) + drive = "none1" + logical_block_size = 512 (0x200) + physical_block_size = 512 (0x200) + min_io_size = 0 (0x0) + opt_io_size = 0 (0x0) + discard_granularity = 4294967295 (0xffffffff) + write-cache = "auto" + drive-type = "144" + dev: floppy, id "" + unit = 1 (0x1) + drive = "none0" + logical_block_size = 512 (0x200) + physical_block_size = 512 (0x200) + min_io_size = 0 (0x0) + opt_io_size = 0 (0x0) + discard_granularity = 4294967295 (0xffffffff) + write-cache = "auto" + drive-type = "144" + +Testing: -drive if=none,file=TEST_DIR/t.qcow2 -drive if=none,file=TEST_DIR/t.qcow2 -global isa-fdc.driveB=none0 -device floppy,drive=none1,unit=0 + + dev: isa-fdc, id "" + iobase = 1008 (0x3f0) + irq = 6 (0x6) + dma = 2 (0x2) + driveA = "" + driveB = "" + check_media_rate = true + fdtypeA = "auto" + fdtypeB = "auto" + fallback = "288" + isa irq 6 + bus: floppy-bus.0 + type floppy-bus + dev: floppy, id "" + unit = 0 (0x0) + drive = "none1" + logical_block_size = 512 (0x200) + physical_block_size = 512 (0x200) + min_io_size = 0 (0x0) + opt_io_size = 0 (0x0) + discard_granularity = 4294967295 (0xffffffff) + write-cache = "auto" + drive-type = "144" + dev: floppy, id "" + unit = 1 (0x1) + drive = "none0" + logical_block_size = 512 (0x200) + physical_block_size = 512 (0x200) + min_io_size = 0 (0x0) + opt_io_size = 0 (0x0) + discard_granularity = 4294967295 (0xffffffff) + write-cache = "auto" + drive-type = "144" + +Testing: -drive if=none,file=TEST_DIR/t.qcow2 -drive if=none,file=TEST_DIR/t.qcow2 -global isa-fdc.driveA=none0 -device floppy,drive=none1,unit=0 +QEMU_PROG: -device floppy,drive=none1,unit=0: Floppy unit 0 is in use +QEMU_PROG: -device floppy,drive=none1,unit=0: Device initialization failed. + +Testing: -drive if=none,file=TEST_DIR/t.qcow2 -drive if=none,file=TEST_DIR/t.qcow2 -global isa-fdc.driveB=none0 -device floppy,drive=none1,unit=1 +QEMU_PROG: -device floppy,drive=none1,unit=1: Floppy unit 1 is in use +QEMU_PROG: -device floppy,drive=none1,unit=1: Device initialization failed. + + +=== Too many floppy drives === + +Testing: -drive if=floppy,file=TEST_DIR/t.qcow2 -drive if=none,file=TEST_DIR/t.qcow2 -drive if=none,file=TEST_DIR/t.qcow2 -global isa-fdc.driveB=none0 -device floppy,drive=none1 +QEMU_PROG: -device floppy,drive=none1: Can't create floppy unit 2, bus supports only 2 units +QEMU_PROG: -device floppy,drive=none1: Device initialization failed. + + +=== Creating an empty drive with anonymous BB === + +Testing: -device floppy + + dev: isa-fdc, id "" + iobase = 1008 (0x3f0) + irq = 6 (0x6) + dma = 2 (0x2) + driveA = "" + driveB = "" + check_media_rate = true + fdtypeA = "auto" + fdtypeB = "auto" + fallback = "288" + isa irq 6 + bus: floppy-bus.0 + type floppy-bus + dev: floppy, id "" + unit = 0 (0x0) + drive = "" + logical_block_size = 512 (0x200) + physical_block_size = 512 (0x200) + min_io_size = 0 (0x0) + opt_io_size = 0 (0x0) + discard_granularity = 4294967295 (0xffffffff) + write-cache = "auto" + drive-type = "288" + +Testing: -device floppy,drive-type=120 + + dev: isa-fdc, id "" + iobase = 1008 (0x3f0) + irq = 6 (0x6) + dma = 2 (0x2) + driveA = "" + driveB = "" + check_media_rate = true + fdtypeA = "auto" + fdtypeB = "auto" + fallback = "288" + isa irq 6 + bus: floppy-bus.0 + type floppy-bus + dev: floppy, id "" + unit = 0 (0x0) + drive = "" + logical_block_size = 512 (0x200) + physical_block_size = 512 (0x200) + min_io_size = 0 (0x0) + opt_io_size = 0 (0x0) + discard_granularity = 4294967295 (0xffffffff) + write-cache = "auto" + drive-type = "120" + +Testing: -device floppy,drive-type=144 + + dev: isa-fdc, id "" + iobase = 1008 (0x3f0) + irq = 6 (0x6) + dma = 2 (0x2) + driveA = "" + driveB = "" + check_media_rate = true + fdtypeA = "auto" + fdtypeB = "auto" + fallback = "288" + isa irq 6 + bus: floppy-bus.0 + type floppy-bus + dev: floppy, id "" + unit = 0 (0x0) + drive = "" + logical_block_size = 512 (0x200) + physical_block_size = 512 (0x200) + min_io_size = 0 (0x0) + opt_io_size = 0 (0x0) + discard_granularity = 4294967295 (0xffffffff) + write-cache = "auto" + drive-type = "144" + +Testing: -device floppy,drive-type=288 + + dev: isa-fdc, id "" + iobase = 1008 (0x3f0) + irq = 6 (0x6) + dma = 2 (0x2) + driveA = "" + driveB = "" + check_media_rate = true + fdtypeA = "auto" + fdtypeB = "auto" + fallback = "288" + isa irq 6 + bus: floppy-bus.0 + type floppy-bus + dev: floppy, id "" + unit = 0 (0x0) + drive = "" + logical_block_size = 512 (0x200) + physical_block_size = 512 (0x200) + min_io_size = 0 (0x0) + opt_io_size = 0 (0x0) + discard_granularity = 4294967295 (0xffffffff) + write-cache = "auto" + drive-type = "288" + + +=== Try passing different drive size with image === + +Testing: -drive if=none,file=TEST_DIR/t.qcow2 -device floppy,drive=none0,drive-type=120 + + dev: isa-fdc, id "" + iobase = 1008 (0x3f0) + irq = 6 (0x6) + dma = 2 (0x2) + driveA = "" + driveB = "" + check_media_rate = true + fdtypeA = "auto" + fdtypeB = "auto" + fallback = "288" + isa irq 6 + bus: floppy-bus.0 + type floppy-bus + dev: floppy, id "" + unit = 0 (0x0) + drive = "none0" + logical_block_size = 512 (0x200) + physical_block_size = 512 (0x200) + min_io_size = 0 (0x0) + opt_io_size = 0 (0x0) + discard_granularity = 4294967295 (0xffffffff) + write-cache = "auto" + drive-type = "120" + +Testing: -drive if=none,file=TEST_DIR/t.qcow2 -device floppy,drive=none0,drive-type=288 + + dev: isa-fdc, id "" + iobase = 1008 (0x3f0) + irq = 6 (0x6) + dma = 2 (0x2) + driveA = "" + driveB = "" + check_media_rate = true + fdtypeA = "auto" + fdtypeB = "auto" + fallback = "288" + isa irq 6 + bus: floppy-bus.0 + type floppy-bus + dev: floppy, id "" + unit = 0 (0x0) + drive = "none0" + logical_block_size = 512 (0x200) + physical_block_size = 512 (0x200) + min_io_size = 0 (0x0) + opt_io_size = 0 (0x0) + discard_granularity = 4294967295 (0xffffffff) + write-cache = "auto" + drive-type = "288" + + +=== Try passing different block sizes === + +Testing: -drive if=none,file=TEST_DIR/t.qcow2 -device floppy,drive=none0,logical_block_size=512 + + dev: isa-fdc, id "" + iobase = 1008 (0x3f0) + irq = 6 (0x6) + dma = 2 (0x2) + driveA = "" + driveB = "" + check_media_rate = true + fdtypeA = "auto" + fdtypeB = "auto" + fallback = "288" + isa irq 6 + bus: floppy-bus.0 + type floppy-bus + dev: floppy, id "" + unit = 0 (0x0) + drive = "none0" + logical_block_size = 512 (0x200) + physical_block_size = 512 (0x200) + min_io_size = 0 (0x0) + opt_io_size = 0 (0x0) + discard_granularity = 4294967295 (0xffffffff) + write-cache = "auto" + drive-type = "144" + +Testing: -drive if=none,file=TEST_DIR/t.qcow2 -device floppy,drive=none0,physical_block_size=512 + + dev: isa-fdc, id "" + iobase = 1008 (0x3f0) + irq = 6 (0x6) + dma = 2 (0x2) + driveA = "" + driveB = "" + check_media_rate = true + fdtypeA = "auto" + fdtypeB = "auto" + fallback = "288" + isa irq 6 + bus: floppy-bus.0 + type floppy-bus + dev: floppy, id "" + unit = 0 (0x0) + drive = "none0" + logical_block_size = 512 (0x200) + physical_block_size = 512 (0x200) + min_io_size = 0 (0x0) + opt_io_size = 0 (0x0) + discard_granularity = 4294967295 (0xffffffff) + write-cache = "auto" + drive-type = "144" + +Testing: -drive if=none,file=TEST_DIR/t.qcow2 -device floppy,drive=none0,logical_block_size=4096 +QEMU_PROG: -device floppy,drive=none0,logical_block_size=4096: Physical and logical block size must be 512 for floppy +QEMU_PROG: -device floppy,drive=none0,logical_block_size=4096: Device initialization failed. + +Testing: -drive if=none,file=TEST_DIR/t.qcow2 -device floppy,drive=none0,physical_block_size=1024 +QEMU_PROG: -device floppy,drive=none0,physical_block_size=1024: Physical and logical block size must be 512 for floppy +QEMU_PROG: -device floppy,drive=none0,physical_block_size=1024: Device initialization failed. + + +=== Writethrough caching === + Cache mode: writeback + Cache mode: writeback + Cache mode: writethrough +*** done diff --git a/tests/qemu-iotests/common.rc b/tests/qemu-iotests/common.rc index 126bd67..3213765 100644 --- a/tests/qemu-iotests/common.rc +++ b/tests/qemu-iotests/common.rc @@ -69,7 +69,7 @@ if [ "$IMGOPTSSYNTAX" = "true" ]; then TEST_IMG="$DRIVER,file.driver=ssh,file.host=127.0.0.1,file.path=$TEST_IMG_FILE" elif [ "$IMGPROTO" = "nfs" ]; then TEST_DIR="$DRIVER,file.driver=nfs,file.filename=nfs://127.0.0.1/$TEST_DIR" - TEST_IMG=$TEST_DIR_OPTS/t.$IMGFMT + TEST_IMG=$TEST_DIR/t.$IMGFMT elif [ "$IMGPROTO" = "archipelago" ]; then TEST_IMG="$DRIVER,file.driver=archipelago,file.volume=:at.$IMGFMT" else diff --git a/tests/qemu-iotests/group b/tests/qemu-iotests/group index 7eb1770..597fc2c 100644 --- a/tests/qemu-iotests/group +++ b/tests/qemu-iotests/group @@ -149,6 +149,7 @@ 144 rw auto quick 145 auto quick 146 auto quick +147 auto 148 rw auto quick 149 rw auto sudo 150 rw auto quick @@ -162,3 +163,4 @@ 160 rw auto quick 162 auto quick 170 rw auto quick +172 auto diff --git a/tests/qemu-iotests/iotests.py b/tests/qemu-iotests/iotests.py index 3329bc1..1f30cfc 100644 --- a/tests/qemu-iotests/iotests.py +++ b/tests/qemu-iotests/iotests.py @@ -39,6 +39,10 @@ qemu_io_args = [os.environ.get('QEMU_IO_PROG', 'qemu-io')] if os.environ.get('QEMU_IO_OPTIONS'): qemu_io_args += os.environ['QEMU_IO_OPTIONS'].strip().split(' ') +qemu_nbd_args = [os.environ.get('QEMU_NBD_PROG', 'qemu-nbd')] +if os.environ.get('QEMU_NBD_OPTIONS'): + qemu_nbd_args += os.environ['QEMU_NBD_OPTIONS'].strip().split(' ') + qemu_prog = os.environ.get('QEMU_PROG', 'qemu') qemu_opts = os.environ.get('QEMU_OPTIONS', '').strip().split(' ') @@ -87,6 +91,10 @@ def qemu_io(*args): sys.stderr.write('qemu-io received signal %i: %s\n' % (-exitcode, ' '.join(args))) return subp.communicate()[0] +def qemu_nbd(*args): + '''Run qemu-nbd in daemon mode and return the parent's exit code''' + return subprocess.call(qemu_nbd_args + ['--fork'] + list(args)) + def compare_images(img1, img2, fmt1=imgfmt, fmt2=imgfmt): '''Return True if two image files are identical''' return qemu_img('compare', '-f', fmt1, @@ -132,8 +140,10 @@ def log(msg, filters=[]): class VM(qtest.QEMUQtestMachine): '''A QEMU VM''' - def __init__(self): - super(VM, self).__init__(qemu_prog, qemu_opts, test_dir=test_dir, + def __init__(self, path_suffix=''): + name = "qemu%s-%d" % (path_suffix, os.getpid()) + super(VM, self).__init__(qemu_prog, qemu_opts, name=name, + test_dir=test_dir, socket_scm_helper=socket_scm_helper) if debug: self._debug = True @@ -212,6 +222,19 @@ class QMPTestCase(unittest.TestCase): self.fail('invalid index "%s" in path "%s" in "%s"' % (idx, path, str(d))) return d + def flatten_qmp_object(self, obj, output=None, basestr=''): + if output is None: + output = dict() + if isinstance(obj, list): + for i in range(len(obj)): + self.flatten_qmp_object(obj[i], output, basestr + str(i) + '.') + elif isinstance(obj, dict): + for key in obj: + self.flatten_qmp_object(obj[key], output, basestr + key + '.') + else: + output[basestr[:-1]] = obj # Strip trailing '.' + return output + def assert_qmp_absent(self, d, path): try: result = self.dictpath(d, path) @@ -242,6 +265,13 @@ class QMPTestCase(unittest.TestCase): self.assertTrue(False, "Cannot find %s %s in result:\n%s" % \ (node_name, file_name, result)) + def assert_json_filename_equal(self, json_filename, reference): + '''Asserts that the given filename is a json: filename and that its + content is equal to the given reference object''' + self.assertEqual(json_filename[:5], 'json:') + self.assertEqual(self.flatten_qmp_object(json.loads(json_filename[5:])), + self.flatten_qmp_object(reference)) + def cancel_and_wait(self, drive='drive0', force=False, resume=False): '''Cancel a block job and wait for it to finish, returning the event''' result = self.vm.qmp('block-job-cancel', device=drive, force=force) diff --git a/tests/qemu-iotests/socket_scm_helper.c b/tests/qemu-iotests/socket_scm_helper.c index 80cadf4..eb76d31 100644 --- a/tests/qemu-iotests/socket_scm_helper.c +++ b/tests/qemu-iotests/socket_scm_helper.c @@ -60,7 +60,7 @@ static int send_fd(int fd, int fd_to_send) } /* Convert string to fd number. */ -static int get_fd_num(const char *fd_str) +static int get_fd_num(const char *fd_str, bool silent) { int sock; char *err; @@ -68,12 +68,16 @@ static int get_fd_num(const char *fd_str) errno = 0; sock = strtol(fd_str, &err, 10); if (errno) { - fprintf(stderr, "Failed in strtol for socket fd, reason: %s\n", - strerror(errno)); + if (!silent) { + fprintf(stderr, "Failed in strtol for socket fd, reason: %s\n", + strerror(errno)); + } return -1; } if (!*fd_str || *err || sock < 0) { - fprintf(stderr, "bad numerical value for socket fd '%s'\n", fd_str); + if (!silent) { + fprintf(stderr, "bad numerical value for socket fd '%s'\n", fd_str); + } return -1; } @@ -104,18 +108,21 @@ int main(int argc, char **argv, char **envp) } - sock = get_fd_num(argv[1]); + sock = get_fd_num(argv[1], false); if (sock < 0) { return EXIT_FAILURE; } - /* Now only open a file in readonly mode for test purpose. If more precise - control is needed, use python script in file operation, which is - supposed to fork and exec this program. */ - fd = open(argv[2], O_RDONLY); + fd = get_fd_num(argv[2], true); if (fd < 0) { - fprintf(stderr, "Failed to open file '%s'\n", argv[2]); - return EXIT_FAILURE; + /* Now only open a file in readonly mode for test purpose. If more + precise control is needed, use python script in file operation, which + is supposed to fork and exec this program. */ + fd = open(argv[2], O_RDONLY); + if (fd < 0) { + fprintf(stderr, "Failed to open file '%s'\n", argv[2]); + return EXIT_FAILURE; + } } ret = send_fd(sock, fd); diff --git a/tests/qht-bench.c b/tests/qht-bench.c index 76360a0..2afa09d 100644 --- a/tests/qht-bench.c +++ b/tests/qht-bench.c @@ -193,7 +193,7 @@ static void *thread_func(void *p) rcu_register_thread(); atomic_inc(&n_ready_threads); - while (!atomic_mb_read(&test_start)) { + while (!atomic_read(&test_start)) { cpu_relax(); } @@ -393,11 +393,11 @@ static void run_test(void) while (atomic_read(&n_ready_threads) != n_rw_threads + n_rz_threads) { cpu_relax(); } - atomic_mb_set(&test_start, true); + atomic_set(&test_start, true); do { remaining = sleep(duration); } while (remaining); - atomic_mb_set(&test_stop, true); + atomic_set(&test_stop, true); for (i = 0; i < n_rw_threads; i++) { qemu_thread_join(&rw_threads[i]); diff --git a/tests/tcg/test-i386.c b/tests/tcg/test-i386.c index b05572b..0f7b943 100644 --- a/tests/tcg/test-i386.c +++ b/tests/tcg/test-i386.c @@ -2250,14 +2250,14 @@ SSE_OP(a ## sd); #define SSE_COMI(op, field)\ {\ - unsigned int eflags;\ + unsigned long eflags;\ XMMReg a, b;\ a.field[0] = a1;\ b.field[0] = b1;\ asm volatile (#op " %2, %1\n"\ "pushf\n"\ "pop %0\n"\ - : "=m" (eflags)\ + : "=rm" (eflags)\ : "x" (a.dq), "x" (b.dq));\ printf("%-9s: a=%f b=%f cc=%04x\n",\ #op, a1, b1,\ diff --git a/tests/test-arm-mptimer.c b/tests/test-arm-mptimer.c new file mode 100644 index 0000000..cb8f2df --- /dev/null +++ b/tests/test-arm-mptimer.c @@ -0,0 +1,1105 @@ +/* + * QTest testcase for the ARM MPTimer + * + * Copyright (c) 2016 Dmitry Osipenko <digetx@gmail.com> + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + */ + +#include "qemu/osdep.h" +#include "qemu/timer.h" +#include "libqtest.h" + +#define TIMER_BLOCK_SCALE(s) ((((s) & 0xff) + 1) * 10) + +#define TIMER_BLOCK_STEP(scaler, steps_nb) \ + clock_step(TIMER_BLOCK_SCALE(scaler) * (int64_t)(steps_nb) + 1) + +#define TIMER_BASE_PHYS 0x1e000600 + +#define TIMER_LOAD 0x00 +#define TIMER_COUNTER 0x04 +#define TIMER_CONTROL 0x08 +#define TIMER_INTSTAT 0x0C + +#define TIMER_CONTROL_ENABLE (1 << 0) +#define TIMER_CONTROL_PERIODIC (1 << 1) +#define TIMER_CONTROL_IT_ENABLE (1 << 2) +#define TIMER_CONTROL_PRESCALER(p) (((p) & 0xff) << 8) + +#define PERIODIC 1 +#define ONESHOT 0 +#define NOSCALE 0 + +static int nonscaled = NOSCALE; +static int scaled = 122; + +static void timer_load(uint32_t load) +{ + writel(TIMER_BASE_PHYS + TIMER_LOAD, load); +} + +static void timer_start(int periodic, uint32_t scale) +{ + uint32_t ctl = TIMER_CONTROL_ENABLE | TIMER_CONTROL_PRESCALER(scale); + + if (periodic) { + ctl |= TIMER_CONTROL_PERIODIC; + } + + writel(TIMER_BASE_PHYS + TIMER_CONTROL, ctl); +} + +static void timer_stop(void) +{ + writel(TIMER_BASE_PHYS + TIMER_CONTROL, 0); +} + +static void timer_int_clr(void) +{ + writel(TIMER_BASE_PHYS + TIMER_INTSTAT, 1); +} + +static void timer_reset(void) +{ + timer_stop(); + timer_load(0); + timer_int_clr(); +} + +static uint32_t timer_get_and_clr_int_sts(void) +{ + uint32_t int_sts = readl(TIMER_BASE_PHYS + TIMER_INTSTAT); + + if (int_sts) { + timer_int_clr(); + } + + return int_sts; +} + +static uint32_t timer_counter(void) +{ + return readl(TIMER_BASE_PHYS + TIMER_COUNTER); +} + +static void timer_set_counter(uint32_t value) +{ + writel(TIMER_BASE_PHYS + TIMER_COUNTER, value); +} + +static void test_timer_oneshot(gconstpointer arg) +{ + int scaler = *((int *) arg); + + timer_reset(); + timer_load(9999999); + timer_start(ONESHOT, scaler); + + TIMER_BLOCK_STEP(scaler, 9999); + + g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, 0); + g_assert_cmpuint(timer_counter(), ==, 9990000); + + TIMER_BLOCK_STEP(scaler, 9990000); + + g_assert_cmpuint(timer_counter(), ==, 0); + g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, 1); + + TIMER_BLOCK_STEP(scaler, 9990000); + + g_assert_cmpuint(timer_counter(), ==, 0); + g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, 0); +} + +static void test_timer_pause(gconstpointer arg) +{ + int scaler = *((int *) arg); + + timer_reset(); + timer_load(999999999); + timer_start(ONESHOT, scaler); + + TIMER_BLOCK_STEP(scaler, 999); + + g_assert_cmpuint(timer_counter(), ==, 999999000); + g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, 0); + + TIMER_BLOCK_STEP(scaler, 9000); + + g_assert_cmpuint(timer_counter(), ==, 999990000); + g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, 0); + + timer_stop(); + + g_assert_cmpuint(timer_counter(), ==, 999990000); + g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, 0); + + TIMER_BLOCK_STEP(scaler, 90000); + + g_assert_cmpuint(timer_counter(), ==, 999990000); + g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, 0); + + timer_start(ONESHOT, scaler); + + TIMER_BLOCK_STEP(scaler, 999990000); + + g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, 1); + g_assert_cmpuint(timer_counter(), ==, 0); + + TIMER_BLOCK_STEP(scaler, 999990000); + + g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, 0); + g_assert_cmpuint(timer_counter(), ==, 0); +} + +static void test_timer_reload(gconstpointer arg) +{ + int scaler = *((int *) arg); + + timer_reset(); + timer_load(UINT32_MAX); + timer_start(ONESHOT, scaler); + + TIMER_BLOCK_STEP(scaler, 90000); + + g_assert_cmpuint(timer_counter(), ==, UINT32_MAX - 90000); + g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, 0); + + timer_load(UINT32_MAX); + + TIMER_BLOCK_STEP(scaler, 90000); + + g_assert_cmpuint(timer_counter(), ==, UINT32_MAX - 90000); + g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, 0); +} + +static void test_timer_periodic(gconstpointer arg) +{ + int scaler = *((int *) arg); + int repeat = 10; + + timer_reset(); + timer_load(100); + timer_start(PERIODIC, scaler); + + while (repeat--) { + clock_step(TIMER_BLOCK_SCALE(scaler) * (101 + repeat) + 1); + + g_assert_cmpuint(timer_counter(), ==, 100 - repeat); + g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, 1); + + clock_step(TIMER_BLOCK_SCALE(scaler) * (101 - repeat) - 1); + } +} + +static void test_timer_oneshot_to_periodic(gconstpointer arg) +{ + int scaler = *((int *) arg); + + timer_reset(); + timer_load(10000); + timer_start(ONESHOT, scaler); + + TIMER_BLOCK_STEP(scaler, 1000); + + g_assert_cmpuint(timer_counter(), ==, 9000); + g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, 0); + + timer_start(PERIODIC, scaler); + + TIMER_BLOCK_STEP(scaler, 14001); + + g_assert_cmpuint(timer_counter(), ==, 5000); + g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, 1); +} + +static void test_timer_periodic_to_oneshot(gconstpointer arg) +{ + int scaler = *((int *) arg); + + timer_reset(); + timer_load(99999999); + timer_start(PERIODIC, scaler); + + TIMER_BLOCK_STEP(scaler, 999); + + g_assert_cmpuint(timer_counter(), ==, 99999000); + g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, 0); + + timer_start(ONESHOT, scaler); + + TIMER_BLOCK_STEP(scaler, 99999009); + + g_assert_cmpuint(timer_counter(), ==, 0); + g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, 1); +} + +static void test_timer_prescaler(void) +{ + timer_reset(); + timer_load(9999999); + timer_start(ONESHOT, NOSCALE); + + TIMER_BLOCK_STEP(NOSCALE, 9999998); + + g_assert_cmpuint(timer_counter(), ==, 1); + g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, 0); + + TIMER_BLOCK_STEP(NOSCALE, 1); + + g_assert_cmpuint(timer_counter(), ==, 0); + g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, 1); + + timer_reset(); + timer_load(9999999); + timer_start(ONESHOT, 0xAB); + + TIMER_BLOCK_STEP(0xAB, 9999998); + + g_assert_cmpuint(timer_counter(), ==, 1); + g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, 0); + + TIMER_BLOCK_STEP(0xAB, 1); + + g_assert_cmpuint(timer_counter(), ==, 0); + g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, 1); +} + +static void test_timer_prescaler_on_the_fly(void) +{ + timer_reset(); + timer_load(9999999); + timer_start(ONESHOT, NOSCALE); + + TIMER_BLOCK_STEP(NOSCALE, 999); + + g_assert_cmpuint(timer_counter(), ==, 9999000); + g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, 0); + + timer_start(ONESHOT, 0xAB); + + TIMER_BLOCK_STEP(0xAB, 9000); + + g_assert_cmpuint(timer_counter(), ==, 9990000); + g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, 0); +} + +static void test_timer_set_oneshot_counter_to_0(gconstpointer arg) +{ + int scaler = *((int *) arg); + + timer_reset(); + timer_load(UINT32_MAX); + timer_start(ONESHOT, scaler); + + TIMER_BLOCK_STEP(scaler, 1); + + g_assert_cmpuint(timer_counter(), ==, UINT32_MAX - 1); + g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, 0); + + timer_set_counter(0); + + TIMER_BLOCK_STEP(scaler, 10); + + g_assert_cmpuint(timer_counter(), ==, 0); + g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, !!scaler); +} + +static void test_timer_set_periodic_counter_to_0(gconstpointer arg) +{ + int scaler = *((int *) arg); + + timer_reset(); + timer_load(UINT32_MAX); + timer_start(PERIODIC, scaler); + + TIMER_BLOCK_STEP(scaler, 1); + + g_assert_cmpuint(timer_counter(), ==, UINT32_MAX - 1); + g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, 0); + + timer_set_counter(0); + + TIMER_BLOCK_STEP(scaler, 1); + + g_assert_cmpuint(timer_counter(), ==, UINT32_MAX - (scaler ? 0 : 1)); + g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, !!scaler); + + timer_reset(); + timer_set_counter(UINT32_MAX); + timer_start(PERIODIC, scaler); + + TIMER_BLOCK_STEP(scaler, 1); + + g_assert_cmpuint(timer_counter(), ==, UINT32_MAX - 1); + g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, 0); + + timer_set_counter(0); + + TIMER_BLOCK_STEP(scaler, 1); + + g_assert_cmpuint(timer_counter(), ==, 0); + g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, !!scaler); +} + +static void test_timer_noload_oneshot(gconstpointer arg) +{ + int scaler = *((int *) arg); + + timer_reset(); + timer_start(ONESHOT, scaler); + + TIMER_BLOCK_STEP(scaler, 1); + + g_assert_cmpuint(timer_counter(), ==, 0); + g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, !!scaler); + + TIMER_BLOCK_STEP(scaler, 1); + + g_assert_cmpuint(timer_counter(), ==, 0); + g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, 0); +} + +static void test_timer_noload_periodic(gconstpointer arg) +{ + int scaler = *((int *) arg); + + timer_reset(); + timer_start(PERIODIC, scaler); + + TIMER_BLOCK_STEP(scaler, 1); + + g_assert_cmpuint(timer_counter(), ==, 0); + g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, !!scaler); + + TIMER_BLOCK_STEP(scaler, 1); + + g_assert_cmpuint(timer_counter(), ==, 0); + g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, !!scaler); +} + +static void test_timer_zero_load_oneshot(gconstpointer arg) +{ + int scaler = *((int *) arg); + + timer_reset(); + timer_start(ONESHOT, scaler); + + TIMER_BLOCK_STEP(scaler, 1); + + g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, !!scaler); + g_assert_cmpuint(timer_counter(), ==, 0); + + timer_load(0); + + TIMER_BLOCK_STEP(scaler, 1); + + g_assert_cmpuint(timer_counter(), ==, 0); + g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, !!scaler); + + TIMER_BLOCK_STEP(scaler, 1); + + g_assert_cmpuint(timer_counter(), ==, 0); + g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, 0); +} + +static void test_timer_zero_load_periodic(gconstpointer arg) +{ + int scaler = *((int *) arg); + + timer_reset(); + timer_start(PERIODIC, scaler); + + TIMER_BLOCK_STEP(scaler, 1); + + g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, !!scaler); + g_assert_cmpuint(timer_counter(), ==, 0); + + timer_load(0); + + TIMER_BLOCK_STEP(scaler, 1); + + g_assert_cmpuint(timer_counter(), ==, 0); + g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, !!scaler); + + TIMER_BLOCK_STEP(scaler, 1); + + g_assert_cmpuint(timer_counter(), ==, 0); + g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, !!scaler); +} + +static void test_timer_zero_load_oneshot_to_nonzero(gconstpointer arg) +{ + int scaler = *((int *) arg); + + timer_reset(); + timer_start(ONESHOT, scaler); + + TIMER_BLOCK_STEP(scaler, 1); + + g_assert_cmpuint(timer_counter(), ==, 0); + g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, !!scaler); + + timer_load(0); + + TIMER_BLOCK_STEP(scaler, 1); + + g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, !!scaler); + g_assert_cmpuint(timer_counter(), ==, 0); + + timer_load(999); + + TIMER_BLOCK_STEP(scaler, 1001); + + g_assert_cmpuint(timer_counter(), ==, 0); + g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, 1); +} + +static void test_timer_zero_load_periodic_to_nonzero(gconstpointer arg) +{ + int scaler = *((int *) arg); + int i; + + timer_reset(); + timer_start(PERIODIC, scaler); + + TIMER_BLOCK_STEP(scaler, 1); + + g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, !!scaler); + g_assert_cmpuint(timer_counter(), ==, 0); + + timer_load(0); + + TIMER_BLOCK_STEP(scaler, 1); + + g_assert_cmpuint(timer_counter(), ==, 0); + g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, !!scaler); + + timer_load(1999999); + + for (i = 1; i < 10; i++) { + TIMER_BLOCK_STEP(scaler, 2000001); + + g_assert_cmpuint(timer_counter(), ==, 1999999 - i); + g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, 1); + g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, 0); + } +} + +static void test_timer_nonzero_load_oneshot_to_zero(gconstpointer arg) +{ + int scaler = *((int *) arg); + + timer_reset(); + timer_start(ONESHOT, scaler); + + TIMER_BLOCK_STEP(scaler, 1); + + g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, !!scaler); + g_assert_cmpuint(timer_counter(), ==, 0); + + timer_load(UINT32_MAX); + timer_load(0); + + TIMER_BLOCK_STEP(scaler, 100); + + g_assert_cmpuint(timer_counter(), ==, 0); + g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, !!scaler); +} + +static void test_timer_nonzero_load_periodic_to_zero(gconstpointer arg) +{ + int scaler = *((int *) arg); + + timer_reset(); + timer_start(PERIODIC, scaler); + + TIMER_BLOCK_STEP(scaler, 1); + + g_assert_cmpuint(timer_counter(), ==, 0); + g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, !!scaler); + + timer_load(UINT32_MAX); + timer_load(0); + + TIMER_BLOCK_STEP(scaler, 100); + + g_assert_cmpuint(timer_counter(), ==, 0); + g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, !!scaler); +} + +static void test_timer_set_periodic_counter_on_the_fly(gconstpointer arg) +{ + int scaler = *((int *) arg); + + timer_reset(); + timer_load(UINT32_MAX / 2); + timer_start(PERIODIC, scaler); + + TIMER_BLOCK_STEP(scaler, 100); + + g_assert_cmpuint(timer_counter(), ==, UINT32_MAX / 2 - 100); + g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, 0); + + timer_set_counter(UINT32_MAX); + + TIMER_BLOCK_STEP(scaler, 100); + + g_assert_cmpuint(timer_counter(), ==, UINT32_MAX - 100); + g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, 0); +} + +static void test_timer_enable_and_set_counter(gconstpointer arg) +{ + int scaler = *((int *) arg); + + timer_reset(); + timer_start(ONESHOT, scaler); + + TIMER_BLOCK_STEP(scaler, 1); + + g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, !!scaler); + + timer_set_counter(UINT32_MAX); + + TIMER_BLOCK_STEP(scaler, 100); + + g_assert_cmpuint(timer_counter(), ==, UINT32_MAX - 100); + g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, 0); +} + +static void test_timer_set_counter_and_enable(gconstpointer arg) +{ + int scaler = *((int *) arg); + + timer_reset(); + timer_set_counter(UINT32_MAX); + timer_start(ONESHOT, scaler); + + TIMER_BLOCK_STEP(scaler, 100); + + g_assert_cmpuint(timer_counter(), ==, UINT32_MAX - 100); + g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, 0); +} + +static void test_timer_set_counter_disabled(void) +{ + timer_reset(); + timer_set_counter(999999999); + + TIMER_BLOCK_STEP(NOSCALE, 100); + + g_assert_cmpuint(timer_counter(), ==, 999999999); + g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, 0); +} + +static void test_timer_load_disabled(void) +{ + timer_reset(); + timer_load(999999999); + + TIMER_BLOCK_STEP(NOSCALE, 100); + + g_assert_cmpuint(timer_counter(), ==, 999999999); + g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, 0); +} + +static void test_timer_oneshot_with_counter_0_on_start(gconstpointer arg) +{ + int scaler = *((int *) arg); + + timer_reset(); + timer_load(999); + timer_set_counter(0); + timer_start(ONESHOT, scaler); + + TIMER_BLOCK_STEP(scaler, 100); + + g_assert_cmpuint(timer_counter(), ==, 0); + g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, !!scaler); + + TIMER_BLOCK_STEP(scaler, 100); + + g_assert_cmpuint(timer_counter(), ==, 0); + g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, 0); +} + +static void test_timer_periodic_with_counter_0_on_start(gconstpointer arg) +{ + int scaler = *((int *) arg); + int i; + + timer_reset(); + timer_load(UINT32_MAX); + timer_set_counter(0); + + g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, 0); + g_assert_cmpuint(timer_counter(), ==, 0); + + timer_start(PERIODIC, scaler); + + TIMER_BLOCK_STEP(scaler, 100); + + g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, !!scaler); + g_assert_cmpuint(timer_counter(), ==, UINT32_MAX + (scaler ? 1 : 0) - 100); + + TIMER_BLOCK_STEP(scaler, 100); + + g_assert_cmpuint(timer_counter(), ==, UINT32_MAX + (scaler ? 1 : 0) - 200); + g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, 0); + + timer_reset(); + timer_load(1999999); + timer_set_counter(0); + + g_assert_cmpuint(timer_counter(), ==, 0); + g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, 0); + + TIMER_BLOCK_STEP(scaler, 1); + + g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, 0); + + timer_start(PERIODIC, scaler); + + TIMER_BLOCK_STEP(scaler, 1); + + g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, !!scaler); + + for (i = 2 - (!!scaler ? 1 : 0); i < 10; i++) { + TIMER_BLOCK_STEP(scaler, 2000001); + + g_assert_cmpuint(timer_counter(), ==, 1999999 - i); + g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, 1); + g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, 0); + } +} + +static void test_periodic_counter(gconstpointer arg) +{ + const int test_load = 10; + int scaler = *((int *) arg); + int test_val; + + timer_reset(); + timer_load(test_load); + timer_start(PERIODIC, scaler); + + clock_step(1); + + for (test_val = 0; test_val <= test_load; test_val++) { + clock_step(TIMER_BLOCK_SCALE(scaler) * test_load); + g_assert_cmpint(timer_counter(), ==, test_val); + } +} + +static void test_timer_set_counter_periodic_with_zero_load(gconstpointer arg) +{ + int scaler = *((int *) arg); + + timer_reset(); + timer_start(PERIODIC, scaler); + timer_load(0); + + TIMER_BLOCK_STEP(scaler, 1); + + g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, !!scaler); + + TIMER_BLOCK_STEP(scaler, 1); + + g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, !!scaler); + + timer_set_counter(999); + + TIMER_BLOCK_STEP(scaler, 999); + + g_assert_cmpuint(timer_counter(), ==, 0); + g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, 1); + + TIMER_BLOCK_STEP(scaler, 1); + + g_assert_cmpuint(timer_counter(), ==, 0); + g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, !!scaler); +} + +static void test_timer_set_oneshot_load_to_0(gconstpointer arg) +{ + int scaler = *((int *) arg); + + timer_reset(); + timer_load(UINT32_MAX); + timer_start(ONESHOT, scaler); + + TIMER_BLOCK_STEP(scaler, 100); + + g_assert_cmpuint(timer_counter(), ==, UINT32_MAX - 100); + g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, 0); + + timer_load(0); + + TIMER_BLOCK_STEP(scaler, 100); + + g_assert_cmpuint(timer_counter(), ==, 0); + g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, !!scaler); + + TIMER_BLOCK_STEP(scaler, 100); + + g_assert_cmpuint(timer_counter(), ==, 0); + g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, 0); +} + +static void test_timer_set_periodic_load_to_0(gconstpointer arg) +{ + int scaler = *((int *) arg); + + timer_reset(); + timer_load(UINT32_MAX); + timer_start(PERIODIC, scaler); + + TIMER_BLOCK_STEP(scaler, 100); + + g_assert_cmpuint(timer_counter(), ==, UINT32_MAX - 100); + g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, 0); + + timer_load(0); + + TIMER_BLOCK_STEP(scaler, 100); + + g_assert_cmpuint(timer_counter(), ==, 0); + g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, !!scaler); + + TIMER_BLOCK_STEP(scaler, 100); + + g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, !!scaler); + g_assert_cmpuint(timer_counter(), ==, 0); +} + +static void test_deferred_trigger(void) +{ + int mode = ONESHOT; + +again: + timer_reset(); + timer_start(mode, 255); + + clock_step(100); + + g_assert_cmpuint(timer_counter(), ==, 0); + + TIMER_BLOCK_STEP(255, 1); + + g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, 1); + + timer_reset(); + timer_load(2); + timer_start(mode, 255); + + clock_step(100); + + g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, 0); + + TIMER_BLOCK_STEP(255, 1); + + g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, 0); + + TIMER_BLOCK_STEP(255, 1); + + g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, 1); + + timer_reset(); + timer_load(UINT32_MAX); + timer_start(mode, 255); + + clock_step(100); + + g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, 0); + + timer_set_counter(0); + + clock_step(100); + + g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, 0); + + TIMER_BLOCK_STEP(255, 1); + + g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, 1); + + timer_reset(); + timer_load(UINT32_MAX); + timer_start(mode, 255); + + clock_step(100); + + g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, 0); + + timer_load(0); + + clock_step(100); + + g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, 0); + + TIMER_BLOCK_STEP(255, 1); + + g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, 1); + + if (mode == ONESHOT) { + mode = PERIODIC; + goto again; + } +} + +static void test_timer_zero_load_mode_switch(gconstpointer arg) +{ + int scaler = *((int *) arg); + + timer_reset(); + timer_load(0); + timer_start(PERIODIC, scaler); + + TIMER_BLOCK_STEP(scaler, 1); + + g_assert_cmpuint(timer_counter(), ==, 0); + g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, !!scaler); + + TIMER_BLOCK_STEP(scaler, 1); + + timer_start(ONESHOT, scaler); + + TIMER_BLOCK_STEP(scaler, 1); + + g_assert_cmpuint(timer_counter(), ==, 0); + g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, !!scaler); + + TIMER_BLOCK_STEP(scaler, 1); + + g_assert_cmpuint(timer_counter(), ==, 0); + g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, 0); + + TIMER_BLOCK_STEP(scaler, 1); + + timer_start(PERIODIC, scaler); + + TIMER_BLOCK_STEP(scaler, 1); + + g_assert_cmpuint(timer_counter(), ==, 0); + g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, !!scaler); +} + +static void test_timer_zero_load_prescaled_periodic_to_nonscaled_oneshot(void) +{ + timer_reset(); + timer_load(0); + timer_start(PERIODIC, 255); + + TIMER_BLOCK_STEP(255, 1); + + g_assert_cmpuint(timer_counter(), ==, 0); + g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, 1); + g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, 0); + + TIMER_BLOCK_STEP(255, 1); + + g_assert_cmpuint(timer_counter(), ==, 0); + g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, 1); + g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, 0); + + TIMER_BLOCK_STEP(255, 1); + + timer_start(ONESHOT, NOSCALE); + + TIMER_BLOCK_STEP(NOSCALE, 1); + + g_assert_cmpuint(timer_counter(), ==, 0); + g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, 1); + g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, 0); + + TIMER_BLOCK_STEP(NOSCALE, 1); + + g_assert_cmpuint(timer_counter(), ==, 0); + g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, 0); +} + +static void test_timer_zero_load_prescaled_oneshot_to_nonscaled_periodic(void) +{ + timer_reset(); + timer_load(0); + timer_start(ONESHOT, 255); + + TIMER_BLOCK_STEP(255, 1); + + g_assert_cmpuint(timer_counter(), ==, 0); + g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, 1); + g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, 0); + + timer_start(PERIODIC, NOSCALE); + + TIMER_BLOCK_STEP(NOSCALE, 1); + + g_assert_cmpuint(timer_counter(), ==, 0); + g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, 0); +} + +static void test_timer_zero_load_nonscaled_oneshot_to_prescaled_periodic(void) +{ + timer_reset(); + timer_load(0); + timer_start(ONESHOT, NOSCALE); + + TIMER_BLOCK_STEP(NOSCALE, 1); + + g_assert_cmpuint(timer_counter(), ==, 0); + g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, 0); + + timer_start(PERIODIC, 255); + + TIMER_BLOCK_STEP(255, 1); + + g_assert_cmpuint(timer_counter(), ==, 0); + g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, 1); + g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, 0); + + TIMER_BLOCK_STEP(255, 1); + + g_assert_cmpuint(timer_counter(), ==, 0); + g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, 1); + g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, 0); +} + +static void test_timer_zero_load_nonscaled_periodic_to_prescaled_oneshot(void) +{ + timer_reset(); + timer_load(0); + timer_start(PERIODIC, NOSCALE); + + TIMER_BLOCK_STEP(NOSCALE, 1); + + g_assert_cmpuint(timer_counter(), ==, 0); + g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, 0); + + timer_start(ONESHOT, 255); + + TIMER_BLOCK_STEP(255, 1); + + g_assert_cmpuint(timer_counter(), ==, 0); + g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, 1); + g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, 0); + + TIMER_BLOCK_STEP(255, 1); + + g_assert_cmpuint(timer_counter(), ==, 0); + g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, 0); +} + +int main(int argc, char **argv) +{ + int *scaler = &nonscaled; + int ret; + + g_test_init(&argc, &argv, NULL); + + qtest_add_func("mptimer/deferred_trigger", test_deferred_trigger); + qtest_add_func("mptimer/load_disabled", test_timer_load_disabled); + qtest_add_func("mptimer/set_counter_disabled", test_timer_set_counter_disabled); + qtest_add_func("mptimer/zero_load_prescaled_periodic_to_nonscaled_oneshot", + test_timer_zero_load_prescaled_periodic_to_nonscaled_oneshot); + qtest_add_func("mptimer/zero_load_prescaled_oneshot_to_nonscaled_periodic", + test_timer_zero_load_prescaled_oneshot_to_nonscaled_periodic); + qtest_add_func("mptimer/zero_load_nonscaled_oneshot_to_prescaled_periodic", + test_timer_zero_load_nonscaled_oneshot_to_prescaled_periodic); + qtest_add_func("mptimer/zero_load_nonscaled_periodic_to_prescaled_oneshot", + test_timer_zero_load_nonscaled_periodic_to_prescaled_oneshot); + qtest_add_func("mptimer/prescaler", test_timer_prescaler); + qtest_add_func("mptimer/prescaler_on_the_fly", test_timer_prescaler_on_the_fly); + +tests_with_prescaler_arg: + qtest_add_data_func( + g_strdup_printf("mptimer/oneshot scaler=%d", *scaler), + scaler, test_timer_oneshot); + qtest_add_data_func( + g_strdup_printf("mptimer/pause scaler=%d", *scaler), + scaler, test_timer_pause); + qtest_add_data_func( + g_strdup_printf("mptimer/reload scaler=%d", *scaler), + scaler, test_timer_reload); + qtest_add_data_func( + g_strdup_printf("mptimer/periodic scaler=%d", *scaler), + scaler, test_timer_periodic); + qtest_add_data_func( + g_strdup_printf("mptimer/oneshot_to_periodic scaler=%d", *scaler), + scaler, test_timer_oneshot_to_periodic); + qtest_add_data_func( + g_strdup_printf("mptimer/periodic_to_oneshot scaler=%d", *scaler), + scaler, test_timer_periodic_to_oneshot); + qtest_add_data_func( + g_strdup_printf("mptimer/set_oneshot_counter_to_0 scaler=%d", *scaler), + scaler, test_timer_set_oneshot_counter_to_0); + qtest_add_data_func( + g_strdup_printf("mptimer/set_periodic_counter_to_0 scaler=%d", *scaler), + scaler, test_timer_set_periodic_counter_to_0); + qtest_add_data_func( + g_strdup_printf("mptimer/noload_oneshot scaler=%d", *scaler), + scaler, test_timer_noload_oneshot); + qtest_add_data_func( + g_strdup_printf("mptimer/noload_periodic scaler=%d", *scaler), + scaler, test_timer_noload_periodic); + qtest_add_data_func( + g_strdup_printf("mptimer/zero_load_oneshot scaler=%d", *scaler), + scaler, test_timer_zero_load_oneshot); + qtest_add_data_func( + g_strdup_printf("mptimer/zero_load_periodic scaler=%d", *scaler), + scaler, test_timer_zero_load_periodic); + qtest_add_data_func( + g_strdup_printf("mptimer/zero_load_oneshot_to_nonzero scaler=%d", *scaler), + scaler, test_timer_zero_load_oneshot_to_nonzero); + qtest_add_data_func( + g_strdup_printf("mptimer/zero_load_periodic_to_nonzero scaler=%d", *scaler), + scaler, test_timer_zero_load_periodic_to_nonzero); + qtest_add_data_func( + g_strdup_printf("mptimer/nonzero_load_oneshot_to_zero scaler=%d", *scaler), + scaler, test_timer_nonzero_load_oneshot_to_zero); + qtest_add_data_func( + g_strdup_printf("mptimer/nonzero_load_periodic_to_zero scaler=%d", *scaler), + scaler, test_timer_nonzero_load_periodic_to_zero); + qtest_add_data_func( + g_strdup_printf("mptimer/set_periodic_counter_on_the_fly scaler=%d", *scaler), + scaler, test_timer_set_periodic_counter_on_the_fly); + qtest_add_data_func( + g_strdup_printf("mptimer/enable_and_set_counter scaler=%d", *scaler), + scaler, test_timer_enable_and_set_counter); + qtest_add_data_func( + g_strdup_printf("mptimer/set_counter_and_enable scaler=%d", *scaler), + scaler, test_timer_set_counter_and_enable); + qtest_add_data_func( + g_strdup_printf("mptimer/oneshot_with_counter_0_on_start scaler=%d", *scaler), + scaler, test_timer_oneshot_with_counter_0_on_start); + qtest_add_data_func( + g_strdup_printf("mptimer/periodic_with_counter_0_on_start scaler=%d", *scaler), + scaler, test_timer_periodic_with_counter_0_on_start); + qtest_add_data_func( + g_strdup_printf("mptimer/periodic_counter scaler=%d", *scaler), + scaler, test_periodic_counter); + qtest_add_data_func( + g_strdup_printf("mptimer/set_counter_periodic_with_zero_load scaler=%d", *scaler), + scaler, test_timer_set_counter_periodic_with_zero_load); + qtest_add_data_func( + g_strdup_printf("mptimer/set_oneshot_load_to_0 scaler=%d", *scaler), + scaler, test_timer_set_oneshot_load_to_0); + qtest_add_data_func( + g_strdup_printf("mptimer/set_periodic_load_to_0 scaler=%d", *scaler), + scaler, test_timer_set_periodic_load_to_0); + qtest_add_data_func( + g_strdup_printf("mptimer/zero_load_mode_switch scaler=%d", *scaler), + scaler, test_timer_zero_load_mode_switch); + + if (scaler == &nonscaled) { + scaler = &scaled; + goto tests_with_prescaler_arg; + } + + qtest_start("-machine vexpress-a9"); + ret = g_test_run(); + qtest_end(); + + return ret; +} diff --git a/tests/test-char.c b/tests/test-char.c new file mode 100644 index 0000000..241685a --- /dev/null +++ b/tests/test-char.c @@ -0,0 +1,253 @@ +#include "qemu/osdep.h" + +#include "qemu-common.h" +#include "qemu/config-file.h" +#include "sysemu/char.h" +#include "sysemu/sysemu.h" +#include "qapi/error.h" +#include "qmp-commands.h" + +typedef struct FeHandler { + int read_count; + int last_event; + char read_buf[128]; +} FeHandler; + +static int fe_can_read(void *opaque) +{ + FeHandler *h = opaque; + + return sizeof(h->read_buf) - h->read_count; +} + +static void fe_read(void *opaque, const uint8_t *buf, int size) +{ + FeHandler *h = opaque; + + g_assert_cmpint(size, <=, fe_can_read(opaque)); + + memcpy(h->read_buf + h->read_count, buf, size); + h->read_count += size; +} + +static void fe_event(void *opaque, int event) +{ + FeHandler *h = opaque; + + h->last_event = event; +} + +#ifdef CONFIG_HAS_GLIB_SUBPROCESS_TESTS +static void char_stdio_test_subprocess(void) +{ + CharDriverState *chr; + CharBackend be; + int ret; + + chr = qemu_chr_new("label", "stdio"); + g_assert_nonnull(chr); + + qemu_chr_fe_init(&be, chr, &error_abort); + qemu_chr_fe_set_open(&be, true); + ret = qemu_chr_fe_write(&be, (void *)"buf", 4); + g_assert_cmpint(ret, ==, 4); + + qemu_chr_fe_deinit(&be); + qemu_chr_delete(chr); +} + +static void char_stdio_test(void) +{ + g_test_trap_subprocess("/char/stdio/subprocess", 0, 0); + g_test_trap_assert_passed(); + g_test_trap_assert_stdout("buf"); +} +#endif + + +static void char_ringbuf_test(void) +{ + QemuOpts *opts; + CharDriverState *chr; + CharBackend be; + char *data; + int ret; + + opts = qemu_opts_create(qemu_find_opts("chardev"), "ringbuf-label", + 1, &error_abort); + qemu_opt_set(opts, "backend", "ringbuf", &error_abort); + + qemu_opt_set(opts, "size", "5", &error_abort); + chr = qemu_chr_new_from_opts(opts, NULL); + g_assert_null(chr); + qemu_opts_del(opts); + + opts = qemu_opts_create(qemu_find_opts("chardev"), "ringbuf-label", + 1, &error_abort); + qemu_opt_set(opts, "backend", "ringbuf", &error_abort); + qemu_opt_set(opts, "size", "2", &error_abort); + chr = qemu_chr_new_from_opts(opts, &error_abort); + g_assert_nonnull(chr); + qemu_opts_del(opts); + + qemu_chr_fe_init(&be, chr, &error_abort); + ret = qemu_chr_fe_write(&be, (void *)"buff", 4); + g_assert_cmpint(ret, ==, 4); + + data = qmp_ringbuf_read("ringbuf-label", 4, false, 0, &error_abort); + g_assert_cmpstr(data, ==, "ff"); + g_free(data); + + data = qmp_ringbuf_read("ringbuf-label", 4, false, 0, &error_abort); + g_assert_cmpstr(data, ==, ""); + g_free(data); + + qemu_chr_fe_deinit(&be); + qemu_chr_delete(chr); +} + +static void char_mux_test(void) +{ + QemuOpts *opts; + CharDriverState *chr, *base; + char *data; + FeHandler h1 = { 0, }, h2 = { 0, }; + CharBackend chr_be1, chr_be2; + + opts = qemu_opts_create(qemu_find_opts("chardev"), "mux-label", + 1, &error_abort); + qemu_opt_set(opts, "backend", "ringbuf", &error_abort); + qemu_opt_set(opts, "size", "128", &error_abort); + qemu_opt_set(opts, "mux", "on", &error_abort); + chr = qemu_chr_new_from_opts(opts, &error_abort); + g_assert_nonnull(chr); + qemu_opts_del(opts); + + qemu_chr_fe_init(&chr_be1, chr, &error_abort); + qemu_chr_fe_set_handlers(&chr_be1, + fe_can_read, + fe_read, + fe_event, + &h1, + NULL, true); + + qemu_chr_fe_init(&chr_be2, chr, &error_abort); + qemu_chr_fe_set_handlers(&chr_be2, + fe_can_read, + fe_read, + fe_event, + &h2, + NULL, true); + qemu_chr_fe_take_focus(&chr_be2); + + base = qemu_chr_find("mux-label-base"); + g_assert_cmpint(qemu_chr_be_can_write(base), !=, 0); + + qemu_chr_be_write(base, (void *)"hello", 6); + g_assert_cmpint(h1.read_count, ==, 0); + g_assert_cmpint(h2.read_count, ==, 6); + g_assert_cmpstr(h2.read_buf, ==, "hello"); + h2.read_count = 0; + + /* switch focus */ + qemu_chr_be_write(base, (void *)"\1c", 2); + + qemu_chr_be_write(base, (void *)"hello", 6); + g_assert_cmpint(h2.read_count, ==, 0); + g_assert_cmpint(h1.read_count, ==, 6); + g_assert_cmpstr(h1.read_buf, ==, "hello"); + h1.read_count = 0; + + /* remove first handler */ + qemu_chr_fe_set_handlers(&chr_be1, NULL, NULL, NULL, NULL, NULL, true); + qemu_chr_be_write(base, (void *)"hello", 6); + g_assert_cmpint(h1.read_count, ==, 0); + g_assert_cmpint(h2.read_count, ==, 0); + + qemu_chr_be_write(base, (void *)"\1c", 2); + qemu_chr_be_write(base, (void *)"hello", 6); + g_assert_cmpint(h1.read_count, ==, 0); + g_assert_cmpint(h2.read_count, ==, 6); + g_assert_cmpstr(h2.read_buf, ==, "hello"); + h2.read_count = 0; + + /* print help */ + qemu_chr_be_write(base, (void *)"\1?", 2); + data = qmp_ringbuf_read("mux-label-base", 128, false, 0, &error_abort); + g_assert_cmpint(strlen(data), !=, 0); + g_free(data); + + qemu_chr_fe_deinit(&chr_be1); + qemu_chr_fe_deinit(&chr_be2); + qemu_chr_delete(chr); +} + +static void char_null_test(void) +{ + Error *err = NULL; + CharDriverState *chr; + CharBackend be; + int ret; + + chr = qemu_chr_find("label-null"); + g_assert_null(chr); + + chr = qemu_chr_new("label-null", "null"); + chr = qemu_chr_find("label-null"); + g_assert_nonnull(chr); + + g_assert(qemu_chr_has_feature(chr, + QEMU_CHAR_FEATURE_FD_PASS) == false); + g_assert(qemu_chr_has_feature(chr, + QEMU_CHAR_FEATURE_RECONNECTABLE) == false); + + /* check max avail */ + qemu_chr_fe_init(&be, chr, &error_abort); + qemu_chr_fe_init(&be, chr, &err); + error_free_or_abort(&err); + + /* deinit & reinit */ + qemu_chr_fe_deinit(&be); + qemu_chr_fe_init(&be, chr, &error_abort); + + qemu_chr_fe_set_open(&be, true); + + qemu_chr_fe_set_handlers(&be, + fe_can_read, + fe_read, + fe_event, + NULL, NULL, true); + + ret = qemu_chr_fe_write(&be, (void *)"buf", 4); + g_assert_cmpint(ret, ==, 4); + + qemu_chr_fe_deinit(&be); + qemu_chr_delete(chr); +} + +static void char_invalid_test(void) +{ + CharDriverState *chr; + + chr = qemu_chr_new("label-invalid", "invalid"); + g_assert_null(chr); +} + +int main(int argc, char **argv) +{ + g_test_init(&argc, &argv, NULL); + + module_call_init(MODULE_INIT_QOM); + qemu_add_opts(&qemu_chardev_opts); + + g_test_add_func("/char/null", char_null_test); + g_test_add_func("/char/invalid", char_invalid_test); + g_test_add_func("/char/ringbuf", char_ringbuf_test); + g_test_add_func("/char/mux", char_mux_test); +#ifdef CONFIG_HAS_GLIB_SUBPROCESS_TESTS + g_test_add_func("/char/stdio/subprocess", char_stdio_test_subprocess); + g_test_add_func("/char/stdio", char_stdio_test); +#endif + + return g_test_run(); +} diff --git a/tests/test-hbitmap.c b/tests/test-hbitmap.c index c0e9895..9b7495c 100644 --- a/tests/test-hbitmap.c +++ b/tests/test-hbitmap.c @@ -11,6 +11,8 @@ #include "qemu/osdep.h" #include "qemu/hbitmap.h" +#include "qemu/bitmap.h" +#include "block/block.h" #define LOG_BITS_PER_LONG (BITS_PER_LONG == 32 ? 5 : 6) @@ -20,6 +22,7 @@ typedef struct TestHBitmapData { HBitmap *hb; + HBitmap *meta; unsigned long *bits; size_t size; size_t old_size; @@ -91,6 +94,14 @@ static void hbitmap_test_init(TestHBitmapData *data, } } +static void hbitmap_test_init_meta(TestHBitmapData *data, + uint64_t size, int granularity, + int meta_chunk) +{ + hbitmap_test_init(data, size, granularity); + data->meta = hbitmap_create_meta(data->hb, meta_chunk); +} + static inline size_t hbitmap_test_array_size(size_t bits) { size_t n = DIV_ROUND_UP(bits, BITS_PER_LONG); @@ -133,6 +144,9 @@ static void hbitmap_test_teardown(TestHBitmapData *data, const void *unused) { if (data->hb) { + if (data->meta) { + hbitmap_free_meta(data->hb); + } hbitmap_free(data->hb); data->hb = NULL; } @@ -634,6 +648,249 @@ static void test_hbitmap_truncate_shrink_large(TestHBitmapData *data, hbitmap_test_truncate(data, size, -diff, 0); } +static void hbitmap_check_meta(TestHBitmapData *data, + int64_t start, int count) +{ + int64_t i; + + for (i = 0; i < data->size; i++) { + if (i >= start && i < start + count) { + g_assert(hbitmap_get(data->meta, i)); + } else { + g_assert(!hbitmap_get(data->meta, i)); + } + } +} + +static void hbitmap_test_meta(TestHBitmapData *data, + int64_t start, int count, + int64_t check_start, int check_count) +{ + hbitmap_reset_all(data->hb); + hbitmap_reset_all(data->meta); + + /* Test "unset" -> "unset" will not update meta. */ + hbitmap_reset(data->hb, start, count); + hbitmap_check_meta(data, 0, 0); + + /* Test "unset" -> "set" will update meta */ + hbitmap_set(data->hb, start, count); + hbitmap_check_meta(data, check_start, check_count); + + /* Test "set" -> "set" will not update meta */ + hbitmap_reset_all(data->meta); + hbitmap_set(data->hb, start, count); + hbitmap_check_meta(data, 0, 0); + + /* Test "set" -> "unset" will update meta */ + hbitmap_reset_all(data->meta); + hbitmap_reset(data->hb, start, count); + hbitmap_check_meta(data, check_start, check_count); +} + +static void hbitmap_test_meta_do(TestHBitmapData *data, int chunk_size) +{ + uint64_t size = chunk_size * 100; + hbitmap_test_init_meta(data, size, 0, chunk_size); + + hbitmap_test_meta(data, 0, 1, 0, chunk_size); + hbitmap_test_meta(data, 0, chunk_size, 0, chunk_size); + hbitmap_test_meta(data, chunk_size - 1, 1, 0, chunk_size); + hbitmap_test_meta(data, chunk_size - 1, 2, 0, chunk_size * 2); + hbitmap_test_meta(data, chunk_size - 1, chunk_size + 1, 0, chunk_size * 2); + hbitmap_test_meta(data, chunk_size - 1, chunk_size + 2, 0, chunk_size * 3); + hbitmap_test_meta(data, 7 * chunk_size - 1, chunk_size + 2, + 6 * chunk_size, chunk_size * 3); + hbitmap_test_meta(data, size - 1, 1, size - chunk_size, chunk_size); + hbitmap_test_meta(data, 0, size, 0, size); +} + +static void test_hbitmap_meta_byte(TestHBitmapData *data, const void *unused) +{ + hbitmap_test_meta_do(data, BITS_PER_BYTE); +} + +static void test_hbitmap_meta_word(TestHBitmapData *data, const void *unused) +{ + hbitmap_test_meta_do(data, BITS_PER_LONG); +} + +static void test_hbitmap_meta_sector(TestHBitmapData *data, const void *unused) +{ + hbitmap_test_meta_do(data, BDRV_SECTOR_SIZE * BITS_PER_BYTE); +} + +/** + * Create an HBitmap and test set/unset. + */ +static void test_hbitmap_meta_one(TestHBitmapData *data, const void *unused) +{ + int i; + int64_t offsets[] = { + 0, 1, L1 - 1, L1, L1 + 1, L2 - 1, L2, L2 + 1, L3 - 1, L3, L3 + 1 + }; + + hbitmap_test_init_meta(data, L3 * 2, 0, 1); + for (i = 0; i < ARRAY_SIZE(offsets); i++) { + hbitmap_test_meta(data, offsets[i], 1, offsets[i], 1); + hbitmap_test_meta(data, offsets[i], L1, offsets[i], L1); + hbitmap_test_meta(data, offsets[i], L2, offsets[i], L2); + } +} + +static void test_hbitmap_serialize_granularity(TestHBitmapData *data, + const void *unused) +{ + int r; + + hbitmap_test_init(data, L3 * 2, 3); + r = hbitmap_serialization_granularity(data->hb); + g_assert_cmpint(r, ==, 64 << 3); +} + +static void test_hbitmap_meta_zero(TestHBitmapData *data, const void *unused) +{ + hbitmap_test_init_meta(data, 0, 0, 1); + + hbitmap_check_meta(data, 0, 0); +} + +static void hbitmap_test_serialize_range(TestHBitmapData *data, + uint8_t *buf, size_t buf_size, + uint64_t pos, uint64_t count) +{ + size_t i; + unsigned long *el = (unsigned long *)buf; + + assert(hbitmap_granularity(data->hb) == 0); + hbitmap_reset_all(data->hb); + memset(buf, 0, buf_size); + if (count) { + hbitmap_set(data->hb, pos, count); + } + hbitmap_serialize_part(data->hb, buf, 0, data->size); + + /* Serialized buffer is inherently LE, convert it back manually to test */ + for (i = 0; i < buf_size / sizeof(unsigned long); i++) { + el[i] = (BITS_PER_LONG == 32 ? le32_to_cpu(el[i]) : le64_to_cpu(el[i])); + } + + for (i = 0; i < data->size; i++) { + int is_set = test_bit(i, (unsigned long *)buf); + if (i >= pos && i < pos + count) { + g_assert(is_set); + } else { + g_assert(!is_set); + } + } + + /* Re-serialize for deserialization testing */ + memset(buf, 0, buf_size); + hbitmap_serialize_part(data->hb, buf, 0, data->size); + hbitmap_reset_all(data->hb); + hbitmap_deserialize_part(data->hb, buf, 0, data->size, true); + + for (i = 0; i < data->size; i++) { + int is_set = hbitmap_get(data->hb, i); + if (i >= pos && i < pos + count) { + g_assert(is_set); + } else { + g_assert(!is_set); + } + } +} + +static void test_hbitmap_serialize_basic(TestHBitmapData *data, + const void *unused) +{ + int i, j; + size_t buf_size; + uint8_t *buf; + uint64_t positions[] = { 0, 1, L1 - 1, L1, L2 - 1, L2, L2 + 1, L3 - 1 }; + int num_positions = sizeof(positions) / sizeof(positions[0]); + + hbitmap_test_init(data, L3, 0); + buf_size = hbitmap_serialization_size(data->hb, 0, data->size); + buf = g_malloc0(buf_size); + + for (i = 0; i < num_positions; i++) { + for (j = 0; j < num_positions; j++) { + hbitmap_test_serialize_range(data, buf, buf_size, + positions[i], + MIN(positions[j], L3 - positions[i])); + } + } + + g_free(buf); +} + +static void test_hbitmap_serialize_part(TestHBitmapData *data, + const void *unused) +{ + int i, j, k; + size_t buf_size; + uint8_t *buf; + uint64_t positions[] = { 0, 1, L1 - 1, L1, L2 - 1, L2, L2 + 1, L3 - 1 }; + int num_positions = sizeof(positions) / sizeof(positions[0]); + + hbitmap_test_init(data, L3, 0); + buf_size = L2; + buf = g_malloc0(buf_size); + + for (i = 0; i < num_positions; i++) { + hbitmap_set(data->hb, positions[i], 1); + } + + for (i = 0; i < data->size; i += buf_size) { + unsigned long *el = (unsigned long *)buf; + hbitmap_serialize_part(data->hb, buf, i, buf_size); + for (j = 0; j < buf_size / sizeof(unsigned long); j++) { + el[j] = (BITS_PER_LONG == 32 ? le32_to_cpu(el[j]) : le64_to_cpu(el[j])); + } + + for (j = 0; j < buf_size; j++) { + bool should_set = false; + for (k = 0; k < num_positions; k++) { + if (positions[k] == j + i) { + should_set = true; + break; + } + } + g_assert_cmpint(should_set, ==, test_bit(j, (unsigned long *)buf)); + } + } + + g_free(buf); +} + +static void test_hbitmap_serialize_zeroes(TestHBitmapData *data, + const void *unused) +{ + int i; + HBitmapIter iter; + int64_t next; + uint64_t min_l1 = MAX(L1, 64); + uint64_t positions[] = { 0, min_l1, L2, L3 - min_l1}; + int num_positions = sizeof(positions) / sizeof(positions[0]); + + hbitmap_test_init(data, L3, 0); + + for (i = 0; i < num_positions; i++) { + hbitmap_set(data->hb, positions[i], L1); + } + + for (i = 0; i < num_positions; i++) { + hbitmap_deserialize_zeroes(data->hb, positions[i], min_l1, true); + hbitmap_iter_init(&iter, data->hb, 0); + next = hbitmap_iter_next(&iter); + if (i == num_positions - 1) { + g_assert_cmpint(next, ==, -1); + } else { + g_assert_cmpint(next, ==, positions[i + 1]); + } + } +} + static void hbitmap_test_add(const char *testpath, void (*test_func)(TestHBitmapData *data, const void *user_data)) { @@ -683,6 +940,21 @@ int main(int argc, char **argv) test_hbitmap_truncate_grow_large); hbitmap_test_add("/hbitmap/truncate/shrink/large", test_hbitmap_truncate_shrink_large); + + hbitmap_test_add("/hbitmap/meta/zero", test_hbitmap_meta_zero); + hbitmap_test_add("/hbitmap/meta/one", test_hbitmap_meta_one); + hbitmap_test_add("/hbitmap/meta/byte", test_hbitmap_meta_byte); + hbitmap_test_add("/hbitmap/meta/word", test_hbitmap_meta_word); + hbitmap_test_add("/hbitmap/meta/sector", test_hbitmap_meta_sector); + + hbitmap_test_add("/hbitmap/serialize/granularity", + test_hbitmap_serialize_granularity); + hbitmap_test_add("/hbitmap/serialize/basic", + test_hbitmap_serialize_basic); + hbitmap_test_add("/hbitmap/serialize/part", + test_hbitmap_serialize_part); + hbitmap_test_add("/hbitmap/serialize/zeroes", + test_hbitmap_serialize_zeroes); g_test_run(); return 0; diff --git a/tests/test-int128.c b/tests/test-int128.c index 4390123..b86a3c7 100644 --- a/tests/test-int128.c +++ b/tests/test-int128.c @@ -41,7 +41,7 @@ static Int128 expand(uint32_t x) uint64_t l, h; l = expand16(x & 65535); h = expand16(x >> 16); - return (Int128) {l, h}; + return (Int128) int128_make128(l, h); }; static void test_and(void) @@ -54,8 +54,8 @@ static void test_and(void) Int128 b = expand(tests[j]); Int128 r = expand(tests[i] & tests[j]); Int128 s = int128_and(a, b); - g_assert_cmpuint(r.lo, ==, s.lo); - g_assert_cmpuint(r.hi, ==, s.hi); + g_assert_cmpuint(int128_getlo(r), ==, int128_getlo(s)); + g_assert_cmpuint(int128_gethi(r), ==, int128_gethi(s)); } } } @@ -70,8 +70,8 @@ static void test_add(void) Int128 b = expand(tests[j]); Int128 r = expand(tests[i] + tests[j]); Int128 s = int128_add(a, b); - g_assert_cmpuint(r.lo, ==, s.lo); - g_assert_cmpuint(r.hi, ==, s.hi); + g_assert_cmpuint(int128_getlo(r), ==, int128_getlo(s)); + g_assert_cmpuint(int128_gethi(r), ==, int128_gethi(s)); } } } @@ -86,8 +86,8 @@ static void test_sub(void) Int128 b = expand(tests[j]); Int128 r = expand(tests[i] - tests[j]); Int128 s = int128_sub(a, b); - g_assert_cmpuint(r.lo, ==, s.lo); - g_assert_cmpuint(r.hi, ==, s.hi); + g_assert_cmpuint(int128_getlo(r), ==, int128_getlo(s)); + g_assert_cmpuint(int128_gethi(r), ==, int128_gethi(s)); } } } @@ -100,8 +100,8 @@ static void test_neg(void) Int128 a = expand(tests[i]); Int128 r = expand(-tests[i]); Int128 s = int128_neg(a); - g_assert_cmpuint(r.lo, ==, s.lo); - g_assert_cmpuint(r.hi, ==, s.hi); + g_assert_cmpuint(int128_getlo(r), ==, int128_getlo(s)); + g_assert_cmpuint(int128_gethi(r), ==, int128_gethi(s)); } } @@ -180,8 +180,8 @@ test_rshift_one(uint32_t x, int n, uint64_t h, uint64_t l) { Int128 a = expand(x); Int128 r = int128_rshift(a, n); - g_assert_cmpuint(r.lo, ==, l); - g_assert_cmpuint(r.hi, ==, h); + g_assert_cmpuint(int128_getlo(r), ==, l); + g_assert_cmpuint(int128_gethi(r), ==, h); } static void test_rshift(void) diff --git a/tests/test-qmp-commands.c b/tests/test-qmp-commands.c index 81cbe54..ff94481 100644 --- a/tests/test-qmp-commands.c +++ b/tests/test-qmp-commands.c @@ -4,7 +4,7 @@ #include "test-qmp-commands.h" #include "qapi/qmp/dispatch.h" #include "qemu/module.h" -#include "qapi/qmp-input-visitor.h" +#include "qapi/qobject-input-visitor.h" #include "tests/test-qapi-types.h" #include "tests/test-qapi-visit.h" @@ -244,7 +244,7 @@ static void test_dealloc_partial(void) ud2_dict = qdict_new(); qdict_put_obj(ud2_dict, "string0", QOBJECT(qstring_from_str(text))); - v = qmp_input_visitor_new(QOBJECT(ud2_dict), true); + v = qobject_input_visitor_new(QOBJECT(ud2_dict), true); visit_type_UserDefTwo(v, NULL, &ud2, &err); visit_free(v); QDECREF(ud2_dict); diff --git a/tests/test-qmp-input-strict.c b/tests/test-qobject-input-strict.c index d87f8b8..4087ea3 100644 --- a/tests/test-qmp-input-strict.c +++ b/tests/test-qobject-input-strict.c @@ -1,5 +1,5 @@ /* - * QMP Input Visitor unit-tests (strict mode). + * QObject Input Visitor unit-tests (strict mode). * * Copyright (C) 2011-2012, 2015 Red Hat Inc. * @@ -15,7 +15,7 @@ #include "qemu-common.h" #include "qapi/error.h" -#include "qapi/qmp-input-visitor.h" +#include "qapi/qobject-input-visitor.h" #include "test-qapi-types.h" #include "test-qapi-visit.h" #include "qapi/qmp/types.h" @@ -53,7 +53,7 @@ static Visitor *validate_test_init_internal(TestInputVisitorData *data, data->obj = qobject_from_jsonv(json_string, ap); g_assert(data->obj); - data->qiv = qmp_input_visitor_new(data->obj, true); + data->qiv = qobject_input_visitor_new(data->obj, true); g_assert(data->qiv); return data->qiv; } diff --git a/tests/test-qmp-input-visitor.c b/tests/test-qobject-input-visitor.c index f583dce..26c5012 100644 --- a/tests/test-qmp-input-visitor.c +++ b/tests/test-qobject-input-visitor.c @@ -1,5 +1,5 @@ /* - * QMP Input Visitor unit-tests. + * QObject Input Visitor unit-tests. * * Copyright (C) 2011-2016 Red Hat Inc. * @@ -14,7 +14,7 @@ #include "qemu-common.h" #include "qapi/error.h" -#include "qapi/qmp-input-visitor.h" +#include "qapi/qobject-input-visitor.h" #include "test-qapi-types.h" #include "test-qapi-visit.h" #include "qapi/qmp/types.h" @@ -49,7 +49,7 @@ static Visitor *visitor_input_test_init_internal(TestInputVisitorData *data, data->obj = qobject_from_jsonv(json_string, ap); g_assert(data->obj); - data->qiv = qmp_input_visitor_new(data->obj, false); + data->qiv = qobject_input_visitor_new(data->obj, false); g_assert(data->qiv); return data->qiv; } @@ -747,10 +747,11 @@ static void test_visitor_in_native_list_number(TestInputVisitorData *data, } static void input_visitor_test_add(const char *testpath, - TestInputVisitorData *data, - void (*test_func)(TestInputVisitorData *data, const void *user_data)) + const void *user_data, + void (*test_func)(TestInputVisitorData *data, + const void *user_data)) { - g_test_add(testpath, TestInputVisitorData, data, NULL, test_func, + g_test_add(testpath, TestInputVisitorData, user_data, NULL, test_func, visitor_input_teardown); } @@ -833,77 +834,64 @@ static void test_visitor_in_wrong_type(TestInputVisitorData *data, int main(int argc, char **argv) { - TestInputVisitorData in_visitor_data; - g_test_init(&argc, &argv, NULL); input_visitor_test_add("/visitor/input/int", - &in_visitor_data, test_visitor_in_int); + NULL, test_visitor_in_int); input_visitor_test_add("/visitor/input/int_overflow", - &in_visitor_data, test_visitor_in_int_overflow); + NULL, test_visitor_in_int_overflow); input_visitor_test_add("/visitor/input/bool", - &in_visitor_data, test_visitor_in_bool); + NULL, test_visitor_in_bool); input_visitor_test_add("/visitor/input/number", - &in_visitor_data, test_visitor_in_number); + NULL, test_visitor_in_number); input_visitor_test_add("/visitor/input/string", - &in_visitor_data, test_visitor_in_string); + NULL, test_visitor_in_string); input_visitor_test_add("/visitor/input/enum", - &in_visitor_data, test_visitor_in_enum); + NULL, test_visitor_in_enum); input_visitor_test_add("/visitor/input/struct", - &in_visitor_data, test_visitor_in_struct); + NULL, test_visitor_in_struct); input_visitor_test_add("/visitor/input/struct-nested", - &in_visitor_data, test_visitor_in_struct_nested); + NULL, test_visitor_in_struct_nested); input_visitor_test_add("/visitor/input/list", - &in_visitor_data, test_visitor_in_list); + NULL, test_visitor_in_list); input_visitor_test_add("/visitor/input/any", - &in_visitor_data, test_visitor_in_any); + NULL, test_visitor_in_any); input_visitor_test_add("/visitor/input/null", - &in_visitor_data, test_visitor_in_null); + NULL, test_visitor_in_null); input_visitor_test_add("/visitor/input/union-flat", - &in_visitor_data, test_visitor_in_union_flat); + NULL, test_visitor_in_union_flat); input_visitor_test_add("/visitor/input/alternate", - &in_visitor_data, test_visitor_in_alternate); + NULL, test_visitor_in_alternate); input_visitor_test_add("/visitor/input/errors", - &in_visitor_data, test_visitor_in_errors); + NULL, test_visitor_in_errors); input_visitor_test_add("/visitor/input/wrong-type", - &in_visitor_data, test_visitor_in_wrong_type); + NULL, test_visitor_in_wrong_type); input_visitor_test_add("/visitor/input/alternate-number", - &in_visitor_data, test_visitor_in_alternate_number); + NULL, test_visitor_in_alternate_number); input_visitor_test_add("/visitor/input/native_list/int", - &in_visitor_data, - test_visitor_in_native_list_int); + NULL, test_visitor_in_native_list_int); input_visitor_test_add("/visitor/input/native_list/int8", - &in_visitor_data, - test_visitor_in_native_list_int8); + NULL, test_visitor_in_native_list_int8); input_visitor_test_add("/visitor/input/native_list/int16", - &in_visitor_data, - test_visitor_in_native_list_int16); + NULL, test_visitor_in_native_list_int16); input_visitor_test_add("/visitor/input/native_list/int32", - &in_visitor_data, - test_visitor_in_native_list_int32); + NULL, test_visitor_in_native_list_int32); input_visitor_test_add("/visitor/input/native_list/int64", - &in_visitor_data, - test_visitor_in_native_list_int64); + NULL, test_visitor_in_native_list_int64); input_visitor_test_add("/visitor/input/native_list/uint8", - &in_visitor_data, - test_visitor_in_native_list_uint8); + NULL, test_visitor_in_native_list_uint8); input_visitor_test_add("/visitor/input/native_list/uint16", - &in_visitor_data, - test_visitor_in_native_list_uint16); + NULL, test_visitor_in_native_list_uint16); input_visitor_test_add("/visitor/input/native_list/uint32", - &in_visitor_data, - test_visitor_in_native_list_uint32); + NULL, test_visitor_in_native_list_uint32); input_visitor_test_add("/visitor/input/native_list/uint64", - &in_visitor_data, - test_visitor_in_native_list_uint64); + NULL, test_visitor_in_native_list_uint64); input_visitor_test_add("/visitor/input/native_list/bool", - &in_visitor_data, test_visitor_in_native_list_bool); + NULL, test_visitor_in_native_list_bool); input_visitor_test_add("/visitor/input/native_list/str", - &in_visitor_data, - test_visitor_in_native_list_string); + NULL, test_visitor_in_native_list_string); input_visitor_test_add("/visitor/input/native_list/number", - &in_visitor_data, - test_visitor_in_native_list_number); + NULL, test_visitor_in_native_list_number); g_test_run(); diff --git a/tests/test-qmp-output-visitor.c b/tests/test-qobject-output-visitor.c index 513d71f..4e2d79c 100644 --- a/tests/test-qmp-output-visitor.c +++ b/tests/test-qobject-output-visitor.c @@ -1,5 +1,5 @@ /* - * QMP Output Visitor unit-tests. + * QObject Output Visitor unit-tests. * * Copyright (C) 2011-2016 Red Hat Inc. * @@ -14,7 +14,7 @@ #include "qemu-common.h" #include "qapi/error.h" -#include "qapi/qmp-output-visitor.h" +#include "qapi/qobject-output-visitor.h" #include "test-qapi-types.h" #include "test-qapi-visit.h" #include "qapi/qmp/types.h" @@ -28,7 +28,7 @@ typedef struct TestOutputVisitorData { static void visitor_output_setup(TestOutputVisitorData *data, const void *unused) { - data->ov = qmp_output_visitor_new(&data->obj); + data->ov = qobject_output_visitor_new(&data->obj); g_assert(data->ov); } diff --git a/tests/test-string-input-visitor.c b/tests/test-string-input-visitor.c index a679fbc..7f10e25 100644 --- a/tests/test-string-input-visitor.c +++ b/tests/test-string-input-visitor.c @@ -4,7 +4,7 @@ * Copyright (C) 2012 Red Hat Inc. * * Authors: - * Paolo Bonzini <pbonzini@redhat.com> (based on test-qmp-input-visitor) + * Paolo Bonzini <pbonzini@redhat.com> (based on test-qobject-input-visitor) * * This work is licensed under the terms of the GNU GPL, version 2 or later. * See the COPYING file in the top-level directory. diff --git a/tests/test-string-output-visitor.c b/tests/test-string-output-visitor.c index 444844a..e736db3 100644 --- a/tests/test-string-output-visitor.c +++ b/tests/test-string-output-visitor.c @@ -4,7 +4,7 @@ * Copyright (C) 2012 Red Hat Inc. * * Authors: - * Paolo Bonzini <pbonzini@redhat.com> (based on test-qmp-output-visitor) + * Paolo Bonzini <pbonzini@redhat.com> (based on test-qobject-output-visitor) * * This work is licensed under the terms of the GNU GPL, version 2 or later. * See the COPYING file in the top-level directory. diff --git a/tests/test-visitor-serialization.c b/tests/test-visitor-serialization.c index dba4670..66b2b1c 100644 --- a/tests/test-visitor-serialization.c +++ b/tests/test-visitor-serialization.c @@ -20,8 +20,8 @@ #include "qapi/error.h" #include "qapi/qmp/types.h" #include "qapi/qmp/qjson.h" -#include "qapi/qmp-input-visitor.h" -#include "qapi/qmp-output-visitor.h" +#include "qapi/qobject-input-visitor.h" +#include "qapi/qobject-output-visitor.h" #include "qapi/string-input-visitor.h" #include "qapi/string-output-visitor.h" #include "qapi-types.h" @@ -1022,7 +1022,7 @@ static void qmp_serialize(void *native_in, void **datap, { QmpSerializeData *d = g_malloc0(sizeof(*d)); - d->qov = qmp_output_visitor_new(&d->obj); + d->qov = qobject_output_visitor_new(&d->obj); visit(d->qov, &native_in, errp); *datap = d; } @@ -1040,7 +1040,7 @@ static void qmp_deserialize(void **native_out, void *datap, obj = qobject_from_json(qstring_get_str(output_json)); QDECREF(output_json); - d->qiv = qmp_input_visitor_new(obj, true); + d->qiv = qobject_input_visitor_new(obj, true); qobject_decref(obj_orig); qobject_decref(obj); visit(d->qiv, native_out, errp); diff --git a/tests/vhost-user-test.c b/tests/vhost-user-test.c index d7c48c5..a7f0629 100644 --- a/tests/vhost-user-test.c +++ b/tests/vhost-user-test.c @@ -11,6 +11,7 @@ #include "qemu/osdep.h" #include "libqtest.h" +#include "qapi/error.h" #include "qemu/option.h" #include "qemu/range.h" #include "qemu/sockets.h" @@ -19,6 +20,7 @@ #include "libqos/libqos.h" #include "libqos/pci-pc.h" #include "libqos/virtio-pci.h" +#include "qapi/error.h" #include "libqos/pci-pc.h" #include "libqos/virtio-pci.h" @@ -141,7 +143,7 @@ typedef struct TestServer { gchar *socket_path; gchar *mig_path; gchar *chr_name; - CharDriverState *chr; + CharBackend chr; int fds_num; int fds[VHOST_MEMORY_MAX_NREGIONS]; VhostUserMemory memory; @@ -261,13 +263,13 @@ static int chr_can_read(void *opaque) static void chr_read(void *opaque, const uint8_t *buf, int size) { TestServer *s = opaque; - CharDriverState *chr = s->chr; + CharBackend *chr = &s->chr; VhostUserMsg msg; uint8_t *p = (uint8_t *) &msg; int fd; if (s->test_fail) { - qemu_chr_disconnect(chr); + qemu_chr_fe_disconnect(chr); /* now switch to non-failure */ s->test_fail = false; } @@ -312,7 +314,7 @@ static void chr_read(void *opaque, const uint8_t *buf, int size) g_assert_cmpint(msg.payload.u64 & (0x1ULL << VHOST_USER_F_PROTOCOL_FEATURES), !=, 0ULL); if (s->test_flags == TEST_FLAGS_DISCONNECT) { - qemu_chr_disconnect(chr); + qemu_chr_fe_disconnect(chr); s->test_flags = TEST_FLAGS_BAD; } break; @@ -344,7 +346,8 @@ static void chr_read(void *opaque, const uint8_t *buf, int size) case VHOST_USER_SET_MEM_TABLE: /* received the mem table */ memcpy(&s->memory, &msg.payload.memory, sizeof(msg.payload.memory)); - s->fds_num = qemu_chr_fe_get_msgfds(chr, s->fds, G_N_ELEMENTS(s->fds)); + s->fds_num = qemu_chr_fe_get_msgfds(chr, s->fds, + G_N_ELEMENTS(s->fds)); /* signal the test that it can continue */ g_cond_signal(&s->data_cond); @@ -453,13 +456,15 @@ static void chr_event(void *opaque, int event) static void test_server_create_chr(TestServer *server, const gchar *opt) { gchar *chr_path; + CharDriverState *chr; chr_path = g_strdup_printf("unix:%s%s", server->socket_path, opt); - server->chr = qemu_chr_new(server->chr_name, chr_path, NULL); + chr = qemu_chr_new(server->chr_name, chr_path); g_free(chr_path); - qemu_chr_add_handlers(server->chr, chr_can_read, chr_read, - chr_event, server); + qemu_chr_fe_init(&server->chr, chr, &error_abort); + qemu_chr_fe_set_handlers(&server->chr, chr_can_read, chr_read, + chr_event, server, NULL, true); } static void test_server_listen(TestServer *server) @@ -483,8 +488,10 @@ static inline void test_server_connect(TestServer *server) static gboolean _test_server_free(TestServer *server) { int i; + CharDriverState *chr = qemu_chr_fe_get_driver(&server->chr); - qemu_chr_delete(server->chr); + qemu_chr_fe_deinit(&server->chr); + qemu_chr_delete(chr); for (i = 0; i < server->fds_num; i++) { close(server->fds[i]); @@ -721,7 +728,7 @@ reconnect_cb(gpointer user_data) { TestServer *s = user_data; - qemu_chr_disconnect(s->chr); + qemu_chr_fe_disconnect(&s->chr); return FALSE; } diff --git a/translate-all.c b/translate-all.c index 8ca393c..76fc18c 100644 --- a/translate-all.c +++ b/translate-all.c @@ -97,34 +97,54 @@ typedef struct PageDesc { #define V_L2_BITS 10 #define V_L2_SIZE (1 << V_L2_BITS) -/* The bits remaining after N lower levels of page tables. */ -#define V_L1_BITS_REM \ - ((L1_MAP_ADDR_SPACE_BITS - TARGET_PAGE_BITS) % V_L2_BITS) - -#if V_L1_BITS_REM < 4 -#define V_L1_BITS (V_L1_BITS_REM + V_L2_BITS) -#else -#define V_L1_BITS V_L1_BITS_REM -#endif - -#define V_L1_SIZE ((target_ulong)1 << V_L1_BITS) - -#define V_L1_SHIFT (L1_MAP_ADDR_SPACE_BITS - TARGET_PAGE_BITS - V_L1_BITS) - uintptr_t qemu_host_page_size; intptr_t qemu_host_page_mask; -/* The bottom level has pointers to PageDesc */ -static void *l1_map[V_L1_SIZE]; +/* + * L1 Mapping properties + */ +static int v_l1_size; +static int v_l1_shift; +static int v_l2_levels; + +/* The bottom level has pointers to PageDesc, and is indexed by + * anything from 4 to (V_L2_BITS + 3) bits, depending on target page size. + */ +#define V_L1_MIN_BITS 4 +#define V_L1_MAX_BITS (V_L2_BITS + 3) +#define V_L1_MAX_SIZE (1 << V_L1_MAX_BITS) + +static void *l1_map[V_L1_MAX_SIZE]; /* code generation context */ TCGContext tcg_ctx; +bool parallel_cpus; /* translation block context */ #ifdef CONFIG_USER_ONLY __thread int have_tb_lock; #endif +static void page_table_config_init(void) +{ + uint32_t v_l1_bits; + + assert(TARGET_PAGE_BITS); + /* The bits remaining after N lower levels of page tables. */ + v_l1_bits = (L1_MAP_ADDR_SPACE_BITS - TARGET_PAGE_BITS) % V_L2_BITS; + if (v_l1_bits < V_L1_MIN_BITS) { + v_l1_bits += V_L2_BITS; + } + + v_l1_size = 1 << v_l1_bits; + v_l1_shift = L1_MAP_ADDR_SPACE_BITS - TARGET_PAGE_BITS - v_l1_bits; + v_l2_levels = v_l1_shift / V_L2_BITS - 1; + + assert(v_l1_bits <= V_L1_MAX_BITS); + assert(v_l1_shift % V_L2_BITS == 0); + assert(v_l2_levels >= 0); +} + void tb_lock(void) { #ifdef CONFIG_USER_ONLY @@ -332,6 +352,8 @@ void page_size_init(void) static void page_init(void) { page_size_init(); + page_table_config_init(); + #if defined(CONFIG_BSD) && defined(CONFIG_USER_ONLY) { #ifdef HAVE_KINFO_GETVMMAP @@ -408,10 +430,10 @@ static PageDesc *page_find_alloc(tb_page_addr_t index, int alloc) int i; /* Level 1. Always allocated. */ - lp = l1_map + ((index >> V_L1_SHIFT) & (V_L1_SIZE - 1)); + lp = l1_map + ((index >> v_l1_shift) & (v_l1_size - 1)); /* Level 2..N-1. */ - for (i = V_L1_SHIFT / V_L2_BITS - 1; i > 0; i--) { + for (i = v_l2_levels; i > 0; i--) { void **p = atomic_rcu_read(lp); if (p == NULL) { @@ -826,10 +848,10 @@ static void page_flush_tb_1(int level, void **lp) static void page_flush_tb(void) { - int i; + int i, l1_sz = v_l1_size; - for (i = 0; i < V_L1_SIZE; i++) { - page_flush_tb_1(V_L1_SHIFT / V_L2_BITS - 1, l1_map + i); + for (i = 0; i < l1_sz; i++) { + page_flush_tb_1(v_l2_levels, l1_map + i); } } @@ -1883,16 +1905,16 @@ static int walk_memory_regions_1(struct walk_memory_regions_data *data, int walk_memory_regions(void *priv, walk_memory_regions_fn fn) { struct walk_memory_regions_data data; - uintptr_t i; + uintptr_t i, l1_sz = v_l1_size; data.fn = fn; data.priv = priv; data.start = -1u; data.prot = 0; - for (i = 0; i < V_L1_SIZE; i++) { - int rc = walk_memory_regions_1(&data, (target_ulong)i << (V_L1_SHIFT + TARGET_PAGE_BITS), - V_L1_SHIFT / V_L2_BITS - 1, l1_map + i); + for (i = 0; i < l1_sz; i++) { + target_ulong base = i << (v_l1_shift + TARGET_PAGE_BITS); + int rc = walk_memory_regions_1(&data, base, v_l2_levels, l1_map + i); if (rc != 0) { return rc; } diff --git a/ui/console.c b/ui/console.c index fa3e658..ed888e5 100644 --- a/ui/console.c +++ b/ui/console.c @@ -1083,6 +1083,7 @@ static void kbd_send_chars(void *opaque) void kbd_put_keysym_console(QemuConsole *s, int keysym) { uint8_t buf[16], *q; + CharBackend *be; int c; if (!s || (s->console_type == GRAPHIC_CONSOLE)) @@ -1125,7 +1126,8 @@ void kbd_put_keysym_console(QemuConsole *s, int keysym) if (s->echo) { console_puts(s->chr, buf, q - buf); } - if (s->chr->chr_read) { + be = s->chr->be; + if (be && be->chr_read) { qemu_fifo_write(&s->out_fifo, buf, q - buf); kbd_send_chars(s); } @@ -2033,8 +2035,6 @@ static void text_console_do_init(CharDriverState *chr, DisplayState *ds) } qemu_chr_be_generic_open(chr); - if (chr->init) - chr->init(chr); } static CharDriverState *text_console_init(ChardevVC *vc, Error **errp) @@ -2079,10 +2079,6 @@ static CharDriverState *text_console_init(ChardevVC *vc, Error **errp) s->chr = chr; chr->opaque = s; chr->chr_set_echo = text_console_set_echo; - /* console/chardev init sometimes completes elsewhere in a 2nd - * stage, so defer OPENED events until they are fully initialized - */ - chr->explicit_be_open = true; if (display_state) { text_console_do_init(chr, display_state); @@ -2093,8 +2089,13 @@ static CharDriverState *text_console_init(ChardevVC *vc, Error **errp) static VcHandler *vc_handler = text_console_init; static CharDriverState *vc_init(const char *id, ChardevBackend *backend, - ChardevReturn *ret, Error **errp) + ChardevReturn *ret, bool *be_opened, + Error **errp) { + /* console/chardev init sometimes completes elsewhere in a 2nd + * stage, so defer OPENED events until they are fully initialized + */ + *be_opened = false; return vc_handler(backend->u.vc.data, errp); } @@ -1685,9 +1685,6 @@ static CharDriverState *gd_vc_handler(ChardevVC *vc, Error **errp) /* Temporary, until gd_vc_vte_init runs. */ chr->opaque = g_new0(VirtualConsole, 1); - /* defer OPENED events until our vc is fully initialized */ - chr->explicit_be_open = true; - vcs[nb_vcs++] = chr; return chr; @@ -1789,9 +1786,6 @@ static GSList *gd_vc_vte_init(GtkDisplayState *s, VirtualConsole *vc, gtk_label_new(vc->label)); qemu_chr_be_generic_open(vc->vte.chr); - if (vc->vte.chr->init) { - vc->vte.chr->init(vc->vte.chr); - } return group; } diff --git a/util/hbitmap.c b/util/hbitmap.c index 99fd2ba..5d1a21c 100644 --- a/util/hbitmap.c +++ b/util/hbitmap.c @@ -78,6 +78,9 @@ struct HBitmap { */ int granularity; + /* A meta dirty bitmap to track the dirtiness of bits in this HBitmap. */ + HBitmap *meta; + /* A number of progressively less coarse bitmaps (i.e. level 0 is the * coarsest). Each bit in level N represents a word in level N+1 that * has a set bit, except the last level where each bit represents the @@ -209,25 +212,27 @@ static uint64_t hb_count_between(HBitmap *hb, uint64_t start, uint64_t last) } /* Setting starts at the last layer and propagates up if an element - * changes from zero to non-zero. + * changes. */ static inline bool hb_set_elem(unsigned long *elem, uint64_t start, uint64_t last) { unsigned long mask; - bool changed; + unsigned long old; assert((last >> BITS_PER_LEVEL) == (start >> BITS_PER_LEVEL)); assert(start <= last); mask = 2UL << (last & (BITS_PER_LONG - 1)); mask -= 1UL << (start & (BITS_PER_LONG - 1)); - changed = (*elem == 0); + old = *elem; *elem |= mask; - return changed; + return old != *elem; } -/* The recursive workhorse (the depth is limited to HBITMAP_LEVELS)... */ -static void hb_set_between(HBitmap *hb, int level, uint64_t start, uint64_t last) +/* The recursive workhorse (the depth is limited to HBITMAP_LEVELS)... + * Returns true if at least one bit is changed. */ +static bool hb_set_between(HBitmap *hb, int level, uint64_t start, + uint64_t last) { size_t pos = start >> BITS_PER_LEVEL; size_t lastpos = last >> BITS_PER_LEVEL; @@ -256,23 +261,28 @@ static void hb_set_between(HBitmap *hb, int level, uint64_t start, uint64_t last if (level > 0 && changed) { hb_set_between(hb, level - 1, pos, lastpos); } + return changed; } void hbitmap_set(HBitmap *hb, uint64_t start, uint64_t count) { /* Compute range in the last layer. */ + uint64_t first, n; uint64_t last = start + count - 1; trace_hbitmap_set(hb, start, count, start >> hb->granularity, last >> hb->granularity); - start >>= hb->granularity; + first = start >> hb->granularity; last >>= hb->granularity; - count = last - start + 1; assert(last < hb->size); + n = last - first + 1; - hb->count += count - hb_count_between(hb, start, last); - hb_set_between(hb, HBITMAP_LEVELS - 1, start, last); + hb->count += n - hb_count_between(hb, first, last); + if (hb_set_between(hb, HBITMAP_LEVELS - 1, first, last) && + hb->meta) { + hbitmap_set(hb->meta, start, count); + } } /* Resetting works the other way round: propagate up if the new @@ -293,8 +303,10 @@ static inline bool hb_reset_elem(unsigned long *elem, uint64_t start, uint64_t l return blanked; } -/* The recursive workhorse (the depth is limited to HBITMAP_LEVELS)... */ -static void hb_reset_between(HBitmap *hb, int level, uint64_t start, uint64_t last) +/* The recursive workhorse (the depth is limited to HBITMAP_LEVELS)... + * Returns true if at least one bit is changed. */ +static bool hb_reset_between(HBitmap *hb, int level, uint64_t start, + uint64_t last) { size_t pos = start >> BITS_PER_LEVEL; size_t lastpos = last >> BITS_PER_LEVEL; @@ -337,22 +349,29 @@ static void hb_reset_between(HBitmap *hb, int level, uint64_t start, uint64_t la if (level > 0 && changed) { hb_reset_between(hb, level - 1, pos, lastpos); } + + return changed; + } void hbitmap_reset(HBitmap *hb, uint64_t start, uint64_t count) { /* Compute range in the last layer. */ + uint64_t first; uint64_t last = start + count - 1; trace_hbitmap_reset(hb, start, count, start >> hb->granularity, last >> hb->granularity); - start >>= hb->granularity; + first = start >> hb->granularity; last >>= hb->granularity; assert(last < hb->size); - hb->count -= hb_count_between(hb, start, last); - hb_reset_between(hb, HBITMAP_LEVELS - 1, start, last); + hb->count -= hb_count_between(hb, first, last); + if (hb_reset_between(hb, HBITMAP_LEVELS - 1, first, last) && + hb->meta) { + hbitmap_set(hb->meta, start, count); + } } void hbitmap_reset_all(HBitmap *hb) @@ -378,9 +397,147 @@ bool hbitmap_get(const HBitmap *hb, uint64_t item) return (hb->levels[HBITMAP_LEVELS - 1][pos >> BITS_PER_LEVEL] & bit) != 0; } +uint64_t hbitmap_serialization_granularity(const HBitmap *hb) +{ + /* Require at least 64 bit granularity to be safe on both 64 bit and 32 bit + * hosts. */ + return 64 << hb->granularity; +} + +/* Start should be aligned to serialization granularity, chunk size should be + * aligned to serialization granularity too, except for last chunk. + */ +static void serialization_chunk(const HBitmap *hb, + uint64_t start, uint64_t count, + unsigned long **first_el, uint64_t *el_count) +{ + uint64_t last = start + count - 1; + uint64_t gran = hbitmap_serialization_granularity(hb); + + assert((start & (gran - 1)) == 0); + assert((last >> hb->granularity) < hb->size); + if ((last >> hb->granularity) != hb->size - 1) { + assert((count & (gran - 1)) == 0); + } + + start = (start >> hb->granularity) >> BITS_PER_LEVEL; + last = (last >> hb->granularity) >> BITS_PER_LEVEL; + + *first_el = &hb->levels[HBITMAP_LEVELS - 1][start]; + *el_count = last - start + 1; +} + +uint64_t hbitmap_serialization_size(const HBitmap *hb, + uint64_t start, uint64_t count) +{ + uint64_t el_count; + unsigned long *cur; + + if (!count) { + return 0; + } + serialization_chunk(hb, start, count, &cur, &el_count); + + return el_count * sizeof(unsigned long); +} + +void hbitmap_serialize_part(const HBitmap *hb, uint8_t *buf, + uint64_t start, uint64_t count) +{ + uint64_t el_count; + unsigned long *cur, *end; + + if (!count) { + return; + } + serialization_chunk(hb, start, count, &cur, &el_count); + end = cur + el_count; + + while (cur != end) { + unsigned long el = + (BITS_PER_LONG == 32 ? cpu_to_le32(*cur) : cpu_to_le64(*cur)); + + memcpy(buf, &el, sizeof(el)); + buf += sizeof(el); + cur++; + } +} + +void hbitmap_deserialize_part(HBitmap *hb, uint8_t *buf, + uint64_t start, uint64_t count, + bool finish) +{ + uint64_t el_count; + unsigned long *cur, *end; + + if (!count) { + return; + } + serialization_chunk(hb, start, count, &cur, &el_count); + end = cur + el_count; + + while (cur != end) { + memcpy(cur, buf, sizeof(*cur)); + + if (BITS_PER_LONG == 32) { + le32_to_cpus((uint32_t *)cur); + } else { + le64_to_cpus((uint64_t *)cur); + } + + buf += sizeof(unsigned long); + cur++; + } + if (finish) { + hbitmap_deserialize_finish(hb); + } +} + +void hbitmap_deserialize_zeroes(HBitmap *hb, uint64_t start, uint64_t count, + bool finish) +{ + uint64_t el_count; + unsigned long *first; + + if (!count) { + return; + } + serialization_chunk(hb, start, count, &first, &el_count); + + memset(first, 0, el_count * sizeof(unsigned long)); + if (finish) { + hbitmap_deserialize_finish(hb); + } +} + +void hbitmap_deserialize_finish(HBitmap *bitmap) +{ + int64_t i, size, prev_size; + int lev; + + /* restore levels starting from penultimate to zero level, assuming + * that the last level is ok */ + size = MAX((bitmap->size + BITS_PER_LONG - 1) >> BITS_PER_LEVEL, 1); + for (lev = HBITMAP_LEVELS - 1; lev-- > 0; ) { + prev_size = size; + size = MAX((size + BITS_PER_LONG - 1) >> BITS_PER_LEVEL, 1); + memset(bitmap->levels[lev], 0, size * sizeof(unsigned long)); + + for (i = 0; i < prev_size; ++i) { + if (bitmap->levels[lev + 1][i]) { + bitmap->levels[lev][i >> BITS_PER_LEVEL] |= + 1UL << (i & (BITS_PER_LONG - 1)); + } + } + } + + bitmap->levels[0][0] |= 1UL << (BITS_PER_LONG - 1); +} + void hbitmap_free(HBitmap *hb) { unsigned i; + assert(!hb->meta); for (i = HBITMAP_LEVELS; i-- > 0; ) { g_free(hb->levels[i]); } @@ -458,6 +615,9 @@ void hbitmap_truncate(HBitmap *hb, uint64_t size) (size - old) * sizeof(*hb->levels[i])); } } + if (hb->meta) { + hbitmap_truncate(hb->meta, hb->size << hb->granularity); + } } @@ -493,3 +653,19 @@ bool hbitmap_merge(HBitmap *a, const HBitmap *b) return true; } + +HBitmap *hbitmap_create_meta(HBitmap *hb, int chunk_size) +{ + assert(!(chunk_size & (chunk_size - 1))); + assert(!hb->meta); + hb->meta = hbitmap_alloc(hb->size << hb->granularity, + hb->granularity + ctz32(chunk_size)); + return hb->meta; +} + +void hbitmap_free_meta(HBitmap *hb) +{ + assert(hb->meta); + hbitmap_free(hb->meta); + hb->meta = NULL; +} diff --git a/util/qemu-sockets.c b/util/qemu-sockets.c index 6db48b3..4cef549 100644 --- a/util/qemu-sockets.c +++ b/util/qemu-sockets.c @@ -21,8 +21,8 @@ #include "qapi/error.h" #include "qemu/sockets.h" #include "qemu/main-loop.h" -#include "qapi/qmp-input-visitor.h" -#include "qapi/qmp-output-visitor.h" +#include "qapi/qobject-input-visitor.h" +#include "qapi/qobject-output-visitor.h" #include "qapi-visit.h" #include "qemu/cutils.h" diff --git a/util/qemu-thread-posix.c b/util/qemu-thread-posix.c index 74a3023..ce51b37 100644 --- a/util/qemu-thread-posix.c +++ b/util/qemu-thread-posix.c @@ -360,7 +360,11 @@ void qemu_event_destroy(QemuEvent *ev) void qemu_event_set(QemuEvent *ev) { - if (atomic_mb_read(&ev->value) != EV_SET) { + /* qemu_event_set has release semantics, but because it *loads* + * ev->value we need a full memory barrier here. + */ + smp_mb(); + if (atomic_read(&ev->value) != EV_SET) { if (atomic_xchg(&ev->value, EV_SET) == EV_BUSY) { /* There were waiters, wake them up. */ futex_wake(ev, INT_MAX); @@ -370,7 +374,11 @@ void qemu_event_set(QemuEvent *ev) void qemu_event_reset(QemuEvent *ev) { - if (atomic_mb_read(&ev->value) == EV_SET) { + unsigned value; + + value = atomic_read(&ev->value); + smp_mb_acquire(); + if (value == EV_SET) { /* * If there was a concurrent reset (or even reset+wait), * do nothing. Otherwise change EV_SET->EV_FREE. @@ -383,7 +391,8 @@ void qemu_event_wait(QemuEvent *ev) { unsigned value; - value = atomic_mb_read(&ev->value); + value = atomic_read(&ev->value); + smp_mb_acquire(); if (value != EV_SET) { if (value == EV_FREE) { /* diff --git a/util/qemu-thread-win32.c b/util/qemu-thread-win32.c index 98a5ddf..072806f 100644 --- a/util/qemu-thread-win32.c +++ b/util/qemu-thread-win32.c @@ -274,7 +274,11 @@ void qemu_event_destroy(QemuEvent *ev) void qemu_event_set(QemuEvent *ev) { - if (atomic_mb_read(&ev->value) != EV_SET) { + /* qemu_event_set has release semantics, but because it *loads* + * ev->value we need a full memory barrier here. + */ + smp_mb(); + if (atomic_read(&ev->value) != EV_SET) { if (atomic_xchg(&ev->value, EV_SET) == EV_BUSY) { /* There were waiters, wake them up. */ SetEvent(ev->event); @@ -284,7 +288,11 @@ void qemu_event_set(QemuEvent *ev) void qemu_event_reset(QemuEvent *ev) { - if (atomic_mb_read(&ev->value) == EV_SET) { + unsigned value; + + value = atomic_read(&ev->value); + smp_mb_acquire(); + if (value == EV_SET) { /* If there was a concurrent reset (or even reset+wait), * do nothing. Otherwise change EV_SET->EV_FREE. */ @@ -296,7 +304,8 @@ void qemu_event_wait(QemuEvent *ev) { unsigned value; - value = atomic_mb_read(&ev->value); + value = atomic_read(&ev->value); + smp_mb_acquire(); if (value != EV_SET) { if (value == EV_FREE) { /* qemu_event_set is not yet going to call SetEvent, but we are @@ -82,14 +82,16 @@ static void wait_for_readers(void) /* Instead of using atomic_mb_set for index->waiting, and * atomic_mb_read for index->ctr, memory barriers are placed * manually since writes to different threads are independent. - * atomic_mb_set has a smp_wmb before... + * qemu_event_reset has acquire semantics, so no memory barrier + * is needed here. */ - smp_wmb(); QLIST_FOREACH(index, ®istry, node) { atomic_set(&index->waiting, true); } - /* ... and a smp_mb after. */ + /* Here, order the stores to index->waiting before the + * loads of index->ctr. + */ smp_mb(); QLIST_FOREACH_SAFE(index, ®istry, node, tmp) { @@ -104,9 +106,6 @@ static void wait_for_readers(void) } } - /* atomic_mb_read has smp_rmb after. */ - smp_rmb(); - if (QLIST_EMPTY(®istry)) { break; } @@ -218,6 +218,7 @@ static struct { { .driver = "isa-serial", .flag = &default_serial }, { .driver = "isa-parallel", .flag = &default_parallel }, { .driver = "isa-fdc", .flag = &default_floppy }, + { .driver = "floppy", .flag = &default_floppy }, { .driver = "ide-cd", .flag = &default_cdrom }, { .driver = "ide-hd", .flag = &default_cdrom }, { .driver = "ide-drive", .flag = &default_cdrom }, @@ -1958,7 +1959,7 @@ static void main_loop(void) static void version(void) { - printf("QEMU emulator version " QEMU_VERSION QEMU_PKGVERSION ", " + printf("QEMU emulator version " QEMU_VERSION QEMU_PKGVERSION "\n" QEMU_COPYRIGHT "\n"); } @@ -2369,7 +2370,7 @@ static int chardev_init_func(void *opaque, QemuOpts *opts, Error **errp) { Error *local_err = NULL; - qemu_chr_new_from_opts(opts, NULL, &local_err); + qemu_chr_new_from_opts(opts, &local_err); if (local_err) { error_report_err(local_err); return -1; @@ -2417,7 +2418,6 @@ static int mon_init_func(void *opaque, QemuOpts *opts, Error **errp) exit(1); } - qemu_chr_fe_claim_no_fail(chr); monitor_init(chr, flags); return 0; } @@ -2514,7 +2514,7 @@ static int serial_parse(const char *devname) exit(1); } snprintf(label, sizeof(label), "serial%d", index); - serial_hds[index] = qemu_chr_new(label, devname, NULL); + serial_hds[index] = qemu_chr_new(label, devname); if (!serial_hds[index]) { error_report("could not connect serial device" " to character backend '%s'", devname); @@ -2536,7 +2536,7 @@ static int parallel_parse(const char *devname) exit(1); } snprintf(label, sizeof(label), "parallel%d", index); - parallel_hds[index] = qemu_chr_new(label, devname, NULL); + parallel_hds[index] = qemu_chr_new(label, devname); if (!parallel_hds[index]) { error_report("could not connect parallel device" " to character backend '%s'", devname); @@ -2567,7 +2567,7 @@ static int virtcon_parse(const char *devname) qemu_opt_set(dev_opts, "driver", "virtconsole", &error_abort); snprintf(label, sizeof(label), "virtcon%d", index); - virtcon_hds[index] = qemu_chr_new(label, devname, NULL); + virtcon_hds[index] = qemu_chr_new(label, devname); if (!virtcon_hds[index]) { error_report("could not connect virtio console" " to character backend '%s'", devname); @@ -2600,7 +2600,7 @@ static int sclp_parse(const char *devname) qemu_opt_set(dev_opts, "driver", "sclpconsole", &error_abort); snprintf(label, sizeof(label), "sclpcon%d", index); - sclp_hds[index] = qemu_chr_new(label, devname, NULL); + sclp_hds[index] = qemu_chr_new(label, devname); if (!sclp_hds[index]) { error_report("could not connect sclp console" " to character backend '%s'", devname); @@ -2616,7 +2616,7 @@ static int debugcon_parse(const char *devname) { QemuOpts *opts; - if (!qemu_chr_new("debugcon", devname, NULL)) { + if (!qemu_chr_new("debugcon", devname)) { exit(1); } opts = qemu_opts_create(qemu_find_opts("device"), "debugcon", 1, NULL); @@ -4089,6 +4089,16 @@ int main(int argc, char **argv, char **envp) } object_property_add_child(object_get_root(), "machine", OBJECT(current_machine), &error_abort); + + if (machine_class->minimum_page_bits) { + if (!set_preferred_target_page_bits(machine_class->minimum_page_bits)) { + /* This would be a board error: specifying a minimum smaller than + * a target's compile-time fixed setting. + */ + g_assert_not_reached(); + } + } + cpu_exec_init_all(); if (machine_class->hw_version) { |