diff options
250 files changed, 11710 insertions, 2507 deletions
diff --git a/.travis.yml b/.travis.yml index 79377c8de0..c1e99237b2 100644 --- a/.travis.yml +++ b/.travis.yml @@ -49,9 +49,10 @@ env: - TEST_CMD="make check" - MAKEFLAGS="-j3" matrix: - - CONFIG="" - - CONFIG="--enable-debug --enable-debug-tcg --enable-trace-backends=log" - - CONFIG="--disable-linux-aio --disable-cap-ng --disable-attr --disable-brlapi --disable-uuid --disable-libusb" + - CONFIG="--disable-system" + - CONFIG="--disable-user" + - CONFIG="--enable-debug --enable-debug-tcg" + - CONFIG="--disable-linux-aio --disable-cap-ng --disable-attr --disable-brlapi --disable-uuid --disable-libusb --disable-user" - CONFIG="--enable-modules --disable-linux-user" - CONFIG="--with-coroutine=ucontext --disable-linux-user" - CONFIG="--with-coroutine=sigaltstack --disable-linux-user" diff --git a/MAINTAINERS b/MAINTAINERS index dac21d53eb..6fa36c7a2d 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -127,7 +127,6 @@ Alpha M: Richard Henderson <rth@twiddle.net> S: Maintained F: target/alpha/ -F: hw/alpha/ F: tests/tcg/alpha/ F: disas/alpha.c @@ -413,6 +412,12 @@ F: include/*/*win32* X: qga/*win32* F: qemu.nsi +Alpha Machines +M: Richard Henderson <rth@twiddle.net> +S: Maintained +F: hw/alpha/ +F: hw/isa/smc37c669-superio.c + ARM Machines ------------ Allwinner-a10 @@ -700,6 +705,8 @@ Fulong 2E M: Yongbok Kim <yongbok.kim@mips.com> S: Odd Fixes F: hw/mips/mips_fulong2e.c +F: hw/isa/vt82c686.c +F: include/hw/isa/vt82c686.h Boston M: Paul Burton <paul.burton@mips.com> @@ -776,9 +783,10 @@ F: hw/ppc/prep_systemio.c F: hw/ppc/rs6000_mc.c F: hw/pci-host/prep.[hc] F: hw/isa/i82378.c -F: hw/isa/pc87312.[hc] +F: hw/isa/pc87312.c F: hw/dma/i82374.c F: hw/timer/m48t59-isa.c +F: include/hw/isa/pc87312.h F: include/hw/timer/m48t59.h F: pc-bios/ppc_rom.bin @@ -924,7 +932,7 @@ M: Michael S. Tsirkin <mst@redhat.com> M: Paolo Bonzini <pbonzini@redhat.com> S: Supported F: hw/char/debugcon.c -F: hw/char/parallel.c +F: hw/char/parallel* F: hw/char/serial* F: hw/dma/i8257* F: hw/i2c/pm_smbus.c @@ -932,6 +940,7 @@ F: hw/input/pckbd.c F: hw/intc/apic* F: hw/intc/ioapic* F: hw/intc/i8259* +F: hw/isa/isa-superio.c F: hw/misc/debugexit.c F: hw/misc/pc-testdev.c F: hw/timer/hpet* @@ -939,8 +948,11 @@ F: hw/timer/i8254* F: hw/timer/mc146818rtc* F: hw/watchdog/wdt_ib700.c F: include/hw/display/vga.h +F: include/hw/char/parallel.h +F: include/hw/dma/i8257.h F: include/hw/i2c/pm_smbus.h -F: include/hw/isa/i8257.h +F: include/hw/input/i8042.h +F: include/hw/isa/superio.h F: include/hw/timer/hpet.h F: include/hw/timer/i8254* F: include/hw/timer/mc146818rtc* @@ -438,21 +438,23 @@ all: $(DOCS) $(TOOLS) $(HELPERS-y) recurse-all modules qemu-version.h: FORCE $(call quiet-command, \ (cd $(SRC_PATH); \ - printf '#define QEMU_PKGVERSION '; \ if test -n "$(PKGVERSION)"; then \ - printf '"$(PKGVERSION)"\n'; \ + pkgvers="$(PKGVERSION)"; \ else \ if test -d .git; then \ - printf '" ('; \ - git describe --match 'v*' 2>/dev/null | tr -d '\n'; \ + pkgvers=$$(git describe --match 'v*' 2>/dev/null | tr -d '\n');\ if ! git diff-index --quiet HEAD &>/dev/null; then \ - printf -- '-dirty'; \ + pkgvers="$${pkgvers}-dirty"; \ fi; \ - printf ')"\n'; \ - else \ - printf '""\n'; \ fi; \ - fi) > $@.tmp) + fi; \ + printf "#define QEMU_PKGVERSION \"$${pkgvers}\"\n"; \ + if test -n "$${pkgvers}"; then \ + printf '#define QEMU_FULL_VERSION QEMU_VERSION " (" QEMU_PKGVERSION ")"\n'; \ + else \ + printf '#define QEMU_FULL_VERSION QEMU_VERSION\n'; \ + fi; \ + ) > $@.tmp) $(call quiet-command, if ! cmp -s $@ $@.tmp; then \ mv $@.tmp $@; \ else \ @@ -1050,6 +1052,9 @@ include $(SRC_PATH)/tests/vm/Makefile.include help: @echo 'Generic targets:' @echo ' all - Build all' +ifdef CONFIG_MODULES + @echo ' modules - Build all modules' +endif @echo ' dir/file.o - Build specified target only' @echo ' install - Install QEMU, documentation and tools' @echo ' ctags/TAGS - Generate tags file for editors' diff --git a/accel/Makefile.objs b/accel/Makefile.objs index 10666eda71..c3718a10c5 100644 --- a/accel/Makefile.objs +++ b/accel/Makefile.objs @@ -1,4 +1,4 @@ obj-$(CONFIG_SOFTMMU) += accel.o -obj-y += kvm/ +obj-$(CONFIG_KVM) += kvm/ obj-$(CONFIG_TCG) += tcg/ obj-y += stubs/ diff --git a/accel/kvm/Makefile.objs b/accel/kvm/Makefile.objs index 85351e7de7..fdfa481578 100644 --- a/accel/kvm/Makefile.objs +++ b/accel/kvm/Makefile.objs @@ -1 +1,2 @@ -obj-$(CONFIG_KVM) += kvm-all.o +obj-y += kvm-all.o +obj-$(call lnot,$(CONFIG_SEV)) += sev-stub.o diff --git a/accel/kvm/kvm-all.c b/accel/kvm/kvm-all.c index b91fcb7160..ffee68e603 100644 --- a/accel/kvm/kvm-all.c +++ b/accel/kvm/kvm-all.c @@ -38,6 +38,7 @@ #include "qemu/event_notifier.h" #include "trace.h" #include "hw/irq.h" +#include "sysemu/sev.h" #include "hw/boards.h" @@ -103,6 +104,10 @@ struct KVMState #endif KVMMemoryListener memory_listener; QLIST_HEAD(, KVMParkedVcpu) kvm_parked_vcpus; + + /* memory encryption */ + void *memcrypt_handle; + int (*memcrypt_encrypt_data)(void *handle, uint8_t *ptr, uint64_t len); }; KVMState *kvm_state; @@ -138,6 +143,26 @@ int kvm_get_max_memslots(void) return s->nr_slots; } +bool kvm_memcrypt_enabled(void) +{ + if (kvm_state && kvm_state->memcrypt_handle) { + return true; + } + + return false; +} + +int kvm_memcrypt_encrypt_data(uint8_t *ptr, uint64_t len) +{ + if (kvm_state->memcrypt_handle && + kvm_state->memcrypt_encrypt_data) { + return kvm_state->memcrypt_encrypt_data(kvm_state->memcrypt_handle, + ptr, len); + } + + return 1; +} + static KVMSlot *kvm_get_free_slot(KVMMemoryListener *kml) { KVMState *s = kvm_state; @@ -1636,6 +1661,20 @@ static int kvm_init(MachineState *ms) kvm_state = s; + /* + * if memory encryption object is specified then initialize the memory + * encryption context. + */ + if (ms->memory_encryption) { + kvm_state->memcrypt_handle = sev_guest_init(ms->memory_encryption); + if (!kvm_state->memcrypt_handle) { + ret = -1; + goto err; + } + + kvm_state->memcrypt_encrypt_data = sev_encrypt_data; + } + ret = kvm_arch_init(ms, s); if (ret < 0) { goto err; diff --git a/accel/kvm/sev-stub.c b/accel/kvm/sev-stub.c new file mode 100644 index 0000000000..4f97452585 --- /dev/null +++ b/accel/kvm/sev-stub.c @@ -0,0 +1,26 @@ +/* + * QEMU SEV stub + * + * Copyright Advanced Micro Devices 2018 + * + * Authors: + * Brijesh Singh <brijesh.singh@amd.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-common.h" +#include "sysemu/sev.h" + +int sev_encrypt_data(void *handle, uint8_t *ptr, uint64_t len) +{ + abort(); +} + +void *sev_guest_init(const char *id) +{ + return NULL; +} diff --git a/accel/stubs/kvm-stub.c b/accel/stubs/kvm-stub.c index c964af3e1c..02d5170031 100644 --- a/accel/stubs/kvm-stub.c +++ b/accel/stubs/kvm-stub.c @@ -105,6 +105,16 @@ int kvm_on_sigbus(int code, void *addr) return 1; } +bool kvm_memcrypt_enabled(void) +{ + return false; +} + +int kvm_memcrypt_encrypt_data(uint8_t *ptr, uint64_t len) +{ + return 1; +} + #ifndef CONFIG_USER_ONLY int kvm_irqchip_add_msi_route(KVMState *s, int vector, PCIDevice *dev) { diff --git a/accel/tcg/cpu-exec.c b/accel/tcg/cpu-exec.c index 280200f737..9cc697205c 100644 --- a/accel/tcg/cpu-exec.c +++ b/accel/tcg/cpu-exec.c @@ -585,6 +585,7 @@ static inline bool cpu_handle_interrupt(CPUState *cpu, else { if (cc->cpu_exec_interrupt(cpu, interrupt_request)) { replay_interrupt(); + cpu->exception_index = -1; *last_tb = NULL; } /* The target hook may have updated the 'cpu->interrupt_request'; @@ -606,7 +607,9 @@ static inline bool cpu_handle_interrupt(CPUState *cpu, if (unlikely(atomic_read(&cpu->exit_request) || (use_icount && cpu->icount_decr.u16.low + cpu->icount_extra == 0))) { atomic_set(&cpu->exit_request, 0); - cpu->exception_index = EXCP_INTERRUPT; + if (cpu->exception_index == -1) { + cpu->exception_index = EXCP_INTERRUPT; + } return true; } diff --git a/accel/tcg/translate-all.c b/accel/tcg/translate-all.c index 67795cd78c..5ad1b919bc 100644 --- a/accel/tcg/translate-all.c +++ b/accel/tcg/translate-all.c @@ -1728,7 +1728,8 @@ void cpu_io_recompile(CPUState *cpu, uintptr_t retaddr) CPUArchState *env = cpu->env_ptr; #endif TranslationBlock *tb; - uint32_t n; + uint32_t n, flags; + target_ulong pc, cs_base; tb_lock(); tb = tb_find_pc(retaddr); @@ -1766,8 +1767,14 @@ void cpu_io_recompile(CPUState *cpu, uintptr_t retaddr) cpu_abort(cpu, "TB too big during recompile"); } - /* Adjust the execution state of the next TB. */ - cpu->cflags_next_tb = curr_cflags() | CF_LAST_IO | n; + pc = tb->pc; + cs_base = tb->cs_base; + flags = tb->flags; + tb_phys_invalidate(tb, -1); + + /* Execute one IO instruction without caching + instead of creating large TB. */ + cpu->cflags_next_tb = curr_cflags() | CF_LAST_IO | CF_NOCACHE | 1; if (tb->cflags & CF_NOCACHE) { if (tb->orig_tb) { @@ -1778,6 +1785,11 @@ void cpu_io_recompile(CPUState *cpu, uintptr_t retaddr) tb_remove(tb); } + /* Generate new TB instead of the current one. */ + /* FIXME: In theory this could raise an exception. In practice + we have already translated the block once so it's probably ok. */ + tb_gen_code(cpu, pc, cs_base, flags, curr_cflags() | CF_LAST_IO | n); + /* TODO: If env->pc != tb->pc (i.e. the faulting instruction was not * the first in the TB) then we end up generating a whole new TB and * repeating the fault, which is horribly inefficient. diff --git a/block/block-backend.c b/block/block-backend.c index f2e0a855ff..681b240b12 100644 --- a/block/block-backend.c +++ b/block/block-backend.c @@ -31,6 +31,13 @@ static AioContext *blk_aiocb_get_aio_context(BlockAIOCB *acb); +typedef struct BlockBackendAioNotifier { + void (*attached_aio_context)(AioContext *new_context, void *opaque); + void (*detach_aio_context)(void *opaque); + void *opaque; + QLIST_ENTRY(BlockBackendAioNotifier) list; +} BlockBackendAioNotifier; + struct BlockBackend { char *name; int refcnt; @@ -69,6 +76,7 @@ struct BlockBackend { bool allow_write_beyond_eof; NotifierList remove_bs_notifiers, insert_bs_notifiers; + QLIST_HEAD(, BlockBackendAioNotifier) aio_notifiers; int quiesce_counter; VMChangeStateEntry *vmsh; @@ -247,6 +255,36 @@ static int blk_root_inactivate(BdrvChild *child) return 0; } +static void blk_root_attach(BdrvChild *child) +{ + BlockBackend *blk = child->opaque; + BlockBackendAioNotifier *notifier; + + trace_blk_root_attach(child, blk, child->bs); + + QLIST_FOREACH(notifier, &blk->aio_notifiers, list) { + bdrv_add_aio_context_notifier(child->bs, + notifier->attached_aio_context, + notifier->detach_aio_context, + notifier->opaque); + } +} + +static void blk_root_detach(BdrvChild *child) +{ + BlockBackend *blk = child->opaque; + BlockBackendAioNotifier *notifier; + + trace_blk_root_detach(child, blk, child->bs); + + QLIST_FOREACH(notifier, &blk->aio_notifiers, list) { + bdrv_remove_aio_context_notifier(child->bs, + notifier->attached_aio_context, + notifier->detach_aio_context, + notifier->opaque); + } +} + static const BdrvChildRole child_root = { .inherit_options = blk_root_inherit_options, @@ -260,6 +298,9 @@ static const BdrvChildRole child_root = { .activate = blk_root_activate, .inactivate = blk_root_inactivate, + + .attach = blk_root_attach, + .detach = blk_root_detach, }; /* @@ -287,6 +328,7 @@ BlockBackend *blk_new(uint64_t perm, uint64_t shared_perm) notifier_list_init(&blk->remove_bs_notifiers); notifier_list_init(&blk->insert_bs_notifiers); + QLIST_INIT(&blk->aio_notifiers); QTAILQ_INSERT_TAIL(&block_backends, blk, link); return blk; @@ -364,6 +406,7 @@ static void blk_delete(BlockBackend *blk) } assert(QLIST_EMPTY(&blk->remove_bs_notifiers.notifiers)); assert(QLIST_EMPTY(&blk->insert_bs_notifiers.notifiers)); + assert(QLIST_EMPTY(&blk->aio_notifiers)); QTAILQ_REMOVE(&block_backends, blk, link); drive_info_del(blk->legacy_dinfo); block_acct_cleanup(&blk->stats); @@ -1857,8 +1900,15 @@ void blk_add_aio_context_notifier(BlockBackend *blk, void (*attached_aio_context)(AioContext *new_context, void *opaque), void (*detach_aio_context)(void *opaque), void *opaque) { + BlockBackendAioNotifier *notifier; BlockDriverState *bs = blk_bs(blk); + notifier = g_new(BlockBackendAioNotifier, 1); + notifier->attached_aio_context = attached_aio_context; + notifier->detach_aio_context = detach_aio_context; + notifier->opaque = opaque; + QLIST_INSERT_HEAD(&blk->aio_notifiers, notifier, list); + if (bs) { bdrv_add_aio_context_notifier(bs, attached_aio_context, detach_aio_context, opaque); @@ -1871,12 +1921,25 @@ void blk_remove_aio_context_notifier(BlockBackend *blk, void (*detach_aio_context)(void *), void *opaque) { + BlockBackendAioNotifier *notifier; BlockDriverState *bs = blk_bs(blk); if (bs) { bdrv_remove_aio_context_notifier(bs, attached_aio_context, detach_aio_context, opaque); } + + QLIST_FOREACH(notifier, &blk->aio_notifiers, list) { + if (notifier->attached_aio_context == attached_aio_context && + notifier->detach_aio_context == detach_aio_context && + notifier->opaque == opaque) { + QLIST_REMOVE(notifier, list); + g_free(notifier); + return; + } + } + + abort(); } void blk_add_remove_bs_notifier(BlockBackend *blk, Notifier *notify) diff --git a/block/dirty-bitmap.c b/block/dirty-bitmap.c index 909f0517f8..967159479d 100644 --- a/block/dirty-bitmap.c +++ b/block/dirty-bitmap.c @@ -40,6 +40,8 @@ struct BdrvDirtyBitmap { QemuMutex *mutex; HBitmap *bitmap; /* Dirty bitmap implementation */ HBitmap *meta; /* Meta dirty bitmap */ + bool qmp_locked; /* Bitmap is locked, it can't be modified + through QMP */ BdrvDirtyBitmap *successor; /* Anonymous child; implies frozen status */ char *name; /* Optional non-empty unique ID */ int64_t size; /* Size of the bitmap, in bytes */ @@ -183,6 +185,18 @@ bool bdrv_dirty_bitmap_frozen(BdrvDirtyBitmap *bitmap) return bitmap->successor; } +void bdrv_dirty_bitmap_set_qmp_locked(BdrvDirtyBitmap *bitmap, bool qmp_locked) +{ + qemu_mutex_lock(bitmap->mutex); + bitmap->qmp_locked = qmp_locked; + qemu_mutex_unlock(bitmap->mutex); +} + +bool bdrv_dirty_bitmap_qmp_locked(BdrvDirtyBitmap *bitmap) +{ + return bitmap->qmp_locked; +} + /* Called with BQL taken. */ bool bdrv_dirty_bitmap_enabled(BdrvDirtyBitmap *bitmap) { @@ -194,6 +208,8 @@ DirtyBitmapStatus bdrv_dirty_bitmap_status(BdrvDirtyBitmap *bitmap) { if (bdrv_dirty_bitmap_frozen(bitmap)) { return DIRTY_BITMAP_STATUS_FROZEN; + } else if (bdrv_dirty_bitmap_qmp_locked(bitmap)) { + return DIRTY_BITMAP_STATUS_LOCKED; } else if (!bdrv_dirty_bitmap_enabled(bitmap)) { return DIRTY_BITMAP_STATUS_DISABLED; } else { @@ -234,6 +250,59 @@ int bdrv_dirty_bitmap_create_successor(BlockDriverState *bs, return 0; } +/* Called with BQL taken. */ +void bdrv_dirty_bitmap_enable_successor(BdrvDirtyBitmap *bitmap) +{ + qemu_mutex_lock(bitmap->mutex); + bdrv_enable_dirty_bitmap(bitmap->successor); + qemu_mutex_unlock(bitmap->mutex); +} + +/* Called within bdrv_dirty_bitmap_lock..unlock */ +static void bdrv_do_release_matching_dirty_bitmap_locked( + BlockDriverState *bs, BdrvDirtyBitmap *bitmap, + bool (*cond)(BdrvDirtyBitmap *bitmap)) +{ + BdrvDirtyBitmap *bm, *next; + + QLIST_FOREACH_SAFE(bm, &bs->dirty_bitmaps, list, next) { + if ((!bitmap || bm == bitmap) && (!cond || cond(bm))) { + 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); + g_free(bm); + + if (bitmap) { + return; + } + } + } + + if (bitmap) { + abort(); + } +} + +/* Called with BQL taken. */ +static void bdrv_do_release_matching_dirty_bitmap( + BlockDriverState *bs, BdrvDirtyBitmap *bitmap, + bool (*cond)(BdrvDirtyBitmap *bitmap)) +{ + bdrv_dirty_bitmaps_lock(bs); + bdrv_do_release_matching_dirty_bitmap_locked(bs, bitmap, cond); + bdrv_dirty_bitmaps_unlock(bs); +} + +/* Called within bdrv_dirty_bitmap_lock..unlock */ +static void bdrv_release_dirty_bitmap_locked(BlockDriverState *bs, + BdrvDirtyBitmap *bitmap) +{ + bdrv_do_release_matching_dirty_bitmap_locked(bs, bitmap, NULL); +} + /** * For a bitmap with a successor, yield our name to the successor, * delete the old bitmap, and return a handle to the new bitmap. @@ -267,11 +336,11 @@ BdrvDirtyBitmap *bdrv_dirty_bitmap_abdicate(BlockDriverState *bs, * In cases of failure where we can no longer safely delete the parent, * we may wish to re-join the parent and child/successor. * The merged parent will be un-frozen, but not explicitly re-enabled. - * Called with BQL taken. + * Called within bdrv_dirty_bitmap_lock..unlock and with BQL taken. */ -BdrvDirtyBitmap *bdrv_reclaim_dirty_bitmap(BlockDriverState *bs, - BdrvDirtyBitmap *parent, - Error **errp) +BdrvDirtyBitmap *bdrv_reclaim_dirty_bitmap_locked(BlockDriverState *bs, + BdrvDirtyBitmap *parent, + Error **errp) { BdrvDirtyBitmap *successor = parent->successor; @@ -284,12 +353,26 @@ BdrvDirtyBitmap *bdrv_reclaim_dirty_bitmap(BlockDriverState *bs, error_setg(errp, "Merging of parent and successor bitmap failed"); return NULL; } - bdrv_release_dirty_bitmap(bs, successor); + bdrv_release_dirty_bitmap_locked(bs, successor); parent->successor = NULL; return parent; } +/* Called with BQL taken. */ +BdrvDirtyBitmap *bdrv_reclaim_dirty_bitmap(BlockDriverState *bs, + BdrvDirtyBitmap *parent, + Error **errp) +{ + BdrvDirtyBitmap *ret; + + qemu_mutex_lock(parent->mutex); + ret = bdrv_reclaim_dirty_bitmap_locked(bs, parent, errp); + qemu_mutex_unlock(parent->mutex); + + return ret; +} + /** * Truncates _all_ bitmaps attached to a BDS. * Called with BQL taken. @@ -314,36 +397,6 @@ static bool bdrv_dirty_bitmap_has_name(BdrvDirtyBitmap *bitmap) } /* Called with BQL taken. */ -static void bdrv_do_release_matching_dirty_bitmap( - BlockDriverState *bs, BdrvDirtyBitmap *bitmap, - bool (*cond)(BdrvDirtyBitmap *bitmap)) -{ - BdrvDirtyBitmap *bm, *next; - bdrv_dirty_bitmaps_lock(bs); - QLIST_FOREACH_SAFE(bm, &bs->dirty_bitmaps, list, next) { - if ((!bitmap || bm == bitmap) && (!cond || cond(bm))) { - 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); - g_free(bm); - - if (bitmap) { - goto out; - } - } - } - if (bitmap) { - abort(); - } - -out: - bdrv_dirty_bitmaps_unlock(bs); -} - -/* Called with BQL taken. */ void bdrv_release_dirty_bitmap(BlockDriverState *bs, BdrvDirtyBitmap *bitmap) { bdrv_do_release_matching_dirty_bitmap(bs, bitmap, NULL); diff --git a/block/gluster.c b/block/gluster.c index 63d3c37d4c..296e036b3d 100644 --- a/block/gluster.c +++ b/block/gluster.c @@ -665,7 +665,7 @@ static int qemu_gluster_parse(BlockdevOptionsGluster *gconf, if (filename) { ret = qemu_gluster_parse_uri(gconf, filename); if (ret < 0) { - error_setg(errp, "invalid URI"); + error_setg(errp, "invalid URI %s", filename); error_append_hint(errp, "Usage: file=gluster[+transport]://" "[host[:port]]volume/path[?socket=...]" "[,file.debug=N]" diff --git a/block/nbd-client.c b/block/nbd-client.c index 7b68499b76..e64e346d69 100644 --- a/block/nbd-client.c +++ b/block/nbd-client.c @@ -228,6 +228,48 @@ static int nbd_parse_offset_hole_payload(NBDStructuredReplyChunk *chunk, return 0; } +/* nbd_parse_blockstatus_payload + * support only one extent in reply and only for + * base:allocation context + */ +static int nbd_parse_blockstatus_payload(NBDClientSession *client, + NBDStructuredReplyChunk *chunk, + uint8_t *payload, uint64_t orig_length, + NBDExtent *extent, Error **errp) +{ + uint32_t context_id; + + if (chunk->length != sizeof(context_id) + sizeof(extent)) { + error_setg(errp, "Protocol error: invalid payload for " + "NBD_REPLY_TYPE_BLOCK_STATUS"); + return -EINVAL; + } + + context_id = payload_advance32(&payload); + if (client->info.meta_base_allocation_id != context_id) { + error_setg(errp, "Protocol error: unexpected context id %d for " + "NBD_REPLY_TYPE_BLOCK_STATUS, when negotiated context " + "id is %d", context_id, + client->info.meta_base_allocation_id); + return -EINVAL; + } + + extent->length = payload_advance32(&payload); + extent->flags = payload_advance32(&payload); + + if (extent->length == 0 || + (client->info.min_block && !QEMU_IS_ALIGNED(extent->length, + client->info.min_block)) || + extent->length > orig_length) + { + error_setg(errp, "Protocol error: server sent status chunk with " + "invalid length"); + return -EINVAL; + } + + return 0; +} + /* nbd_parse_error_payload * on success @errp contains message describing nbd error reply */ @@ -481,6 +523,7 @@ static coroutine_fn int nbd_co_receive_one_chunk( typedef struct NBDReplyChunkIter { int ret; + bool fatal; Error *err; bool done, only_structured; } NBDReplyChunkIter; @@ -490,11 +533,12 @@ static void nbd_iter_error(NBDReplyChunkIter *iter, bool fatal, { assert(ret < 0); - if (fatal || iter->ret == 0) { + if ((fatal && !iter->fatal) || iter->ret == 0) { if (iter->ret != 0) { error_free(iter->err); iter->err = NULL; } + iter->fatal = fatal; iter->ret = ret; error_propagate(&iter->err, *local_err); } else { @@ -640,6 +684,68 @@ static int nbd_co_receive_cmdread_reply(NBDClientSession *s, uint64_t handle, return iter.ret; } +static int nbd_co_receive_blockstatus_reply(NBDClientSession *s, + uint64_t handle, uint64_t length, + NBDExtent *extent, Error **errp) +{ + NBDReplyChunkIter iter; + NBDReply reply; + void *payload = NULL; + Error *local_err = NULL; + bool received = false; + + assert(!extent->length); + NBD_FOREACH_REPLY_CHUNK(s, iter, handle, s->info.structured_reply, + NULL, &reply, &payload) + { + int ret; + NBDStructuredReplyChunk *chunk = &reply.structured; + + assert(nbd_reply_is_structured(&reply)); + + switch (chunk->type) { + case NBD_REPLY_TYPE_BLOCK_STATUS: + if (received) { + s->quit = true; + error_setg(&local_err, "Several BLOCK_STATUS chunks in reply"); + nbd_iter_error(&iter, true, -EINVAL, &local_err); + } + received = true; + + ret = nbd_parse_blockstatus_payload(s, &reply.structured, + payload, length, extent, + &local_err); + if (ret < 0) { + s->quit = true; + nbd_iter_error(&iter, true, ret, &local_err); + } + break; + default: + if (!nbd_reply_type_is_error(chunk->type)) { + s->quit = true; + error_setg(&local_err, + "Unexpected reply type: %d (%s) " + "for CMD_BLOCK_STATUS", + chunk->type, nbd_reply_type_lookup(chunk->type)); + nbd_iter_error(&iter, true, -EINVAL, &local_err); + } + } + + g_free(payload); + payload = NULL; + } + + if (!extent->length && !iter.err) { + error_setg(&iter.err, + "Server did not reply with any status extents"); + if (!iter.ret) { + iter.ret = -EIO; + } + } + error_propagate(errp, iter.err); + return iter.ret; +} + static int nbd_co_request(BlockDriverState *bs, NBDRequest *request, QEMUIOVector *write_qiov) { @@ -782,6 +888,51 @@ int nbd_client_co_pdiscard(BlockDriverState *bs, int64_t offset, int bytes) return nbd_co_request(bs, &request, NULL); } +int coroutine_fn nbd_client_co_block_status(BlockDriverState *bs, + bool want_zero, + int64_t offset, int64_t bytes, + int64_t *pnum, int64_t *map, + BlockDriverState **file) +{ + int64_t ret; + NBDExtent extent = { 0 }; + NBDClientSession *client = nbd_get_client_session(bs); + Error *local_err = NULL; + + NBDRequest request = { + .type = NBD_CMD_BLOCK_STATUS, + .from = offset, + .len = MIN(MIN_NON_ZERO(QEMU_ALIGN_DOWN(INT_MAX, + bs->bl.request_alignment), + client->info.max_block), bytes), + .flags = NBD_CMD_FLAG_REQ_ONE, + }; + + if (!client->info.base_allocation) { + *pnum = bytes; + return BDRV_BLOCK_DATA; + } + + ret = nbd_co_send_request(bs, &request, NULL); + if (ret < 0) { + return ret; + } + + ret = nbd_co_receive_blockstatus_reply(client, request.handle, bytes, + &extent, &local_err); + if (local_err) { + error_report_err(local_err); + } + if (ret < 0) { + return ret; + } + + assert(extent.length); + *pnum = extent.length; + return (extent.flags & NBD_STATE_HOLE ? 0 : BDRV_BLOCK_DATA) | + (extent.flags & NBD_STATE_ZERO ? BDRV_BLOCK_ZERO : 0); +} + void nbd_client_detach_aio_context(BlockDriverState *bs) { NBDClientSession *client = nbd_get_client_session(bs); @@ -826,6 +977,7 @@ int nbd_client_init(BlockDriverState *bs, client->info.request_sizes = true; client->info.structured_reply = true; + client->info.base_allocation = true; ret = nbd_receive_negotiate(QIO_CHANNEL(sioc), export, tlscreds, hostname, &client->ioc, &client->info, errp); diff --git a/block/nbd-client.h b/block/nbd-client.h index 612c4c21a0..0ece76e5af 100644 --- a/block/nbd-client.h +++ b/block/nbd-client.h @@ -61,4 +61,10 @@ void nbd_client_detach_aio_context(BlockDriverState *bs); void nbd_client_attach_aio_context(BlockDriverState *bs, AioContext *new_context); +int coroutine_fn nbd_client_co_block_status(BlockDriverState *bs, + bool want_zero, + int64_t offset, int64_t bytes, + int64_t *pnum, int64_t *map, + BlockDriverState **file); + #endif /* NBD_CLIENT_H */ diff --git a/block/nbd.c b/block/nbd.c index d4e4172c08..1e2b3ba2d3 100644 --- a/block/nbd.c +++ b/block/nbd.c @@ -585,6 +585,7 @@ static BlockDriver bdrv_nbd = { .bdrv_detach_aio_context = nbd_detach_aio_context, .bdrv_attach_aio_context = nbd_attach_aio_context, .bdrv_refresh_filename = nbd_refresh_filename, + .bdrv_co_block_status = nbd_client_co_block_status, }; static BlockDriver bdrv_nbd_tcp = { @@ -604,6 +605,7 @@ static BlockDriver bdrv_nbd_tcp = { .bdrv_detach_aio_context = nbd_detach_aio_context, .bdrv_attach_aio_context = nbd_attach_aio_context, .bdrv_refresh_filename = nbd_refresh_filename, + .bdrv_co_block_status = nbd_client_co_block_status, }; static BlockDriver bdrv_nbd_unix = { @@ -623,6 +625,7 @@ static BlockDriver bdrv_nbd_unix = { .bdrv_detach_aio_context = nbd_detach_aio_context, .bdrv_attach_aio_context = nbd_attach_aio_context, .bdrv_refresh_filename = nbd_refresh_filename, + .bdrv_co_block_status = nbd_client_co_block_status, }; static void bdrv_nbd_init(void) diff --git a/block/sheepdog.c b/block/sheepdog.c index 8680b2926f..797ea5953b 100644 --- a/block/sheepdog.c +++ b/block/sheepdog.c @@ -1036,7 +1036,7 @@ static void sd_parse_uri(SheepdogConfig *cfg, const char *filename, cfg->uri = uri = uri_parse(filename); if (!uri) { - error_setg(&err, "invalid URI"); + error_setg(&err, "invalid URI '%s'", filename); goto out; } diff --git a/block/trace-events b/block/trace-events index 02dd80ff0c..7493d521dc 100644 --- a/block/trace-events +++ b/block/trace-events @@ -7,6 +7,8 @@ bdrv_lock_medium(void *bs, bool locked) "bs %p locked %d" # block/block-backend.c blk_co_preadv(void *blk, void *bs, int64_t offset, unsigned int bytes, int flags) "blk %p bs %p offset %"PRId64" bytes %u flags 0x%x" blk_co_pwritev(void *blk, void *bs, int64_t offset, unsigned int bytes, int flags) "blk %p bs %p offset %"PRId64" bytes %u flags 0x%x" +blk_root_attach(void *child, void *blk, void *bs) "child %p blk %p bs %p" +blk_root_detach(void *child, void *blk, void *bs) "child %p blk %p bs %p" # block/io.c bdrv_co_preadv(void *bs, int64_t offset, int64_t nbytes, unsigned int flags) "bs %p offset %"PRId64" nbytes %"PRId64" flags 0x%x" diff --git a/blockdev.c b/blockdev.c index 1fbfd3a2c4..b9de18f3b2 100644 --- a/blockdev.c +++ b/blockdev.c @@ -2118,6 +2118,9 @@ static void block_dirty_bitmap_clear_prepare(BlkActionState *common, if (bdrv_dirty_bitmap_frozen(state->bitmap)) { error_setg(errp, "Cannot modify a frozen bitmap"); return; + } else if (bdrv_dirty_bitmap_qmp_locked(state->bitmap)) { + error_setg(errp, "Cannot modify a locked bitmap"); + return; } else if (!bdrv_dirty_bitmap_enabled(state->bitmap)) { error_setg(errp, "Cannot clear a disabled bitmap"); return; @@ -2862,6 +2865,11 @@ void qmp_block_dirty_bitmap_remove(const char *node, const char *name, "Bitmap '%s' is currently frozen and cannot be removed", name); return; + } else if (bdrv_dirty_bitmap_qmp_locked(bitmap)) { + error_setg(errp, + "Bitmap '%s' is currently locked and cannot be removed", + name); + return; } if (bdrv_dirty_bitmap_get_persistance(bitmap)) { @@ -2896,6 +2904,11 @@ void qmp_block_dirty_bitmap_clear(const char *node, const char *name, "Bitmap '%s' is currently frozen and cannot be modified", name); return; + } else if (bdrv_dirty_bitmap_qmp_locked(bitmap)) { + error_setg(errp, + "Bitmap '%s' is currently locked and cannot be modified", + name); + return; } else if (!bdrv_dirty_bitmap_enabled(bitmap)) { error_setg(errp, "Bitmap '%s' is currently disabled and cannot be cleared", @@ -3370,6 +3383,12 @@ static BlockJob *do_drive_backup(DriveBackup *backup, BlockJobTxn *txn, bdrv_unref(target_bs); goto out; } + if (bdrv_dirty_bitmap_qmp_locked(bmap)) { + error_setg(errp, + "Bitmap '%s' is currently locked and cannot be used for " + "backup", backup->bitmap); + goto out; + } } job = backup_job_create(backup->job_id, bs, target_bs, backup->speed, diff --git a/bsd-user/main.c b/bsd-user/main.c index efef5ff8c5..05aa5594a9 100644 --- a/bsd-user/main.c +++ b/bsd-user/main.c @@ -649,7 +649,7 @@ void cpu_loop(CPUSPARCState *env) static void usage(void) { - printf("qemu-" TARGET_NAME " version " QEMU_VERSION QEMU_PKGVERSION + printf("qemu-" TARGET_NAME " version " QEMU_FULL_VERSION "\n" QEMU_COPYRIGHT "\n" "usage: qemu-" TARGET_NAME " [options] program [arguments...]\n" "BSD CPU emulator (compiled for %s emulation)\n" diff --git a/chardev/char-mux.c b/chardev/char-mux.c index d48e78103a..1b925c8dec 100644 --- a/chardev/char-mux.c +++ b/chardev/char-mux.c @@ -27,6 +27,7 @@ #include "qemu/option.h" #include "chardev/char.h" #include "sysemu/block-backend.h" +#include "sysemu/sysemu.h" #include "chardev/char-mux.h" /* MUX driver for serial I/O splitting */ @@ -230,14 +231,12 @@ static void mux_chr_read(void *opaque, const uint8_t *buf, int size) } } -bool muxes_realized; - void mux_chr_send_all_event(Chardev *chr, int event) { MuxChardev *d = MUX_CHARDEV(chr); int i; - if (!muxes_realized) { + if (!machine_init_done) { return; } @@ -327,7 +326,7 @@ static void qemu_chr_open_mux(Chardev *chr, /* only default to opened state if we've realized the initial * set of muxes */ - *be_opened = muxes_realized; + *be_opened = machine_init_done; qemu_chr_fe_init(&d->chr, drv, errp); } @@ -347,6 +346,31 @@ static void qemu_chr_parse_mux(QemuOpts *opts, ChardevBackend *backend, mux->chardev = g_strdup(chardev); } +/** + * Called after processing of default and command-line-specified + * chardevs to deliver CHR_EVENT_OPENED events to any FEs attached + * to a mux chardev. This is done here to ensure that + * output/prompts/banners are only displayed for the FE that has + * focus when initial command-line processing/machine init is + * completed. + * + * After this point, any new FE attached to any new or existing + * mux will receive CHR_EVENT_OPENED notifications for the BE + * immediately. + */ +static int open_muxes(Chardev *chr) +{ + /* send OPENED to all already-attached FEs */ + mux_chr_send_all_event(chr, CHR_EVENT_OPENED); + /* + * mark mux as OPENED so any new FEs will immediately receive + * OPENED event + */ + qemu_chr_be_event(chr, CHR_EVENT_OPENED); + + return 0; +} + static void char_mux_class_init(ObjectClass *oc, void *data) { ChardevClass *cc = CHARDEV_CLASS(oc); @@ -357,6 +381,7 @@ static void char_mux_class_init(ObjectClass *oc, void *data) cc->chr_accept_input = mux_chr_accept_input; cc->chr_add_watch = mux_chr_add_watch; cc->chr_be_event = mux_chr_be_event; + cc->chr_machine_done = open_muxes; } static const TypeInfo char_mux_type_info = { diff --git a/chardev/char-socket.c b/chardev/char-socket.c index a220803c01..d92c5aee73 100644 --- a/chardev/char-socket.c +++ b/chardev/char-socket.c @@ -41,6 +41,11 @@ #define TCP_MAX_FDS 16 typedef struct { + char buf[21]; + size_t buflen; +} TCPChardevTelnetInit; + +typedef struct { Chardev parent; QIOChannel *ioc; /* Client I/O channel */ QIOChannelSocket *sioc; /* Client master channel */ @@ -60,6 +65,8 @@ typedef struct { bool is_listen; bool is_telnet; bool is_tn3270; + GSource *telnet_source; + TCPChardevTelnetInit *telnet_init; GSource *reconnect_timer; int64_t reconnect_time; @@ -70,6 +77,7 @@ typedef struct { OBJECT_CHECK(SocketChardev, (obj), TYPE_CHARDEV_SOCKET) static gboolean socket_reconnect_timeout(gpointer opaque); +static void tcp_chr_telnet_init(Chardev *chr); static void tcp_chr_reconn_timer_cancel(SocketChardev *s) { @@ -423,8 +431,8 @@ static void tcp_chr_disconnect(Chardev *chr) tcp_chr_free_connection(chr); if (s->listener) { - qio_net_listener_set_client_func(s->listener, tcp_chr_accept, - chr, NULL); + qio_net_listener_set_client_func_full(s->listener, tcp_chr_accept, + chr, NULL, chr->gcontext); } update_disconnected_filename(s); if (emit_close) { @@ -450,7 +458,7 @@ static gboolean tcp_chr_read(QIOChannel *chan, GIOCondition cond, void *opaque) len = s->max_size; } size = tcp_chr_recv(chr, (void *)buf, len); - if (size == 0 || size == -1) { + if (size == 0 || (size == -1 && errno != EAGAIN)) { /* connection closed */ tcp_chr_disconnect(chr); } else if (size > 0) { @@ -556,10 +564,33 @@ static void tcp_chr_connect(void *opaque) qemu_chr_be_event(chr, CHR_EVENT_OPENED); } +static void tcp_chr_telnet_destroy(SocketChardev *s) +{ + if (s->telnet_source) { + g_source_destroy(s->telnet_source); + g_source_unref(s->telnet_source); + s->telnet_source = NULL; + } +} + static void tcp_chr_update_read_handler(Chardev *chr) { SocketChardev *s = SOCKET_CHARDEV(chr); + if (s->listener) { + /* + * It's possible that chardev context is changed in + * qemu_chr_be_update_read_handlers(). Reset it for QIO net + * listener if there is. + */ + qio_net_listener_set_client_func_full(s->listener, tcp_chr_accept, + chr, NULL, chr->gcontext); + } + + if (s->telnet_source) { + tcp_chr_telnet_init(CHARDEV(s)); + } + if (!s->connected) { return; } @@ -573,32 +604,30 @@ static void tcp_chr_update_read_handler(Chardev *chr) } } -typedef struct { - Chardev *chr; - char buf[21]; - size_t buflen; -} TCPChardevTelnetInit; - static gboolean tcp_chr_telnet_init_io(QIOChannel *ioc, GIOCondition cond G_GNUC_UNUSED, gpointer user_data) { - TCPChardevTelnetInit *init = user_data; + SocketChardev *s = user_data; + Chardev *chr = CHARDEV(s); + TCPChardevTelnetInit *init = s->telnet_init; ssize_t ret; + assert(init); + ret = qio_channel_write(ioc, init->buf, init->buflen, NULL); if (ret < 0) { if (ret == QIO_CHANNEL_ERR_BLOCK) { ret = 0; } else { - tcp_chr_disconnect(init->chr); + tcp_chr_disconnect(chr); goto end; } } init->buflen -= ret; if (init->buflen == 0) { - tcp_chr_connect(init->chr); + tcp_chr_connect(chr); goto end; } @@ -607,16 +636,30 @@ static gboolean tcp_chr_telnet_init_io(QIOChannel *ioc, return G_SOURCE_CONTINUE; end: - g_free(init); + g_free(s->telnet_init); + s->telnet_init = NULL; + g_source_unref(s->telnet_source); + s->telnet_source = NULL; return G_SOURCE_REMOVE; } static void tcp_chr_telnet_init(Chardev *chr) { SocketChardev *s = SOCKET_CHARDEV(chr); - TCPChardevTelnetInit *init = g_new0(TCPChardevTelnetInit, 1); + TCPChardevTelnetInit *init; size_t n = 0; + /* Destroy existing task */ + tcp_chr_telnet_destroy(s); + + if (s->telnet_init) { + /* We are possibly during a handshake already */ + goto cont; + } + + s->telnet_init = g_new0(TCPChardevTelnetInit, 1); + init = s->telnet_init; + #define IACSET(x, a, b, c) \ do { \ x[n++] = a; \ @@ -624,7 +667,6 @@ static void tcp_chr_telnet_init(Chardev *chr) x[n++] = c; \ } while (0) - init->chr = chr; if (!s->is_tn3270) { init->buflen = 12; /* Prep the telnet negotion to put telnet in binary, @@ -647,10 +689,11 @@ static void tcp_chr_telnet_init(Chardev *chr) #undef IACSET - qio_channel_add_watch( - s->ioc, G_IO_OUT, - tcp_chr_telnet_init_io, - init, NULL); +cont: + s->telnet_source = qio_channel_add_watch_source(s->ioc, G_IO_OUT, + tcp_chr_telnet_init_io, + s, NULL, + chr->gcontext); } @@ -663,8 +706,7 @@ static void tcp_chr_tls_handshake(QIOTask *task, if (qio_task_propagate_error(task, NULL)) { tcp_chr_disconnect(chr); } else { - /* tn3270 does not support TLS yet */ - if (s->do_telnetopt && !s->is_tn3270) { + if (s->do_telnetopt) { tcp_chr_telnet_init(chr); } else { tcp_chr_connect(chr); @@ -708,7 +750,7 @@ static void tcp_chr_tls_init(Chardev *chr) tcp_chr_tls_handshake, chr, NULL, - NULL); + chr->gcontext); } @@ -744,7 +786,8 @@ static int tcp_chr_new_client(Chardev *chr, QIOChannelSocket *sioc) qio_channel_set_delay(s->ioc, false); } if (s->listener) { - qio_net_listener_set_client_func(s->listener, NULL, NULL, NULL); + qio_net_listener_set_client_func_full(s->listener, NULL, NULL, + NULL, chr->gcontext); } if (s->tls_creds) { @@ -824,8 +867,11 @@ static void char_socket_finalize(Object *obj) tcp_chr_free_connection(chr); tcp_chr_reconn_timer_cancel(s); qapi_free_SocketAddress(s->addr); + tcp_chr_telnet_destroy(s); + g_free(s->telnet_init); if (s->listener) { - qio_net_listener_set_client_func(s->listener, NULL, NULL, NULL); + qio_net_listener_set_client_func_full(s->listener, NULL, NULL, + NULL, chr->gcontext); object_unref(OBJECT(s->listener)); } if (s->tls_creds) { @@ -855,11 +901,22 @@ cleanup: object_unref(OBJECT(sioc)); } +static void tcp_chr_connect_async(Chardev *chr) +{ + SocketChardev *s = SOCKET_CHARDEV(chr); + QIOChannelSocket *sioc; + + sioc = qio_channel_socket_new(); + tcp_chr_set_client_ioc_name(chr, sioc); + qio_channel_socket_connect_async(sioc, s->addr, + qemu_chr_socket_connected, + chr, NULL, chr->gcontext); +} + static gboolean socket_reconnect_timeout(gpointer opaque) { Chardev *chr = CHARDEV(opaque); SocketChardev *s = SOCKET_CHARDEV(opaque); - QIOChannelSocket *sioc; g_source_unref(s->reconnect_timer); s->reconnect_timer = NULL; @@ -868,11 +925,7 @@ static gboolean socket_reconnect_timeout(gpointer opaque) return false; } - sioc = qio_channel_socket_new(); - tcp_chr_set_client_ioc_name(chr, sioc); - qio_channel_socket_connect_async(sioc, s->addr, - qemu_chr_socket_connected, - chr, NULL, NULL); + tcp_chr_connect_async(chr); return false; } @@ -951,13 +1004,8 @@ static void qmp_chardev_open_socket(Chardev *chr, s->reconnect_time = reconnect; } - if (s->reconnect_time) { - sioc = qio_channel_socket_new(); - tcp_chr_set_client_ioc_name(chr, sioc); - qio_channel_socket_connect_async(sioc, s->addr, - qemu_chr_socket_connected, - chr, NULL, NULL); - } else { + /* If reconnect_time is set, will do that in chr_machine_done. */ + if (!s->reconnect_time) { if (s->is_listen) { char *name; s->listener = qio_net_listener_new(); @@ -981,8 +1029,10 @@ static void qmp_chardev_open_socket(Chardev *chr, return; } if (!s->ioc) { - qio_net_listener_set_client_func(s->listener, tcp_chr_accept, - chr, NULL); + qio_net_listener_set_client_func_full(s->listener, + tcp_chr_accept, + chr, NULL, + chr->gcontext); } } else if (qemu_chr_wait_connected(chr, errp) < 0) { goto error; @@ -1009,25 +1059,36 @@ static void qemu_chr_parse_socket(QemuOpts *opts, ChardevBackend *backend, const char *path = qemu_opt_get(opts, "path"); const char *host = qemu_opt_get(opts, "host"); const char *port = qemu_opt_get(opts, "port"); + const char *fd = qemu_opt_get(opts, "fd"); const char *tls_creds = qemu_opt_get(opts, "tls-creds"); SocketAddressLegacy *addr; ChardevSocket *sock; + if ((!!path + !!fd + !!host) != 1) { + error_setg(errp, + "Exactly one of 'path', 'fd' or 'host' required"); + return; + } + backend->type = CHARDEV_BACKEND_KIND_SOCKET; - if (!path) { - if (!host) { - error_setg(errp, "chardev: socket: no host given"); + if (path) { + if (tls_creds) { + error_setg(errp, "TLS can only be used over TCP socket"); return; } + } else if (host) { if (!port) { error_setg(errp, "chardev: socket: no port given"); return; } - } else { - if (tls_creds) { - error_setg(errp, "TLS can only be used over TCP socket"); + } else if (fd) { + /* We don't know what host to validate against when in client mode */ + if (tls_creds && !is_listen) { + error_setg(errp, "TLS can not be used with pre-opened client FD"); return; } + } else { + g_assert_not_reached(); } sock = backend->u.socket.data = g_new0(ChardevSocket, 1); @@ -1053,7 +1114,7 @@ static void qemu_chr_parse_socket(QemuOpts *opts, ChardevBackend *backend, addr->type = SOCKET_ADDRESS_LEGACY_KIND_UNIX; q_unix = addr->u.q_unix.data = g_new0(UnixSocketAddress, 1); q_unix->path = g_strdup(path); - } else { + } else if (host) { addr->type = SOCKET_ADDRESS_LEGACY_KIND_INET; addr->u.inet.data = g_new(InetSocketAddress, 1); *addr->u.inet.data = (InetSocketAddress) { @@ -1066,6 +1127,12 @@ static void qemu_chr_parse_socket(QemuOpts *opts, ChardevBackend *backend, .has_ipv6 = qemu_opt_get(opts, "ipv6"), .ipv6 = qemu_opt_get_bool(opts, "ipv6", 0), }; + } else if (fd) { + addr->type = SOCKET_ADDRESS_LEGACY_KIND_FD; + addr->u.fd.data = g_new(String, 1); + addr->u.fd.data->str = g_strdup(fd); + } else { + g_assert_not_reached(); } sock->addr = addr; } @@ -1087,6 +1154,17 @@ char_socket_get_connected(Object *obj, Error **errp) return s->connected; } +static int tcp_chr_machine_done_hook(Chardev *chr) +{ + SocketChardev *s = SOCKET_CHARDEV(chr); + + if (s->reconnect_time) { + tcp_chr_connect_async(chr); + } + + return 0; +} + static void char_socket_class_init(ObjectClass *oc, void *data) { ChardevClass *cc = CHARDEV_CLASS(oc); @@ -1102,6 +1180,7 @@ static void char_socket_class_init(ObjectClass *oc, void *data) cc->chr_add_client = tcp_chr_add_client; cc->chr_add_watch = tcp_chr_add_watch; cc->chr_update_read_handler = tcp_chr_update_read_handler; + cc->chr_machine_done = tcp_chr_machine_done_hook; object_class_property_add(oc, "addr", "SocketAddress", char_socket_get_addr, NULL, diff --git a/chardev/char.c b/chardev/char.c index 5d7b079ef0..76d866e6fe 100644 --- a/chardev/char.c +++ b/chardev/char.c @@ -281,40 +281,31 @@ static const TypeInfo char_type_info = { .class_init = char_class_init, }; -/** - * Called after processing of default and command-line-specified - * chardevs to deliver CHR_EVENT_OPENED events to any FEs attached - * to a mux chardev. This is done here to ensure that - * output/prompts/banners are only displayed for the FE that has - * focus when initial command-line processing/machine init is - * completed. - * - * After this point, any new FE attached to any new or existing - * mux will receive CHR_EVENT_OPENED notifications for the BE - * immediately. - */ -static int open_muxes(Object *child, void *opaque) +static int chardev_machine_done_notify_one(Object *child, void *opaque) { - if (CHARDEV_IS_MUX(child)) { - /* send OPENED to all already-attached FEs */ - mux_chr_send_all_event(CHARDEV(child), CHR_EVENT_OPENED); - /* mark mux as OPENED so any new FEs will immediately receive - * OPENED event - */ - qemu_chr_be_event(CHARDEV(child), CHR_EVENT_OPENED); + Chardev *chr = (Chardev *)child; + ChardevClass *class = CHARDEV_GET_CLASS(chr); + + if (class->chr_machine_done) { + return class->chr_machine_done(chr); } return 0; } -static void muxes_realize_done(Notifier *notifier, void *unused) +static void chardev_machine_done_hook(Notifier *notifier, void *unused) { - muxes_realized = true; - object_child_foreach(get_chardevs_root(), open_muxes, NULL); + int ret = object_child_foreach(get_chardevs_root(), + chardev_machine_done_notify_one, NULL); + + if (ret) { + error_report("Failed to call chardev machine_done hooks"); + exit(1); + } } -static Notifier muxes_realize_notify = { - .notify = muxes_realize_done, +static Notifier chardev_machine_done_notify = { + .notify = chardev_machine_done_hook, }; static bool qemu_chr_is_busy(Chardev *s) @@ -808,6 +799,9 @@ QemuOptsList qemu_chardev_opts = { .name = "port", .type = QEMU_OPT_STRING, },{ + .name = "fd", + .type = QEMU_OPT_STRING, + },{ .name = "localaddr", .type = QEMU_OPT_STRING, },{ @@ -1118,7 +1112,7 @@ static void register_types(void) * as part of realize functions like serial_isa_realizefn when -nographic * is specified */ - qemu_add_machine_init_done_notifier(&muxes_realize_notify); + qemu_add_machine_init_done_notifier(&chardev_machine_done_notify); } type_init(register_types); @@ -342,7 +342,7 @@ attr="" libattr="" xfs="" tcg="yes" - +membarrier="" vhost_net="no" vhost_crypto="no" vhost_scsi="no" @@ -1161,9 +1161,13 @@ for opt do ;; --enable-attr) attr="yes" ;; + --disable-membarrier) membarrier="no" + ;; + --enable-membarrier) membarrier="yes" + ;; --disable-blobs) blobs="no" ;; - --with-pkgversion=*) pkgversion=" ($optarg)" + --with-pkgversion=*) pkgversion="$optarg" ;; --with-coroutine=*) coroutine="$optarg" ;; @@ -1577,6 +1581,7 @@ disabled with --disable-FEATURE, default is enabled if available: xen-pci-passthrough brlapi BrlAPI (Braile) curl curl connectivity + membarrier membarrier system call (for Linux 4.14+ or Windows) fdt fdt device tree bluez bluez stack connectivity kvm KVM acceleration support @@ -5139,6 +5144,37 @@ if compile_prog "" "" ; then fi ########################################## +# check for usable membarrier system call +if test "$membarrier" = "yes"; then + have_membarrier=no + if test "$mingw32" = "yes" ; then + have_membarrier=yes + elif test "$linux" = "yes" ; then + cat > $TMPC << EOF + #include <linux/membarrier.h> + #include <sys/syscall.h> + #include <unistd.h> + #include <stdlib.h> + int main(void) { + syscall(__NR_membarrier, MEMBARRIER_CMD_QUERY, 0); + syscall(__NR_membarrier, MEMBARRIER_CMD_SHARED, 0); + exit(0); + } +EOF + if compile_prog "" "" ; then + have_membarrier=yes + fi + fi + if test "$have_membarrier" = "no"; then + feature_not_found "membarrier" "membarrier system call not available" + fi +else + # Do not enable it by default even for Mingw32, because it doesn't + # work on Wine. + membarrier=no +fi + +########################################## # check if rtnetlink.h exists and is useful have_rtnetlink=no cat > $TMPC << EOF @@ -5764,6 +5800,7 @@ fi echo "malloc trim support $malloc_trim" echo "RDMA support $rdma" echo "fdt support $fdt" +echo "membarrier $membarrier" echo "preadv support $preadv" echo "fdatasync $fdatasync" echo "madvise $madvise" @@ -6251,6 +6288,9 @@ fi if test "$fdt" = "yes" ; then echo "CONFIG_FDT=y" >> $config_host_mak fi +if test "$membarrier" = "yes" ; then + echo "CONFIG_MEMBARRIER=y" >> $config_host_mak +fi if test "$signalfd" = "yes" ; then echo "CONFIG_SIGNALFD=y" >> $config_host_mak fi @@ -1317,6 +1317,8 @@ static void prepare_icount_for_run(CPUState *cpu) insns_left = MIN(0xffff, cpu->icount_budget); cpu->icount_decr.u16.low = insns_left; cpu->icount_extra = cpu->icount_budget - insns_left; + + replay_mutex_lock(); } } @@ -1332,6 +1334,8 @@ static void process_icount_data(CPUState *cpu) cpu->icount_budget = 0; replay_account_executed_instructions(); + + replay_mutex_unlock(); } } @@ -1346,11 +1350,9 @@ static int tcg_cpu_exec(CPUState *cpu) #ifdef CONFIG_PROFILER ti = profile_getclock(); #endif - qemu_mutex_unlock_iothread(); cpu_exec_start(cpu); ret = cpu_exec(cpu); cpu_exec_end(cpu); - qemu_mutex_lock_iothread(); #ifdef CONFIG_PROFILER tcg_time += profile_getclock() - ti; #endif @@ -1417,6 +1419,9 @@ static void *qemu_tcg_rr_cpu_thread_fn(void *arg) cpu->exit_request = 1; while (1) { + qemu_mutex_unlock_iothread(); + replay_mutex_lock(); + qemu_mutex_lock_iothread(); /* Account partial waits to QEMU_CLOCK_VIRTUAL. */ qemu_account_warp_timer(); @@ -1425,6 +1430,8 @@ static void *qemu_tcg_rr_cpu_thread_fn(void *arg) */ handle_icount_deadline(); + replay_mutex_unlock(); + if (!cpu) { cpu = first_cpu; } @@ -1440,11 +1447,13 @@ static void *qemu_tcg_rr_cpu_thread_fn(void *arg) if (cpu_can_run(cpu)) { int r; + qemu_mutex_unlock_iothread(); prepare_icount_for_run(cpu); r = tcg_cpu_exec(cpu); process_icount_data(cpu); + qemu_mutex_lock_iothread(); if (r == EXCP_DEBUG) { cpu_handle_guest_debug(cpu); @@ -1634,7 +1643,9 @@ static void *qemu_tcg_cpu_thread_fn(void *arg) while (1) { if (cpu_can_run(cpu)) { int r; + qemu_mutex_unlock_iothread(); r = tcg_cpu_exec(cpu); + qemu_mutex_lock_iothread(); switch (r) { case EXCP_DEBUG: cpu_handle_guest_debug(cpu); @@ -1781,12 +1792,21 @@ void pause_all_vcpus(void) } } + /* We need to drop the replay_lock so any vCPU threads woken up + * can finish their replay tasks + */ + replay_mutex_unlock(); + while (!all_vcpus_paused()) { qemu_cond_wait(&qemu_pause_cond, &qemu_global_mutex); CPU_FOREACH(cpu) { qemu_cpu_kick(cpu); } } + + qemu_mutex_unlock_iothread(); + replay_mutex_lock(); + qemu_mutex_lock_iothread(); } void cpu_resume(CPUState *cpu) diff --git a/default-configs/alpha-softmmu.mak b/default-configs/alpha-softmmu.mak index e0d75e3058..bbe361f01a 100644 --- a/default-configs/alpha-softmmu.mak +++ b/default-configs/alpha-softmmu.mak @@ -4,7 +4,12 @@ include pci.mak include usb.mak CONFIG_SERIAL=y CONFIG_SERIAL_ISA=y +CONFIG_I82374=y CONFIG_I8254=y +CONFIG_I8257=y +CONFIG_PARALLEL=y +CONFIG_PARALLEL_ISA=y +CONFIG_FDC=y CONFIG_PCKBD=y CONFIG_VGA_CIRRUS=y CONFIG_IDE_CORE=y diff --git a/default-configs/i386-softmmu.mak b/default-configs/i386-softmmu.mak index 3326e3e0bb..8c7d4a0fa0 100644 --- a/default-configs/i386-softmmu.mak +++ b/default-configs/i386-softmmu.mak @@ -63,3 +63,6 @@ CONFIG_PXB=y CONFIG_ACPI_VMGENID=y CONFIG_FW_CFG_DMA=y CONFIG_I2C=y +CONFIG_SEV=$(CONFIG_KVM) +CONFIG_VTD=y +CONFIG_AMD_IOMMU=y diff --git a/default-configs/x86_64-softmmu.mak b/default-configs/x86_64-softmmu.mak index 1c6cda1d9a..0390b4303c 100644 --- a/default-configs/x86_64-softmmu.mak +++ b/default-configs/x86_64-softmmu.mak @@ -63,3 +63,6 @@ CONFIG_PXB=y CONFIG_ACPI_VMGENID=y CONFIG_FW_CFG_DMA=y CONFIG_I2C=y +CONFIG_SEV=$(CONFIG_KVM) +CONFIG_VTD=y +CONFIG_AMD_IOMMU=y diff --git a/docs/amd-memory-encryption.txt b/docs/amd-memory-encryption.txt new file mode 100644 index 0000000000..f483795eaa --- /dev/null +++ b/docs/amd-memory-encryption.txt @@ -0,0 +1,109 @@ +Secure Encrypted Virtualization (SEV) is a feature found on AMD processors. + +SEV is an extension to the AMD-V architecture which supports running encrypted +virtual machine (VMs) under the control of KVM. Encrypted VMs have their pages +(code and data) secured such that only the guest itself has access to the +unencrypted version. Each encrypted VM is associated with a unique encryption +key; if its data is accessed to a different entity using a different key the +encrypted guests data will be incorrectly decrypted, leading to unintelligible +data. + +The key management of this feature is handled by separate processor known as +AMD secure processor (AMD-SP) which is present in AMD SOCs. Firmware running +inside the AMD-SP provide commands to support common VM lifecycle. This +includes commands for launching, snapshotting, migrating and debugging the +encrypted guest. Those SEV command can be issued via KVM_MEMORY_ENCRYPT_OP +ioctls. + +Launching +--------- +Boot images (such as bios) must be encrypted before guest can be booted. +MEMORY_ENCRYPT_OP ioctl provides commands to encrypt the images :LAUNCH_START, +LAUNCH_UPDATE_DATA, LAUNCH_MEASURE and LAUNCH_FINISH. These four commands +together generate a fresh memory encryption key for the VM, encrypt the boot +images and provide a measurement than can be used as an attestation of the +successful launch. + +LAUNCH_START is called first to create a cryptographic launch context within +the firmware. To create this context, guest owner must provides guest policy, +its public Diffie-Hellman key (PDH) and session parameters. These inputs +should be treated as binary blob and must be passed as-is to the SEV firmware. + +The guest policy is passed as plaintext and hypervisor may able to read it +but should not modify it (any modification of the policy bits will result +in bad measurement). The guest policy is a 4-byte data structure containing +several flags that restricts what can be done on running SEV guest. +See KM Spec section 3 and 6.2 for more details. + +The guest policy can be provided via the 'policy' property (see below) + +# ${QEMU} \ + sev-guest,id=sev0,policy=0x1...\ + +Guest owners provided DH certificate and session parameters will be used to +establish a cryptographic session with the guest owner to negotiate keys used +for the attestation. + +The DH certificate and session blob can be provided via 'dh-cert-file' and +'session-file' property (see below + +# ${QEMU} \ + sev-guest,id=sev0,dh-cert-file=<file1>,session-file=<file2> + +LAUNCH_UPDATE_DATA encrypts the memory region using the cryptographic context +created via LAUNCH_START command. If required, this command can be called +multiple times to encrypt different memory regions. The command also calculates +the measurement of the memory contents as it encrypts. + +LAUNCH_MEASURE command can be used to retrieve the measurement of encrypted +memory. This measurement is a signature of the memory contents that can be +sent to the guest owner as an attestation that the memory was encrypted +correctly by the firmware. The guest owner may wait to provide the guest +confidential information until it can verify the attestation measurement. +Since the guest owner knows the initial contents of the guest at boot, the +attestation measurement can be verified by comparing it to what the guest owner +expects. + +LAUNCH_FINISH command finalizes the guest launch and destroy's the cryptographic +context. + +See SEV KM API Spec [1] 'Launching a guest' usage flow (Appendix A) for the +complete flow chart. + +To launch a SEV guest + +# ${QEMU} \ + -machine ...,memory-encryption=sev0 \ + -object sev-guest,id=sev0,cbitpos=47,reduced-phys-bits=1 + +Debugging +----------- +Since memory contents of SEV guest is encrypted hence hypervisor access to the +guest memory will get a cipher text. If guest policy allows debugging, then +hypervisor can use DEBUG_DECRYPT and DEBUG_ENCRYPT commands access the guest +memory region for debug purposes. This is not supported in QEMU yet. + +Snapshot/Restore +----------------- +TODO + +Live Migration +---------------- +TODO + +References +----------------- + +AMD Memory Encryption whitepaper: +http://amd-dev.wpengine.netdna-cdn.com/wordpress/media/2013/12/AMD_Memory_Encryption_Whitepaper_v7-Public.pdf + +Secure Encrypted Virutualization Key Management: +[1] http://support.amd.com/TechDocs/55766_SEV-KM API_Specification.pdf + +KVM Forum slides: +http://www.linux-kvm.org/images/7/74/02x08A-Thomas_Lendacky-AMDs_Virtualizatoin_Memory_Encryption_Technology.pdf + +AMD64 Architecture Programmer's Manual: + http://support.amd.com/TechDocs/24593.pdf + SME is section 7.10 + SEV is section 15.34 diff --git a/docs/devel/atomics.txt b/docs/devel/atomics.txt index 10c5fa37e8..a4db3a4aaa 100644 --- a/docs/devel/atomics.txt +++ b/docs/devel/atomics.txt @@ -122,20 +122,30 @@ In general, if the algorithm you are writing includes both writes and reads on the same side, it is generally simpler to use sequentially consistent primitives. -When using this model, variables are accessed with atomic_read() and -atomic_set(), and restrictions to the ordering of accesses is enforced +When using this model, variables are accessed with: + +- atomic_read() and atomic_set(); these prevent the compiler from + optimizing accesses out of existence and creating unsolicited + accesses, but do not otherwise impose any ordering on loads and + stores: both the compiler and the processor are free to reorder + them. + +- atomic_load_acquire(), which guarantees the LOAD to appear to + happen, with respect to the other components of the system, + before all the LOAD or STORE operations specified afterwards. + Operations coming before atomic_load_acquire() can still be + reordered after it. + +- atomic_store_release(), which guarantees the STORE to appear to + happen, with respect to the other components of the system, + after all the LOAD or STORE operations specified afterwards. + Operations coming after atomic_store_release() can still be + reordered after it. + +Restrictions to the ordering of accesses can also be specified 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 -on the one hand, or that might create unsolicited accesses on the other. -In general this should not have any effect, because the same compiler -barriers are already implied by memory barriers. However, it is useful -to do so, because it tells readers which variables are shared with -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 six kinds: @@ -232,7 +242,7 @@ make atomic_mb_set() the more expensive operation. There are two common cases in which atomic_mb_read and atomic_mb_set generate too many memory barriers, and thus it can be useful to manually -place barriers instead: +place barriers, or use atomic_load_acquire/atomic_store_release instead: - when a data structure has one thread that is always a writer and one thread that is always a reader, manual placement of @@ -243,18 +253,15 @@ place barriers instead: thread 1 thread 1 ------------------------- ------------------------ (other writes) - smp_mb_release() - atomic_mb_set(&a, x) atomic_set(&a, x) - smp_wmb() - atomic_mb_set(&b, y) atomic_set(&b, y) + atomic_mb_set(&a, x) atomic_store_release(&a, x) + atomic_mb_set(&b, y) atomic_store_release(&b, y) => thread 2 thread 2 ------------------------- ------------------------ - y = atomic_mb_read(&b) y = atomic_read(&b) - smp_rmb() - x = atomic_mb_read(&a) x = atomic_read(&a) - smp_mb_acquire() + y = atomic_mb_read(&b) y = atomic_load_acquire(&b) + x = atomic_mb_read(&a) x = atomic_load_acquire(&a) + (other reads) 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 @@ -276,7 +283,6 @@ place barriers instead: smp_mb_acquire(); Similarly, atomic_mb_set() can be transformed as follows: - smp_mb(): smp_mb_release(); for (i = 0; i < 10; i++) => for (i = 0; i < 10; i++) @@ -284,6 +290,8 @@ place barriers instead: smp_mb(); + The other thread can still use atomic_mb_read()/atomic_mb_set(). + 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(): @@ -296,8 +304,6 @@ expensive smp_mb(): atomic_set(&a[i], false); smp_mb(); - The other thread can still use atomic_mb_read()/atomic_mb_set() - Memory barrier pairing ---------------------- @@ -386,10 +392,7 @@ 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. 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. - + detail. SOURCES ======= diff --git a/docs/replay.txt b/docs/replay.txt index 486c1e0e9d..2e21e9ccb0 100644 --- a/docs/replay.txt +++ b/docs/replay.txt @@ -7,14 +7,10 @@ See the COPYING file in the top-level directory. Record/replay ------------- -Record/replay functions are used for the reverse execution and deterministic -replay of qemu execution. This implementation of deterministic replay can -be used for deterministic debugging of guest code through a gdb remote -interface. - +Record/replay functions are used for the deterministic replay of qemu execution. Execution recording writes a non-deterministic events log, which can be later used for replaying the execution anywhere and for unlimited number of times. -It also supports checkpointing for faster rewinding during reverse debugging. +It also supports checkpointing for faster rewind to the specific replay moment. Execution replaying reads the log and replays all non-deterministic events including external input, hardware clocks, and interrupts. @@ -28,16 +24,36 @@ Deterministic replay has the following features: input devices. Usage of the record/replay: - * First, record the execution, by adding the following arguments to the command line: - '-icount shift=7,rr=record,rrfile=replay.bin -net none'. - Block devices' images are not actually changed in the recording mode, + * First, record the execution with the following command line: + qemu-system-i386 \ + -icount shift=7,rr=record,rrfile=replay.bin \ + -drive file=disk.qcow2,if=none,id=img-direct \ + -drive driver=blkreplay,if=none,image=img-direct,id=img-blkreplay \ + -device ide-hd,drive=img-blkreplay \ + -netdev user,id=net1 -device rtl8139,netdev=net1 \ + -object filter-replay,id=replay,netdev=net1 + * After recording, you can replay it by using another command line: + qemu-system-i386 \ + -icount shift=7,rr=replay,rrfile=replay.bin \ + -drive file=disk.qcow2,if=none,id=img-direct \ + -drive driver=blkreplay,if=none,image=img-direct,id=img-blkreplay \ + -device ide-hd,drive=img-blkreplay \ + -netdev user,id=net1 -device rtl8139,netdev=net1 \ + -object filter-replay,id=replay,netdev=net1 + The only difference with recording is changing the rr option + from record to replay. + * Block device images are not actually changed in the recording mode, because all of the changes are written to the temporary overlay file. - * Then you can replay it by using another command - line option: '-icount shift=7,rr=replay,rrfile=replay.bin -net none' - * '-net none' option should also be specified if network replay patches - are not applied. - -Papers with description of deterministic replay implementation: + This behavior is enabled by using blkreplay driver. It should be used + for every enabled block device, as described in 'Block devices' section. + * '-net none' option should be specified when network is not used, + because QEMU adds network card by default. When network is needed, + it should be configured explicitly with replay filter, as described + in 'Network devices' section. + * Interaction with audio devices and serial ports are recorded and replayed + automatically when such devices are enabled. + +Academic papers with description of deterministic replay implementation: http://www.computer.org/csdl/proceedings/csmr/2012/4666/00/4666a553-abs.html http://dl.acm.org/citation.cfm?id=2786805.2803179 @@ -46,8 +62,33 @@ Modifications of qemu include: * saving different asynchronous events (e.g. system shutdown) into the log * synchronization of the bottom halves execution * synchronization of the threads from thread pool - * recording/replaying user input (mouse and keyboard) + * recording/replaying user input (mouse, keyboard, and microphone) * adding internal checkpoints for cpu and io synchronization + * network filter for recording and replaying the packets + * block driver for making block layer deterministic + * serial port input record and replay + +Locking and thread synchronisation +---------------------------------- + +Previously the synchronisation of the main thread and the vCPU thread +was ensured by the holding of the BQL. However the trend has been to +reduce the time the BQL was held across the system including under TCG +system emulation. As it is important that batches of events are kept +in sequence (e.g. expiring timers and checkpoints in the main thread +while instruction checkpoints are written by the vCPU thread) we need +another lock to keep things in lock-step. This role is now handled by +the replay_mutex_lock. It used to be held only for each event being +written but now it is held for a whole execution period. This results +in a deterministic ping-pong between the two main threads. + +As the BQL is now a finer grained lock than the replay_lock it is almost +certainly a bug, and a source of deadlocks, to take the +replay_mutex_lock while the BQL is held. This is enforced by an assert. +While the unlocks are usually in the reverse order, this is not +necessary; you can drop the replay_lock while holding the BQL, without +doing a more complicated unlock_iothread/replay_unlock/lock_iothread +sequence. Non-deterministic events ------------------------ @@ -55,12 +96,11 @@ Non-deterministic events Our record/replay system is based on saving and replaying non-deterministic events (e.g. keyboard input) and simulating deterministic ones (e.g. reading from HDD or memory of the VM). Saving only non-deterministic events makes -log file smaller, simulation faster, and allows using reverse debugging even -for realtime applications. +log file smaller and simulation faster. The following non-deterministic data from peripheral devices is saved into the log: mouse and keyboard input, network packets, audio controller input, -USB packets, serial port input, and hardware clocks (they are non-deterministic +serial port input, and hardware clocks (they are non-deterministic too, because their values are taken from the host machine). Inputs from simulated hardware, memory of VM, software interrupts, and execution of instructions are not saved into the log, because they are deterministic and @@ -183,7 +223,7 @@ Block devices record/replay module intercepts calls of bdrv coroutine functions at the top of block drivers stack. To record and replay block operations the drive must be configured as following: - -drive file=disk.qcow,if=none,id=img-direct + -drive file=disk.qcow2,if=none,id=img-direct -drive driver=blkreplay,if=none,image=img-direct,id=img-blkreplay -device ide-hd,drive=img-blkreplay @@ -212,6 +252,12 @@ This snapshot is created at start of recording and restored at start of replaying. It also can be loaded while replaying to roll back the execution. +Use QEMU monitor to create additional snapshots. 'savevm <name>' command +created the snapshot and 'loadvm <name>' restores it. To prevent corruption +of the original disk image, use overlay files linked to the original images. +Therefore all new snapshots (including the starting one) will be saved in +overlays and the original image remains unchanged. + Network devices --------------- @@ -232,3 +278,80 @@ Audio devices Audio data is recorded and replay automatically. The command line for recording and replaying must contain identical specifications of audio hardware, e.g.: -soundhw ac97 + +Serial ports +------------ + +Serial ports input is recorded and replay automatically. The command lines +for recording and replaying must contain identical number of ports in record +and replay modes, but their backends may differ. +E.g., '-serial stdio' in record mode, and '-serial null' in replay mode. + +Replay log format +----------------- + +Record/replay log consits of the header and the sequence of execution +events. The header includes 4-byte replay version id and 8-byte reserved +field. Version is updated every time replay log format changes to prevent +using replay log created by another build of qemu. + +The sequence of the events describes virtual machine state changes. +It includes all non-deterministic inputs of VM, synchronization marks and +instruction counts used to correctly inject inputs at replay. + +Synchronization marks (checkpoints) are used for synchronizing qemu threads +that perform operations with virtual hardware. These operations may change +system's state (e.g., change some register or generate interrupt) and +therefore should execute synchronously with CPU thread. + +Every event in the log includes 1-byte event id and optional arguments. +When argument is an array, it is stored as 4-byte array length +and corresponding number of bytes with data. +Here is the list of events that are written into the log: + + - EVENT_INSTRUCTION. Instructions executed since last event. + Argument: 4-byte number of executed instructions. + - EVENT_INTERRUPT. Used to synchronize interrupt processing. + - EVENT_EXCEPTION. Used to synchronize exception handling. + - EVENT_ASYNC. This is a group of events. They are always processed + together with checkpoints. When such an event is generated, it is + stored in the queue and processed only when checkpoint occurs. + Every such event is followed by 1-byte checkpoint id and 1-byte + async event id from the following list: + - REPLAY_ASYNC_EVENT_BH. Bottom-half callback. This event synchronizes + callbacks that affect virtual machine state, but normally called + asyncronously. + Argument: 8-byte operation id. + - REPLAY_ASYNC_EVENT_INPUT. Input device event. Contains + parameters of keyboard and mouse input operations + (key press/release, mouse pointer movement). + Arguments: 9-16 bytes depending of input event. + - REPLAY_ASYNC_EVENT_INPUT_SYNC. Internal input synchronization event. + - REPLAY_ASYNC_EVENT_CHAR_READ. Character (e.g., serial port) device input + initiated by the sender. + Arguments: 1-byte character device id. + Array with bytes were read. + - REPLAY_ASYNC_EVENT_BLOCK. Block device operation. Used to synchronize + operations with disk and flash drives with CPU. + Argument: 8-byte operation id. + - REPLAY_ASYNC_EVENT_NET. Incoming network packet. + Arguments: 1-byte network adapter id. + 4-byte packet flags. + Array with packet bytes. + - EVENT_SHUTDOWN. Occurs when user sends shutdown event to qemu, + e.g., by closing the window. + - EVENT_CHAR_WRITE. Used to synchronize character output operations. + Arguments: 4-byte output function return value. + 4-byte offset in the output array. + - EVENT_CHAR_READ_ALL. Used to synchronize character input operations, + initiated by qemu. + Argument: Array with bytes that were read. + - EVENT_CHAR_READ_ALL_ERROR. Unsuccessful character input operation, + initiated by qemu. + Argument: 4-byte error code. + - EVENT_CLOCK + clock_id. Group of events for host clock read operations. + Argument: 8-byte clock value. + - EVENT_CHECKPOINT + checkpoint_id. Checkpoint for synchronization of + CPU, internal threads, and asynchronous input events. May be followed + by one or more EVENT_ASYNC events. + - EVENT_END. Last event in the log. diff --git a/hmp-commands-info.hx b/hmp-commands-info.hx index ad590a4ffb..ddfcd5adcc 100644 --- a/hmp-commands-info.hx +++ b/hmp-commands-info.hx @@ -867,6 +867,22 @@ Display the amount of initially allocated and present hotpluggable (if enabled) memory in bytes. ETEXI +#if defined(TARGET_I386) + { + .name = "sev", + .args_type = "", + .params = "", + .help = "show SEV information", + .cmd = hmp_info_sev, + }, +#endif + +STEXI +@item info sev +@findex info sev +Show SEV information. +ETEXI + STEXI @end table ETEXI @@ -143,5 +143,6 @@ void hmp_info_ramblock(Monitor *mon, const QDict *qdict); void hmp_hotpluggable_cpus(Monitor *mon, const QDict *qdict); void hmp_info_vm_generation_id(Monitor *mon, const QDict *qdict); void hmp_info_memory_size_summary(Monitor *mon, const QDict *qdict); +void hmp_info_sev(Monitor *mon, const QDict *qdict); #endif diff --git a/hw/alpha/dp264.c b/hw/alpha/dp264.c index 766373eec7..80b987f7fb 100644 --- a/hw/alpha/dp264.c +++ b/hw/alpha/dp264.c @@ -19,7 +19,8 @@ #include "hw/timer/mc146818rtc.h" #include "hw/ide.h" #include "hw/timer/i8254.h" -#include "hw/char/serial.h" +#include "hw/isa/superio.h" +#include "hw/dma/i8257.h" #include "qemu/cutils.h" #define MAX_IDE_BUS 2 @@ -81,19 +82,21 @@ static void clipper_init(MachineState *machine) mc146818_rtc_init(isa_bus, 1900, rtc_irq); i8254_pit_init(isa_bus, 0x40, 0, NULL); - isa_create_simple(isa_bus, "i8042"); /* VGA setup. Don't bother loading the bios. */ pci_vga_init(pci_bus); - /* Serial code setup. */ - 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++) { pci_nic_init_nofail(&nd_table[i], pci_bus, "e1000", NULL); } + /* 2 82C37 (dma) */ + isa_create_simple(isa_bus, "i82374"); + + /* Super I/O */ + isa_create_simple(isa_bus, TYPE_SMC37C669_SUPERIO); + /* IDE disk setup. */ { DriveInfo *hd[MAX_IDE_BUS * MAX_IDE_DEVS]; diff --git a/hw/arm/highbank.c b/hw/arm/highbank.c index 287392bbdc..1742cf6f6c 100644 --- a/hw/arm/highbank.c +++ b/hw/arm/highbank.c @@ -27,7 +27,6 @@ #include "sysemu/kvm.h" #include "sysemu/sysemu.h" #include "hw/boards.h" -#include "sysemu/block-backend.h" #include "exec/address-spaces.h" #include "qemu/error-report.h" #include "hw/char/pl011.h" diff --git a/hw/arm/msf2-soc.c b/hw/arm/msf2-soc.c index a8ec2cdf36..f68df56b97 100644 --- a/hw/arm/msf2-soc.c +++ b/hw/arm/msf2-soc.c @@ -29,7 +29,6 @@ #include "exec/address-spaces.h" #include "hw/char/serial.h" #include "hw/boards.h" -#include "sysemu/block-backend.h" #include "qemu/cutils.h" #include "hw/arm/msf2-soc.h" #include "hw/misc/unimp.h" diff --git a/hw/arm/realview.c b/hw/arm/realview.c index 87cd1e583c..2139a62e25 100644 --- a/hw/arm/realview.c +++ b/hw/arm/realview.c @@ -20,7 +20,6 @@ #include "sysemu/sysemu.h" #include "hw/boards.h" #include "hw/i2c/i2c.h" -#include "sysemu/block-backend.h" #include "exec/address-spaces.h" #include "qemu/error-report.h" #include "hw/char/pl011.h" diff --git a/hw/arm/tosa.c b/hw/arm/tosa.c index a55b1a369c..7a925fa5e6 100644 --- a/hw/arm/tosa.c +++ b/hw/arm/tosa.c @@ -22,7 +22,6 @@ #include "hw/boards.h" #include "hw/i2c/i2c.h" #include "hw/ssi/ssi.h" -#include "sysemu/block-backend.h" #include "hw/sysbus.h" #include "exec/address-spaces.h" #include "sysemu/sysemu.h" diff --git a/hw/char/Makefile.objs b/hw/char/Makefile.objs index 1bcd37e98d..1b979100b7 100644 --- a/hw/char/Makefile.objs +++ b/hw/char/Makefile.objs @@ -1,6 +1,7 @@ common-obj-$(CONFIG_IPACK) += ipoctal232.o common-obj-$(CONFIG_ESCC) += escc.o common-obj-$(CONFIG_PARALLEL) += parallel.o +common-obj-$(CONFIG_PARALLEL) += parallel-isa.o common-obj-$(CONFIG_PL011) += pl011.o common-obj-$(CONFIG_SERIAL) += serial.o common-obj-$(CONFIG_SERIAL_ISA) += serial-isa.o diff --git a/hw/char/parallel-isa.c b/hw/char/parallel-isa.c new file mode 100644 index 0000000000..639e179585 --- /dev/null +++ b/hw/char/parallel-isa.c @@ -0,0 +1,36 @@ +/* + * QEMU Parallel PORT (ISA bus helpers) + * + * Copyright (c) 2003 Fabrice Bellard + * + * SPDX-License-Identifier: MIT + */ +#include "qemu/osdep.h" +#include "sysemu/sysemu.h" +#include "hw/isa/isa.h" +#include "hw/char/parallel.h" + +static void parallel_init(ISABus *bus, int index, Chardev *chr) +{ + DeviceState *dev; + ISADevice *isadev; + + isadev = isa_create(bus, "isa-parallel"); + dev = DEVICE(isadev); + qdev_prop_set_uint32(dev, "index", index); + qdev_prop_set_chr(dev, "chardev", chr); + qdev_init_nofail(dev); +} + +void parallel_hds_isa_init(ISABus *bus, int n) +{ + int i; + + assert(n <= MAX_PARALLEL_PORTS); + + for (i = 0; i < n; i++) { + if (parallel_hds[i]) { + parallel_init(bus, i, parallel_hds[i]); + } + } +} diff --git a/hw/char/parallel.c b/hw/char/parallel.c index f79dc76543..1542d62201 100644 --- a/hw/char/parallel.c +++ b/hw/char/parallel.c @@ -28,7 +28,7 @@ #include "chardev/char-parallel.h" #include "chardev/char-fe.h" #include "hw/isa/isa.h" -#include "hw/i386/pc.h" +#include "hw/char/parallel.h" #include "sysemu/sysemu.h" //#define DEBUG_PARALLEL diff --git a/hw/core/machine.c b/hw/core/machine.c index 5e2bbcdace..2040177664 100644 --- a/hw/core/machine.c +++ b/hw/core/machine.c @@ -334,6 +334,22 @@ static bool machine_get_enforce_config_section(Object *obj, Error **errp) return ms->enforce_config_section; } +static char *machine_get_memory_encryption(Object *obj, Error **errp) +{ + MachineState *ms = MACHINE(obj); + + return g_strdup(ms->memory_encryption); +} + +static void machine_set_memory_encryption(Object *obj, const char *value, + Error **errp) +{ + MachineState *ms = MACHINE(obj); + + g_free(ms->memory_encryption); + ms->memory_encryption = g_strdup(value); +} + void machine_class_allow_dynamic_sysbus_dev(MachineClass *mc, const char *type) { strList *item = g_new0(strList, 1); @@ -612,6 +628,12 @@ static void machine_class_init(ObjectClass *oc, void *data) &error_abort); object_class_property_set_description(oc, "enforce-config-section", "Set on to enforce configuration section migration", &error_abort); + + object_class_property_add_str(oc, "memory-encryption", + machine_get_memory_encryption, machine_set_memory_encryption, + &error_abort); + object_class_property_set_description(oc, "memory-encryption", + "Set memory encyption object to use", &error_abort); } static void machine_class_base_init(ObjectClass *oc, void *data) diff --git a/hw/display/vga-pci.c b/hw/display/vga-pci.c index 1674bd3581..f312930664 100644 --- a/hw/display/vga-pci.c +++ b/hw/display/vga-pci.c @@ -292,6 +292,14 @@ static void pci_secondary_vga_realize(PCIDevice *dev, Error **errp) pci_register_bar(&d->dev, 2, PCI_BASE_ADDRESS_SPACE_MEMORY, &d->mmio); } +static void pci_secondary_vga_exit(PCIDevice *dev) +{ + PCIVGAState *d = PCI_VGA(dev); + VGACommonState *s = &d->vga; + + graphic_console_close(s->con); +} + static void pci_secondary_vga_init(Object *obj) { /* Expose framebuffer byteorder via QOM */ @@ -361,6 +369,7 @@ static void secondary_class_init(ObjectClass *klass, void *data) PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); k->realize = pci_secondary_vga_realize; + k->exit = pci_secondary_vga_exit; k->class_id = PCI_CLASS_DISPLAY_OTHER; dc->props = secondary_pci_properties; dc->reset = pci_secondary_vga_reset; diff --git a/hw/dma/i82374.c b/hw/dma/i82374.c index 6c0f975df0..83c87d92e0 100644 --- a/hw/dma/i82374.c +++ b/hw/dma/i82374.c @@ -24,6 +24,7 @@ #include "qemu/osdep.h" #include "hw/isa/isa.h" +#include "hw/dma/i8257.h" #define TYPE_I82374 "i82374" #define I82374(obj) OBJECT_CHECK(I82374State, (obj), TYPE_I82374) @@ -123,7 +124,7 @@ static void i82374_realize(DeviceState *dev, Error **errp) portio_list_add(&s->port_list, isa_address_space_io(&s->parent_obj), s->iobase); - DMA_init(isa_bus_from_device(ISA_DEVICE(dev)), 1); + i8257_dma_init(isa_bus_from_device(ISA_DEVICE(dev)), true); memset(s->commands, 0, sizeof(s->commands)); } diff --git a/hw/dma/i8257.c b/hw/dma/i8257.c index bd23e893bf..52675e97c9 100644 --- a/hw/dma/i8257.c +++ b/hw/dma/i8257.c @@ -24,7 +24,7 @@ #include "qemu/osdep.h" #include "hw/hw.h" #include "hw/isa/isa.h" -#include "hw/isa/i8257.h" +#include "hw/dma/i8257.h" #include "qemu/main-loop.h" #include "trace.h" @@ -622,7 +622,7 @@ static void i8257_register_types(void) type_init(i8257_register_types) -void DMA_init(ISABus *bus, int high_page_enable) +void i8257_dma_init(ISABus *bus, bool high_page_enable) { ISADevice *isa1, *isa2; DeviceState *d; diff --git a/hw/i386/Makefile.objs b/hw/i386/Makefile.objs index fd279e7584..fa87a14152 100644 --- a/hw/i386/Makefile.objs +++ b/hw/i386/Makefile.objs @@ -2,8 +2,8 @@ obj-$(CONFIG_KVM) += kvm/ obj-y += multiboot.o obj-y += pc.o pc_piix.o pc_q35.o obj-y += pc_sysfw.o -obj-y += x86-iommu.o intel_iommu.o -obj-y += amd_iommu.o +obj-$(CONFIG_VTD) += x86-iommu.o intel_iommu.o +obj-$(CONFIG_AMD_IOMMU) += x86-iommu.o amd_iommu.o obj-$(CONFIG_XEN) += ../xenpv/ xen/ obj-$(CONFIG_VMPORT) += vmport.o obj-$(CONFIG_VMMOUSE) += vmmouse.o diff --git a/hw/i386/pc.c b/hw/i386/pc.c index 35fcb6efdf..d36bac8c89 100644 --- a/hw/i386/pc.c +++ b/hw/i386/pc.c @@ -26,6 +26,7 @@ #include "hw/hw.h" #include "hw/i386/pc.h" #include "hw/char/serial.h" +#include "hw/char/parallel.h" #include "hw/i386/apic.h" #include "hw/i386/topology.h" #include "sysemu/cpus.h" @@ -40,7 +41,9 @@ #include "elf.h" #include "multiboot.h" #include "hw/timer/mc146818rtc.h" +#include "hw/dma/i8257.h" #include "hw/timer/i8254.h" +#include "hw/input/i8042.h" #include "hw/audio/pcspk.h" #include "hw/pci/msi.h" #include "hw/sysbus.h" @@ -50,8 +53,6 @@ #include "sysemu/qtest.h" #include "kvm_i386.h" #include "hw/xen/xen.h" -#include "sysemu/block-backend.h" -#include "hw/block/block.h" #include "ui/qemu-spice.h" #include "exec/memory.h" #include "exec/address-spaces.h" @@ -1516,6 +1517,44 @@ static const MemoryRegionOps ioportF0_io_ops = { }, }; +static void pc_superio_init(ISABus *isa_bus, bool create_fdctrl, bool no_vmport) +{ + int i; + DriveInfo *fd[MAX_FD]; + qemu_irq *a20_line; + ISADevice *i8042, *port92, *vmmouse; + + serial_hds_isa_init(isa_bus, 0, MAX_SERIAL_PORTS); + parallel_hds_isa_init(isa_bus, MAX_PARALLEL_PORTS); + + for (i = 0; i < MAX_FD; i++) { + fd[i] = drive_get(IF_FLOPPY, 0, i); + create_fdctrl |= !!fd[i]; + } + if (create_fdctrl) { + fdctrl_init_isa(isa_bus, fd); + } + + i8042 = isa_create_simple(isa_bus, "i8042"); + if (!no_vmport) { + vmport_init(isa_bus); + vmmouse = isa_try_create(isa_bus, "vmmouse"); + } else { + vmmouse = NULL; + } + if (vmmouse) { + DeviceState *dev = DEVICE(vmmouse); + qdev_prop_set_ptr(dev, "ps2_mouse", i8042); + qdev_init_nofail(dev); + } + port92 = isa_create_simple(isa_bus, "port92"); + + a20_line = qemu_allocate_irqs(handle_a20_line_change, first_cpu, 2); + i8042_setup_a20_line(i8042, a20_line[0]); + port92_init(port92, a20_line[1]); + g_free(a20_line); +} + void pc_basic_device_init(ISABus *isa_bus, qemu_irq *gsi, ISADevice **rtc_state, bool create_fdctrl, @@ -1524,13 +1563,11 @@ void pc_basic_device_init(ISABus *isa_bus, qemu_irq *gsi, uint32_t hpet_irqs) { int i; - DriveInfo *fd[MAX_FD]; DeviceState *hpet = NULL; int pit_isa_irq = 0; qemu_irq pit_alt_irq = NULL; qemu_irq rtc_irq = NULL; - qemu_irq *a20_line; - ISADevice *i8042, *port92, *vmmouse, *pit = NULL; + ISADevice *pit = NULL; MemoryRegion *ioport80_io = g_new(MemoryRegion, 1); MemoryRegion *ioportF0_io = g_new(MemoryRegion, 1); @@ -1587,50 +1624,25 @@ void pc_basic_device_init(ISABus *isa_bus, qemu_irq *gsi, pcspk_init(isa_bus, pit); } - 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); - i8042 = isa_create_simple(isa_bus, "i8042"); - i8042_setup_a20_line(i8042, a20_line[0]); - if (!no_vmport) { - vmport_init(isa_bus); - vmmouse = isa_try_create(isa_bus, "vmmouse"); - } else { - vmmouse = NULL; - } - if (vmmouse) { - DeviceState *dev = DEVICE(vmmouse); - qdev_prop_set_ptr(dev, "ps2_mouse", i8042); - qdev_init_nofail(dev); - } - port92 = isa_create_simple(isa_bus, "port92"); - port92_init(port92, a20_line[1]); - g_free(a20_line); - - DMA_init(isa_bus, 0); + i8257_dma_init(isa_bus, 0); - for(i = 0; i < MAX_FD; i++) { - fd[i] = drive_get(IF_FLOPPY, 0, i); - create_fdctrl |= !!fd[i]; - } - if (create_fdctrl) { - fdctrl_init_isa(isa_bus, fd); - } + /* Super I/O */ + pc_superio_init(isa_bus, create_fdctrl, no_vmport); } -void pc_nic_init(ISABus *isa_bus, PCIBus *pci_bus) +void pc_nic_init(PCMachineClass *pcmc, ISABus *isa_bus, PCIBus *pci_bus) { int i; rom_set_order_override(FW_CFG_ORDER_OVERRIDE_NIC); for (i = 0; i < nb_nics; i++) { NICInfo *nd = &nd_table[i]; + const char *model = nd->model ? nd->model : pcmc->default_nic_model; - if (!pci_bus || (nd->model && strcmp(nd->model, "ne2k_isa") == 0)) { + if (g_str_equal(model, "ne2k_isa")) { pc_init_ne2k_isa(isa_bus, nd); } else { - pci_nic_init_nofail(nd, pci_bus, "e1000", NULL); + pci_nic_init_nofail(nd, pci_bus, model, NULL); } } rom_reset_order_override(); diff --git a/hw/i386/pc_piix.c b/hw/i386/pc_piix.c index 8658bcba63..729a0508aa 100644 --- a/hw/i386/pc_piix.c +++ b/hw/i386/pc_piix.c @@ -40,7 +40,6 @@ #include "sysemu/sysemu.h" #include "hw/sysbus.h" #include "sysemu/arch_init.h" -#include "sysemu/block-backend.h" #include "hw/i2c/smbus.h" #include "hw/xen/xen.h" #include "exec/memory.h" @@ -240,7 +239,7 @@ static void pc_init1(MachineState *machine, pc_basic_device_init(isa_bus, pcms->gsi, &rtc_state, true, (pcms->vmport != ON_OFF_AUTO_ON), pcms->pit, 0x4); - pc_nic_init(isa_bus, pci_bus); + pc_nic_init(pcmc, isa_bus, pci_bus); ide_drive_get(hd, ARRAY_SIZE(hd)); if (pcmc->pci_enabled) { @@ -417,6 +416,9 @@ static void pc_xen_hvm_init(MachineState *machine) static void pc_i440fx_machine_options(MachineClass *m) { + PCMachineClass *pcmc = PC_MACHINE_CLASS(m); + pcmc->default_nic_model = "e1000"; + m->family = "pc_piix"; m->desc = "Standard PC (i440FX + PIIX, 1996)"; m->default_machine_opts = "firmware=bios-256k.bin"; @@ -1114,6 +1116,7 @@ static void isapc_machine_options(MachineClass *m) pcmc->gigabyte_align = false; pcmc->smbios_legacy_mode = true; pcmc->has_reserved_memory = false; + pcmc->default_nic_model = "ne2k_isa"; m->default_cpu_type = X86_CPU_TYPE_NAME("486"); } diff --git a/hw/i386/pc_q35.c b/hw/i386/pc_q35.c index 0c0bc48137..9ae916327e 100644 --- a/hw/i386/pc_q35.c +++ b/hw/i386/pc_q35.c @@ -272,7 +272,7 @@ static void pc_q35_init(MachineState *machine) /* the rest devices to which pci devfn is automatically assigned */ pc_vga_init(isa_bus, host_bus); - pc_nic_init(isa_bus, host_bus); + pc_nic_init(pcmc, isa_bus, host_bus); if (pcms->acpi_nvdimm_state.is_enabled) { nvdimm_init_acpi_state(&pcms->acpi_nvdimm_state, system_io, @@ -294,6 +294,9 @@ static void pc_q35_init(MachineState *machine) static void pc_q35_machine_options(MachineClass *m) { + PCMachineClass *pcmc = PC_MACHINE_CLASS(m); + pcmc->default_nic_model = "e1000e"; + m->family = "pc_q35"; m->desc = "Standard PC (Q35 + ICH9, 2009)"; m->units_per_default_bus = 1; @@ -316,7 +319,10 @@ DEFINE_Q35_MACHINE(v2_12, "pc-q35-2.12", NULL, static void pc_q35_2_11_machine_options(MachineClass *m) { + PCMachineClass *pcmc = PC_MACHINE_CLASS(m); + pc_q35_2_12_machine_options(m); + pcmc->default_nic_model = "e1000"; m->alias = NULL; SET_MACHINE_COMPAT(m, PC_COMPAT_2_11); } diff --git a/hw/i386/pc_sysfw.c b/hw/i386/pc_sysfw.c index 4325575e7d..73ac783f20 100644 --- a/hw/i386/pc_sysfw.c +++ b/hw/i386/pc_sysfw.c @@ -113,6 +113,8 @@ static void pc_system_flash_init(MemoryRegion *rom_memory) pflash_t *system_flash; MemoryRegion *flash_mem; char name[64]; + void *flash_ptr; + int ret, flash_size; sector_bits = 12; sector_size = 1 << sector_bits; @@ -169,6 +171,17 @@ static void pc_system_flash_init(MemoryRegion *rom_memory) if (unit == 0) { flash_mem = pflash_cfi01_get_memory(system_flash); pc_isa_bios_init(rom_memory, flash_mem, size); + + /* Encrypt the pflash boot ROM */ + if (kvm_memcrypt_enabled()) { + flash_ptr = memory_region_get_ram_ptr(flash_mem); + flash_size = memory_region_size(flash_mem); + ret = kvm_memcrypt_encrypt_data(flash_ptr, flash_size); + if (ret) { + error_report("failed to encrypt pflash rom"); + exit(1); + } + } } } } diff --git a/hw/i386/vmmouse.c b/hw/i386/vmmouse.c index 65ef55329e..5d2d278be4 100644 --- a/hw/i386/vmmouse.c +++ b/hw/i386/vmmouse.c @@ -25,6 +25,7 @@ #include "hw/hw.h" #include "ui/console.h" #include "hw/i386/pc.h" +#include "hw/input/i8042.h" #include "hw/qdev.h" /* debug only vmmouse */ diff --git a/hw/i386/vmport.c b/hw/i386/vmport.c index 116aa09819..3bf8cfe041 100644 --- a/hw/i386/vmport.c +++ b/hw/i386/vmport.c @@ -25,6 +25,7 @@ #include "hw/hw.h" #include "hw/isa/isa.h" #include "hw/i386/pc.h" +#include "hw/input/i8042.h" #include "sysemu/hw_accel.h" #include "hw/qdev.h" #include "qemu/log.h" diff --git a/hw/ide/ahci-allwinner.c b/hw/ide/ahci-allwinner.c index c3f1604936..5397483fd8 100644 --- a/hw/ide/ahci-allwinner.c +++ b/hw/ide/ahci-allwinner.c @@ -18,7 +18,6 @@ #include "qemu/osdep.h" #include "hw/hw.h" #include "qemu/error-report.h" -#include "sysemu/block-backend.h" #include "sysemu/dma.h" #include "hw/ide/internal.h" #include "hw/ide/ahci_internal.h" diff --git a/hw/ide/cmd646.c b/hw/ide/cmd646.c index 65aff518ec..6bb92d717f 100644 --- a/hw/ide/cmd646.c +++ b/hw/ide/cmd646.c @@ -26,7 +26,6 @@ #include "hw/hw.h" #include "hw/pci/pci.h" #include "hw/isa/isa.h" -#include "sysemu/block-backend.h" #include "sysemu/sysemu.h" #include "sysemu/dma.h" diff --git a/hw/ide/ich.c b/hw/ide/ich.c index c01b24ecbe..134478ebb2 100644 --- a/hw/ide/ich.c +++ b/hw/ide/ich.c @@ -65,7 +65,6 @@ #include "hw/pci/msi.h" #include "hw/pci/pci.h" #include "hw/isa/isa.h" -#include "sysemu/block-backend.h" #include "sysemu/dma.h" #include "hw/ide/pci.h" #include "hw/ide/ahci_internal.h" diff --git a/hw/ide/isa.c b/hw/ide/isa.c index 9fb24fc92b..028bd61774 100644 --- a/hw/ide/isa.c +++ b/hw/ide/isa.c @@ -25,7 +25,6 @@ #include "qemu/osdep.h" #include "hw/hw.h" #include "hw/isa/isa.h" -#include "sysemu/block-backend.h" #include "sysemu/dma.h" #include "hw/ide/internal.h" diff --git a/hw/ide/microdrive.c b/hw/ide/microdrive.c index 58e4f52f7d..34bb98dce8 100644 --- a/hw/ide/microdrive.c +++ b/hw/ide/microdrive.c @@ -25,7 +25,6 @@ #include "qemu/osdep.h" #include "hw/hw.h" #include "hw/pcmcia.h" -#include "sysemu/block-backend.h" #include "sysemu/dma.h" #include "hw/ide/internal.h" diff --git a/hw/ide/mmio.c b/hw/ide/mmio.c index 6f12f456ed..42fcf139ee 100644 --- a/hw/ide/mmio.c +++ b/hw/ide/mmio.c @@ -25,7 +25,6 @@ #include "qemu/osdep.h" #include "hw/hw.h" #include "hw/sysbus.h" -#include "sysemu/block-backend.h" #include "sysemu/dma.h" #include "hw/ide/internal.h" diff --git a/hw/input/pckbd.c b/hw/input/pckbd.c index c479f827b6..f17f18e51b 100644 --- a/hw/input/pckbd.c +++ b/hw/input/pckbd.c @@ -26,6 +26,7 @@ #include "hw/isa/isa.h" #include "hw/i386/pc.h" #include "hw/input/ps2.h" +#include "hw/input/i8042.h" #include "sysemu/sysemu.h" /* debug PC keyboard */ @@ -480,7 +481,6 @@ void i8042_mm_init(qemu_irq kbd_irq, qemu_irq mouse_irq, qemu_register_reset(kbd_reset, s); } -#define TYPE_I8042 "i8042" #define I8042(obj) OBJECT_CHECK(ISAKBDState, (obj), TYPE_I8042) typedef struct ISAKBDState { diff --git a/hw/isa/Makefile.objs b/hw/isa/Makefile.objs index fb37c55cf2..83e06f6c04 100644 --- a/hw/isa/Makefile.objs +++ b/hw/isa/Makefile.objs @@ -1,4 +1,5 @@ common-obj-$(CONFIG_ISA_BUS) += isa-bus.o +common-obj-$(CONFIG_ISA_BUS) += isa-superio.o smc37c669-superio.o common-obj-$(CONFIG_APM) += apm.o common-obj-$(CONFIG_I82378) += i82378.o common-obj-$(CONFIG_PC87312) += pc87312.o diff --git a/hw/isa/isa-bus.c b/hw/isa/isa-bus.c index 0f2e426d02..63fa77effc 100644 --- a/hw/isa/isa-bus.c +++ b/hw/isa/isa-bus.c @@ -24,7 +24,6 @@ #include "hw/sysbus.h" #include "sysemu/sysemu.h" #include "hw/isa/isa.h" -#include "hw/i386/pc.h" static ISABus *isabus; @@ -288,28 +287,3 @@ MemoryRegion *isa_address_space_io(ISADevice *dev) } type_init(isabus_register_types) - -static void parallel_init(ISABus *bus, int index, Chardev *chr) -{ - DeviceState *dev; - ISADevice *isadev; - - isadev = isa_create(bus, "isa-parallel"); - dev = DEVICE(isadev); - qdev_prop_set_uint32(dev, "index", index); - qdev_prop_set_chr(dev, "chardev", chr); - qdev_init_nofail(dev); -} - -void parallel_hds_isa_init(ISABus *bus, int n) -{ - int i; - - assert(n <= MAX_PARALLEL_PORTS); - - for (i = 0; i < n; i++) { - if (parallel_hds[i]) { - parallel_init(bus, i, parallel_hds[i]); - } - } -} diff --git a/hw/isa/isa-superio.c b/hw/isa/isa-superio.c new file mode 100644 index 0000000000..b95608a003 --- /dev/null +++ b/hw/isa/isa-superio.c @@ -0,0 +1,214 @@ +/* + * Generic ISA Super I/O + * + * Copyright (c) 2010-2012 Herve Poussineau + * Copyright (c) 2011-2012 Andreas Färber + * Copyright (c) 2018 Philippe Mathieu-Daudé + * + * This code is licensed under the GNU GPLv2 and later. + * See the COPYING file in the top-level directory. + * SPDX-License-Identifier: GPL-2.0-or-later + */ +#include "qemu/osdep.h" +#include "qemu/error-report.h" +#include "qapi/error.h" +#include "sysemu/sysemu.h" +#include "sysemu/block-backend.h" +#include "sysemu/blockdev.h" +#include "chardev/char.h" +#include "hw/isa/superio.h" +#include "hw/input/i8042.h" +#include "hw/char/serial.h" +#include "trace.h" + +static void isa_superio_realize(DeviceState *dev, Error **errp) +{ + ISASuperIODevice *sio = ISA_SUPERIO(dev); + ISASuperIOClass *k = ISA_SUPERIO_GET_CLASS(sio); + ISABus *bus = isa_bus_from_device(ISA_DEVICE(dev)); + ISADevice *isa; + DeviceState *d; + Chardev *chr; + DriveInfo *drive; + char *name; + int i; + + /* Parallel port */ + for (i = 0; i < k->parallel.count; i++) { + if (i >= ARRAY_SIZE(sio->parallel)) { + warn_report("superio: ignoring %td parallel controllers", + k->parallel.count - ARRAY_SIZE(sio->parallel)); + break; + } + if (!k->parallel.is_enabled || k->parallel.is_enabled(sio, i)) { + /* FIXME use a qdev chardev prop instead of parallel_hds[] */ + chr = parallel_hds[i]; + if (chr == NULL || chr->be) { + name = g_strdup_printf("discarding-parallel%d", i); + chr = qemu_chr_new(name, "null"); + } else { + name = g_strdup_printf("parallel%d", i); + } + isa = isa_create(bus, "isa-parallel"); + d = DEVICE(isa); + qdev_prop_set_uint32(d, "index", i); + if (k->parallel.get_iobase) { + qdev_prop_set_uint32(d, "iobase", + k->parallel.get_iobase(sio, i)); + } + if (k->parallel.get_irq) { + qdev_prop_set_uint32(d, "irq", k->parallel.get_irq(sio, i)); + } + qdev_prop_set_chr(d, "chardev", chr); + qdev_init_nofail(d); + sio->parallel[i] = isa; + trace_superio_create_parallel(i, + k->parallel.get_iobase ? + k->parallel.get_iobase(sio, i) : -1, + k->parallel.get_irq ? + k->parallel.get_irq(sio, i) : -1); + object_property_add_child(OBJECT(dev), name, + OBJECT(sio->parallel[i]), NULL); + g_free(name); + } + } + + /* Serial */ + for (i = 0; i < k->serial.count; i++) { + if (i >= ARRAY_SIZE(sio->serial)) { + warn_report("superio: ignoring %td serial controllers", + k->serial.count - ARRAY_SIZE(sio->serial)); + break; + } + if (!k->serial.is_enabled || k->serial.is_enabled(sio, i)) { + /* FIXME use a qdev chardev prop instead of serial_hds[] */ + chr = serial_hds[i]; + if (chr == NULL || chr->be) { + name = g_strdup_printf("discarding-serial%d", i); + chr = qemu_chr_new(name, "null"); + } else { + name = g_strdup_printf("serial%d", i); + } + isa = isa_create(bus, TYPE_ISA_SERIAL); + d = DEVICE(isa); + qdev_prop_set_uint32(d, "index", i); + if (k->serial.get_iobase) { + qdev_prop_set_uint32(d, "iobase", + k->serial.get_iobase(sio, i)); + } + if (k->serial.get_irq) { + qdev_prop_set_uint32(d, "irq", k->serial.get_irq(sio, i)); + } + qdev_prop_set_chr(d, "chardev", chr); + qdev_init_nofail(d); + sio->serial[i] = isa; + trace_superio_create_serial(i, + k->serial.get_iobase ? + k->serial.get_iobase(sio, i) : -1, + k->serial.get_irq ? + k->serial.get_irq(sio, i) : -1); + object_property_add_child(OBJECT(dev), name, + OBJECT(sio->serial[0]), NULL); + g_free(name); + } + } + + /* Floppy disc */ + if (!k->floppy.is_enabled || k->floppy.is_enabled(sio, 0)) { + isa = isa_create(bus, "isa-fdc"); + d = DEVICE(isa); + if (k->floppy.get_iobase) { + qdev_prop_set_uint32(d, "iobase", k->floppy.get_iobase(sio, 0)); + } + if (k->floppy.get_irq) { + qdev_prop_set_uint32(d, "irq", k->floppy.get_irq(sio, 0)); + } + /* FIXME use a qdev drive property instead of drive_get() */ + drive = drive_get(IF_FLOPPY, 0, 0); + if (drive != NULL) { + qdev_prop_set_drive(d, "driveA", blk_by_legacy_dinfo(drive), + &error_fatal); + } + /* FIXME use a qdev drive property instead of drive_get() */ + drive = drive_get(IF_FLOPPY, 0, 1); + if (drive != NULL) { + qdev_prop_set_drive(d, "driveB", blk_by_legacy_dinfo(drive), + &error_fatal); + } + qdev_init_nofail(d); + sio->floppy = isa; + trace_superio_create_floppy(0, + k->floppy.get_iobase ? + k->floppy.get_iobase(sio, 0) : -1, + k->floppy.get_irq ? + k->floppy.get_irq(sio, 0) : -1); + } + + /* Keyboard, mouse */ + sio->kbc = isa_create_simple(bus, TYPE_I8042); + + /* IDE */ + if (k->ide.count && (!k->ide.is_enabled || k->ide.is_enabled(sio, 0))) { + isa = isa_create(bus, "isa-ide"); + d = DEVICE(isa); + if (k->ide.get_iobase) { + qdev_prop_set_uint32(d, "iobase", k->ide.get_iobase(sio, 0)); + } + if (k->ide.get_iobase) { + qdev_prop_set_uint32(d, "iobase2", k->ide.get_iobase(sio, 1)); + } + if (k->ide.get_irq) { + qdev_prop_set_uint32(d, "irq", k->ide.get_irq(sio, 0)); + } + qdev_init_nofail(d); + sio->ide = isa; + trace_superio_create_ide(0, + k->ide.get_iobase ? + k->ide.get_iobase(sio, 0) : -1, + k->ide.get_irq ? + k->ide.get_irq(sio, 0) : -1); + } +} + +static void isa_superio_class_init(ObjectClass *oc, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(oc); + + dc->realize = isa_superio_realize; + /* Reason: Uses parallel_hds[0] in realize(), so it can't be used twice */ + dc->user_creatable = false; +} + +static const TypeInfo isa_superio_type_info = { + .name = TYPE_ISA_SUPERIO, + .parent = TYPE_ISA_DEVICE, + .abstract = true, + .class_size = sizeof(ISASuperIOClass), + .class_init = isa_superio_class_init, +}; + +/* SMS FDC37M817 Super I/O */ +static void fdc37m81x_class_init(ObjectClass *klass, void *data) +{ + ISASuperIOClass *sc = ISA_SUPERIO_CLASS(klass); + + sc->serial.count = 2; /* NS16C550A */ + sc->parallel.count = 1; + sc->floppy.count = 1; /* SMSC 82077AA Compatible */ + sc->ide.count = 0; +} + +static const TypeInfo fdc37m81x_type_info = { + .name = TYPE_FDC37M81X_SUPERIO, + .parent = TYPE_ISA_SUPERIO, + .instance_size = sizeof(ISASuperIODevice), + .class_init = fdc37m81x_class_init, +}; + +static void isa_superio_register_types(void) +{ + type_register_static(&isa_superio_type_info); + type_register_static(&fdc37m81x_type_info); +} + +type_init(isa_superio_register_types) diff --git a/hw/isa/pc87312.c b/hw/isa/pc87312.c index 48b29e3c3c..5cf64505fe 100644 --- a/hw/isa/pc87312.c +++ b/hw/isa/pc87312.c @@ -27,10 +27,6 @@ #include "hw/isa/pc87312.h" #include "qapi/error.h" #include "qemu/error-report.h" -#include "sysemu/block-backend.h" -#include "sysemu/blockdev.h" -#include "sysemu/sysemu.h" -#include "chardev/char.h" #include "trace.h" @@ -64,22 +60,25 @@ /* Parallel port */ -static inline bool is_parallel_enabled(PC87312State *s) +static bool is_parallel_enabled(ISASuperIODevice *sio, uint8_t index) { - return s->regs[REG_FER] & FER_PARALLEL_EN; + PC87312State *s = PC87312(sio); + return index ? false : s->regs[REG_FER] & FER_PARALLEL_EN; } -static const uint32_t parallel_base[] = { 0x378, 0x3bc, 0x278, 0x00 }; +static const uint16_t parallel_base[] = { 0x378, 0x3bc, 0x278, 0x00 }; -static inline uint32_t get_parallel_iobase(PC87312State *s) +static uint16_t get_parallel_iobase(ISASuperIODevice *sio, uint8_t index) { + PC87312State *s = PC87312(sio); return parallel_base[s->regs[REG_FAR] & FAR_PARALLEL_ADDR]; } -static const uint32_t parallel_irq[] = { 5, 7, 5, 0 }; +static const unsigned int parallel_irq[] = { 5, 7, 5, 0 }; -static inline uint32_t get_parallel_irq(PC87312State *s) +static unsigned int get_parallel_irq(ISASuperIODevice *sio, uint8_t index) { + PC87312State *s = PC87312(sio); int idx; idx = (s->regs[REG_FAR] & FAR_PARALLEL_ADDR); if (idx == 0) { @@ -92,13 +91,14 @@ static inline uint32_t get_parallel_irq(PC87312State *s) /* UARTs */ -static const uint32_t uart_base[2][4] = { +static const uint16_t uart_base[2][4] = { { 0x3e8, 0x338, 0x2e8, 0x220 }, { 0x2e8, 0x238, 0x2e0, 0x228 } }; -static inline uint32_t get_uart_iobase(PC87312State *s, int i) +static uint16_t get_uart_iobase(ISASuperIODevice *sio, uint8_t i) { + PC87312State *s = PC87312(sio); int idx; idx = (s->regs[REG_FAR] >> (2 * i + 2)) & 0x3; if (idx == 0) { @@ -110,44 +110,68 @@ static inline uint32_t get_uart_iobase(PC87312State *s, int i) } } -static inline uint32_t get_uart_irq(PC87312State *s, int i) +static unsigned int get_uart_irq(ISASuperIODevice *sio, uint8_t i) { + PC87312State *s = PC87312(sio); int idx; idx = (s->regs[REG_FAR] >> (2 * i + 2)) & 0x3; return (idx & 1) ? 3 : 4; } -static inline bool is_uart_enabled(PC87312State *s, int i) +static bool is_uart_enabled(ISASuperIODevice *sio, uint8_t i) { + PC87312State *s = PC87312(sio); return s->regs[REG_FER] & (FER_UART1_EN << i); } /* Floppy controller */ -static inline bool is_fdc_enabled(PC87312State *s) +static bool is_fdc_enabled(ISASuperIODevice *sio, uint8_t index) { + PC87312State *s = PC87312(sio); + assert(!index); return s->regs[REG_FER] & FER_FDC_EN; } -static inline uint32_t get_fdc_iobase(PC87312State *s) +static uint16_t get_fdc_iobase(ISASuperIODevice *sio, uint8_t index) { + PC87312State *s = PC87312(sio); + assert(!index); return (s->regs[REG_FER] & FER_FDC_ADDR) ? 0x370 : 0x3f0; } +static unsigned int get_fdc_irq(ISASuperIODevice *sio, uint8_t index) +{ + assert(!index); + return 6; +} + /* IDE controller */ -static inline bool is_ide_enabled(PC87312State *s) +static bool is_ide_enabled(ISASuperIODevice *sio, uint8_t index) { + PC87312State *s = PC87312(sio); + return s->regs[REG_FER] & FER_IDE_EN; } -static inline uint32_t get_ide_iobase(PC87312State *s) +static uint16_t get_ide_iobase(ISASuperIODevice *sio, uint8_t index) { + PC87312State *s = PC87312(sio); + + if (index == 1) { + return get_ide_iobase(sio, 0) + 0x206; + } return (s->regs[REG_FER] & FER_IDE_ADDR) ? 0x170 : 0x1f0; } +static unsigned int get_ide_irq(ISASuperIODevice *sio, uint8_t index) +{ + assert(index == 0); + return 14; +} static void reconfigure_devices(PC87312State *s) { @@ -265,90 +289,18 @@ static void pc87312_reset(DeviceState *d) static void pc87312_realize(DeviceState *dev, Error **errp) { PC87312State *s; - DeviceState *d; ISADevice *isa; - ISABus *bus; - Chardev *chr; - DriveInfo *drive; - char name[5]; - int i; + Error *local_err = NULL; s = PC87312(dev); isa = ISA_DEVICE(dev); - bus = isa_bus_from_device(isa); isa_register_ioport(isa, &s->io, s->iobase); pc87312_hard_reset(s); - if (is_parallel_enabled(s)) { - /* FIXME use a qdev chardev prop instead of parallel_hds[] */ - chr = parallel_hds[0]; - if (chr == NULL) { - chr = qemu_chr_new("par0", "null"); - } - isa = isa_create(bus, "isa-parallel"); - d = DEVICE(isa); - qdev_prop_set_uint32(d, "index", 0); - qdev_prop_set_uint32(d, "iobase", get_parallel_iobase(s)); - qdev_prop_set_uint32(d, "irq", get_parallel_irq(s)); - qdev_prop_set_chr(d, "chardev", chr); - qdev_init_nofail(d); - s->parallel.dev = isa; - trace_pc87312_info_parallel(get_parallel_iobase(s), - get_parallel_irq(s)); - } - - for (i = 0; i < 2; i++) { - if (is_uart_enabled(s, i)) { - /* FIXME use a qdev chardev prop instead of serial_hds[] */ - chr = serial_hds[i]; - if (chr == NULL) { - snprintf(name, sizeof(name), "ser%d", i); - chr = qemu_chr_new(name, "null"); - } - isa = isa_create(bus, "isa-serial"); - d = DEVICE(isa); - qdev_prop_set_uint32(d, "index", i); - qdev_prop_set_uint32(d, "iobase", get_uart_iobase(s, i)); - qdev_prop_set_uint32(d, "irq", get_uart_irq(s, i)); - qdev_prop_set_chr(d, "chardev", chr); - qdev_init_nofail(d); - s->uart[i].dev = isa; - trace_pc87312_info_serial(i, get_uart_iobase(s, i), - get_uart_irq(s, i)); - } - } - - if (is_fdc_enabled(s)) { - isa = isa_create(bus, "isa-fdc"); - d = DEVICE(isa); - qdev_prop_set_uint32(d, "iobase", get_fdc_iobase(s)); - qdev_prop_set_uint32(d, "irq", 6); - /* FIXME use a qdev drive property instead of drive_get() */ - drive = drive_get(IF_FLOPPY, 0, 0); - if (drive != NULL) { - qdev_prop_set_drive(d, "driveA", blk_by_legacy_dinfo(drive), - &error_fatal); - } - /* FIXME use a qdev drive property instead of drive_get() */ - drive = drive_get(IF_FLOPPY, 0, 1); - if (drive != NULL) { - qdev_prop_set_drive(d, "driveB", blk_by_legacy_dinfo(drive), - &error_fatal); - } - qdev_init_nofail(d); - s->fdc.dev = isa; - trace_pc87312_info_floppy(get_fdc_iobase(s)); - } - - if (is_ide_enabled(s)) { - isa = isa_create(bus, "isa-ide"); - d = DEVICE(isa); - qdev_prop_set_uint32(d, "iobase", get_ide_iobase(s)); - qdev_prop_set_uint32(d, "iobase2", get_ide_iobase(s) + 0x206); - qdev_prop_set_uint32(d, "irq", 14); - qdev_init_nofail(d); - s->ide.dev = isa; - trace_pc87312_info_ide(get_ide_iobase(s)); + ISA_SUPERIO_GET_CLASS(dev)->parent_realize(dev, &local_err); + if (local_err) { + error_propagate(errp, local_err); + return; } } @@ -373,7 +325,7 @@ static const VMStateDescription vmstate_pc87312 = { }; static Property pc87312_properties[] = { - DEFINE_PROP_UINT32("iobase", PC87312State, iobase, 0x398), + DEFINE_PROP_UINT16("iobase", PC87312State, iobase, 0x398), DEFINE_PROP_UINT8("config", PC87312State, config, 1), DEFINE_PROP_END_OF_LIST() }; @@ -381,21 +333,47 @@ static Property pc87312_properties[] = { static void pc87312_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); + ISASuperIOClass *sc = ISA_SUPERIO_CLASS(klass); + sc->parent_realize = dc->realize; dc->realize = pc87312_realize; dc->reset = pc87312_reset; dc->vmsd = &vmstate_pc87312; dc->props = pc87312_properties; - /* Reason: Uses parallel_hds[0] in realize(), so it can't be used twice */ - dc->user_creatable = false; + + sc->parallel = (ISASuperIOFuncs){ + .count = 1, + .is_enabled = is_parallel_enabled, + .get_iobase = get_parallel_iobase, + .get_irq = get_parallel_irq, + }; + sc->serial = (ISASuperIOFuncs){ + .count = 2, + .is_enabled = is_uart_enabled, + .get_iobase = get_uart_iobase, + .get_irq = get_uart_irq, + }; + sc->floppy = (ISASuperIOFuncs){ + .count = 1, + .is_enabled = is_fdc_enabled, + .get_iobase = get_fdc_iobase, + .get_irq = get_fdc_irq, + }; + sc->ide = (ISASuperIOFuncs){ + .count = 1, + .is_enabled = is_ide_enabled, + .get_iobase = get_ide_iobase, + .get_irq = get_ide_irq, + }; } static const TypeInfo pc87312_type_info = { - .name = TYPE_PC87312, - .parent = TYPE_ISA_DEVICE, + .name = TYPE_PC87312_SUPERIO, + .parent = TYPE_ISA_SUPERIO, .instance_size = sizeof(PC87312State), .instance_init = pc87312_initfn, .class_init = pc87312_class_init, + /* FIXME use a qdev drive property instead of drive_get() */ }; static void pc87312_register_types(void) diff --git a/hw/isa/smc37c669-superio.c b/hw/isa/smc37c669-superio.c new file mode 100644 index 0000000000..aa233c6967 --- /dev/null +++ b/hw/isa/smc37c669-superio.c @@ -0,0 +1,115 @@ +/* + * SMC FDC37C669 Super I/O controller + * + * Copyright (c) 2018 Philippe Mathieu-Daudé + * + * This code is licensed under the GNU GPLv2 and later. + * See the COPYING file in the top-level directory. + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "qemu/osdep.h" +#include "hw/isa/superio.h" + +/* UARTs (compatible with NS16450 or PC16550) */ + +static bool is_serial_enabled(ISASuperIODevice *sio, uint8_t index) +{ + return index < 2; +} + +static uint16_t get_serial_iobase(ISASuperIODevice *sio, uint8_t index) +{ + return index ? 0x2f8 : 0x3f8; +} + +static unsigned int get_serial_irq(ISASuperIODevice *sio, uint8_t index) +{ + return index ? 3 : 4; +} + +/* Parallel port */ + +static bool is_parallel_enabled(ISASuperIODevice *sio, uint8_t index) +{ + return index < 1; +} + +static uint16_t get_parallel_iobase(ISASuperIODevice *sio, uint8_t index) +{ + return 0x3bc; +} + +static unsigned int get_parallel_irq(ISASuperIODevice *sio, uint8_t index) +{ + return 7; +} + +static unsigned int get_parallel_dma(ISASuperIODevice *sio, uint8_t index) +{ + return 3; +} + +/* Diskette controller (Software compatible with the Intel PC8477) */ + +static bool is_fdc_enabled(ISASuperIODevice *sio, uint8_t index) +{ + return index < 1; +} + +static uint16_t get_fdc_iobase(ISASuperIODevice *sio, uint8_t index) +{ + return 0x3f0; +} + +static unsigned int get_fdc_irq(ISASuperIODevice *sio, uint8_t index) +{ + return 6; +} + +static unsigned int get_fdc_dma(ISASuperIODevice *sio, uint8_t index) +{ + return 2; +} + +static void smc37c669_class_init(ObjectClass *klass, void *data) +{ + ISASuperIOClass *sc = ISA_SUPERIO_CLASS(klass); + + sc->parallel = (ISASuperIOFuncs){ + .count = 1, + .is_enabled = is_parallel_enabled, + .get_iobase = get_parallel_iobase, + .get_irq = get_parallel_irq, + .get_dma = get_parallel_dma, + }; + sc->serial = (ISASuperIOFuncs){ + .count = 2, + .is_enabled = is_serial_enabled, + .get_iobase = get_serial_iobase, + .get_irq = get_serial_irq, + }; + sc->floppy = (ISASuperIOFuncs){ + .count = 1, + .is_enabled = is_fdc_enabled, + .get_iobase = get_fdc_iobase, + .get_irq = get_fdc_irq, + .get_dma = get_fdc_dma, + }; + sc->ide.count = 0; +} + +static const TypeInfo smc37c669_type_info = { + .name = TYPE_SMC37C669_SUPERIO, + .parent = TYPE_ISA_SUPERIO, + .instance_size = sizeof(ISASuperIODevice), + .class_size = sizeof(ISASuperIOClass), + .class_init = smc37c669_class_init, +}; + +static void smc37c669_register_types(void) +{ + type_register_static(&smc37c669_type_info); +} + +type_init(smc37c669_register_types) diff --git a/hw/isa/trace-events b/hw/isa/trace-events index a4ab4e3634..80ac6175d6 100644 --- a/hw/isa/trace-events +++ b/hw/isa/trace-events @@ -1,9 +1,11 @@ # See docs/devel/tracing.txt for syntax documentation. +# hw/isa/isa-superio.c +superio_create_parallel(int id, uint16_t base, unsigned int irq) "id=%d, base 0x%03x, irq %u" +superio_create_serial(int id, uint16_t base, unsigned int irq) "id=%d, base 0x%03x, irq %u" +superio_create_floppy(int id, uint16_t base, unsigned int irq) "id=%d, base 0x%03x, irq %u" +superio_create_ide(int id, uint16_t base, unsigned int irq) "id=%d, base 0x%03x, irq %u" + # hw/isa/pc87312.c pc87312_io_read(uint32_t addr, uint32_t val) "read addr=0x%x val=0x%x" pc87312_io_write(uint32_t addr, uint32_t val) "write addr=0x%x val=0x%x" -pc87312_info_floppy(uint32_t base) "base 0x%x" -pc87312_info_ide(uint32_t base) "base 0x%x" -pc87312_info_parallel(uint32_t base, uint32_t irq) "base 0x%x, irq %u" -pc87312_info_serial(int n, uint32_t base, uint32_t irq) "id=%d, base 0x%x, irq %u" diff --git a/hw/isa/vt82c686.c b/hw/isa/vt82c686.c index 070cc1889f..cff1946232 100644 --- a/hw/isa/vt82c686.c +++ b/hw/isa/vt82c686.c @@ -17,6 +17,7 @@ #include "hw/i2c/smbus.h" #include "hw/pci/pci.h" #include "hw/isa/isa.h" +#include "hw/isa/superio.h" #include "hw/sysbus.h" #include "hw/mips/mips.h" #include "hw/isa/apm.h" @@ -478,7 +479,7 @@ static void vt82c686b_realize(PCIDevice *d, Error **errp) qemu_register_reset(vt82c686b_reset, d); } -ISABus *vt82c686b_init(PCIBus *bus, int devfn) +ISABus *vt82c686b_isa_init(PCIBus *bus, int devfn) { PCIDevice *d; @@ -519,11 +520,30 @@ static const TypeInfo via_info = { }, }; +static void vt82c686b_superio_class_init(ObjectClass *klass, void *data) +{ + ISASuperIOClass *sc = ISA_SUPERIO_CLASS(klass); + + sc->serial.count = 2; + sc->parallel.count = 1; + sc->ide.count = 0; + sc->floppy.count = 1; +} + +static const TypeInfo via_superio_info = { + .name = TYPE_VT82C686B_SUPERIO, + .parent = TYPE_ISA_SUPERIO, + .instance_size = sizeof(ISASuperIODevice), + .class_size = sizeof(ISASuperIOClass), + .class_init = vt82c686b_superio_class_init, +}; + static void vt82c686b_register_types(void) { type_register_static(&via_ac97_info); type_register_static(&via_mc97_info); type_register_static(&via_pm_info); + type_register_static(&via_superio_info); type_register_static(&via_info); } diff --git a/hw/mips/mips_fulong2e.c b/hw/mips/mips_fulong2e.c index f68c625666..02fb2fdcc4 100644 --- a/hw/mips/mips_fulong2e.c +++ b/hw/mips/mips_fulong2e.c @@ -22,17 +22,15 @@ #include "qapi/error.h" #include "hw/hw.h" #include "hw/i386/pc.h" -#include "hw/char/serial.h" -#include "hw/block/fdc.h" +#include "hw/dma/i8257.h" +#include "hw/isa/superio.h" #include "net/net.h" #include "hw/boards.h" #include "hw/i2c/smbus.h" -#include "sysemu/block-backend.h" #include "hw/block/flash.h" #include "hw/mips/mips.h" #include "hw/mips/cpudevs.h" #include "hw/pci/pci.h" -#include "sysemu/sysemu.h" #include "audio/audio.h" #include "qemu/log.h" #include "hw/loader.h" @@ -42,7 +40,6 @@ #include "hw/isa/vt82c686.h" #include "hw/timer/mc146818rtc.h" #include "hw/timer/i8254.h" -#include "sysemu/blockdev.h" #include "exec/address-spaces.h" #include "sysemu/qtest.h" #include "qemu/error-report.h" @@ -75,8 +72,6 @@ #define FULONG2E_ATI_SLOT 6 #define FULONG2E_RTL8139_SLOT 7 -static ISADevice *pit; - static struct _loaderparams { int ram_size; const char *kernel_filename; @@ -229,11 +224,40 @@ static const uint8_t eeprom_spd[0x80] = { 0x20,0x30,0x20 }; -/* Audio support */ -static void audio_init (PCIBus *pci_bus) +static void vt82c686b_southbridge_init(PCIBus *pci_bus, int slot, qemu_irq intc, + I2CBus **i2c_bus, ISABus **p_isa_bus) { - vt82c686b_ac97_init(pci_bus, PCI_DEVFN(FULONG2E_VIA_SLOT, 5)); - vt82c686b_mc97_init(pci_bus, PCI_DEVFN(FULONG2E_VIA_SLOT, 6)); + qemu_irq *i8259; + ISABus *isa_bus; + DriveInfo *hd[MAX_IDE_BUS * MAX_IDE_DEVS]; + + isa_bus = vt82c686b_isa_init(pci_bus, PCI_DEVFN(slot, 0)); + if (!isa_bus) { + fprintf(stderr, "vt82c686b_init error\n"); + exit(1); + } + *p_isa_bus = isa_bus; + /* Interrupt controller */ + /* The 8259 -> IP5 */ + i8259 = i8259_init(isa_bus, intc); + isa_bus_irqs(isa_bus, i8259); + /* init other devices */ + i8254_pit_init(isa_bus, 0x40, 0, NULL); + i8257_dma_init(isa_bus, 0); + /* Super I/O */ + isa_create_simple(isa_bus, TYPE_VT82C686B_SUPERIO); + + ide_drive_get(hd, ARRAY_SIZE(hd)); + vt82c686b_ide_init(pci_bus, hd, PCI_DEVFN(slot, 1)); + + pci_create_simple(pci_bus, PCI_DEVFN(slot, 2), "vt82c686b-usb-uhci"); + pci_create_simple(pci_bus, PCI_DEVFN(slot, 3), "vt82c686b-usb-uhci"); + + *i2c_bus = vt82c686b_pm_init(pci_bus, PCI_DEVFN(slot, 4), 0xeee1, NULL); + + /* Audio support */ + vt82c686b_ac97_init(pci_bus, PCI_DEVFN(slot, 5)); + vt82c686b_mc97_init(pci_bus, PCI_DEVFN(slot, 6)); } /* Network support */ @@ -266,11 +290,9 @@ static void mips_fulong2e_init(MachineState *machine) MemoryRegion *bios = g_new(MemoryRegion, 1); long bios_size; int64_t kernel_entry; - qemu_irq *i8259; PCIBus *pci_bus; ISABus *isa_bus; I2CBus *smbus; - DriveInfo *hd[MAX_IDE_BUS * MAX_IDE_DEVS]; MIPSCPU *cpu; CPUMIPSState *env; @@ -332,46 +354,16 @@ static void mips_fulong2e_init(MachineState *machine) /* North bridge, Bonito --> IP2 */ pci_bus = bonito_init((qemu_irq *)&(env->irq[2])); - /* South bridge */ - ide_drive_get(hd, ARRAY_SIZE(hd)); + /* South bridge -> IP5 */ + vt82c686b_southbridge_init(pci_bus, FULONG2E_VIA_SLOT, env->irq[5], + &smbus, &isa_bus); - isa_bus = vt82c686b_init(pci_bus, PCI_DEVFN(FULONG2E_VIA_SLOT, 0)); - if (!isa_bus) { - error_report("vt82c686b_init error"); - exit(1); - } - - /* Interrupt controller */ - /* The 8259 -> IP5 */ - i8259 = i8259_init(isa_bus, env->irq[5]); - isa_bus_irqs(isa_bus, i8259); - - vt82c686b_ide_init(pci_bus, hd, PCI_DEVFN(FULONG2E_VIA_SLOT, 1)); - pci_create_simple(pci_bus, PCI_DEVFN(FULONG2E_VIA_SLOT, 2), - "vt82c686b-usb-uhci"); - pci_create_simple(pci_bus, PCI_DEVFN(FULONG2E_VIA_SLOT, 3), - "vt82c686b-usb-uhci"); - - smbus = vt82c686b_pm_init(pci_bus, PCI_DEVFN(FULONG2E_VIA_SLOT, 4), - 0xeee1, NULL); /* TODO: Populate SPD eeprom data. */ smbus_eeprom_init(smbus, 1, eeprom_spd, sizeof(eeprom_spd)); - /* init other devices */ - pit = i8254_pit_init(isa_bus, 0x40, 0, NULL); - DMA_init(isa_bus, 0); - - /* Super I/O */ - isa_create_simple(isa_bus, "i8042"); - mc146818_rtc_init(isa_bus, 2000, NULL); - serial_hds_isa_init(isa_bus, 0, MAX_SERIAL_PORTS); - parallel_hds_isa_init(isa_bus, 1); - - /* Sound card */ - audio_init(pci_bus); - /* Network card */ + /* Network card: RTL8139D */ network_init(pci_bus); } diff --git a/hw/mips/mips_jazz.c b/hw/mips/mips_jazz.c index b09871a814..7223085547 100644 --- a/hw/mips/mips_jazz.c +++ b/hw/mips/mips_jazz.c @@ -27,7 +27,9 @@ #include "hw/mips/mips.h" #include "hw/mips/cpudevs.h" #include "hw/i386/pc.h" +#include "hw/dma/i8257.h" #include "hw/char/serial.h" +#include "hw/char/parallel.h" #include "hw/isa/isa.h" #include "hw/block/fdc.h" #include "sysemu/sysemu.h" @@ -41,7 +43,7 @@ #include "hw/timer/i8254.h" #include "hw/display/vga.h" #include "hw/audio/pcspk.h" -#include "sysemu/block-backend.h" +#include "hw/input/i8042.h" #include "hw/sysbus.h" #include "exec/address-spaces.h" #include "sysemu/qtest.h" @@ -147,6 +149,7 @@ static void mips_jazz_init(MachineState *machine, MemoryRegion *ram = g_new(MemoryRegion, 1); MemoryRegion *bios = g_new(MemoryRegion, 1); MemoryRegion *bios2 = g_new(MemoryRegion, 1); + ESPState *esp; /* init CPUs */ cpu = MIPS_CPU(cpu_create(machine->cpu_type)); @@ -219,7 +222,7 @@ static void mips_jazz_init(MachineState *machine, /* ISA devices */ i8259 = i8259_init(isa_bus, env->irq[4]); isa_bus_irqs(isa_bus, i8259); - DMA_init(isa_bus, 0); + i8257_dma_init(isa_bus, 0); pit = i8254_pit_init(isa_bus, 0x40, 0, NULL); pcspk_init(isa_bus, pit); @@ -278,9 +281,9 @@ static void mips_jazz_init(MachineState *machine, } /* SCSI adapter */ - esp_init(0x80002000, 0, - rc4030_dma_read, rc4030_dma_write, dmas[0], - qdev_get_gpio_in(rc4030, 5), &esp_reset, &dma_enable); + esp = esp_init(0x80002000, 0, rc4030_dma_read, rc4030_dma_write, dmas[0], + qdev_get_gpio_in(rc4030, 5), &esp_reset, &dma_enable); + scsi_bus_legacy_handle_cmdline(&esp->bus); /* Floppy */ for (n = 0; n < MAX_FD; n++) { diff --git a/hw/mips/mips_malta.c b/hw/mips/mips_malta.c index 6f0deb99e7..f6513a4fd5 100644 --- a/hw/mips/mips_malta.c +++ b/hw/mips/mips_malta.c @@ -27,12 +27,12 @@ #include "cpu.h" #include "hw/hw.h" #include "hw/i386/pc.h" +#include "hw/isa/superio.h" +#include "hw/dma/i8257.h" #include "hw/char/serial.h" -#include "hw/block/fdc.h" #include "net/net.h" #include "hw/boards.h" #include "hw/i2c/smbus.h" -#include "sysemu/block-backend.h" #include "hw/block/flash.h" #include "hw/mips/mips.h" #include "hw/mips/cpudevs.h" @@ -1002,10 +1002,8 @@ void mips_malta_init(MachineState *machine) qemu_irq cbus_irq, i8259_irq; int piix4_devfn; I2CBus *smbus; - int i; DriveInfo *dinfo; DriveInfo *hd[MAX_IDE_BUS * MAX_IDE_DEVS]; - DriveInfo *fd[MAX_FD]; int fl_idx = 0; int fl_sectors = bios_size >> 16; int be; @@ -1020,15 +1018,6 @@ void mips_malta_init(MachineState *machine) qdev_init_nofail(dev); - /* Make sure the first 3 serial ports are associated with a device. */ - for(i = 0; i < 3; i++) { - if (!serial_hds[i]) { - char label[32]; - snprintf(label, sizeof(label), "serial%d", i); - serial_hds[i] = qemu_chr_new(label, "null"); - } - } - /* create CPU */ mips_create_cpu(s, machine->cpu_type, &cbus_irq, &i8259_irq); @@ -1059,16 +1048,19 @@ void mips_malta_init(MachineState *machine) memory_region_add_subregion(system_memory, 512 << 20, ram_low_postio); } - /* generate SPD EEPROM data */ - generate_eeprom_spd(&smbus_eeprom_buf[0 * 256], ram_size); - generate_eeprom_serial(&smbus_eeprom_buf[6 * 256]); - #ifdef TARGET_WORDS_BIGENDIAN be = 1; #else be = 0; #endif + /* FPGA */ + + /* Make sure the second serial port is associated with a device. */ + if (!serial_hds[2]) { + serial_hds[2] = qemu_chr_new("fpga-uart", "null"); + } + /* The CBUS UART is attached to the MIPS CPU INT2 pin, ie interrupt 4 */ malta_fpga_init(system_memory, FPGA_ADDRESS, cbus_irq, serial_hds[2]); @@ -1205,22 +1197,18 @@ void mips_malta_init(MachineState *machine) pci_create_simple(pci_bus, piix4_devfn + 2, "piix4-usb-uhci"); smbus = piix4_pm_init(pci_bus, piix4_devfn + 3, 0x1100, isa_get_irq(NULL, 9), NULL, 0, NULL); - smbus_eeprom_init(smbus, 8, smbus_eeprom_buf, smbus_eeprom_size); - g_free(smbus_eeprom_buf); pit = i8254_pit_init(isa_bus, 0x40, 0, NULL); - DMA_init(isa_bus, 0); - - /* Super I/O */ - isa_create_simple(isa_bus, "i8042"); - + i8257_dma_init(isa_bus, 0); mc146818_rtc_init(isa_bus, 2000, NULL); - serial_hds_isa_init(isa_bus, 0, 2); - parallel_hds_isa_init(isa_bus, 1); - for(i = 0; i < MAX_FD; i++) { - fd[i] = drive_get(IF_FLOPPY, 0, i); - } - fdctrl_init_isa(isa_bus, fd); + /* generate SPD EEPROM data */ + generate_eeprom_spd(&smbus_eeprom_buf[0 * 256], ram_size); + generate_eeprom_serial(&smbus_eeprom_buf[6 * 256]); + smbus_eeprom_init(smbus, 8, smbus_eeprom_buf, smbus_eeprom_size); + g_free(smbus_eeprom_buf); + + /* Super I/O: SMS FDC37M817 */ + isa_create_simple(isa_bus, TYPE_FDC37M81X_SUPERIO); /* Network card */ network_init(pci_bus); diff --git a/hw/mips/mips_r4k.c b/hw/mips/mips_r4k.c index 830ee7732c..aeadc4a340 100644 --- a/hw/mips/mips_r4k.c +++ b/hw/mips/mips_r4k.c @@ -28,6 +28,7 @@ #include "hw/loader.h" #include "elf.h" #include "hw/timer/mc146818rtc.h" +#include "hw/input/i8042.h" #include "hw/timer/i8254.h" #include "sysemu/block-backend.h" #include "exec/address-spaces.h" @@ -286,7 +287,7 @@ void mips_r4k_init(MachineState *machine) hd[MAX_IDE_DEVS * i], hd[MAX_IDE_DEVS * i + 1]); - isa_create_simple(isa_bus, "i8042"); + isa_create_simple(isa_bus, TYPE_I8042); } static void mips_machine_init(MachineClass *mc) diff --git a/hw/pci/pci.c b/hw/pci/pci.c index 2174c254eb..67a3f72bd6 100644 --- a/hw/pci/pci.c +++ b/hw/pci/pci.c @@ -1815,49 +1815,48 @@ PciInfoList *qmp_query_pci(Error **errp) return head; } -static const char * const pci_nic_models[] = { - "ne2k_pci", - "i82551", - "i82557b", - "i82559er", - "rtl8139", - "e1000", - "pcnet", - "virtio", - "sungem", - NULL -}; - -static const char * const pci_nic_names[] = { - "ne2k_pci", - "i82551", - "i82557b", - "i82559er", - "rtl8139", - "e1000", - "pcnet", - "virtio-net-pci", - "sungem", - NULL -}; - /* Initialize a PCI NIC. */ PCIDevice *pci_nic_init_nofail(NICInfo *nd, PCIBus *rootbus, const char *default_model, const char *default_devaddr) { const char *devaddr = nd->devaddr ? nd->devaddr : default_devaddr; + GSList *list; + GPtrArray *pci_nic_models; PCIBus *bus; PCIDevice *pci_dev; DeviceState *dev; int devfn; int i; - if (qemu_show_nic_models(nd->model, pci_nic_models)) { + if (nd->model && !strcmp(nd->model, "virtio")) { + g_free(nd->model); + nd->model = g_strdup("virtio-net-pci"); + } + + list = object_class_get_list_sorted(TYPE_PCI_DEVICE, false); + pci_nic_models = g_ptr_array_new(); + while (list) { + DeviceClass *dc = OBJECT_CLASS_CHECK(DeviceClass, list->data, + TYPE_DEVICE); + GSList *next; + if (test_bit(DEVICE_CATEGORY_NETWORK, dc->categories) && + dc->user_creatable) { + const char *name = object_class_get_name(list->data); + g_ptr_array_add(pci_nic_models, (gpointer)name); + } + next = list->next; + g_slist_free_1(list); + list = next; + } + g_ptr_array_add(pci_nic_models, NULL); + + if (qemu_show_nic_models(nd->model, (const char **)pci_nic_models->pdata)) { exit(0); } - i = qemu_find_nic_model(nd, pci_nic_models, default_model); + i = qemu_find_nic_model(nd, (const char **)pci_nic_models->pdata, + default_model); if (i < 0) { exit(1); } @@ -1865,15 +1864,15 @@ PCIDevice *pci_nic_init_nofail(NICInfo *nd, PCIBus *rootbus, bus = pci_get_bus_devfn(&devfn, rootbus, devaddr); if (!bus) { error_report("Invalid PCI device address %s for device %s", - devaddr, pci_nic_names[i]); + devaddr, nd->model); exit(1); } - pci_dev = pci_create(bus, devfn, pci_nic_names[i]); + pci_dev = pci_create(bus, devfn, nd->model); dev = &pci_dev->qdev; qdev_set_nic_properties(dev, nd); qdev_init_nofail(dev); - + g_ptr_array_free(pci_nic_models, true); return pci_dev; } diff --git a/hw/ppc/e500.c b/hw/ppc/e500.c index 43c15d18c4..2238f963c4 100644 --- a/hw/ppc/e500.c +++ b/hw/ppc/e500.c @@ -916,7 +916,7 @@ void ppce500_init(MachineState *machine, PPCE500Params *params) if (pci_bus) { /* Register network interfaces. */ for (i = 0; i < nb_nics; i++) { - pci_nic_init_nofail(&nd_table[i], pci_bus, "virtio", NULL); + pci_nic_init_nofail(&nd_table[i], pci_bus, "virtio-net-pci", NULL); } } diff --git a/hw/ppc/mac_newworld.c b/hw/ppc/mac_newworld.c index a749e2565d..2f5b6f651a 100644 --- a/hw/ppc/mac_newworld.c +++ b/hw/ppc/mac_newworld.c @@ -69,7 +69,6 @@ #include "sysemu/kvm.h" #include "kvm_ppc.h" #include "hw/usb.h" -#include "sysemu/block-backend.h" #include "exec/address-spaces.h" #include "hw/sysbus.h" #include "qemu/cutils.h" diff --git a/hw/ppc/mac_oldworld.c b/hw/ppc/mac_oldworld.c index 935493c966..10e291ca22 100644 --- a/hw/ppc/mac_oldworld.c +++ b/hw/ppc/mac_oldworld.c @@ -44,7 +44,6 @@ #include "qemu/error-report.h" #include "sysemu/kvm.h" #include "kvm_ppc.h" -#include "sysemu/block-backend.h" #include "exec/address-spaces.h" #include "qemu/cutils.h" diff --git a/hw/ppc/prep.c b/hw/ppc/prep.c index 096d4d4cfb..5c78503069 100644 --- a/hw/ppc/prep.c +++ b/hw/ppc/prep.c @@ -41,9 +41,9 @@ #include "hw/ide.h" #include "hw/loader.h" #include "hw/timer/mc146818rtc.h" +#include "hw/input/i8042.h" #include "hw/isa/pc87312.h" #include "hw/net/ne2000-isa.h" -#include "sysemu/block-backend.h" #include "sysemu/arch_init.h" #include "sysemu/kvm.h" #include "sysemu/qtest.h" @@ -612,7 +612,7 @@ static void ppc_prep_init(MachineState *machine) isa_bus = ISA_BUS(qdev_get_child_bus(DEVICE(pci), "isa.0")); /* Super I/O (parallel + serial ports) */ - isa = isa_create(isa_bus, TYPE_PC87312); + isa = isa_create(isa_bus, TYPE_PC87312_SUPERIO); dev = DEVICE(isa); qdev_prop_set_uint8(dev, "config", 13); /* fdc, ser0, ser1, par0 */ qdev_init_nofail(dev); @@ -641,7 +641,6 @@ static void ppc_prep_init(MachineState *machine) hd[2 * i], hd[2 * i + 1]); } - isa_create_simple(isa_bus, "i8042"); cpu = POWERPC_CPU(first_cpu); sysctrl->reset_irq = cpu->env.irq_inputs[PPC6xx_INPUT_HRESET]; @@ -771,7 +770,7 @@ static void ibm_40p_init(MachineState *machine) /* add some more devices */ if (defaults_enabled()) { - isa_create_simple(isa_bus, "i8042"); + isa_create_simple(isa_bus, TYPE_I8042); m48t59 = NVRAM(isa_create_simple(isa_bus, "isa-m48t59")); dev = DEVICE(isa_create(isa_bus, "cs4231a")); diff --git a/hw/ppc/spapr.c b/hw/ppc/spapr.c index 7e1c858566..032d03423f 100644 --- a/hw/ppc/spapr.c +++ b/hw/ppc/spapr.c @@ -2855,6 +2855,11 @@ static void spapr_set_modern_hotplug_events(Object *obj, bool value, spapr->use_hotplug_event_source = value; } +static bool spapr_get_msix_emulation(Object *obj, Error **errp) +{ + return true; +} + static char *spapr_get_resize_hpt(Object *obj, Error **errp) { sPAPRMachineState *spapr = SPAPR_MACHINE(obj); @@ -2936,6 +2941,8 @@ static void spapr_instance_init(Object *obj) object_property_set_description(obj, "vsmt", "Virtual SMT: KVM behaves as if this were" " the host's SMT mode", &error_abort); + object_property_add_bool(obj, "vfio-no-msix-emulation", + spapr_get_msix_emulation, NULL, NULL); } static void spapr_machine_finalizefn(Object *obj) diff --git a/hw/s390x/s390-stattrib.c b/hw/s390x/s390-stattrib.c index adf07ef312..70b95550a8 100644 --- a/hw/s390x/s390-stattrib.c +++ b/hw/s390x/s390-stattrib.c @@ -183,15 +183,16 @@ static int cmma_save_setup(QEMUFile *f, void *opaque) } static void cmma_save_pending(QEMUFile *f, void *opaque, uint64_t max_size, - uint64_t *non_postcopiable_pending, - uint64_t *postcopiable_pending) + uint64_t *res_precopy_only, + uint64_t *res_compatible, + uint64_t *res_postcopy_only) { S390StAttribState *sas = S390_STATTRIB(opaque); S390StAttribClass *sac = S390_STATTRIB_GET_CLASS(sas); long long res = sac->get_dirtycount(sas); if (res >= 0) { - *non_postcopiable_pending += res; + *res_precopy_only += res; } } diff --git a/hw/scsi/esp.c b/hw/scsi/esp.c index 45975c21e8..64ec285826 100644 --- a/hw/scsi/esp.c +++ b/hw/scsi/esp.c @@ -618,11 +618,11 @@ static const MemoryRegionOps sysbus_esp_mem_ops = { .valid.accepts = esp_mem_accepts, }; -void esp_init(hwaddr espaddr, int it_shift, - ESPDMAMemoryReadWriteFunc dma_memory_read, - ESPDMAMemoryReadWriteFunc dma_memory_write, - void *dma_opaque, qemu_irq irq, qemu_irq *reset, - qemu_irq *dma_enable) +ESPState *esp_init(hwaddr espaddr, int it_shift, + ESPDMAMemoryReadWriteFunc dma_memory_read, + ESPDMAMemoryReadWriteFunc dma_memory_write, + void *dma_opaque, qemu_irq irq, qemu_irq *reset, + qemu_irq *dma_enable) { DeviceState *dev; SysBusDevice *s; @@ -644,6 +644,8 @@ void esp_init(hwaddr espaddr, int it_shift, sysbus_mmio_map(s, 0, espaddr); *reset = qdev_get_gpio_in(dev, 0); *dma_enable = qdev_get_gpio_in(dev, 1); + + return esp; } static const struct SCSIBusInfo esp_scsi_info = { diff --git a/hw/scsi/mptendian.c b/hw/scsi/mptendian.c index 3415229b5e..8ae39a76f4 100644 --- a/hw/scsi/mptendian.c +++ b/hw/scsi/mptendian.c @@ -24,7 +24,6 @@ #include "hw/hw.h" #include "hw/pci/pci.h" #include "sysemu/dma.h" -#include "sysemu/block-backend.h" #include "hw/pci/msi.h" #include "qemu/iov.h" #include "hw/scsi/scsi.h" diff --git a/hw/scsi/scsi-bus.c b/hw/scsi/scsi-bus.c index 1eaeffc830..9646743a7d 100644 --- a/hw/scsi/scsi-bus.c +++ b/hw/scsi/scsi-bus.c @@ -944,7 +944,7 @@ static int scsi_req_xfer(SCSICommand *cmd, SCSIDevice *dev, uint8_t *buf) break; case WRITE_SAME_10: case WRITE_SAME_16: - cmd->xfer = dev->blocksize; + cmd->xfer = buf[1] & 1 ? 0 : dev->blocksize; break; case READ_CAPACITY_10: cmd->xfer = 8; diff --git a/hw/scsi/scsi-disk.c b/hw/scsi/scsi-disk.c index 49d2559d93..5b7a48f5a5 100644 --- a/hw/scsi/scsi-disk.c +++ b/hw/scsi/scsi-disk.c @@ -704,6 +704,21 @@ static int scsi_disk_emulate_inquiry(SCSIRequest *req, uint8_t *outbuf) page_code); return -1; } + if (s->qdev.type == TYPE_DISK) { + int max_transfer_blk = blk_get_max_transfer(s->qdev.conf.blk); + int max_io_sectors_blk = + max_transfer_blk / s->qdev.blocksize; + + max_io_sectors = + MIN_NON_ZERO(max_io_sectors_blk, max_io_sectors); + + /* min_io_size and opt_io_size can't be greater than + * max_io_sectors */ + min_io_size = + MIN_NON_ZERO(min_io_size, max_io_sectors); + opt_io_size = + MIN_NON_ZERO(opt_io_size, max_io_sectors); + } /* required VPD size with unmap support */ buflen = 0x40; memset(outbuf + 4, 0, buflen - 4); @@ -1792,7 +1807,7 @@ static void scsi_disk_emulate_write_same(SCSIDiskReq *r, uint8_t *inbuf) return; } - if (buffer_is_zero(inbuf, s->qdev.blocksize)) { + if ((req->cmd.buf[1] & 0x1) || buffer_is_zero(inbuf, s->qdev.blocksize)) { int flags = (req->cmd.buf[1] & 0x8) ? BDRV_REQ_MAY_UNMAP : 0; /* The request is used as the AIO opaque value, so add a ref. */ diff --git a/hw/sd/core.c b/hw/sd/core.c index 3c6eae6c88..820345f704 100644 --- a/hw/sd/core.c +++ b/hw/sd/core.c @@ -21,7 +21,6 @@ #include "qemu/osdep.h" #include "hw/qdev-core.h" -#include "sysemu/block-backend.h" #include "hw/sd/sd.h" #include "trace.h" diff --git a/hw/sparc/sun4m.c b/hw/sparc/sun4m.c index 0f5804b3b4..6471aca25d 100644 --- a/hw/sparc/sun4m.c +++ b/hw/sparc/sun4m.c @@ -44,7 +44,6 @@ #include "hw/empty_slot.h" #include "hw/loader.h" #include "elf.h" -#include "sysemu/block-backend.h" #include "trace.h" #include "qemu/cutils.h" @@ -99,10 +98,6 @@ struct sun4m_hwdef { uint8_t nvram_machine_id; }; -void DMA_init(ISABus *bus, int high_page_enable) -{ -} - static void fw_cfg_boot_set(void *opaque, const char *boot_device, Error **errp) { diff --git a/hw/sparc64/sun4u.c b/hw/sparc64/sun4u.c index da28ab9413..2044a52ded 100644 --- a/hw/sparc64/sun4u.c +++ b/hw/sparc64/sun4u.c @@ -34,7 +34,9 @@ #include "hw/pci-host/sabre.h" #include "hw/i386/pc.h" #include "hw/char/serial.h" +#include "hw/char/parallel.h" #include "hw/timer/m48t59.h" +#include "hw/input/i8042.h" #include "hw/block/fdc.h" #include "net/net.h" #include "qemu/timer.h" @@ -89,10 +91,6 @@ typedef struct EbusState { #define TYPE_EBUS "ebus" #define EBUS(obj) OBJECT_CHECK(EbusState, (obj), TYPE_EBUS) -void DMA_init(ISABus *bus, int high_page_enable) -{ -} - static void fw_cfg_boot_set(void *opaque, const char *boot_device, Error **errp) { diff --git a/hw/tricore/tricore_testboard.c b/hw/tricore/tricore_testboard.c index ac75eb2128..8e61dfc3e6 100644 --- a/hw/tricore/tricore_testboard.c +++ b/hw/tricore/tricore_testboard.c @@ -28,9 +28,7 @@ #include "sysemu/sysemu.h" #include "hw/boards.h" #include "hw/loader.h" -#include "sysemu/block-backend.h" #include "exec/address-spaces.h" -#include "hw/block/flash.h" #include "elf.h" #include "hw/tricore/tricore.h" #include "qemu/error-report.h" diff --git a/hw/unicore32/puv3.c b/hw/unicore32/puv3.c index db26959a1d..830fe3face 100644 --- a/hw/unicore32/puv3.c +++ b/hw/unicore32/puv3.c @@ -20,6 +20,7 @@ #undef DEBUG_PUV3 #include "hw/unicore32/puv3.h" +#include "hw/input/i8042.h" #define KERNEL_LOAD_ADDR 0x03000000 #define KERNEL_MAX_SIZE 0x00800000 /* Just a guess */ diff --git a/hw/vfio/Makefile.objs b/hw/vfio/Makefile.objs index c3ab9097f1..a2e7a0a7cf 100644 --- a/hw/vfio/Makefile.objs +++ b/hw/vfio/Makefile.objs @@ -1,6 +1,6 @@ ifeq ($(CONFIG_LINUX), y) obj-$(CONFIG_SOFTMMU) += common.o -obj-$(CONFIG_PCI) += pci.o pci-quirks.o +obj-$(CONFIG_PCI) += pci.o pci-quirks.o display.o obj-$(CONFIG_VFIO_CCW) += ccw.o obj-$(CONFIG_SOFTMMU) += platform.o obj-$(CONFIG_VFIO_XGMAC) += calxeda-xgmac.o diff --git a/hw/vfio/common.c b/hw/vfio/common.c index f895e3c335..5e84716218 100644 --- a/hw/vfio/common.c +++ b/hw/vfio/common.c @@ -544,18 +544,40 @@ static void vfio_listener_region_add(MemoryListener *listener, llsize = int128_sub(llend, int128_make64(iova)); + if (memory_region_is_ram_device(section->mr)) { + hwaddr pgmask = (1ULL << ctz64(hostwin->iova_pgsizes)) - 1; + + if ((iova & pgmask) || (int128_get64(llsize) & pgmask)) { + error_report("Region 0x%"HWADDR_PRIx"..0x%"HWADDR_PRIx + " is not aligned to 0x%"HWADDR_PRIx + " and cannot be mapped for DMA", + section->offset_within_region, + int128_getlo(section->size), + pgmask + 1); + return; + } + } + ret = vfio_dma_map(container, iova, int128_get64(llsize), vaddr, section->readonly); if (ret) { error_report("vfio_dma_map(%p, 0x%"HWADDR_PRIx", " "0x%"HWADDR_PRIx", %p) = %d (%m)", container, iova, int128_get64(llsize), vaddr, ret); + if (memory_region_is_ram_device(section->mr)) { + /* Allow unexpected mappings not to be fatal for RAM devices */ + return; + } goto fail; } return; fail: + if (memory_region_is_ram_device(section->mr)) { + error_report("failed to vfio_dma_map. pci p2p may not work"); + return; + } /* * On the initfn path, store the first error in the container so we * can gracefully fail. Runtime, there's not much we can do other @@ -577,6 +599,7 @@ static void vfio_listener_region_del(MemoryListener *listener, hwaddr iova, end; Int128 llend, llsize; int ret; + bool try_unmap = true; if (vfio_listener_skipped_section(section)) { trace_vfio_listener_region_del_skip( @@ -629,14 +652,34 @@ static void vfio_listener_region_del(MemoryListener *listener, trace_vfio_listener_region_del(iova, end); - ret = vfio_dma_unmap(container, iova, int128_get64(llsize)); - memory_region_unref(section->mr); - if (ret) { - error_report("vfio_dma_unmap(%p, 0x%"HWADDR_PRIx", " - "0x%"HWADDR_PRIx") = %d (%m)", - container, iova, int128_get64(llsize), ret); + if (memory_region_is_ram_device(section->mr)) { + hwaddr pgmask; + VFIOHostDMAWindow *hostwin; + bool hostwin_found = false; + + QLIST_FOREACH(hostwin, &container->hostwin_list, hostwin_next) { + if (hostwin->min_iova <= iova && end <= hostwin->max_iova) { + hostwin_found = true; + break; + } + } + assert(hostwin_found); /* or region_add() would have failed */ + + pgmask = (1ULL << ctz64(hostwin->iova_pgsizes)) - 1; + try_unmap = !((iova & pgmask) || (int128_get64(llsize) & pgmask)); } + if (try_unmap) { + ret = vfio_dma_unmap(container, iova, int128_get64(llsize)); + if (ret) { + error_report("vfio_dma_unmap(%p, 0x%"HWADDR_PRIx", " + "0x%"HWADDR_PRIx") = %d (%m)", + container, iova, int128_get64(llsize), ret); + } + } + + memory_region_unref(section->mr); + if (container->iommu_type == VFIO_SPAPR_TCE_v2_IOMMU) { vfio_spapr_remove_window(container, section->offset_within_address_space); @@ -858,6 +901,13 @@ void vfio_region_finalize(VFIORegion *region) g_free(region->mmaps); trace_vfio_region_finalize(region->vbasedev->name, region->nr); + + region->mem = NULL; + region->mmaps = NULL; + region->nr_mmaps = 0; + region->size = 0; + region->flags = 0; + region->nr = 0; } void vfio_region_mmaps_set_enabled(VFIORegion *region, bool enabled) @@ -1421,6 +1471,21 @@ int vfio_get_dev_region_info(VFIODevice *vbasedev, uint32_t type, return -ENODEV; } +bool vfio_has_region_cap(VFIODevice *vbasedev, int region, uint16_t cap_type) +{ + struct vfio_region_info *info = NULL; + bool ret = false; + + if (!vfio_get_region_info(vbasedev, region, &info)) { + if (vfio_get_region_info_cap(info, cap_type)) { + ret = true; + } + g_free(info); + } + + return ret; +} + /* * Interfaces for IBM EEH (Enhanced Error Handling) */ diff --git a/hw/vfio/display.c b/hw/vfio/display.c new file mode 100644 index 0000000000..7d727ce910 --- /dev/null +++ b/hw/vfio/display.c @@ -0,0 +1,347 @@ +/* + * display support for mdev based vgpu devices + * + * Copyright Red Hat, Inc. 2017 + * + * Authors: + * Gerd Hoffmann + * + * This work is licensed under the terms of the GNU GPL, version 2. See + * the COPYING file in the top-level directory. + */ + +#include "qemu/osdep.h" +#include <linux/vfio.h> +#include <sys/ioctl.h> + +#include "sysemu/sysemu.h" +#include "ui/console.h" +#include "qapi/error.h" +#include "pci.h" + +#ifndef DRM_PLANE_TYPE_PRIMARY +# define DRM_PLANE_TYPE_PRIMARY 1 +# define DRM_PLANE_TYPE_CURSOR 2 +#endif + +static void vfio_display_update_cursor(VFIODMABuf *dmabuf, + struct vfio_device_gfx_plane_info *plane) +{ + if (dmabuf->pos_x != plane->x_pos || dmabuf->pos_y != plane->y_pos) { + dmabuf->pos_x = plane->x_pos; + dmabuf->pos_y = plane->y_pos; + dmabuf->pos_updates++; + } + if (dmabuf->hot_x != plane->x_hot || dmabuf->hot_y != plane->y_hot) { + dmabuf->hot_x = plane->x_hot; + dmabuf->hot_y = plane->y_hot; + dmabuf->hot_updates++; + } +} + +static VFIODMABuf *vfio_display_get_dmabuf(VFIOPCIDevice *vdev, + uint32_t plane_type) +{ + VFIODisplay *dpy = vdev->dpy; + struct vfio_device_gfx_plane_info plane; + VFIODMABuf *dmabuf; + int fd, ret; + + memset(&plane, 0, sizeof(plane)); + plane.argsz = sizeof(plane); + plane.flags = VFIO_GFX_PLANE_TYPE_DMABUF; + plane.drm_plane_type = plane_type; + ret = ioctl(vdev->vbasedev.fd, VFIO_DEVICE_QUERY_GFX_PLANE, &plane); + if (ret < 0) { + return NULL; + } + if (!plane.drm_format || !plane.size) { + return NULL; + } + + QTAILQ_FOREACH(dmabuf, &dpy->dmabuf.bufs, next) { + if (dmabuf->dmabuf_id == plane.dmabuf_id) { + /* found in list, move to head, return it */ + QTAILQ_REMOVE(&dpy->dmabuf.bufs, dmabuf, next); + QTAILQ_INSERT_HEAD(&dpy->dmabuf.bufs, dmabuf, next); + if (plane_type == DRM_PLANE_TYPE_CURSOR) { + vfio_display_update_cursor(dmabuf, &plane); + } + return dmabuf; + } + } + + fd = ioctl(vdev->vbasedev.fd, VFIO_DEVICE_GET_GFX_DMABUF, &plane.dmabuf_id); + if (fd < 0) { + return NULL; + } + + dmabuf = g_new0(VFIODMABuf, 1); + dmabuf->dmabuf_id = plane.dmabuf_id; + dmabuf->buf.width = plane.width; + dmabuf->buf.height = plane.height; + dmabuf->buf.stride = plane.stride; + dmabuf->buf.fourcc = plane.drm_format; + dmabuf->buf.fd = fd; + if (plane_type == DRM_PLANE_TYPE_CURSOR) { + vfio_display_update_cursor(dmabuf, &plane); + } + + QTAILQ_INSERT_HEAD(&dpy->dmabuf.bufs, dmabuf, next); + return dmabuf; +} + +static void vfio_display_free_one_dmabuf(VFIODisplay *dpy, VFIODMABuf *dmabuf) +{ + QTAILQ_REMOVE(&dpy->dmabuf.bufs, dmabuf, next); + dpy_gl_release_dmabuf(dpy->con, &dmabuf->buf); + close(dmabuf->buf.fd); + g_free(dmabuf); +} + +static void vfio_display_free_dmabufs(VFIOPCIDevice *vdev) +{ + VFIODisplay *dpy = vdev->dpy; + VFIODMABuf *dmabuf, *tmp; + uint32_t keep = 5; + + QTAILQ_FOREACH_SAFE(dmabuf, &dpy->dmabuf.bufs, next, tmp) { + if (keep > 0) { + keep--; + continue; + } + assert(dmabuf != dpy->dmabuf.primary); + vfio_display_free_one_dmabuf(dpy, dmabuf); + } +} + +static void vfio_display_dmabuf_update(void *opaque) +{ + VFIOPCIDevice *vdev = opaque; + VFIODisplay *dpy = vdev->dpy; + VFIODMABuf *primary, *cursor; + bool free_bufs = false, new_cursor = false;; + + primary = vfio_display_get_dmabuf(vdev, DRM_PLANE_TYPE_PRIMARY); + if (primary == NULL) { + return; + } + + if (dpy->dmabuf.primary != primary) { + dpy->dmabuf.primary = primary; + qemu_console_resize(dpy->con, + primary->buf.width, primary->buf.height); + dpy_gl_scanout_dmabuf(dpy->con, &primary->buf); + free_bufs = true; + } + + cursor = vfio_display_get_dmabuf(vdev, DRM_PLANE_TYPE_CURSOR); + if (dpy->dmabuf.cursor != cursor) { + dpy->dmabuf.cursor = cursor; + new_cursor = true; + free_bufs = true; + } + + if (cursor && (new_cursor || cursor->hot_updates)) { + bool have_hot = (cursor->hot_x != 0xffffffff && + cursor->hot_y != 0xffffffff); + dpy_gl_cursor_dmabuf(dpy->con, &cursor->buf, have_hot, + cursor->hot_x, cursor->hot_y); + cursor->hot_updates = 0; + } else if (!cursor && new_cursor) { + dpy_gl_cursor_dmabuf(dpy->con, NULL, false, 0, 0); + } + + if (cursor && cursor->pos_updates) { + dpy_gl_cursor_position(dpy->con, + cursor->pos_x, + cursor->pos_y); + cursor->pos_updates = 0; + } + + dpy_gl_update(dpy->con, 0, 0, primary->buf.width, primary->buf.height); + + if (free_bufs) { + vfio_display_free_dmabufs(vdev); + } +} + +static const GraphicHwOps vfio_display_dmabuf_ops = { + .gfx_update = vfio_display_dmabuf_update, +}; + +static int vfio_display_dmabuf_init(VFIOPCIDevice *vdev, Error **errp) +{ + if (!display_opengl) { + error_setg(errp, "vfio-display-dmabuf: opengl not available"); + return -1; + } + + vdev->dpy = g_new0(VFIODisplay, 1); + vdev->dpy->con = graphic_console_init(DEVICE(vdev), 0, + &vfio_display_dmabuf_ops, + vdev); + return 0; +} + +static void vfio_display_dmabuf_exit(VFIODisplay *dpy) +{ + VFIODMABuf *dmabuf; + + if (QTAILQ_EMPTY(&dpy->dmabuf.bufs)) { + return; + } + + while ((dmabuf = QTAILQ_FIRST(&dpy->dmabuf.bufs)) != NULL) { + vfio_display_free_one_dmabuf(dpy, dmabuf); + } +} + +/* ---------------------------------------------------------------------- */ + +static void vfio_display_region_update(void *opaque) +{ + VFIOPCIDevice *vdev = opaque; + VFIODisplay *dpy = vdev->dpy; + struct vfio_device_gfx_plane_info plane = { + .argsz = sizeof(plane), + .flags = VFIO_GFX_PLANE_TYPE_REGION + }; + pixman_format_code_t format; + int ret; + + ret = ioctl(vdev->vbasedev.fd, VFIO_DEVICE_QUERY_GFX_PLANE, &plane); + if (ret < 0) { + error_report("ioctl VFIO_DEVICE_QUERY_GFX_PLANE: %s", + strerror(errno)); + return; + } + if (!plane.drm_format || !plane.size) { + return; + } + format = qemu_drm_format_to_pixman(plane.drm_format); + if (!format) { + return; + } + + if (dpy->region.buffer.size && + dpy->region.buffer.nr != plane.region_index) { + /* region changed */ + vfio_region_exit(&dpy->region.buffer); + vfio_region_finalize(&dpy->region.buffer); + dpy->region.surface = NULL; + } + + if (dpy->region.surface && + (surface_width(dpy->region.surface) != plane.width || + surface_height(dpy->region.surface) != plane.height || + surface_format(dpy->region.surface) != format)) { + /* size changed */ + dpy->region.surface = NULL; + } + + if (!dpy->region.buffer.size) { + /* mmap region */ + ret = vfio_region_setup(OBJECT(vdev), &vdev->vbasedev, + &dpy->region.buffer, + plane.region_index, + "display"); + if (ret != 0) { + error_report("%s: vfio_region_setup(%d): %s", + __func__, plane.region_index, strerror(-ret)); + goto err; + } + ret = vfio_region_mmap(&dpy->region.buffer); + if (ret != 0) { + error_report("%s: vfio_region_mmap(%d): %s", __func__, + plane.region_index, strerror(-ret)); + goto err; + } + assert(dpy->region.buffer.mmaps[0].mmap != NULL); + } + + if (dpy->region.surface == NULL) { + /* create surface */ + dpy->region.surface = qemu_create_displaysurface_from + (plane.width, plane.height, format, + plane.stride, dpy->region.buffer.mmaps[0].mmap); + dpy_gfx_replace_surface(dpy->con, dpy->region.surface); + } + + /* full screen update */ + dpy_gfx_update(dpy->con, 0, 0, + surface_width(dpy->region.surface), + surface_height(dpy->region.surface)); + return; + +err: + vfio_region_exit(&dpy->region.buffer); + vfio_region_finalize(&dpy->region.buffer); +} + +static const GraphicHwOps vfio_display_region_ops = { + .gfx_update = vfio_display_region_update, +}; + +static int vfio_display_region_init(VFIOPCIDevice *vdev, Error **errp) +{ + vdev->dpy = g_new0(VFIODisplay, 1); + vdev->dpy->con = graphic_console_init(DEVICE(vdev), 0, + &vfio_display_region_ops, + vdev); + return 0; +} + +static void vfio_display_region_exit(VFIODisplay *dpy) +{ + if (!dpy->region.buffer.size) { + return; + } + + vfio_region_exit(&dpy->region.buffer); + vfio_region_finalize(&dpy->region.buffer); +} + +/* ---------------------------------------------------------------------- */ + +int vfio_display_probe(VFIOPCIDevice *vdev, Error **errp) +{ + struct vfio_device_gfx_plane_info probe; + int ret; + + memset(&probe, 0, sizeof(probe)); + probe.argsz = sizeof(probe); + probe.flags = VFIO_GFX_PLANE_TYPE_PROBE | VFIO_GFX_PLANE_TYPE_DMABUF; + ret = ioctl(vdev->vbasedev.fd, VFIO_DEVICE_QUERY_GFX_PLANE, &probe); + if (ret == 0) { + return vfio_display_dmabuf_init(vdev, errp); + } + + memset(&probe, 0, sizeof(probe)); + probe.argsz = sizeof(probe); + probe.flags = VFIO_GFX_PLANE_TYPE_PROBE | VFIO_GFX_PLANE_TYPE_REGION; + ret = ioctl(vdev->vbasedev.fd, VFIO_DEVICE_QUERY_GFX_PLANE, &probe); + if (ret == 0) { + return vfio_display_region_init(vdev, errp); + } + + if (vdev->display == ON_OFF_AUTO_AUTO) { + /* not an error in automatic mode */ + return 0; + } + + error_setg(errp, "vfio: device doesn't support any (known) display method"); + return -1; +} + +void vfio_display_finalize(VFIOPCIDevice *vdev) +{ + if (!vdev->dpy) { + return; + } + + graphic_console_close(vdev->dpy->con); + vfio_display_dmabuf_exit(vdev->dpy); + vfio_display_region_exit(vdev->dpy); + g_free(vdev->dpy); +} diff --git a/hw/vfio/pci.c b/hw/vfio/pci.c index 3ba3cbc146..b9bc6cd310 100644 --- a/hw/vfio/pci.c +++ b/hw/vfio/pci.c @@ -1295,6 +1295,15 @@ static void vfio_pci_fixup_msix_region(VFIOPCIDevice *vdev) VFIORegion *region = &vdev->bars[vdev->msix->table_bar].region; /* + * If the host driver allows mapping of a MSIX data, we are going to + * do map the entire BAR and emulate MSIX table on top of that. + */ + if (vfio_has_region_cap(&vdev->vbasedev, region->nr, + VFIO_REGION_INFO_CAP_MSIX_MAPPABLE)) { + return; + } + + /* * We expect to find a single mmap covering the whole BAR, anything else * means it's either unsupported or already setup. */ @@ -1572,6 +1581,19 @@ static int vfio_msix_setup(VFIOPCIDevice *vdev, int pos, Error **errp) */ memory_region_set_enabled(&vdev->pdev.msix_pba_mmio, false); + /* + * The emulated machine may provide a paravirt interface for MSIX setup + * so it is not strictly necessary to emulate MSIX here. This becomes + * helpful when frequently accessed MMIO registers are located in + * subpages adjacent to the MSIX table but the MSIX data containing page + * cannot be mapped because of a host page size bigger than the MSIX table + * alignment. + */ + if (object_property_get_bool(OBJECT(qdev_get_machine()), + "vfio-no-msix-emulation", NULL)) { + memory_region_set_enabled(&vdev->pdev.msix_table_mmio, false); + } + return 0; } @@ -3015,6 +3037,13 @@ static void vfio_realize(PCIDevice *pdev, Error **errp) } } + if (vdev->display != ON_OFF_AUTO_OFF) { + ret = vfio_display_probe(vdev, errp); + if (ret) { + goto out_teardown; + } + } + vfio_register_err_notifier(vdev); vfio_register_req_notifier(vdev); vfio_setup_resetfn_quirk(vdev); @@ -3035,6 +3064,7 @@ static void vfio_instance_finalize(Object *obj) VFIOPCIDevice *vdev = DO_UPCAST(VFIOPCIDevice, pdev, pci_dev); VFIOGroup *group = vdev->vbasedev.group; + vfio_display_finalize(vdev); vfio_bars_finalize(vdev); g_free(vdev->emulated_config_bits); g_free(vdev->rom); @@ -3123,6 +3153,8 @@ static void vfio_instance_init(Object *obj) static Property vfio_pci_dev_properties[] = { DEFINE_PROP_PCI_HOST_DEVADDR("host", VFIOPCIDevice, host), DEFINE_PROP_STRING("sysfsdev", VFIOPCIDevice, vbasedev.sysfsdev), + DEFINE_PROP_ON_OFF_AUTO("display", VFIOPCIDevice, + display, ON_OFF_AUTO_AUTO), DEFINE_PROP_UINT32("x-intx-mmap-timeout-ms", VFIOPCIDevice, intx.mmap_timeout, 1100), DEFINE_PROP_BIT("x-vga", VFIOPCIDevice, features, diff --git a/hw/vfio/pci.h b/hw/vfio/pci.h index f4aa13e021..629c875701 100644 --- a/hw/vfio/pci.h +++ b/hw/vfio/pci.h @@ -133,6 +133,7 @@ typedef struct VFIOPCIDevice { #define VFIO_FEATURE_ENABLE_IGD_OPREGION_BIT 2 #define VFIO_FEATURE_ENABLE_IGD_OPREGION \ (1 << VFIO_FEATURE_ENABLE_IGD_OPREGION_BIT) + OnOffAuto display; int32_t bootindex; uint32_t igd_gms; OffAutoPCIBAR msix_relo; @@ -147,6 +148,7 @@ typedef struct VFIOPCIDevice { bool no_kvm_msi; bool no_kvm_msix; bool no_geforce_quirks; + VFIODisplay *dpy; } VFIOPCIDevice; uint32_t vfio_pci_read_config(PCIDevice *pdev, uint32_t addr, int len); @@ -174,4 +176,7 @@ int vfio_pci_igd_opregion_init(VFIOPCIDevice *vdev, struct vfio_region_info *info, Error **errp); +int vfio_display_probe(VFIOPCIDevice *vdev, Error **errp); +void vfio_display_finalize(VFIOPCIDevice *vdev); + #endif /* HW_VFIO_VFIO_PCI_H */ diff --git a/include/block/dirty-bitmap.h b/include/block/dirty-bitmap.h index 09efec609f..1ff8949b1b 100644 --- a/include/block/dirty-bitmap.h +++ b/include/block/dirty-bitmap.h @@ -21,6 +21,7 @@ BdrvDirtyBitmap *bdrv_dirty_bitmap_abdicate(BlockDriverState *bs, BdrvDirtyBitmap *bdrv_reclaim_dirty_bitmap(BlockDriverState *bs, BdrvDirtyBitmap *bitmap, Error **errp); +void bdrv_dirty_bitmap_enable_successor(BdrvDirtyBitmap *bitmap); BdrvDirtyBitmap *bdrv_find_dirty_bitmap(BlockDriverState *bs, const char *name); void bdrv_dirty_bitmap_make_anon(BdrvDirtyBitmap *bitmap); @@ -68,6 +69,8 @@ void bdrv_dirty_bitmap_deserialize_finish(BdrvDirtyBitmap *bitmap); void bdrv_dirty_bitmap_set_readonly(BdrvDirtyBitmap *bitmap, bool value); void bdrv_dirty_bitmap_set_persistance(BdrvDirtyBitmap *bitmap, bool persistent); +void bdrv_dirty_bitmap_set_qmp_locked(BdrvDirtyBitmap *bitmap, bool qmp_locked); + /* Functions that require manual locking. */ void bdrv_dirty_bitmap_lock(BdrvDirtyBitmap *bitmap); @@ -87,10 +90,14 @@ bool bdrv_dirty_bitmap_readonly(const BdrvDirtyBitmap *bitmap); bool bdrv_has_readonly_bitmaps(BlockDriverState *bs); bool bdrv_dirty_bitmap_get_autoload(const BdrvDirtyBitmap *bitmap); bool bdrv_dirty_bitmap_get_persistance(BdrvDirtyBitmap *bitmap); +bool bdrv_dirty_bitmap_qmp_locked(BdrvDirtyBitmap *bitmap); bool bdrv_has_changed_persistent_bitmaps(BlockDriverState *bs); BdrvDirtyBitmap *bdrv_dirty_bitmap_next(BlockDriverState *bs, BdrvDirtyBitmap *bitmap); char *bdrv_dirty_bitmap_sha256(const BdrvDirtyBitmap *bitmap, Error **errp); int64_t bdrv_dirty_bitmap_next_zero(BdrvDirtyBitmap *bitmap, uint64_t start); +BdrvDirtyBitmap *bdrv_reclaim_dirty_bitmap_locked(BlockDriverState *bs, + BdrvDirtyBitmap *bitmap, + Error **errp); #endif diff --git a/include/block/nbd.h b/include/block/nbd.h index 2285637e67..fcdcd54502 100644 --- a/include/block/nbd.h +++ b/include/block/nbd.h @@ -260,6 +260,7 @@ struct NBDExportInfo { /* In-out fields, set by client before nbd_receive_negotiate() and * updated by server results during nbd_receive_negotiate() */ bool structured_reply; + bool base_allocation; /* base:allocation context for NBD_CMD_BLOCK_STATUS */ /* Set by server results during nbd_receive_negotiate() */ uint64_t size; @@ -267,6 +268,8 @@ struct NBDExportInfo { uint32_t min_block; uint32_t opt_block; uint32_t max_block; + + uint32_t meta_base_allocation_id; }; typedef struct NBDExportInfo NBDExportInfo; diff --git a/include/chardev/char-mux.h b/include/chardev/char-mux.h index 8928977897..1e13187767 100644 --- a/include/chardev/char-mux.h +++ b/include/chardev/char-mux.h @@ -27,8 +27,6 @@ #include "chardev/char.h" #include "chardev/char-fe.h" -extern bool muxes_realized; - #define MAX_MUX 4 #define MUX_BUFFER_SIZE 32 /* Must be a power of 2. */ #define MUX_BUFFER_MASK (MUX_BUFFER_SIZE - 1) diff --git a/include/chardev/char.h b/include/chardev/char.h index ebf1e0ba04..04de45795e 100644 --- a/include/chardev/char.h +++ b/include/chardev/char.h @@ -248,6 +248,8 @@ typedef struct ChardevClass { void (*chr_set_echo)(Chardev *chr, bool echo); void (*chr_set_fe_open)(Chardev *chr, int fe_open); void (*chr_be_event)(Chardev *s, int event); + /* Return 0 if succeeded, 1 if failed */ + int (*chr_machine_done)(Chardev *chr); } ChardevClass; Chardev *qemu_chardev_new(const char *id, const char *typename, diff --git a/include/hw/boards.h b/include/hw/boards.h index efb0a9edfd..8ce9a7a21d 100644 --- a/include/hw/boards.h +++ b/include/hw/boards.h @@ -243,6 +243,7 @@ struct MachineState { bool suppress_vmdesc; bool enforce_config_section; bool enable_graphics; + char *memory_encryption; ram_addr_t ram_size; ram_addr_t maxram_size; diff --git a/include/hw/char/parallel.h b/include/hw/char/parallel.h new file mode 100644 index 0000000000..d6dd62fb9f --- /dev/null +++ b/include/hw/char/parallel.h @@ -0,0 +1,14 @@ +#ifndef HW_PARALLEL_H +#define HW_PARALLEL_H + +#include "exec/memory.h" +#include "hw/isa/isa.h" +#include "chardev/char.h" + +void parallel_hds_isa_init(ISABus *bus, int n); + +bool parallel_mm_init(MemoryRegion *address_space, + hwaddr base, int it_shift, qemu_irq irq, + Chardev *chr); + +#endif diff --git a/include/hw/isa/i8257.h b/include/hw/dma/i8257.h index 88a2766a3f..2cab50bb6c 100644 --- a/include/hw/isa/i8257.h +++ b/include/hw/dma/i8257.h @@ -1,6 +1,10 @@ #ifndef HW_I8257_H #define HW_I8257_H +#include "hw/hw.h" +#include "hw/isa/isa.h" +#include "exec/ioport.h" + #define TYPE_I8257 "i8257" typedef struct I8257Regs { @@ -40,4 +44,6 @@ typedef struct I8257State { PortioList portio_pageh; } I8257State; +void i8257_dma_init(ISABus *bus, bool high_page_enable); + #endif diff --git a/include/hw/i386/pc.h b/include/hw/i386/pc.h index bb49165fe0..ffee8413f0 100644 --- a/include/hw/i386/pc.h +++ b/include/hw/i386/pc.h @@ -114,6 +114,7 @@ struct PCMachineClass { /* Device configuration: */ bool pci_enabled; bool kvmclock_enabled; + const char *default_nic_model; /* Compat options: */ @@ -151,14 +152,6 @@ struct PCMachineClass { #define PC_MACHINE_CLASS(klass) \ OBJECT_CLASS_CHECK(PCMachineClass, (klass), TYPE_PC_MACHINE) -/* parallel.c */ - -void parallel_hds_isa_init(ISABus *bus, int n); - -bool parallel_mm_init(MemoryRegion *address_space, - hwaddr base, int it_shift, qemu_irq irq, - Chardev *chr); - /* i8259.c */ extern DeviceState *isa_pic; @@ -196,15 +189,6 @@ void vmport_register(unsigned char command, VMPortReadFunc *func, void *opaque); void vmmouse_get_data(uint32_t *data); void vmmouse_set_data(const uint32_t *data); -/* pckbd.c */ -#define I8042_A20_LINE "a20" - -void i8042_mm_init(qemu_irq kbd_irq, qemu_irq mouse_irq, - MemoryRegion *region, ram_addr_t size, - hwaddr mask); -void i8042_isa_mouse_fake_event(void *opaque); -void i8042_setup_a20_line(ISADevice *dev, qemu_irq a20_out); - /* pc.c */ extern int fd_bootchk; @@ -248,7 +232,7 @@ void pc_init_ne2k_isa(ISABus *bus, NICInfo *nd); void pc_cmos_init(PCMachineState *pcms, BusState *ide0, BusState *ide1, ISADevice *s); -void pc_nic_init(ISABus *isa_bus, PCIBus *pci_bus); +void pc_nic_init(PCMachineClass *pcmc, ISABus *isa_bus, PCIBus *pci_bus); void pc_pci_device_init(PCIBus *pci_bus); typedef void (*cpu_set_smm_t)(int smm, void *arg); @@ -617,7 +601,7 @@ bool e820_get_entry(int, uint32_t, uint64_t *, uint64_t *); #define PC_COMPAT_2_2 \ HW_COMPAT_2_2 \ - PC_CPU_MODEL_IDS("2.3.0") \ + PC_CPU_MODEL_IDS("2.2.0") \ {\ .driver = "kvm64" "-" TYPE_X86_CPU,\ .property = "vme",\ diff --git a/include/hw/input/i8042.h b/include/hw/input/i8042.h new file mode 100644 index 0000000000..f6ff146364 --- /dev/null +++ b/include/hw/input/i8042.h @@ -0,0 +1,24 @@ +/* + * QEMU PS/2 Controller + * + * Copyright (c) 2003 Fabrice Bellard + * + * SPDX-License-Identifier: MIT + */ +#ifndef HW_INPUT_I8042_H +#define HW_INPUT_I8042_H + +#include "hw/hw.h" +#include "hw/isa/isa.h" + +#define TYPE_I8042 "i8042" + +#define I8042_A20_LINE "a20" + +void i8042_mm_init(qemu_irq kbd_irq, qemu_irq mouse_irq, + MemoryRegion *region, ram_addr_t size, + hwaddr mask); +void i8042_isa_mouse_fake_event(void *opaque); +void i8042_setup_a20_line(ISADevice *dev, qemu_irq a20_out); + +#endif /* HW_INPUT_I8042_H */ diff --git a/include/hw/isa/isa.h b/include/hw/isa/isa.h index 95593408ef..b9dbab24b4 100644 --- a/include/hw/isa/isa.h +++ b/include/hw/isa/isa.h @@ -151,6 +151,4 @@ static inline ISABus *isa_bus_from_device(ISADevice *d) return ISA_BUS(qdev_get_parent_bus(DEVICE(d))); } -/* i8257.c */ -void DMA_init(ISABus *bus, int high_page_enable); #endif diff --git a/include/hw/isa/pc87312.h b/include/hw/isa/pc87312.h index bf74470d40..e16263d4b1 100644 --- a/include/hw/isa/pc87312.h +++ b/include/hw/isa/pc87312.h @@ -25,32 +25,22 @@ #ifndef QEMU_PC87312_H #define QEMU_PC87312_H -#include "hw/isa/isa.h" +#include "hw/isa/superio.h" -#define TYPE_PC87312 "pc87312" -#define PC87312(obj) OBJECT_CHECK(PC87312State, (obj), TYPE_PC87312) +#define TYPE_PC87312_SUPERIO "pc87312" +#define PC87312(obj) OBJECT_CHECK(PC87312State, (obj), TYPE_PC87312_SUPERIO) typedef struct PC87312State { - ISADevice dev; + /*< private >*/ + ISASuperIODevice parent_dev; + /*< public >*/ - uint32_t iobase; + uint16_t iobase; uint8_t config; /* initial configuration */ struct { ISADevice *dev; - } parallel; - - struct { - ISADevice *dev; - } uart[2]; - - struct { - ISADevice *dev; - } fdc; - - struct { - ISADevice *dev; } ide; MemoryRegion io; diff --git a/include/hw/isa/superio.h b/include/hw/isa/superio.h new file mode 100644 index 0000000000..f9ba29aa30 --- /dev/null +++ b/include/hw/isa/superio.h @@ -0,0 +1,60 @@ +/* + * Generic ISA Super I/O + * + * Copyright (c) 2018 Philippe Mathieu-Daudé + * + * This code is licensed under the GNU GPLv2 and later. + * See the COPYING file in the top-level directory. + * SPDX-License-Identifier: GPL-2.0-or-later + */ +#ifndef HW_ISA_SUPERIO_H +#define HW_ISA_SUPERIO_H + +#include "qemu-common.h" +#include "sysemu/sysemu.h" +#include "hw/isa/isa.h" + +#define TYPE_ISA_SUPERIO "isa-superio" +#define ISA_SUPERIO(obj) \ + OBJECT_CHECK(ISASuperIODevice, (obj), TYPE_ISA_SUPERIO) +#define ISA_SUPERIO_GET_CLASS(obj) \ + OBJECT_GET_CLASS(ISASuperIOClass, (obj), TYPE_ISA_SUPERIO) +#define ISA_SUPERIO_CLASS(klass) \ + OBJECT_CLASS_CHECK(ISASuperIOClass, (klass), TYPE_ISA_SUPERIO) + +typedef struct ISASuperIODevice { + /*< private >*/ + ISADevice parent_obj; + /*< public >*/ + + ISADevice *parallel[MAX_PARALLEL_PORTS]; + ISADevice *serial[MAX_SERIAL_PORTS]; + ISADevice *floppy; + ISADevice *kbc; + ISADevice *ide; +} ISASuperIODevice; + +typedef struct ISASuperIOFuncs { + size_t count; + bool (*is_enabled)(ISASuperIODevice *sio, uint8_t index); + uint16_t (*get_iobase)(ISASuperIODevice *sio, uint8_t index); + unsigned int (*get_irq)(ISASuperIODevice *sio, uint8_t index); + unsigned int (*get_dma)(ISASuperIODevice *sio, uint8_t index); +} ISASuperIOFuncs; + +typedef struct ISASuperIOClass { + /*< private >*/ + ISADeviceClass parent_class; + /*< public >*/ + DeviceRealize parent_realize; + + ISASuperIOFuncs parallel; + ISASuperIOFuncs serial; + ISASuperIOFuncs floppy; + ISASuperIOFuncs ide; +} ISASuperIOClass; + +#define TYPE_FDC37M81X_SUPERIO "fdc37m81x-superio" +#define TYPE_SMC37C669_SUPERIO "smc37c669-superio" + +#endif /* HW_ISA_SUPERIO_H */ diff --git a/include/hw/isa/vt82c686.h b/include/hw/isa/vt82c686.h index 471b5e9e53..c3c2b6e786 100644 --- a/include/hw/isa/vt82c686.h +++ b/include/hw/isa/vt82c686.h @@ -1,8 +1,10 @@ #ifndef HW_VT82C686_H #define HW_VT82C686_H +#define TYPE_VT82C686B_SUPERIO "vt82c686b-superio" + /* vt82c686.c */ -ISABus *vt82c686b_init(PCIBus * bus, int devfn); +ISABus *vt82c686b_isa_init(PCIBus * bus, int devfn); void vt82c686b_ac97_init(PCIBus *bus, int devfn); void vt82c686b_mc97_init(PCIBus *bus, int devfn); I2CBus *vt82c686b_pm_init(PCIBus *bus, int devfn, uint32_t smb_io_base, diff --git a/include/hw/scsi/esp.h b/include/hw/scsi/esp.h index 3b160f858c..93fdaced67 100644 --- a/include/hw/scsi/esp.h +++ b/include/hw/scsi/esp.h @@ -7,11 +7,6 @@ /* esp.c */ #define ESP_MAX_DEVS 7 typedef void (*ESPDMAMemoryReadWriteFunc)(void *opaque, uint8_t *buf, int len); -void esp_init(hwaddr espaddr, int it_shift, - ESPDMAMemoryReadWriteFunc dma_memory_read, - ESPDMAMemoryReadWriteFunc dma_memory_write, - void *dma_opaque, qemu_irq irq, qemu_irq *reset, - qemu_irq *dma_enable); #define ESP_REGS 16 #define TI_BUFSZ 16 @@ -136,6 +131,11 @@ typedef struct { #define TCHI_FAS100A 0x4 #define TCHI_AM53C974 0x12 +ESPState *esp_init(hwaddr espaddr, int it_shift, + ESPDMAMemoryReadWriteFunc dma_memory_read, + ESPDMAMemoryReadWriteFunc dma_memory_write, + void *dma_opaque, qemu_irq irq, qemu_irq *reset, + qemu_irq *dma_enable); void esp_dma_enable(ESPState *s, int irq, int level); void esp_request_cancelled(SCSIRequest *req); void esp_command_complete(SCSIRequest *req, uint32_t status, size_t resid); diff --git a/include/hw/vfio/vfio-common.h b/include/hw/vfio/vfio-common.h index f3a2ac9fee..d9360148e6 100644 --- a/include/hw/vfio/vfio-common.h +++ b/include/hw/vfio/vfio-common.h @@ -26,6 +26,7 @@ #include "exec/memory.h" #include "qemu/queue.h" #include "qemu/notify.h" +#include "ui/console.h" #ifdef CONFIG_LINUX #include <linux/vfio.h> #endif @@ -142,6 +143,27 @@ typedef struct VFIOGroup { QLIST_ENTRY(VFIOGroup) container_next; } VFIOGroup; +typedef struct VFIODMABuf { + QemuDmaBuf buf; + uint32_t pos_x, pos_y, pos_updates; + uint32_t hot_x, hot_y, hot_updates; + int dmabuf_id; + QTAILQ_ENTRY(VFIODMABuf) next; +} VFIODMABuf; + +typedef struct VFIODisplay { + QemuConsole *con; + struct { + VFIORegion buffer; + DisplaySurface *surface; + } region; + struct { + QTAILQ_HEAD(, VFIODMABuf) bufs; + VFIODMABuf *primary; + VFIODMABuf *cursor; + } dmabuf; +} VFIODisplay; + void vfio_put_base_device(VFIODevice *vbasedev); void vfio_disable_irqindex(VFIODevice *vbasedev, int index); void vfio_unmask_single_irqindex(VFIODevice *vbasedev, int index); @@ -171,6 +193,7 @@ int vfio_get_region_info(VFIODevice *vbasedev, int index, struct vfio_region_info **info); int vfio_get_dev_region_info(VFIODevice *vbasedev, uint32_t type, uint32_t subtype, struct vfio_region_info **info); +bool vfio_has_region_cap(VFIODevice *vbasedev, int region, uint16_t cap_type); #endif extern const MemoryListener vfio_prereg_listener; diff --git a/include/migration/misc.h b/include/migration/misc.h index 77fd4f587c..4ebf24c6c2 100644 --- a/include/migration/misc.h +++ b/include/migration/misc.h @@ -56,4 +56,7 @@ bool migration_has_failed(MigrationState *); bool migration_in_postcopy_after_devices(MigrationState *); void migration_global_dump(Monitor *mon); +/* migration/block-dirty-bitmap.c */ +void dirty_bitmap_mig_init(void); + #endif diff --git a/include/migration/register.h b/include/migration/register.h index f4f7bdc177..f6f12f9b1a 100644 --- a/include/migration/register.h +++ b/include/migration/register.h @@ -26,6 +26,15 @@ typedef struct SaveVMHandlers { bool (*is_active)(void *opaque); bool (*has_postcopy)(void *opaque); + /* is_active_iterate + * If it is not NULL then qemu_savevm_state_iterate will skip iteration if + * it returns false. For example, it is needed for only-postcopy-states, + * which needs to be handled by qemu_savevm_state_setup and + * qemu_savevm_state_pending, but do not need iterations until not in + * postcopy stage. + */ + bool (*is_active_iterate)(void *opaque); + /* This runs outside the iothread lock in the migration case, and * within the lock in the savevm case. The callback had better only * use data that is local to the migration thread or protected @@ -37,8 +46,21 @@ typedef struct SaveVMHandlers { int (*save_setup)(QEMUFile *f, void *opaque); void (*save_live_pending)(QEMUFile *f, void *opaque, uint64_t threshold_size, - uint64_t *non_postcopiable_pending, - uint64_t *postcopiable_pending); + uint64_t *res_precopy_only, + uint64_t *res_compatible, + uint64_t *res_postcopy_only); + /* Note for save_live_pending: + * - res_precopy_only is for data which must be migrated in precopy phase + * or in stopped state, in other words - before target vm start + * - res_compatible is for data which may be migrated in any phase + * - res_postcopy_only is for data which must be migrated in postcopy phase + * or in stopped state, in other words - after source vm stop + * + * Sum of res_postcopy_only, res_compatible and res_postcopy_only is the + * whole amount of pending data. + */ + + LoadStateHandler *load_state; int (*load_setup)(QEMUFile *f, void *opaque); int (*load_cleanup)(void *opaque); diff --git a/include/qemu/cutils.h b/include/qemu/cutils.h index f0878eaafa..a663340b23 100644 --- a/include/qemu/cutils.h +++ b/include/qemu/cutils.h @@ -126,6 +126,10 @@ time_t mktimegm(struct tm *tm); int qemu_fdatasync(int fd); int fcntl_setfl(int fd, int flag); int qemu_parse_fd(const char *param); +int qemu_strtoi(const char *nptr, const char **endptr, int base, + int *result); +int qemu_strtoui(const char *nptr, const char **endptr, int base, + unsigned int *result); int qemu_strtol(const char *nptr, const char **endptr, int base, long *result); int qemu_strtoul(const char *nptr, const char **endptr, int base, diff --git a/include/qemu/rcu.h b/include/qemu/rcu.h index f19413d649..22876d1428 100644 --- a/include/qemu/rcu.h +++ b/include/qemu/rcu.h @@ -27,6 +27,7 @@ #include "qemu/thread.h" #include "qemu/queue.h" #include "qemu/atomic.h" +#include "qemu/sys_membarrier.h" #ifdef __cplusplus extern "C" { @@ -79,7 +80,10 @@ static inline void rcu_read_lock(void) } ctr = atomic_read(&rcu_gp_ctr); - atomic_xchg(&p_rcu_reader->ctr, ctr); + atomic_set(&p_rcu_reader->ctr, ctr); + + /* Write p_rcu_reader->ctr before reading RCU-protected pointers. */ + smp_mb_placeholder(); } static inline void rcu_read_unlock(void) @@ -91,7 +95,15 @@ static inline void rcu_read_unlock(void) return; } - atomic_xchg(&p_rcu_reader->ctr, 0); + /* Ensure that the critical section is seen to precede the + * store to p_rcu_reader->ctr. Together with the following + * smp_mb_placeholder(), this ensures writes to p_rcu_reader->ctr + * are sequentially consistent. + */ + atomic_store_release(&p_rcu_reader->ctr, 0); + + /* Write p_rcu_reader->ctr before reading p_rcu_reader->waiting. */ + smp_mb_placeholder(); if (unlikely(atomic_read(&p_rcu_reader->waiting))) { atomic_set(&p_rcu_reader->waiting, false); qemu_event_set(&rcu_gp_event); diff --git a/include/qemu/sockets.h b/include/qemu/sockets.h index e88d4c37ab..8140fea685 100644 --- a/include/qemu/sockets.h +++ b/include/qemu/sockets.h @@ -12,6 +12,7 @@ int inet_aton(const char *cp, struct in_addr *ia); #include "qapi/qapi-types-sockets.h" /* misc helpers */ +bool fd_is_socket(int fd); int qemu_socket(int domain, int type, int protocol); int qemu_accept(int s, struct sockaddr *addr, socklen_t *addrlen); int socket_set_cork(int fd, int v); diff --git a/include/qemu/sys_membarrier.h b/include/qemu/sys_membarrier.h new file mode 100644 index 0000000000..316e3dc4a2 --- /dev/null +++ b/include/qemu/sys_membarrier.h @@ -0,0 +1,27 @@ +/* + * Process-global memory barriers + * + * Copyright (c) 2018 Red Hat, Inc. + * + * Author: Paolo Bonzini <pbonzini@redhat.com> + */ + +#ifndef QEMU_SYS_MEMBARRIER_H +#define QEMU_SYS_MEMBARRIER_H 1 + +#ifdef CONFIG_MEMBARRIER +/* Only block reordering at the compiler level in the performance-critical + * side. The slow side forces processor-level ordering on all other cores + * through a system call. + */ +extern void smp_mb_global_init(void); +extern void smp_mb_global(void); +#define smp_mb_placeholder() barrier() +#else +/* Keep it simple, execute a real memory barrier on both sides. */ +static inline void smp_mb_global_init(void) {} +#define smp_mb_global() smp_mb() +#define smp_mb_placeholder() smp_mb() +#endif + +#endif diff --git a/include/qemu/timer.h b/include/qemu/timer.h index 3b5a54b014..39ea907e65 100644 --- a/include/qemu/timer.h +++ b/include/qemu/timer.h @@ -251,6 +251,20 @@ bool qemu_clock_run_timers(QEMUClockType type); */ bool qemu_clock_run_all_timers(void); +/** + * qemu_clock_get_last: + * + * Returns last clock query time. + */ +uint64_t qemu_clock_get_last(QEMUClockType type); +/** + * qemu_clock_set_last: + * + * Sets last clock query time. + */ +void qemu_clock_set_last(QEMUClockType type, uint64_t last); + + /* * QEMUTimerList */ diff --git a/include/qom/object.h b/include/qom/object.h index 4f07090db0..96ce81bc5e 100644 --- a/include/qom/object.h +++ b/include/qom/object.h @@ -914,6 +914,17 @@ GSList *object_class_get_list(const char *implements_type, bool include_abstract); /** + * object_class_get_list_sorted: + * @implements_type: The type to filter for, including its derivatives. + * @include_abstract: Whether to include abstract classes. + * + * Returns: A singly-linked list of the classes in alphabetical + * case-insensitive order. + */ +GSList *object_class_get_list_sorted(const char *implements_type, + bool include_abstract); + +/** * object_ref: * @obj: the object * diff --git a/include/standard-headers/drm/drm_fourcc.h b/include/standard-headers/drm/drm_fourcc.h new file mode 100644 index 0000000000..11912fde24 --- /dev/null +++ b/include/standard-headers/drm/drm_fourcc.h @@ -0,0 +1,411 @@ +/* + * Copyright 2011 Intel Corporation + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * VA LINUX SYSTEMS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#ifndef DRM_FOURCC_H +#define DRM_FOURCC_H + + +#if defined(__cplusplus) +extern "C" { +#endif + +#define fourcc_code(a, b, c, d) ((uint32_t)(a) | ((uint32_t)(b) << 8) | \ + ((uint32_t)(c) << 16) | ((uint32_t)(d) << 24)) + +#define DRM_FORMAT_BIG_ENDIAN (1<<31) /* format is big endian instead of little endian */ + +/* color index */ +#define DRM_FORMAT_C8 fourcc_code('C', '8', ' ', ' ') /* [7:0] C */ + +/* 8 bpp Red */ +#define DRM_FORMAT_R8 fourcc_code('R', '8', ' ', ' ') /* [7:0] R */ + +/* 16 bpp Red */ +#define DRM_FORMAT_R16 fourcc_code('R', '1', '6', ' ') /* [15:0] R little endian */ + +/* 16 bpp RG */ +#define DRM_FORMAT_RG88 fourcc_code('R', 'G', '8', '8') /* [15:0] R:G 8:8 little endian */ +#define DRM_FORMAT_GR88 fourcc_code('G', 'R', '8', '8') /* [15:0] G:R 8:8 little endian */ + +/* 32 bpp RG */ +#define DRM_FORMAT_RG1616 fourcc_code('R', 'G', '3', '2') /* [31:0] R:G 16:16 little endian */ +#define DRM_FORMAT_GR1616 fourcc_code('G', 'R', '3', '2') /* [31:0] G:R 16:16 little endian */ + +/* 8 bpp RGB */ +#define DRM_FORMAT_RGB332 fourcc_code('R', 'G', 'B', '8') /* [7:0] R:G:B 3:3:2 */ +#define DRM_FORMAT_BGR233 fourcc_code('B', 'G', 'R', '8') /* [7:0] B:G:R 2:3:3 */ + +/* 16 bpp RGB */ +#define DRM_FORMAT_XRGB4444 fourcc_code('X', 'R', '1', '2') /* [15:0] x:R:G:B 4:4:4:4 little endian */ +#define DRM_FORMAT_XBGR4444 fourcc_code('X', 'B', '1', '2') /* [15:0] x:B:G:R 4:4:4:4 little endian */ +#define DRM_FORMAT_RGBX4444 fourcc_code('R', 'X', '1', '2') /* [15:0] R:G:B:x 4:4:4:4 little endian */ +#define DRM_FORMAT_BGRX4444 fourcc_code('B', 'X', '1', '2') /* [15:0] B:G:R:x 4:4:4:4 little endian */ + +#define DRM_FORMAT_ARGB4444 fourcc_code('A', 'R', '1', '2') /* [15:0] A:R:G:B 4:4:4:4 little endian */ +#define DRM_FORMAT_ABGR4444 fourcc_code('A', 'B', '1', '2') /* [15:0] A:B:G:R 4:4:4:4 little endian */ +#define DRM_FORMAT_RGBA4444 fourcc_code('R', 'A', '1', '2') /* [15:0] R:G:B:A 4:4:4:4 little endian */ +#define DRM_FORMAT_BGRA4444 fourcc_code('B', 'A', '1', '2') /* [15:0] B:G:R:A 4:4:4:4 little endian */ + +#define DRM_FORMAT_XRGB1555 fourcc_code('X', 'R', '1', '5') /* [15:0] x:R:G:B 1:5:5:5 little endian */ +#define DRM_FORMAT_XBGR1555 fourcc_code('X', 'B', '1', '5') /* [15:0] x:B:G:R 1:5:5:5 little endian */ +#define DRM_FORMAT_RGBX5551 fourcc_code('R', 'X', '1', '5') /* [15:0] R:G:B:x 5:5:5:1 little endian */ +#define DRM_FORMAT_BGRX5551 fourcc_code('B', 'X', '1', '5') /* [15:0] B:G:R:x 5:5:5:1 little endian */ + +#define DRM_FORMAT_ARGB1555 fourcc_code('A', 'R', '1', '5') /* [15:0] A:R:G:B 1:5:5:5 little endian */ +#define DRM_FORMAT_ABGR1555 fourcc_code('A', 'B', '1', '5') /* [15:0] A:B:G:R 1:5:5:5 little endian */ +#define DRM_FORMAT_RGBA5551 fourcc_code('R', 'A', '1', '5') /* [15:0] R:G:B:A 5:5:5:1 little endian */ +#define DRM_FORMAT_BGRA5551 fourcc_code('B', 'A', '1', '5') /* [15:0] B:G:R:A 5:5:5:1 little endian */ + +#define DRM_FORMAT_RGB565 fourcc_code('R', 'G', '1', '6') /* [15:0] R:G:B 5:6:5 little endian */ +#define DRM_FORMAT_BGR565 fourcc_code('B', 'G', '1', '6') /* [15:0] B:G:R 5:6:5 little endian */ + +/* 24 bpp RGB */ +#define DRM_FORMAT_RGB888 fourcc_code('R', 'G', '2', '4') /* [23:0] R:G:B little endian */ +#define DRM_FORMAT_BGR888 fourcc_code('B', 'G', '2', '4') /* [23:0] B:G:R little endian */ + +/* 32 bpp RGB */ +#define DRM_FORMAT_XRGB8888 fourcc_code('X', 'R', '2', '4') /* [31:0] x:R:G:B 8:8:8:8 little endian */ +#define DRM_FORMAT_XBGR8888 fourcc_code('X', 'B', '2', '4') /* [31:0] x:B:G:R 8:8:8:8 little endian */ +#define DRM_FORMAT_RGBX8888 fourcc_code('R', 'X', '2', '4') /* [31:0] R:G:B:x 8:8:8:8 little endian */ +#define DRM_FORMAT_BGRX8888 fourcc_code('B', 'X', '2', '4') /* [31:0] B:G:R:x 8:8:8:8 little endian */ + +#define DRM_FORMAT_ARGB8888 fourcc_code('A', 'R', '2', '4') /* [31:0] A:R:G:B 8:8:8:8 little endian */ +#define DRM_FORMAT_ABGR8888 fourcc_code('A', 'B', '2', '4') /* [31:0] A:B:G:R 8:8:8:8 little endian */ +#define DRM_FORMAT_RGBA8888 fourcc_code('R', 'A', '2', '4') /* [31:0] R:G:B:A 8:8:8:8 little endian */ +#define DRM_FORMAT_BGRA8888 fourcc_code('B', 'A', '2', '4') /* [31:0] B:G:R:A 8:8:8:8 little endian */ + +#define DRM_FORMAT_XRGB2101010 fourcc_code('X', 'R', '3', '0') /* [31:0] x:R:G:B 2:10:10:10 little endian */ +#define DRM_FORMAT_XBGR2101010 fourcc_code('X', 'B', '3', '0') /* [31:0] x:B:G:R 2:10:10:10 little endian */ +#define DRM_FORMAT_RGBX1010102 fourcc_code('R', 'X', '3', '0') /* [31:0] R:G:B:x 10:10:10:2 little endian */ +#define DRM_FORMAT_BGRX1010102 fourcc_code('B', 'X', '3', '0') /* [31:0] B:G:R:x 10:10:10:2 little endian */ + +#define DRM_FORMAT_ARGB2101010 fourcc_code('A', 'R', '3', '0') /* [31:0] A:R:G:B 2:10:10:10 little endian */ +#define DRM_FORMAT_ABGR2101010 fourcc_code('A', 'B', '3', '0') /* [31:0] A:B:G:R 2:10:10:10 little endian */ +#define DRM_FORMAT_RGBA1010102 fourcc_code('R', 'A', '3', '0') /* [31:0] R:G:B:A 10:10:10:2 little endian */ +#define DRM_FORMAT_BGRA1010102 fourcc_code('B', 'A', '3', '0') /* [31:0] B:G:R:A 10:10:10:2 little endian */ + +/* packed YCbCr */ +#define DRM_FORMAT_YUYV fourcc_code('Y', 'U', 'Y', 'V') /* [31:0] Cr0:Y1:Cb0:Y0 8:8:8:8 little endian */ +#define DRM_FORMAT_YVYU fourcc_code('Y', 'V', 'Y', 'U') /* [31:0] Cb0:Y1:Cr0:Y0 8:8:8:8 little endian */ +#define DRM_FORMAT_UYVY fourcc_code('U', 'Y', 'V', 'Y') /* [31:0] Y1:Cr0:Y0:Cb0 8:8:8:8 little endian */ +#define DRM_FORMAT_VYUY fourcc_code('V', 'Y', 'U', 'Y') /* [31:0] Y1:Cb0:Y0:Cr0 8:8:8:8 little endian */ + +#define DRM_FORMAT_AYUV fourcc_code('A', 'Y', 'U', 'V') /* [31:0] A:Y:Cb:Cr 8:8:8:8 little endian */ + +/* + * 2 plane RGB + A + * index 0 = RGB plane, same format as the corresponding non _A8 format has + * index 1 = A plane, [7:0] A + */ +#define DRM_FORMAT_XRGB8888_A8 fourcc_code('X', 'R', 'A', '8') +#define DRM_FORMAT_XBGR8888_A8 fourcc_code('X', 'B', 'A', '8') +#define DRM_FORMAT_RGBX8888_A8 fourcc_code('R', 'X', 'A', '8') +#define DRM_FORMAT_BGRX8888_A8 fourcc_code('B', 'X', 'A', '8') +#define DRM_FORMAT_RGB888_A8 fourcc_code('R', '8', 'A', '8') +#define DRM_FORMAT_BGR888_A8 fourcc_code('B', '8', 'A', '8') +#define DRM_FORMAT_RGB565_A8 fourcc_code('R', '5', 'A', '8') +#define DRM_FORMAT_BGR565_A8 fourcc_code('B', '5', 'A', '8') + +/* + * 2 plane YCbCr + * index 0 = Y plane, [7:0] Y + * index 1 = Cr:Cb plane, [15:0] Cr:Cb little endian + * or + * index 1 = Cb:Cr plane, [15:0] Cb:Cr little endian + */ +#define DRM_FORMAT_NV12 fourcc_code('N', 'V', '1', '2') /* 2x2 subsampled Cr:Cb plane */ +#define DRM_FORMAT_NV21 fourcc_code('N', 'V', '2', '1') /* 2x2 subsampled Cb:Cr plane */ +#define DRM_FORMAT_NV16 fourcc_code('N', 'V', '1', '6') /* 2x1 subsampled Cr:Cb plane */ +#define DRM_FORMAT_NV61 fourcc_code('N', 'V', '6', '1') /* 2x1 subsampled Cb:Cr plane */ +#define DRM_FORMAT_NV24 fourcc_code('N', 'V', '2', '4') /* non-subsampled Cr:Cb plane */ +#define DRM_FORMAT_NV42 fourcc_code('N', 'V', '4', '2') /* non-subsampled Cb:Cr plane */ + +/* + * 3 plane YCbCr + * index 0: Y plane, [7:0] Y + * index 1: Cb plane, [7:0] Cb + * index 2: Cr plane, [7:0] Cr + * or + * index 1: Cr plane, [7:0] Cr + * index 2: Cb plane, [7:0] Cb + */ +#define DRM_FORMAT_YUV410 fourcc_code('Y', 'U', 'V', '9') /* 4x4 subsampled Cb (1) and Cr (2) planes */ +#define DRM_FORMAT_YVU410 fourcc_code('Y', 'V', 'U', '9') /* 4x4 subsampled Cr (1) and Cb (2) planes */ +#define DRM_FORMAT_YUV411 fourcc_code('Y', 'U', '1', '1') /* 4x1 subsampled Cb (1) and Cr (2) planes */ +#define DRM_FORMAT_YVU411 fourcc_code('Y', 'V', '1', '1') /* 4x1 subsampled Cr (1) and Cb (2) planes */ +#define DRM_FORMAT_YUV420 fourcc_code('Y', 'U', '1', '2') /* 2x2 subsampled Cb (1) and Cr (2) planes */ +#define DRM_FORMAT_YVU420 fourcc_code('Y', 'V', '1', '2') /* 2x2 subsampled Cr (1) and Cb (2) planes */ +#define DRM_FORMAT_YUV422 fourcc_code('Y', 'U', '1', '6') /* 2x1 subsampled Cb (1) and Cr (2) planes */ +#define DRM_FORMAT_YVU422 fourcc_code('Y', 'V', '1', '6') /* 2x1 subsampled Cr (1) and Cb (2) planes */ +#define DRM_FORMAT_YUV444 fourcc_code('Y', 'U', '2', '4') /* non-subsampled Cb (1) and Cr (2) planes */ +#define DRM_FORMAT_YVU444 fourcc_code('Y', 'V', '2', '4') /* non-subsampled Cr (1) and Cb (2) planes */ + + +/* + * Format Modifiers: + * + * Format modifiers describe, typically, a re-ordering or modification + * of the data in a plane of an FB. This can be used to express tiled/ + * swizzled formats, or compression, or a combination of the two. + * + * The upper 8 bits of the format modifier are a vendor-id as assigned + * below. The lower 56 bits are assigned as vendor sees fit. + */ + +/* Vendor Ids: */ +#define DRM_FORMAT_MOD_NONE 0 +#define DRM_FORMAT_MOD_VENDOR_NONE 0 +#define DRM_FORMAT_MOD_VENDOR_INTEL 0x01 +#define DRM_FORMAT_MOD_VENDOR_AMD 0x02 +#define DRM_FORMAT_MOD_VENDOR_NVIDIA 0x03 +#define DRM_FORMAT_MOD_VENDOR_SAMSUNG 0x04 +#define DRM_FORMAT_MOD_VENDOR_QCOM 0x05 +#define DRM_FORMAT_MOD_VENDOR_VIVANTE 0x06 +#define DRM_FORMAT_MOD_VENDOR_BROADCOM 0x07 +/* add more to the end as needed */ + +#define DRM_FORMAT_RESERVED ((1ULL << 56) - 1) + +#define fourcc_mod_code(vendor, val) \ + ((((uint64_t)DRM_FORMAT_MOD_VENDOR_## vendor) << 56) | ((val) & 0x00ffffffffffffffULL)) + +/* + * Format Modifier tokens: + * + * When adding a new token please document the layout with a code comment, + * similar to the fourcc codes above. drm_fourcc.h is considered the + * authoritative source for all of these. + */ + +/* + * Invalid Modifier + * + * This modifier can be used as a sentinel to terminate the format modifiers + * list, or to initialize a variable with an invalid modifier. It might also be + * used to report an error back to userspace for certain APIs. + */ +#define DRM_FORMAT_MOD_INVALID fourcc_mod_code(NONE, DRM_FORMAT_RESERVED) + +/* + * Linear Layout + * + * Just plain linear layout. Note that this is different from no specifying any + * modifier (e.g. not setting DRM_MODE_FB_MODIFIERS in the DRM_ADDFB2 ioctl), + * which tells the driver to also take driver-internal information into account + * and so might actually result in a tiled framebuffer. + */ +#define DRM_FORMAT_MOD_LINEAR fourcc_mod_code(NONE, 0) + +/* Intel framebuffer modifiers */ + +/* + * Intel X-tiling layout + * + * This is a tiled layout using 4Kb tiles (except on gen2 where the tiles 2Kb) + * in row-major layout. Within the tile bytes are laid out row-major, with + * a platform-dependent stride. On top of that the memory can apply + * platform-depending swizzling of some higher address bits into bit6. + * + * This format is highly platforms specific and not useful for cross-driver + * sharing. It exists since on a given platform it does uniquely identify the + * layout in a simple way for i915-specific userspace. + */ +#define I915_FORMAT_MOD_X_TILED fourcc_mod_code(INTEL, 1) + +/* + * Intel Y-tiling layout + * + * This is a tiled layout using 4Kb tiles (except on gen2 where the tiles 2Kb) + * in row-major layout. Within the tile bytes are laid out in OWORD (16 bytes) + * chunks column-major, with a platform-dependent height. On top of that the + * memory can apply platform-depending swizzling of some higher address bits + * into bit6. + * + * This format is highly platforms specific and not useful for cross-driver + * sharing. It exists since on a given platform it does uniquely identify the + * layout in a simple way for i915-specific userspace. + */ +#define I915_FORMAT_MOD_Y_TILED fourcc_mod_code(INTEL, 2) + +/* + * Intel Yf-tiling layout + * + * This is a tiled layout using 4Kb tiles in row-major layout. + * Within the tile pixels are laid out in 16 256 byte units / sub-tiles which + * are arranged in four groups (two wide, two high) with column-major layout. + * Each group therefore consits out of four 256 byte units, which are also laid + * out as 2x2 column-major. + * 256 byte units are made out of four 64 byte blocks of pixels, producing + * either a square block or a 2:1 unit. + * 64 byte blocks of pixels contain four pixel rows of 16 bytes, where the width + * in pixel depends on the pixel depth. + */ +#define I915_FORMAT_MOD_Yf_TILED fourcc_mod_code(INTEL, 3) + +/* + * Intel color control surface (CCS) for render compression + * + * The framebuffer format must be one of the 8:8:8:8 RGB formats. + * The main surface will be plane index 0 and must be Y/Yf-tiled, + * the CCS will be plane index 1. + * + * Each CCS tile matches a 1024x512 pixel area of the main surface. + * To match certain aspects of the 3D hardware the CCS is + * considered to be made up of normal 128Bx32 Y tiles, Thus + * the CCS pitch must be specified in multiples of 128 bytes. + * + * In reality the CCS tile appears to be a 64Bx64 Y tile, composed + * of QWORD (8 bytes) chunks instead of OWORD (16 bytes) chunks. + * But that fact is not relevant unless the memory is accessed + * directly. + */ +#define I915_FORMAT_MOD_Y_TILED_CCS fourcc_mod_code(INTEL, 4) +#define I915_FORMAT_MOD_Yf_TILED_CCS fourcc_mod_code(INTEL, 5) + +/* + * Tiled, NV12MT, grouped in 64 (pixels) x 32 (lines) -sized macroblocks + * + * Macroblocks are laid in a Z-shape, and each pixel data is following the + * standard NV12 style. + * As for NV12, an image is the result of two frame buffers: one for Y, + * one for the interleaved Cb/Cr components (1/2 the height of the Y buffer). + * Alignment requirements are (for each buffer): + * - multiple of 128 pixels for the width + * - multiple of 32 pixels for the height + * + * For more information: see https://linuxtv.org/downloads/v4l-dvb-apis/re32.html + */ +#define DRM_FORMAT_MOD_SAMSUNG_64_32_TILE fourcc_mod_code(SAMSUNG, 1) + +/* Vivante framebuffer modifiers */ + +/* + * Vivante 4x4 tiling layout + * + * This is a simple tiled layout using tiles of 4x4 pixels in a row-major + * layout. + */ +#define DRM_FORMAT_MOD_VIVANTE_TILED fourcc_mod_code(VIVANTE, 1) + +/* + * Vivante 64x64 super-tiling layout + * + * This is a tiled layout using 64x64 pixel super-tiles, where each super-tile + * contains 8x4 groups of 2x4 tiles of 4x4 pixels (like above) each, all in row- + * major layout. + * + * For more information: see + * https://github.com/etnaviv/etna_viv/blob/master/doc/hardware.md#texture-tiling + */ +#define DRM_FORMAT_MOD_VIVANTE_SUPER_TILED fourcc_mod_code(VIVANTE, 2) + +/* + * Vivante 4x4 tiling layout for dual-pipe + * + * Same as the 4x4 tiling layout, except every second 4x4 pixel tile starts at a + * different base address. Offsets from the base addresses are therefore halved + * compared to the non-split tiled layout. + */ +#define DRM_FORMAT_MOD_VIVANTE_SPLIT_TILED fourcc_mod_code(VIVANTE, 3) + +/* + * Vivante 64x64 super-tiling layout for dual-pipe + * + * Same as the 64x64 super-tiling layout, except every second 4x4 pixel tile + * starts at a different base address. Offsets from the base addresses are + * therefore halved compared to the non-split super-tiled layout. + */ +#define DRM_FORMAT_MOD_VIVANTE_SPLIT_SUPER_TILED fourcc_mod_code(VIVANTE, 4) + +/* NVIDIA frame buffer modifiers */ + +/* + * Tegra Tiled Layout, used by Tegra 2, 3 and 4. + * + * Pixels are arranged in simple tiles of 16 x 16 bytes. + */ +#define DRM_FORMAT_MOD_NVIDIA_TEGRA_TILED fourcc_mod_code(NVIDIA, 1) + +/* + * 16Bx2 Block Linear layout, used by desktop GPUs, and Tegra K1 and later + * + * Pixels are arranged in 64x8 Groups Of Bytes (GOBs). GOBs are then stacked + * vertically by a power of 2 (1 to 32 GOBs) to form a block. + * + * Within a GOB, data is ordered as 16B x 2 lines sectors laid in Z-shape. + * + * Parameter 'v' is the log2 encoding of the number of GOBs stacked vertically. + * Valid values are: + * + * 0 == ONE_GOB + * 1 == TWO_GOBS + * 2 == FOUR_GOBS + * 3 == EIGHT_GOBS + * 4 == SIXTEEN_GOBS + * 5 == THIRTYTWO_GOBS + * + * Chapter 20 "Pixel Memory Formats" of the Tegra X1 TRM describes this format + * in full detail. + */ +#define DRM_FORMAT_MOD_NVIDIA_16BX2_BLOCK(v) \ + fourcc_mod_code(NVIDIA, 0x10 | ((v) & 0xf)) + +#define DRM_FORMAT_MOD_NVIDIA_16BX2_BLOCK_ONE_GOB \ + fourcc_mod_code(NVIDIA, 0x10) +#define DRM_FORMAT_MOD_NVIDIA_16BX2_BLOCK_TWO_GOB \ + fourcc_mod_code(NVIDIA, 0x11) +#define DRM_FORMAT_MOD_NVIDIA_16BX2_BLOCK_FOUR_GOB \ + fourcc_mod_code(NVIDIA, 0x12) +#define DRM_FORMAT_MOD_NVIDIA_16BX2_BLOCK_EIGHT_GOB \ + fourcc_mod_code(NVIDIA, 0x13) +#define DRM_FORMAT_MOD_NVIDIA_16BX2_BLOCK_SIXTEEN_GOB \ + fourcc_mod_code(NVIDIA, 0x14) +#define DRM_FORMAT_MOD_NVIDIA_16BX2_BLOCK_THIRTYTWO_GOB \ + fourcc_mod_code(NVIDIA, 0x15) + +/* + * Broadcom VC4 "T" format + * + * This is the primary layout that the V3D GPU can texture from (it + * can't do linear). The T format has: + * + * - 64b utiles of pixels in a raster-order grid according to cpp. It's 4x4 + * pixels at 32 bit depth. + * + * - 1k subtiles made of a 4x4 raster-order grid of 64b utiles (so usually + * 16x16 pixels). + * + * - 4k tiles made of a 2x2 grid of 1k subtiles (so usually 32x32 pixels). On + * even 4k tile rows, they're arranged as (BL, TL, TR, BR), and on odd rows + * they're (TR, BR, BL, TL), where bottom left is start of memory. + * + * - an image made of 4k tiles in rows either left-to-right (even rows of 4k + * tiles) or right-to-left (odd rows of 4k tiles). + */ +#define DRM_FORMAT_MOD_BROADCOM_VC4_T_TILED fourcc_mod_code(BROADCOM, 1) + +#if defined(__cplusplus) +} +#endif + +#endif /* DRM_FOURCC_H */ diff --git a/include/standard-headers/linux/input-event-codes.h b/include/standard-headers/linux/input-event-codes.h index 79841b543f..9e6a8ba4ce 100644 --- a/include/standard-headers/linux/input-event-codes.h +++ b/include/standard-headers/linux/input-event-codes.h @@ -594,6 +594,7 @@ #define BTN_DPAD_RIGHT 0x223 #define KEY_ALS_TOGGLE 0x230 /* Ambient light sensor */ +#define KEY_ROTATE_LOCK_TOGGLE 0x231 /* Display rotation lock */ #define KEY_BUTTONCONFIG 0x240 /* AL Button Configuration */ #define KEY_TASKMANAGER 0x241 /* AL Task/Project Manager */ diff --git a/include/standard-headers/linux/input.h b/include/standard-headers/linux/input.h index bc3e6d3d5b..939b62775c 100644 --- a/include/standard-headers/linux/input.h +++ b/include/standard-headers/linux/input.h @@ -18,10 +18,21 @@ /* * The event structure itself + * Note that __USE_TIME_BITS64 is defined by libc based on + * application's request to use 64 bit time_t. */ struct input_event { +#if (HOST_LONG_BITS != 32 || !defined(__USE_TIME_BITS64)) && !defined(__KERNEL) struct timeval time; +#define input_event_sec time.tv_sec +#define input_event_usec time.tv_usec +#else + __kernel_ulong_t __sec; + __kernel_ulong_t __usec; +#define input_event_sec __sec +#define input_event_usec __usec +#endif uint16_t type; uint16_t code; int32_t value; diff --git a/include/standard-headers/linux/pci_regs.h b/include/standard-headers/linux/pci_regs.h index 70c2b2ade0..0c79eac5e9 100644 --- a/include/standard-headers/linux/pci_regs.h +++ b/include/standard-headers/linux/pci_regs.h @@ -622,15 +622,19 @@ * safely. */ #define PCI_EXP_DEVCAP2 36 /* Device Capabilities 2 */ +#define PCI_EXP_DEVCAP2_COMP_TMOUT_DIS 0x00000010 /* Completion Timeout Disable supported */ #define PCI_EXP_DEVCAP2_ARI 0x00000020 /* Alternative Routing-ID */ #define PCI_EXP_DEVCAP2_ATOMIC_ROUTE 0x00000040 /* Atomic Op routing */ -#define PCI_EXP_DEVCAP2_ATOMIC_COMP64 0x00000100 /* Atomic 64-bit compare */ +#define PCI_EXP_DEVCAP2_ATOMIC_COMP32 0x00000080 /* 32b AtomicOp completion */ +#define PCI_EXP_DEVCAP2_ATOMIC_COMP64 0x00000100 /* 64b AtomicOp completion */ +#define PCI_EXP_DEVCAP2_ATOMIC_COMP128 0x00000200 /* 128b AtomicOp completion */ #define PCI_EXP_DEVCAP2_LTR 0x00000800 /* Latency tolerance reporting */ #define PCI_EXP_DEVCAP2_OBFF_MASK 0x000c0000 /* OBFF support mechanism */ #define PCI_EXP_DEVCAP2_OBFF_MSG 0x00040000 /* New message signaling */ #define PCI_EXP_DEVCAP2_OBFF_WAKE 0x00080000 /* Re-use WAKE# for OBFF */ #define PCI_EXP_DEVCTL2 40 /* Device Control 2 */ #define PCI_EXP_DEVCTL2_COMP_TIMEOUT 0x000f /* Completion Timeout Value */ +#define PCI_EXP_DEVCTL2_COMP_TMOUT_DIS 0x0010 /* Completion Timeout Disable */ #define PCI_EXP_DEVCTL2_ARI 0x0020 /* Alternative Routing-ID */ #define PCI_EXP_DEVCTL2_ATOMIC_REQ 0x0040 /* Set Atomic requests */ #define PCI_EXP_DEVCTL2_ATOMIC_EGRESS_BLOCK 0x0080 /* Block atomic egress */ @@ -966,26 +970,28 @@ /* Downstream Port Containment */ #define PCI_EXP_DPC_CAP 4 /* DPC Capability */ -#define PCI_EXP_DPC_IRQ 0x1f /* DPC Interrupt Message Number */ -#define PCI_EXP_DPC_CAP_RP_EXT 0x20 /* Root Port Extensions for DPC */ -#define PCI_EXP_DPC_CAP_POISONED_TLP 0x40 /* Poisoned TLP Egress Blocking Supported */ -#define PCI_EXP_DPC_CAP_SW_TRIGGER 0x80 /* Software Triggering Supported */ -#define PCI_EXP_DPC_RP_PIO_LOG_SIZE 0xF00 /* RP PIO log size */ +#define PCI_EXP_DPC_IRQ 0x001F /* Interrupt Message Number */ +#define PCI_EXP_DPC_CAP_RP_EXT 0x0020 /* Root Port Extensions */ +#define PCI_EXP_DPC_CAP_POISONED_TLP 0x0040 /* Poisoned TLP Egress Blocking Supported */ +#define PCI_EXP_DPC_CAP_SW_TRIGGER 0x0080 /* Software Triggering Supported */ +#define PCI_EXP_DPC_RP_PIO_LOG_SIZE 0x0F00 /* RP PIO Log Size */ #define PCI_EXP_DPC_CAP_DL_ACTIVE 0x1000 /* ERR_COR signal on DL_Active supported */ #define PCI_EXP_DPC_CTL 6 /* DPC control */ -#define PCI_EXP_DPC_CTL_EN_NONFATAL 0x02 /* Enable trigger on ERR_NONFATAL message */ -#define PCI_EXP_DPC_CTL_INT_EN 0x08 /* DPC Interrupt Enable */ +#define PCI_EXP_DPC_CTL_EN_NONFATAL 0x0002 /* Enable trigger on ERR_NONFATAL message */ +#define PCI_EXP_DPC_CTL_INT_EN 0x0008 /* DPC Interrupt Enable */ #define PCI_EXP_DPC_STATUS 8 /* DPC Status */ -#define PCI_EXP_DPC_STATUS_TRIGGER 0x01 /* Trigger Status */ -#define PCI_EXP_DPC_STATUS_INTERRUPT 0x08 /* Interrupt Status */ -#define PCI_EXP_DPC_RP_BUSY 0x10 /* Root Port Busy */ +#define PCI_EXP_DPC_STATUS_TRIGGER 0x0001 /* Trigger Status */ +#define PCI_EXP_DPC_STATUS_TRIGGER_RSN 0x0006 /* Trigger Reason */ +#define PCI_EXP_DPC_STATUS_INTERRUPT 0x0008 /* Interrupt Status */ +#define PCI_EXP_DPC_RP_BUSY 0x0010 /* Root Port Busy */ +#define PCI_EXP_DPC_STATUS_TRIGGER_RSN_EXT 0x0060 /* Trig Reason Extension */ #define PCI_EXP_DPC_SOURCE_ID 10 /* DPC Source Identifier */ #define PCI_EXP_DPC_RP_PIO_STATUS 0x0C /* RP PIO Status */ -#define PCI_EXP_DPC_RP_PIO_MASK 0x10 /* RP PIO MASK */ +#define PCI_EXP_DPC_RP_PIO_MASK 0x10 /* RP PIO Mask */ #define PCI_EXP_DPC_RP_PIO_SEVERITY 0x14 /* RP PIO Severity */ #define PCI_EXP_DPC_RP_PIO_SYSERROR 0x18 /* RP PIO SysError */ #define PCI_EXP_DPC_RP_PIO_EXCEPTION 0x1C /* RP PIO Exception */ diff --git a/include/standard-headers/linux/virtio_net.h b/include/standard-headers/linux/virtio_net.h index 30ff24940d..e9f255ea3f 100644 --- a/include/standard-headers/linux/virtio_net.h +++ b/include/standard-headers/linux/virtio_net.h @@ -57,6 +57,8 @@ * Steering */ #define VIRTIO_NET_F_CTRL_MAC_ADDR 23 /* Set MAC address */ +#define VIRTIO_NET_F_SPEED_DUPLEX 63 /* Device set linkspeed and duplex */ + #ifndef VIRTIO_NET_NO_LEGACY #define VIRTIO_NET_F_GSO 6 /* Host handles pkts w/ any GSO type */ #endif /* VIRTIO_NET_NO_LEGACY */ @@ -76,6 +78,17 @@ struct virtio_net_config { uint16_t max_virtqueue_pairs; /* Default maximum transmit unit advice */ uint16_t mtu; + /* + * speed, in units of 1Mb. All values 0 to INT_MAX are legal. + * Any other value stands for unknown. + */ + uint32_t speed; + /* + * 0x00 - half duplex + * 0x01 - full duplex + * Any other value stands for unknown. + */ + uint8_t duplex; } QEMU_PACKED; /* diff --git a/include/standard-headers/linux/virtio_ring.h b/include/standard-headers/linux/virtio_ring.h index f1dc05df25..d26e72bc6b 100644 --- a/include/standard-headers/linux/virtio_ring.h +++ b/include/standard-headers/linux/virtio_ring.h @@ -78,7 +78,7 @@ struct vring_avail { __virtio16 ring[]; }; -/* u32 is used here for ids for padding reasons. */ +/* uint32_t is used here for ids for padding reasons. */ struct vring_used_elem { /* Index of start of used descriptor chain. */ __virtio32 id; diff --git a/include/standard-headers/rdma/vmw_pvrdma-abi.h b/include/standard-headers/rdma/vmw_pvrdma-abi.h index 0d0f7a8aca..07a820d337 100644 --- a/include/standard-headers/rdma/vmw_pvrdma-abi.h +++ b/include/standard-headers/rdma/vmw_pvrdma-abi.h @@ -1,3 +1,4 @@ +/* SPDX-License-Identifier: ((GPL-2.0 WITH Linux-syscall-note) OR BSD-2-Clause) */ /* * Copyright (c) 2012-2016 VMware, Inc. All rights reserved. * @@ -51,12 +52,14 @@ #define PVRDMA_UVERBS_ABI_VERSION 3 /* ABI Version. */ #define PVRDMA_UAR_HANDLE_MASK 0x00FFFFFF /* Bottom 24 bits. */ #define PVRDMA_UAR_QP_OFFSET 0 /* QP doorbell. */ -#define PVRDMA_UAR_QP_SEND BIT(30) /* Send bit. */ -#define PVRDMA_UAR_QP_RECV BIT(31) /* Recv bit. */ +#define PVRDMA_UAR_QP_SEND (1 << 30) /* Send bit. */ +#define PVRDMA_UAR_QP_RECV (1 << 31) /* Recv bit. */ #define PVRDMA_UAR_CQ_OFFSET 4 /* CQ doorbell. */ -#define PVRDMA_UAR_CQ_ARM_SOL BIT(29) /* Arm solicited bit. */ -#define PVRDMA_UAR_CQ_ARM BIT(30) /* Arm bit. */ -#define PVRDMA_UAR_CQ_POLL BIT(31) /* Poll bit. */ +#define PVRDMA_UAR_CQ_ARM_SOL (1 << 29) /* Arm solicited bit. */ +#define PVRDMA_UAR_CQ_ARM (1 << 30) /* Arm bit. */ +#define PVRDMA_UAR_CQ_POLL (1 << 31) /* Poll bit. */ +#define PVRDMA_UAR_SRQ_OFFSET 8 /* SRQ doorbell. */ +#define PVRDMA_UAR_SRQ_RECV (1 << 30) /* Recv bit. */ enum pvrdma_wr_opcode { PVRDMA_WR_RDMA_WRITE, diff --git a/include/sysemu/kvm.h b/include/sysemu/kvm.h index 85002ac49a..23669c4d5a 100644 --- a/include/sysemu/kvm.h +++ b/include/sysemu/kvm.h @@ -231,6 +231,23 @@ int kvm_destroy_vcpu(CPUState *cpu); */ bool kvm_arm_supports_user_irq(void); +/** + * kvm_memcrypt_enabled - return boolean indicating whether memory encryption + * is enabled + * Returns: 1 memory encryption is enabled + * 0 memory encryption is disabled + */ +bool kvm_memcrypt_enabled(void); + +/** + * kvm_memcrypt_encrypt_data: encrypt the memory range + * + * Return: 1 failed to encrypt the range + * 0 succesfully encrypted memory region + */ +int kvm_memcrypt_encrypt_data(uint8_t *ptr, uint64_t len); + + #ifdef NEED_CPU_H #include "cpu.h" diff --git a/include/sysemu/replay.h b/include/sysemu/replay.h index c0204e641c..3ced6bc231 100644 --- a/include/sysemu/replay.h +++ b/include/sysemu/replay.h @@ -48,6 +48,19 @@ extern ReplayMode replay_mode; /* Name of the initial VM snapshot */ extern char *replay_snapshot; +/* Replay locking + * + * The locks are needed to protect the shared structures and log file + * when doing record/replay. They also are the main sync-point between + * the main-loop thread and the vCPU thread. This was a role + * previously filled by the BQL which has been busy trying to reduce + * its impact across the code. This ensures blocks of events stay + * sequential and reproducible. + */ + +void replay_mutex_lock(void); +void replay_mutex_unlock(void); + /* Replay process control functions */ /*! Enables recording or saving event log with specified parameters */ @@ -166,5 +179,8 @@ void replay_audio_in(int *recorded, void *samples, int *wpos, int size); /*! Called at the start of execution. Loads or saves initial vmstate depending on execution mode. */ void replay_vmstate_init(void); +/*! Called to ensure that replay state is consistent and VM snapshot + can be created */ +bool replay_can_snapshot(void); #endif diff --git a/include/sysemu/sev.h b/include/sysemu/sev.h new file mode 100644 index 0000000000..98c1ec8d38 --- /dev/null +++ b/include/sysemu/sev.h @@ -0,0 +1,21 @@ +/* + * QEMU Secure Encrypted Virutualization (SEV) support + * + * Copyright: Advanced Micro Devices, 2016-2018 + * + * Authors: + * Brijesh Singh <brijesh.singh@amd.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. + * + */ + +#ifndef QEMU_SEV_H +#define QEMU_SEV_H + +#include "sysemu/kvm.h" + +void *sev_guest_init(const char *id); +int sev_encrypt_data(void *handle, uint8_t *ptr, uint64_t len); +#endif diff --git a/include/sysemu/sysemu.h b/include/sysemu/sysemu.h index 356bfdc1c1..2b42151c63 100644 --- a/include/sysemu/sysemu.h +++ b/include/sysemu/sysemu.h @@ -88,6 +88,8 @@ void qemu_system_guest_panicked(GuestPanicInformation *info); void qemu_add_exit_notifier(Notifier *notify); void qemu_remove_exit_notifier(Notifier *notify); +extern bool machine_init_done; + void qemu_add_machine_init_done_notifier(Notifier *notify); void qemu_remove_machine_init_done_notifier(Notifier *notify); diff --git a/include/ui/console.h b/include/ui/console.h index 5fca9afcbc..6d2c052068 100644 --- a/include/ui/console.h +++ b/include/ui/console.h @@ -386,6 +386,7 @@ QemuConsole *graphic_console_init(DeviceState *dev, uint32_t head, void graphic_console_set_hwops(QemuConsole *con, const GraphicHwOps *hw_ops, void *opaque); +void graphic_console_close(QemuConsole *con); void graphic_hw_update(QemuConsole *con); void graphic_hw_invalidate(QemuConsole *con); @@ -398,6 +399,7 @@ QemuConsole *qemu_console_lookup_by_index(unsigned int index); QemuConsole *qemu_console_lookup_by_device(DeviceState *dev, uint32_t head); QemuConsole *qemu_console_lookup_by_device_name(const char *device_id, uint32_t head, Error **errp); +QemuConsole *qemu_console_lookup_unused(void); bool qemu_console_is_visible(QemuConsole *con); bool qemu_console_is_graphic(QemuConsole *con); bool qemu_console_is_fixedsize(QemuConsole *con); diff --git a/include/ui/qemu-pixman.h b/include/ui/qemu-pixman.h index 4a67e01232..b7c82d17fc 100644 --- a/include/ui/qemu-pixman.h +++ b/include/ui/qemu-pixman.h @@ -33,6 +33,8 @@ # define PIXMAN_BE_r8g8b8a8 PIXMAN_r8g8b8a8 # define PIXMAN_BE_x8b8g8r8 PIXMAN_x8b8g8r8 # define PIXMAN_BE_a8b8g8r8 PIXMAN_a8b8g8r8 +# define PIXMAN_LE_r8g8b8 PIXMAN_b8g8r8 +# define PIXMAN_LE_a8r8g8b8 PIXMAN_b8g8r8a8 # define PIXMAN_LE_x8r8g8b8 PIXMAN_b8g8r8x8 #else # define PIXMAN_BE_r8g8b8 PIXMAN_b8g8r8 @@ -44,6 +46,8 @@ # define PIXMAN_BE_r8g8b8a8 PIXMAN_a8b8g8r8 # define PIXMAN_BE_x8b8g8r8 PIXMAN_r8g8b8x8 # define PIXMAN_BE_a8b8g8r8 PIXMAN_r8g8b8a8 +# define PIXMAN_LE_r8g8b8 PIXMAN_r8g8b8 +# define PIXMAN_LE_a8r8g8b8 PIXMAN_a8r8g8b8 # define PIXMAN_LE_x8r8g8b8 PIXMAN_x8r8g8b8 #endif @@ -51,6 +55,7 @@ PixelFormat qemu_pixelformat_from_pixman(pixman_format_code_t format); pixman_format_code_t qemu_default_pixman_format(int bpp, bool native_endian); +pixman_format_code_t qemu_drm_format_to_pixman(uint32_t drm_format); int qemu_pixman_get_type(int rshift, int gshift, int bshift); pixman_format_code_t qemu_pixman_get_format(PixelFormat *pf); bool qemu_pixman_check_format(DisplayChangeListener *dcl, diff --git a/io/channel-util.c b/io/channel-util.c index 0fb4bd0837..423d79845a 100644 --- a/io/channel-util.c +++ b/io/channel-util.c @@ -24,19 +24,6 @@ #include "io/channel-socket.h" -static bool fd_is_socket(int fd) -{ - int optval; - socklen_t optlen; - optlen = sizeof(optval); - return qemu_getsockopt(fd, - SOL_SOCKET, - SO_TYPE, - (char *)&optval, - &optlen) == 0; -} - - QIOChannel *qio_channel_new_fd(int fd, Error **errp) { diff --git a/linux-headers/asm-powerpc/kvm.h b/linux-headers/asm-powerpc/kvm.h index 637b7263cb..833ed9a16a 100644 --- a/linux-headers/asm-powerpc/kvm.h +++ b/linux-headers/asm-powerpc/kvm.h @@ -632,6 +632,8 @@ struct kvm_ppc_cpu_char { #define KVM_REG_PPC_TIDR (KVM_REG_PPC | KVM_REG_SIZE_U64 | 0xbc) #define KVM_REG_PPC_PSSCR (KVM_REG_PPC | KVM_REG_SIZE_U64 | 0xbd) +#define KVM_REG_PPC_DEC_EXPIRY (KVM_REG_PPC | KVM_REG_SIZE_U64 | 0xbe) + /* Transactional Memory checkpointed state: * This is all GPRs, all VSX regs and a subset of SPRs */ diff --git a/linux-headers/asm-powerpc/unistd.h b/linux-headers/asm-powerpc/unistd.h index 36abf58582..0c08edcfcd 100644 --- a/linux-headers/asm-powerpc/unistd.h +++ b/linux-headers/asm-powerpc/unistd.h @@ -395,5 +395,8 @@ #define __NR_pwritev2 381 #define __NR_kexec_file_load 382 #define __NR_statx 383 +#define __NR_pkey_alloc 384 +#define __NR_pkey_free 385 +#define __NR_pkey_mprotect 386 #endif /* _ASM_POWERPC_UNISTD_H_ */ diff --git a/linux-headers/asm-s390/unistd.h b/linux-headers/asm-s390/unistd.h index 99223b874a..27b8b211c8 100644 --- a/linux-headers/asm-s390/unistd.h +++ b/linux-headers/asm-s390/unistd.h @@ -8,405 +8,10 @@ #ifndef _ASM_S390_UNISTD_H_ #define _ASM_S390_UNISTD_H_ -/* - * This file contains the system call numbers. - */ - -#define __NR_exit 1 -#define __NR_fork 2 -#define __NR_read 3 -#define __NR_write 4 -#define __NR_open 5 -#define __NR_close 6 -#define __NR_restart_syscall 7 -#define __NR_creat 8 -#define __NR_link 9 -#define __NR_unlink 10 -#define __NR_execve 11 -#define __NR_chdir 12 -#define __NR_mknod 14 -#define __NR_chmod 15 -#define __NR_lseek 19 -#define __NR_getpid 20 -#define __NR_mount 21 -#define __NR_umount 22 -#define __NR_ptrace 26 -#define __NR_alarm 27 -#define __NR_pause 29 -#define __NR_utime 30 -#define __NR_access 33 -#define __NR_nice 34 -#define __NR_sync 36 -#define __NR_kill 37 -#define __NR_rename 38 -#define __NR_mkdir 39 -#define __NR_rmdir 40 -#define __NR_dup 41 -#define __NR_pipe 42 -#define __NR_times 43 -#define __NR_brk 45 -#define __NR_signal 48 -#define __NR_acct 51 -#define __NR_umount2 52 -#define __NR_ioctl 54 -#define __NR_fcntl 55 -#define __NR_setpgid 57 -#define __NR_umask 60 -#define __NR_chroot 61 -#define __NR_ustat 62 -#define __NR_dup2 63 -#define __NR_getppid 64 -#define __NR_getpgrp 65 -#define __NR_setsid 66 -#define __NR_sigaction 67 -#define __NR_sigsuspend 72 -#define __NR_sigpending 73 -#define __NR_sethostname 74 -#define __NR_setrlimit 75 -#define __NR_getrusage 77 -#define __NR_gettimeofday 78 -#define __NR_settimeofday 79 -#define __NR_symlink 83 -#define __NR_readlink 85 -#define __NR_uselib 86 -#define __NR_swapon 87 -#define __NR_reboot 88 -#define __NR_readdir 89 -#define __NR_mmap 90 -#define __NR_munmap 91 -#define __NR_truncate 92 -#define __NR_ftruncate 93 -#define __NR_fchmod 94 -#define __NR_getpriority 96 -#define __NR_setpriority 97 -#define __NR_statfs 99 -#define __NR_fstatfs 100 -#define __NR_socketcall 102 -#define __NR_syslog 103 -#define __NR_setitimer 104 -#define __NR_getitimer 105 -#define __NR_stat 106 -#define __NR_lstat 107 -#define __NR_fstat 108 -#define __NR_lookup_dcookie 110 -#define __NR_vhangup 111 -#define __NR_idle 112 -#define __NR_wait4 114 -#define __NR_swapoff 115 -#define __NR_sysinfo 116 -#define __NR_ipc 117 -#define __NR_fsync 118 -#define __NR_sigreturn 119 -#define __NR_clone 120 -#define __NR_setdomainname 121 -#define __NR_uname 122 -#define __NR_adjtimex 124 -#define __NR_mprotect 125 -#define __NR_sigprocmask 126 -#define __NR_create_module 127 -#define __NR_init_module 128 -#define __NR_delete_module 129 -#define __NR_get_kernel_syms 130 -#define __NR_quotactl 131 -#define __NR_getpgid 132 -#define __NR_fchdir 133 -#define __NR_bdflush 134 -#define __NR_sysfs 135 -#define __NR_personality 136 -#define __NR_afs_syscall 137 /* Syscall for Andrew File System */ -#define __NR_getdents 141 -#define __NR_flock 143 -#define __NR_msync 144 -#define __NR_readv 145 -#define __NR_writev 146 -#define __NR_getsid 147 -#define __NR_fdatasync 148 -#define __NR__sysctl 149 -#define __NR_mlock 150 -#define __NR_munlock 151 -#define __NR_mlockall 152 -#define __NR_munlockall 153 -#define __NR_sched_setparam 154 -#define __NR_sched_getparam 155 -#define __NR_sched_setscheduler 156 -#define __NR_sched_getscheduler 157 -#define __NR_sched_yield 158 -#define __NR_sched_get_priority_max 159 -#define __NR_sched_get_priority_min 160 -#define __NR_sched_rr_get_interval 161 -#define __NR_nanosleep 162 -#define __NR_mremap 163 -#define __NR_query_module 167 -#define __NR_poll 168 -#define __NR_nfsservctl 169 -#define __NR_prctl 172 -#define __NR_rt_sigreturn 173 -#define __NR_rt_sigaction 174 -#define __NR_rt_sigprocmask 175 -#define __NR_rt_sigpending 176 -#define __NR_rt_sigtimedwait 177 -#define __NR_rt_sigqueueinfo 178 -#define __NR_rt_sigsuspend 179 -#define __NR_pread64 180 -#define __NR_pwrite64 181 -#define __NR_getcwd 183 -#define __NR_capget 184 -#define __NR_capset 185 -#define __NR_sigaltstack 186 -#define __NR_sendfile 187 -#define __NR_getpmsg 188 -#define __NR_putpmsg 189 -#define __NR_vfork 190 -#define __NR_pivot_root 217 -#define __NR_mincore 218 -#define __NR_madvise 219 -#define __NR_getdents64 220 -#define __NR_readahead 222 -#define __NR_setxattr 224 -#define __NR_lsetxattr 225 -#define __NR_fsetxattr 226 -#define __NR_getxattr 227 -#define __NR_lgetxattr 228 -#define __NR_fgetxattr 229 -#define __NR_listxattr 230 -#define __NR_llistxattr 231 -#define __NR_flistxattr 232 -#define __NR_removexattr 233 -#define __NR_lremovexattr 234 -#define __NR_fremovexattr 235 -#define __NR_gettid 236 -#define __NR_tkill 237 -#define __NR_futex 238 -#define __NR_sched_setaffinity 239 -#define __NR_sched_getaffinity 240 -#define __NR_tgkill 241 -/* Number 242 is reserved for tux */ -#define __NR_io_setup 243 -#define __NR_io_destroy 244 -#define __NR_io_getevents 245 -#define __NR_io_submit 246 -#define __NR_io_cancel 247 -#define __NR_exit_group 248 -#define __NR_epoll_create 249 -#define __NR_epoll_ctl 250 -#define __NR_epoll_wait 251 -#define __NR_set_tid_address 252 -#define __NR_fadvise64 253 -#define __NR_timer_create 254 -#define __NR_timer_settime 255 -#define __NR_timer_gettime 256 -#define __NR_timer_getoverrun 257 -#define __NR_timer_delete 258 -#define __NR_clock_settime 259 -#define __NR_clock_gettime 260 -#define __NR_clock_getres 261 -#define __NR_clock_nanosleep 262 -/* Number 263 is reserved for vserver */ -#define __NR_statfs64 265 -#define __NR_fstatfs64 266 -#define __NR_remap_file_pages 267 -#define __NR_mbind 268 -#define __NR_get_mempolicy 269 -#define __NR_set_mempolicy 270 -#define __NR_mq_open 271 -#define __NR_mq_unlink 272 -#define __NR_mq_timedsend 273 -#define __NR_mq_timedreceive 274 -#define __NR_mq_notify 275 -#define __NR_mq_getsetattr 276 -#define __NR_kexec_load 277 -#define __NR_add_key 278 -#define __NR_request_key 279 -#define __NR_keyctl 280 -#define __NR_waitid 281 -#define __NR_ioprio_set 282 -#define __NR_ioprio_get 283 -#define __NR_inotify_init 284 -#define __NR_inotify_add_watch 285 -#define __NR_inotify_rm_watch 286 -#define __NR_migrate_pages 287 -#define __NR_openat 288 -#define __NR_mkdirat 289 -#define __NR_mknodat 290 -#define __NR_fchownat 291 -#define __NR_futimesat 292 -#define __NR_unlinkat 294 -#define __NR_renameat 295 -#define __NR_linkat 296 -#define __NR_symlinkat 297 -#define __NR_readlinkat 298 -#define __NR_fchmodat 299 -#define __NR_faccessat 300 -#define __NR_pselect6 301 -#define __NR_ppoll 302 -#define __NR_unshare 303 -#define __NR_set_robust_list 304 -#define __NR_get_robust_list 305 -#define __NR_splice 306 -#define __NR_sync_file_range 307 -#define __NR_tee 308 -#define __NR_vmsplice 309 -#define __NR_move_pages 310 -#define __NR_getcpu 311 -#define __NR_epoll_pwait 312 -#define __NR_utimes 313 -#define __NR_fallocate 314 -#define __NR_utimensat 315 -#define __NR_signalfd 316 -#define __NR_timerfd 317 -#define __NR_eventfd 318 -#define __NR_timerfd_create 319 -#define __NR_timerfd_settime 320 -#define __NR_timerfd_gettime 321 -#define __NR_signalfd4 322 -#define __NR_eventfd2 323 -#define __NR_inotify_init1 324 -#define __NR_pipe2 325 -#define __NR_dup3 326 -#define __NR_epoll_create1 327 -#define __NR_preadv 328 -#define __NR_pwritev 329 -#define __NR_rt_tgsigqueueinfo 330 -#define __NR_perf_event_open 331 -#define __NR_fanotify_init 332 -#define __NR_fanotify_mark 333 -#define __NR_prlimit64 334 -#define __NR_name_to_handle_at 335 -#define __NR_open_by_handle_at 336 -#define __NR_clock_adjtime 337 -#define __NR_syncfs 338 -#define __NR_setns 339 -#define __NR_process_vm_readv 340 -#define __NR_process_vm_writev 341 -#define __NR_s390_runtime_instr 342 -#define __NR_kcmp 343 -#define __NR_finit_module 344 -#define __NR_sched_setattr 345 -#define __NR_sched_getattr 346 -#define __NR_renameat2 347 -#define __NR_seccomp 348 -#define __NR_getrandom 349 -#define __NR_memfd_create 350 -#define __NR_bpf 351 -#define __NR_s390_pci_mmio_write 352 -#define __NR_s390_pci_mmio_read 353 -#define __NR_execveat 354 -#define __NR_userfaultfd 355 -#define __NR_membarrier 356 -#define __NR_recvmmsg 357 -#define __NR_sendmmsg 358 -#define __NR_socket 359 -#define __NR_socketpair 360 -#define __NR_bind 361 -#define __NR_connect 362 -#define __NR_listen 363 -#define __NR_accept4 364 -#define __NR_getsockopt 365 -#define __NR_setsockopt 366 -#define __NR_getsockname 367 -#define __NR_getpeername 368 -#define __NR_sendto 369 -#define __NR_sendmsg 370 -#define __NR_recvfrom 371 -#define __NR_recvmsg 372 -#define __NR_shutdown 373 -#define __NR_mlock2 374 -#define __NR_copy_file_range 375 -#define __NR_preadv2 376 -#define __NR_pwritev2 377 -#define __NR_s390_guarded_storage 378 -#define __NR_statx 379 -#define __NR_s390_sthyi 380 -#define NR_syscalls 381 - -/* - * There are some system calls that are not present on 64 bit, some - * have a different name although they do the same (e.g. __NR_chown32 - * is __NR_chown on 64 bit). - */ -#ifndef __s390x__ - -#define __NR_time 13 -#define __NR_lchown 16 -#define __NR_setuid 23 -#define __NR_getuid 24 -#define __NR_stime 25 -#define __NR_setgid 46 -#define __NR_getgid 47 -#define __NR_geteuid 49 -#define __NR_getegid 50 -#define __NR_setreuid 70 -#define __NR_setregid 71 -#define __NR_getrlimit 76 -#define __NR_getgroups 80 -#define __NR_setgroups 81 -#define __NR_fchown 95 -#define __NR_ioperm 101 -#define __NR_setfsuid 138 -#define __NR_setfsgid 139 -#define __NR__llseek 140 -#define __NR__newselect 142 -#define __NR_setresuid 164 -#define __NR_getresuid 165 -#define __NR_setresgid 170 -#define __NR_getresgid 171 -#define __NR_chown 182 -#define __NR_ugetrlimit 191 /* SuS compliant getrlimit */ -#define __NR_mmap2 192 -#define __NR_truncate64 193 -#define __NR_ftruncate64 194 -#define __NR_stat64 195 -#define __NR_lstat64 196 -#define __NR_fstat64 197 -#define __NR_lchown32 198 -#define __NR_getuid32 199 -#define __NR_getgid32 200 -#define __NR_geteuid32 201 -#define __NR_getegid32 202 -#define __NR_setreuid32 203 -#define __NR_setregid32 204 -#define __NR_getgroups32 205 -#define __NR_setgroups32 206 -#define __NR_fchown32 207 -#define __NR_setresuid32 208 -#define __NR_getresuid32 209 -#define __NR_setresgid32 210 -#define __NR_getresgid32 211 -#define __NR_chown32 212 -#define __NR_setuid32 213 -#define __NR_setgid32 214 -#define __NR_setfsuid32 215 -#define __NR_setfsgid32 216 -#define __NR_fcntl64 221 -#define __NR_sendfile64 223 -#define __NR_fadvise64_64 264 -#define __NR_fstatat64 293 - +#ifdef __s390x__ +#include <asm/unistd_64.h> #else - -#define __NR_select 142 -#define __NR_getrlimit 191 /* SuS compliant getrlimit */ -#define __NR_lchown 198 -#define __NR_getuid 199 -#define __NR_getgid 200 -#define __NR_geteuid 201 -#define __NR_getegid 202 -#define __NR_setreuid 203 -#define __NR_setregid 204 -#define __NR_getgroups 205 -#define __NR_setgroups 206 -#define __NR_fchown 207 -#define __NR_setresuid 208 -#define __NR_getresuid 209 -#define __NR_setresgid 210 -#define __NR_getresgid 211 -#define __NR_chown 212 -#define __NR_setuid 213 -#define __NR_setgid 214 -#define __NR_setfsuid 215 -#define __NR_setfsgid 216 -#define __NR_newfstatat 293 - +#include <asm/unistd_32.h> #endif #endif /* _ASM_S390_UNISTD_H_ */ diff --git a/linux-headers/asm-s390/unistd_32.h b/linux-headers/asm-s390/unistd_32.h new file mode 100644 index 0000000000..1ae66a263b --- /dev/null +++ b/linux-headers/asm-s390/unistd_32.h @@ -0,0 +1,364 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ +#ifndef _ASM_S390_UNISTD_32_H +#define _ASM_S390_UNISTD_32_H + +#define __NR_exit 1 +#define __NR_fork 2 +#define __NR_read 3 +#define __NR_write 4 +#define __NR_open 5 +#define __NR_close 6 +#define __NR_restart_syscall 7 +#define __NR_creat 8 +#define __NR_link 9 +#define __NR_unlink 10 +#define __NR_execve 11 +#define __NR_chdir 12 +#define __NR_time 13 +#define __NR_mknod 14 +#define __NR_chmod 15 +#define __NR_lchown 16 +#define __NR_lseek 19 +#define __NR_getpid 20 +#define __NR_mount 21 +#define __NR_umount 22 +#define __NR_setuid 23 +#define __NR_getuid 24 +#define __NR_stime 25 +#define __NR_ptrace 26 +#define __NR_alarm 27 +#define __NR_pause 29 +#define __NR_utime 30 +#define __NR_access 33 +#define __NR_nice 34 +#define __NR_sync 36 +#define __NR_kill 37 +#define __NR_rename 38 +#define __NR_mkdir 39 +#define __NR_rmdir 40 +#define __NR_dup 41 +#define __NR_pipe 42 +#define __NR_times 43 +#define __NR_brk 45 +#define __NR_setgid 46 +#define __NR_getgid 47 +#define __NR_signal 48 +#define __NR_geteuid 49 +#define __NR_getegid 50 +#define __NR_acct 51 +#define __NR_umount2 52 +#define __NR_ioctl 54 +#define __NR_fcntl 55 +#define __NR_setpgid 57 +#define __NR_umask 60 +#define __NR_chroot 61 +#define __NR_ustat 62 +#define __NR_dup2 63 +#define __NR_getppid 64 +#define __NR_getpgrp 65 +#define __NR_setsid 66 +#define __NR_sigaction 67 +#define __NR_setreuid 70 +#define __NR_setregid 71 +#define __NR_sigsuspend 72 +#define __NR_sigpending 73 +#define __NR_sethostname 74 +#define __NR_setrlimit 75 +#define __NR_getrlimit 76 +#define __NR_getrusage 77 +#define __NR_gettimeofday 78 +#define __NR_settimeofday 79 +#define __NR_getgroups 80 +#define __NR_setgroups 81 +#define __NR_symlink 83 +#define __NR_readlink 85 +#define __NR_uselib 86 +#define __NR_swapon 87 +#define __NR_reboot 88 +#define __NR_readdir 89 +#define __NR_mmap 90 +#define __NR_munmap 91 +#define __NR_truncate 92 +#define __NR_ftruncate 93 +#define __NR_fchmod 94 +#define __NR_fchown 95 +#define __NR_getpriority 96 +#define __NR_setpriority 97 +#define __NR_statfs 99 +#define __NR_fstatfs 100 +#define __NR_ioperm 101 +#define __NR_socketcall 102 +#define __NR_syslog 103 +#define __NR_setitimer 104 +#define __NR_getitimer 105 +#define __NR_stat 106 +#define __NR_lstat 107 +#define __NR_fstat 108 +#define __NR_lookup_dcookie 110 +#define __NR_vhangup 111 +#define __NR_idle 112 +#define __NR_wait4 114 +#define __NR_swapoff 115 +#define __NR_sysinfo 116 +#define __NR_ipc 117 +#define __NR_fsync 118 +#define __NR_sigreturn 119 +#define __NR_clone 120 +#define __NR_setdomainname 121 +#define __NR_uname 122 +#define __NR_adjtimex 124 +#define __NR_mprotect 125 +#define __NR_sigprocmask 126 +#define __NR_create_module 127 +#define __NR_init_module 128 +#define __NR_delete_module 129 +#define __NR_get_kernel_syms 130 +#define __NR_quotactl 131 +#define __NR_getpgid 132 +#define __NR_fchdir 133 +#define __NR_bdflush 134 +#define __NR_sysfs 135 +#define __NR_personality 136 +#define __NR_afs_syscall 137 +#define __NR_setfsuid 138 +#define __NR_setfsgid 139 +#define __NR__llseek 140 +#define __NR_getdents 141 +#define __NR__newselect 142 +#define __NR_flock 143 +#define __NR_msync 144 +#define __NR_readv 145 +#define __NR_writev 146 +#define __NR_getsid 147 +#define __NR_fdatasync 148 +#define __NR__sysctl 149 +#define __NR_mlock 150 +#define __NR_munlock 151 +#define __NR_mlockall 152 +#define __NR_munlockall 153 +#define __NR_sched_setparam 154 +#define __NR_sched_getparam 155 +#define __NR_sched_setscheduler 156 +#define __NR_sched_getscheduler 157 +#define __NR_sched_yield 158 +#define __NR_sched_get_priority_max 159 +#define __NR_sched_get_priority_min 160 +#define __NR_sched_rr_get_interval 161 +#define __NR_nanosleep 162 +#define __NR_mremap 163 +#define __NR_setresuid 164 +#define __NR_getresuid 165 +#define __NR_query_module 167 +#define __NR_poll 168 +#define __NR_nfsservctl 169 +#define __NR_setresgid 170 +#define __NR_getresgid 171 +#define __NR_prctl 172 +#define __NR_rt_sigreturn 173 +#define __NR_rt_sigaction 174 +#define __NR_rt_sigprocmask 175 +#define __NR_rt_sigpending 176 +#define __NR_rt_sigtimedwait 177 +#define __NR_rt_sigqueueinfo 178 +#define __NR_rt_sigsuspend 179 +#define __NR_pread64 180 +#define __NR_pwrite64 181 +#define __NR_chown 182 +#define __NR_getcwd 183 +#define __NR_capget 184 +#define __NR_capset 185 +#define __NR_sigaltstack 186 +#define __NR_sendfile 187 +#define __NR_getpmsg 188 +#define __NR_putpmsg 189 +#define __NR_vfork 190 +#define __NR_ugetrlimit 191 +#define __NR_mmap2 192 +#define __NR_truncate64 193 +#define __NR_ftruncate64 194 +#define __NR_stat64 195 +#define __NR_lstat64 196 +#define __NR_fstat64 197 +#define __NR_lchown32 198 +#define __NR_getuid32 199 +#define __NR_getgid32 200 +#define __NR_geteuid32 201 +#define __NR_getegid32 202 +#define __NR_setreuid32 203 +#define __NR_setregid32 204 +#define __NR_getgroups32 205 +#define __NR_setgroups32 206 +#define __NR_fchown32 207 +#define __NR_setresuid32 208 +#define __NR_getresuid32 209 +#define __NR_setresgid32 210 +#define __NR_getresgid32 211 +#define __NR_chown32 212 +#define __NR_setuid32 213 +#define __NR_setgid32 214 +#define __NR_setfsuid32 215 +#define __NR_setfsgid32 216 +#define __NR_pivot_root 217 +#define __NR_mincore 218 +#define __NR_madvise 219 +#define __NR_getdents64 220 +#define __NR_fcntl64 221 +#define __NR_readahead 222 +#define __NR_sendfile64 223 +#define __NR_setxattr 224 +#define __NR_lsetxattr 225 +#define __NR_fsetxattr 226 +#define __NR_getxattr 227 +#define __NR_lgetxattr 228 +#define __NR_fgetxattr 229 +#define __NR_listxattr 230 +#define __NR_llistxattr 231 +#define __NR_flistxattr 232 +#define __NR_removexattr 233 +#define __NR_lremovexattr 234 +#define __NR_fremovexattr 235 +#define __NR_gettid 236 +#define __NR_tkill 237 +#define __NR_futex 238 +#define __NR_sched_setaffinity 239 +#define __NR_sched_getaffinity 240 +#define __NR_tgkill 241 +#define __NR_io_setup 243 +#define __NR_io_destroy 244 +#define __NR_io_getevents 245 +#define __NR_io_submit 246 +#define __NR_io_cancel 247 +#define __NR_exit_group 248 +#define __NR_epoll_create 249 +#define __NR_epoll_ctl 250 +#define __NR_epoll_wait 251 +#define __NR_set_tid_address 252 +#define __NR_fadvise64 253 +#define __NR_timer_create 254 +#define __NR_timer_settime 255 +#define __NR_timer_gettime 256 +#define __NR_timer_getoverrun 257 +#define __NR_timer_delete 258 +#define __NR_clock_settime 259 +#define __NR_clock_gettime 260 +#define __NR_clock_getres 261 +#define __NR_clock_nanosleep 262 +#define __NR_fadvise64_64 264 +#define __NR_statfs64 265 +#define __NR_fstatfs64 266 +#define __NR_remap_file_pages 267 +#define __NR_mbind 268 +#define __NR_get_mempolicy 269 +#define __NR_set_mempolicy 270 +#define __NR_mq_open 271 +#define __NR_mq_unlink 272 +#define __NR_mq_timedsend 273 +#define __NR_mq_timedreceive 274 +#define __NR_mq_notify 275 +#define __NR_mq_getsetattr 276 +#define __NR_kexec_load 277 +#define __NR_add_key 278 +#define __NR_request_key 279 +#define __NR_keyctl 280 +#define __NR_waitid 281 +#define __NR_ioprio_set 282 +#define __NR_ioprio_get 283 +#define __NR_inotify_init 284 +#define __NR_inotify_add_watch 285 +#define __NR_inotify_rm_watch 286 +#define __NR_migrate_pages 287 +#define __NR_openat 288 +#define __NR_mkdirat 289 +#define __NR_mknodat 290 +#define __NR_fchownat 291 +#define __NR_futimesat 292 +#define __NR_fstatat64 293 +#define __NR_unlinkat 294 +#define __NR_renameat 295 +#define __NR_linkat 296 +#define __NR_symlinkat 297 +#define __NR_readlinkat 298 +#define __NR_fchmodat 299 +#define __NR_faccessat 300 +#define __NR_pselect6 301 +#define __NR_ppoll 302 +#define __NR_unshare 303 +#define __NR_set_robust_list 304 +#define __NR_get_robust_list 305 +#define __NR_splice 306 +#define __NR_sync_file_range 307 +#define __NR_tee 308 +#define __NR_vmsplice 309 +#define __NR_move_pages 310 +#define __NR_getcpu 311 +#define __NR_epoll_pwait 312 +#define __NR_utimes 313 +#define __NR_fallocate 314 +#define __NR_utimensat 315 +#define __NR_signalfd 316 +#define __NR_timerfd 317 +#define __NR_eventfd 318 +#define __NR_timerfd_create 319 +#define __NR_timerfd_settime 320 +#define __NR_timerfd_gettime 321 +#define __NR_signalfd4 322 +#define __NR_eventfd2 323 +#define __NR_inotify_init1 324 +#define __NR_pipe2 325 +#define __NR_dup3 326 +#define __NR_epoll_create1 327 +#define __NR_preadv 328 +#define __NR_pwritev 329 +#define __NR_rt_tgsigqueueinfo 330 +#define __NR_perf_event_open 331 +#define __NR_fanotify_init 332 +#define __NR_fanotify_mark 333 +#define __NR_prlimit64 334 +#define __NR_name_to_handle_at 335 +#define __NR_open_by_handle_at 336 +#define __NR_clock_adjtime 337 +#define __NR_syncfs 338 +#define __NR_setns 339 +#define __NR_process_vm_readv 340 +#define __NR_process_vm_writev 341 +#define __NR_s390_runtime_instr 342 +#define __NR_kcmp 343 +#define __NR_finit_module 344 +#define __NR_sched_setattr 345 +#define __NR_sched_getattr 346 +#define __NR_renameat2 347 +#define __NR_seccomp 348 +#define __NR_getrandom 349 +#define __NR_memfd_create 350 +#define __NR_bpf 351 +#define __NR_s390_pci_mmio_write 352 +#define __NR_s390_pci_mmio_read 353 +#define __NR_execveat 354 +#define __NR_userfaultfd 355 +#define __NR_membarrier 356 +#define __NR_recvmmsg 357 +#define __NR_sendmmsg 358 +#define __NR_socket 359 +#define __NR_socketpair 360 +#define __NR_bind 361 +#define __NR_connect 362 +#define __NR_listen 363 +#define __NR_accept4 364 +#define __NR_getsockopt 365 +#define __NR_setsockopt 366 +#define __NR_getsockname 367 +#define __NR_getpeername 368 +#define __NR_sendto 369 +#define __NR_sendmsg 370 +#define __NR_recvfrom 371 +#define __NR_recvmsg 372 +#define __NR_shutdown 373 +#define __NR_mlock2 374 +#define __NR_copy_file_range 375 +#define __NR_preadv2 376 +#define __NR_pwritev2 377 +#define __NR_s390_guarded_storage 378 +#define __NR_statx 379 +#define __NR_s390_sthyi 380 + +#endif /* _ASM_S390_UNISTD_32_H */ diff --git a/linux-headers/asm-s390/unistd_64.h b/linux-headers/asm-s390/unistd_64.h new file mode 100644 index 0000000000..8aa9d046a9 --- /dev/null +++ b/linux-headers/asm-s390/unistd_64.h @@ -0,0 +1,331 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ +#ifndef _ASM_S390_UNISTD_64_H +#define _ASM_S390_UNISTD_64_H + +#define __NR_exit 1 +#define __NR_fork 2 +#define __NR_read 3 +#define __NR_write 4 +#define __NR_open 5 +#define __NR_close 6 +#define __NR_restart_syscall 7 +#define __NR_creat 8 +#define __NR_link 9 +#define __NR_unlink 10 +#define __NR_execve 11 +#define __NR_chdir 12 +#define __NR_mknod 14 +#define __NR_chmod 15 +#define __NR_lseek 19 +#define __NR_getpid 20 +#define __NR_mount 21 +#define __NR_umount 22 +#define __NR_ptrace 26 +#define __NR_alarm 27 +#define __NR_pause 29 +#define __NR_utime 30 +#define __NR_access 33 +#define __NR_nice 34 +#define __NR_sync 36 +#define __NR_kill 37 +#define __NR_rename 38 +#define __NR_mkdir 39 +#define __NR_rmdir 40 +#define __NR_dup 41 +#define __NR_pipe 42 +#define __NR_times 43 +#define __NR_brk 45 +#define __NR_signal 48 +#define __NR_acct 51 +#define __NR_umount2 52 +#define __NR_ioctl 54 +#define __NR_fcntl 55 +#define __NR_setpgid 57 +#define __NR_umask 60 +#define __NR_chroot 61 +#define __NR_ustat 62 +#define __NR_dup2 63 +#define __NR_getppid 64 +#define __NR_getpgrp 65 +#define __NR_setsid 66 +#define __NR_sigaction 67 +#define __NR_sigsuspend 72 +#define __NR_sigpending 73 +#define __NR_sethostname 74 +#define __NR_setrlimit 75 +#define __NR_getrusage 77 +#define __NR_gettimeofday 78 +#define __NR_settimeofday 79 +#define __NR_symlink 83 +#define __NR_readlink 85 +#define __NR_uselib 86 +#define __NR_swapon 87 +#define __NR_reboot 88 +#define __NR_readdir 89 +#define __NR_mmap 90 +#define __NR_munmap 91 +#define __NR_truncate 92 +#define __NR_ftruncate 93 +#define __NR_fchmod 94 +#define __NR_getpriority 96 +#define __NR_setpriority 97 +#define __NR_statfs 99 +#define __NR_fstatfs 100 +#define __NR_socketcall 102 +#define __NR_syslog 103 +#define __NR_setitimer 104 +#define __NR_getitimer 105 +#define __NR_stat 106 +#define __NR_lstat 107 +#define __NR_fstat 108 +#define __NR_lookup_dcookie 110 +#define __NR_vhangup 111 +#define __NR_idle 112 +#define __NR_wait4 114 +#define __NR_swapoff 115 +#define __NR_sysinfo 116 +#define __NR_ipc 117 +#define __NR_fsync 118 +#define __NR_sigreturn 119 +#define __NR_clone 120 +#define __NR_setdomainname 121 +#define __NR_uname 122 +#define __NR_adjtimex 124 +#define __NR_mprotect 125 +#define __NR_sigprocmask 126 +#define __NR_create_module 127 +#define __NR_init_module 128 +#define __NR_delete_module 129 +#define __NR_get_kernel_syms 130 +#define __NR_quotactl 131 +#define __NR_getpgid 132 +#define __NR_fchdir 133 +#define __NR_bdflush 134 +#define __NR_sysfs 135 +#define __NR_personality 136 +#define __NR_afs_syscall 137 +#define __NR_getdents 141 +#define __NR_select 142 +#define __NR_flock 143 +#define __NR_msync 144 +#define __NR_readv 145 +#define __NR_writev 146 +#define __NR_getsid 147 +#define __NR_fdatasync 148 +#define __NR__sysctl 149 +#define __NR_mlock 150 +#define __NR_munlock 151 +#define __NR_mlockall 152 +#define __NR_munlockall 153 +#define __NR_sched_setparam 154 +#define __NR_sched_getparam 155 +#define __NR_sched_setscheduler 156 +#define __NR_sched_getscheduler 157 +#define __NR_sched_yield 158 +#define __NR_sched_get_priority_max 159 +#define __NR_sched_get_priority_min 160 +#define __NR_sched_rr_get_interval 161 +#define __NR_nanosleep 162 +#define __NR_mremap 163 +#define __NR_query_module 167 +#define __NR_poll 168 +#define __NR_nfsservctl 169 +#define __NR_prctl 172 +#define __NR_rt_sigreturn 173 +#define __NR_rt_sigaction 174 +#define __NR_rt_sigprocmask 175 +#define __NR_rt_sigpending 176 +#define __NR_rt_sigtimedwait 177 +#define __NR_rt_sigqueueinfo 178 +#define __NR_rt_sigsuspend 179 +#define __NR_pread64 180 +#define __NR_pwrite64 181 +#define __NR_getcwd 183 +#define __NR_capget 184 +#define __NR_capset 185 +#define __NR_sigaltstack 186 +#define __NR_sendfile 187 +#define __NR_getpmsg 188 +#define __NR_putpmsg 189 +#define __NR_vfork 190 +#define __NR_getrlimit 191 +#define __NR_lchown 198 +#define __NR_getuid 199 +#define __NR_getgid 200 +#define __NR_geteuid 201 +#define __NR_getegid 202 +#define __NR_setreuid 203 +#define __NR_setregid 204 +#define __NR_getgroups 205 +#define __NR_setgroups 206 +#define __NR_fchown 207 +#define __NR_setresuid 208 +#define __NR_getresuid 209 +#define __NR_setresgid 210 +#define __NR_getresgid 211 +#define __NR_chown 212 +#define __NR_setuid 213 +#define __NR_setgid 214 +#define __NR_setfsuid 215 +#define __NR_setfsgid 216 +#define __NR_pivot_root 217 +#define __NR_mincore 218 +#define __NR_madvise 219 +#define __NR_getdents64 220 +#define __NR_readahead 222 +#define __NR_setxattr 224 +#define __NR_lsetxattr 225 +#define __NR_fsetxattr 226 +#define __NR_getxattr 227 +#define __NR_lgetxattr 228 +#define __NR_fgetxattr 229 +#define __NR_listxattr 230 +#define __NR_llistxattr 231 +#define __NR_flistxattr 232 +#define __NR_removexattr 233 +#define __NR_lremovexattr 234 +#define __NR_fremovexattr 235 +#define __NR_gettid 236 +#define __NR_tkill 237 +#define __NR_futex 238 +#define __NR_sched_setaffinity 239 +#define __NR_sched_getaffinity 240 +#define __NR_tgkill 241 +#define __NR_io_setup 243 +#define __NR_io_destroy 244 +#define __NR_io_getevents 245 +#define __NR_io_submit 246 +#define __NR_io_cancel 247 +#define __NR_exit_group 248 +#define __NR_epoll_create 249 +#define __NR_epoll_ctl 250 +#define __NR_epoll_wait 251 +#define __NR_set_tid_address 252 +#define __NR_fadvise64 253 +#define __NR_timer_create 254 +#define __NR_timer_settime 255 +#define __NR_timer_gettime 256 +#define __NR_timer_getoverrun 257 +#define __NR_timer_delete 258 +#define __NR_clock_settime 259 +#define __NR_clock_gettime 260 +#define __NR_clock_getres 261 +#define __NR_clock_nanosleep 262 +#define __NR_statfs64 265 +#define __NR_fstatfs64 266 +#define __NR_remap_file_pages 267 +#define __NR_mbind 268 +#define __NR_get_mempolicy 269 +#define __NR_set_mempolicy 270 +#define __NR_mq_open 271 +#define __NR_mq_unlink 272 +#define __NR_mq_timedsend 273 +#define __NR_mq_timedreceive 274 +#define __NR_mq_notify 275 +#define __NR_mq_getsetattr 276 +#define __NR_kexec_load 277 +#define __NR_add_key 278 +#define __NR_request_key 279 +#define __NR_keyctl 280 +#define __NR_waitid 281 +#define __NR_ioprio_set 282 +#define __NR_ioprio_get 283 +#define __NR_inotify_init 284 +#define __NR_inotify_add_watch 285 +#define __NR_inotify_rm_watch 286 +#define __NR_migrate_pages 287 +#define __NR_openat 288 +#define __NR_mkdirat 289 +#define __NR_mknodat 290 +#define __NR_fchownat 291 +#define __NR_futimesat 292 +#define __NR_newfstatat 293 +#define __NR_unlinkat 294 +#define __NR_renameat 295 +#define __NR_linkat 296 +#define __NR_symlinkat 297 +#define __NR_readlinkat 298 +#define __NR_fchmodat 299 +#define __NR_faccessat 300 +#define __NR_pselect6 301 +#define __NR_ppoll 302 +#define __NR_unshare 303 +#define __NR_set_robust_list 304 +#define __NR_get_robust_list 305 +#define __NR_splice 306 +#define __NR_sync_file_range 307 +#define __NR_tee 308 +#define __NR_vmsplice 309 +#define __NR_move_pages 310 +#define __NR_getcpu 311 +#define __NR_epoll_pwait 312 +#define __NR_utimes 313 +#define __NR_fallocate 314 +#define __NR_utimensat 315 +#define __NR_signalfd 316 +#define __NR_timerfd 317 +#define __NR_eventfd 318 +#define __NR_timerfd_create 319 +#define __NR_timerfd_settime 320 +#define __NR_timerfd_gettime 321 +#define __NR_signalfd4 322 +#define __NR_eventfd2 323 +#define __NR_inotify_init1 324 +#define __NR_pipe2 325 +#define __NR_dup3 326 +#define __NR_epoll_create1 327 +#define __NR_preadv 328 +#define __NR_pwritev 329 +#define __NR_rt_tgsigqueueinfo 330 +#define __NR_perf_event_open 331 +#define __NR_fanotify_init 332 +#define __NR_fanotify_mark 333 +#define __NR_prlimit64 334 +#define __NR_name_to_handle_at 335 +#define __NR_open_by_handle_at 336 +#define __NR_clock_adjtime 337 +#define __NR_syncfs 338 +#define __NR_setns 339 +#define __NR_process_vm_readv 340 +#define __NR_process_vm_writev 341 +#define __NR_s390_runtime_instr 342 +#define __NR_kcmp 343 +#define __NR_finit_module 344 +#define __NR_sched_setattr 345 +#define __NR_sched_getattr 346 +#define __NR_renameat2 347 +#define __NR_seccomp 348 +#define __NR_getrandom 349 +#define __NR_memfd_create 350 +#define __NR_bpf 351 +#define __NR_s390_pci_mmio_write 352 +#define __NR_s390_pci_mmio_read 353 +#define __NR_execveat 354 +#define __NR_userfaultfd 355 +#define __NR_membarrier 356 +#define __NR_recvmmsg 357 +#define __NR_sendmmsg 358 +#define __NR_socket 359 +#define __NR_socketpair 360 +#define __NR_bind 361 +#define __NR_connect 362 +#define __NR_listen 363 +#define __NR_accept4 364 +#define __NR_getsockopt 365 +#define __NR_setsockopt 366 +#define __NR_getsockname 367 +#define __NR_getpeername 368 +#define __NR_sendto 369 +#define __NR_sendmsg 370 +#define __NR_recvfrom 371 +#define __NR_recvmsg 372 +#define __NR_shutdown 373 +#define __NR_mlock2 374 +#define __NR_copy_file_range 375 +#define __NR_preadv2 376 +#define __NR_pwritev2 377 +#define __NR_s390_guarded_storage 378 +#define __NR_statx 379 +#define __NR_s390_sthyi 380 + +#endif /* _ASM_S390_UNISTD_64_H */ diff --git a/linux-headers/asm-x86/kvm_para.h b/linux-headers/asm-x86/kvm_para.h index 4c300f6aaa..4c58184395 100644 --- a/linux-headers/asm-x86/kvm_para.h +++ b/linux-headers/asm-x86/kvm_para.h @@ -25,6 +25,8 @@ #define KVM_FEATURE_STEAL_TIME 5 #define KVM_FEATURE_PV_EOI 6 #define KVM_FEATURE_PV_UNHALT 7 +#define KVM_FEATURE_PV_TLB_FLUSH 9 +#define KVM_FEATURE_ASYNC_PF_VMEXIT 10 /* The last 8 bits are used to indicate how to interpret the flags field * in pvclock structure. If no bits are set, all flags are ignored. @@ -51,6 +53,9 @@ struct kvm_steal_time { __u32 pad[11]; }; +#define KVM_VCPU_PREEMPTED (1 << 0) +#define KVM_VCPU_FLUSH_TLB (1 << 1) + #define KVM_CLOCK_PAIRING_WALLCLOCK 0 struct kvm_clock_pairing { __s64 sec; diff --git a/linux-headers/linux/kvm.h b/linux-headers/linux/kvm.h index d92c9b2f0e..a167be89d1 100644 --- a/linux-headers/linux/kvm.h +++ b/linux-headers/linux/kvm.h @@ -761,6 +761,7 @@ struct kvm_ppc_resize_hpt { #define KVM_TRACE_PAUSE __KVM_DEPRECATED_MAIN_0x07 #define KVM_TRACE_DISABLE __KVM_DEPRECATED_MAIN_0x08 #define KVM_GET_EMULATED_CPUID _IOWR(KVMIO, 0x09, struct kvm_cpuid2) +#define KVM_GET_MSR_FEATURE_INDEX_LIST _IOWR(KVMIO, 0x0a, struct kvm_msr_list) /* * Extension capability list. @@ -934,6 +935,7 @@ struct kvm_ppc_resize_hpt { #define KVM_CAP_S390_AIS_MIGRATION 150 #define KVM_CAP_PPC_GET_CPU_CHAR 151 #define KVM_CAP_S390_BPB 152 +#define KVM_CAP_GET_MSR_FEATURES 153 #ifdef KVM_CAP_IRQ_ROUTING @@ -1362,6 +1364,96 @@ struct kvm_s390_ucas_mapping { /* Available with KVM_CAP_S390_CMMA_MIGRATION */ #define KVM_S390_GET_CMMA_BITS _IOWR(KVMIO, 0xb8, struct kvm_s390_cmma_log) #define KVM_S390_SET_CMMA_BITS _IOW(KVMIO, 0xb9, struct kvm_s390_cmma_log) +/* Memory Encryption Commands */ +#define KVM_MEMORY_ENCRYPT_OP _IOWR(KVMIO, 0xba, unsigned long) + +struct kvm_enc_region { + __u64 addr; + __u64 size; +}; + +#define KVM_MEMORY_ENCRYPT_REG_REGION _IOR(KVMIO, 0xbb, struct kvm_enc_region) +#define KVM_MEMORY_ENCRYPT_UNREG_REGION _IOR(KVMIO, 0xbc, struct kvm_enc_region) + +/* Secure Encrypted Virtualization command */ +enum sev_cmd_id { + /* Guest initialization commands */ + KVM_SEV_INIT = 0, + KVM_SEV_ES_INIT, + /* Guest launch commands */ + KVM_SEV_LAUNCH_START, + KVM_SEV_LAUNCH_UPDATE_DATA, + KVM_SEV_LAUNCH_UPDATE_VMSA, + KVM_SEV_LAUNCH_SECRET, + KVM_SEV_LAUNCH_MEASURE, + KVM_SEV_LAUNCH_FINISH, + /* Guest migration commands (outgoing) */ + KVM_SEV_SEND_START, + KVM_SEV_SEND_UPDATE_DATA, + KVM_SEV_SEND_UPDATE_VMSA, + KVM_SEV_SEND_FINISH, + /* Guest migration commands (incoming) */ + KVM_SEV_RECEIVE_START, + KVM_SEV_RECEIVE_UPDATE_DATA, + KVM_SEV_RECEIVE_UPDATE_VMSA, + KVM_SEV_RECEIVE_FINISH, + /* Guest status and debug commands */ + KVM_SEV_GUEST_STATUS, + KVM_SEV_DBG_DECRYPT, + KVM_SEV_DBG_ENCRYPT, + /* Guest certificates commands */ + KVM_SEV_CERT_EXPORT, + + KVM_SEV_NR_MAX, +}; + +struct kvm_sev_cmd { + __u32 id; + __u64 data; + __u32 error; + __u32 sev_fd; +}; + +struct kvm_sev_launch_start { + __u32 handle; + __u32 policy; + __u64 dh_uaddr; + __u32 dh_len; + __u64 session_uaddr; + __u32 session_len; +}; + +struct kvm_sev_launch_update_data { + __u64 uaddr; + __u32 len; +}; + + +struct kvm_sev_launch_secret { + __u64 hdr_uaddr; + __u32 hdr_len; + __u64 guest_uaddr; + __u32 guest_len; + __u64 trans_uaddr; + __u32 trans_len; +}; + +struct kvm_sev_launch_measure { + __u64 uaddr; + __u32 len; +}; + +struct kvm_sev_guest_status { + __u32 handle; + __u32 policy; + __u32 state; +}; + +struct kvm_sev_dbg { + __u64 src_uaddr; + __u64 dst_uaddr; + __u32 len; +}; #define KVM_DEV_ASSIGN_ENABLE_IOMMU (1 << 0) #define KVM_DEV_ASSIGN_PCI_2_3 (1 << 1) diff --git a/linux-headers/linux/psci.h b/linux-headers/linux/psci.h index ccd17731c6..3905492d18 100644 --- a/linux-headers/linux/psci.h +++ b/linux-headers/linux/psci.h @@ -88,6 +88,9 @@ (((ver) & PSCI_VERSION_MAJOR_MASK) >> PSCI_VERSION_MAJOR_SHIFT) #define PSCI_VERSION_MINOR(ver) \ ((ver) & PSCI_VERSION_MINOR_MASK) +#define PSCI_VERSION(maj, min) \ + ((((maj) << PSCI_VERSION_MAJOR_SHIFT) & PSCI_VERSION_MAJOR_MASK) | \ + ((min) & PSCI_VERSION_MINOR_MASK)) /* PSCI features decoding (>=1.0) */ #define PSCI_1_0_FEATURES_CPU_SUSPEND_PF_SHIFT 1 diff --git a/linux-headers/linux/psp-sev.h b/linux-headers/linux/psp-sev.h new file mode 100644 index 0000000000..33e247471a --- /dev/null +++ b/linux-headers/linux/psp-sev.h @@ -0,0 +1,142 @@ +/* + * Userspace interface for AMD Secure Encrypted Virtualization (SEV) + * platform management commands. + * + * Copyright (C) 2016-2017 Advanced Micro Devices, Inc. + * + * Author: Brijesh Singh <brijesh.singh@amd.com> + * + * SEV spec 0.14 is available at: + * http://support.amd.com/TechDocs/55766_SEV-KM%20API_Specification.pdf + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef __PSP_SEV_USER_H__ +#define __PSP_SEV_USER_H__ + +#include <linux/types.h> + +/** + * SEV platform commands + */ +enum { + SEV_FACTORY_RESET = 0, + SEV_PLATFORM_STATUS, + SEV_PEK_GEN, + SEV_PEK_CSR, + SEV_PDH_GEN, + SEV_PDH_CERT_EXPORT, + SEV_PEK_CERT_IMPORT, + + SEV_MAX, +}; + +/** + * SEV Firmware status code + */ +typedef enum { + SEV_RET_SUCCESS = 0, + SEV_RET_INVALID_PLATFORM_STATE, + SEV_RET_INVALID_GUEST_STATE, + SEV_RET_INAVLID_CONFIG, + SEV_RET_INVALID_LEN, + SEV_RET_ALREADY_OWNED, + SEV_RET_INVALID_CERTIFICATE, + SEV_RET_POLICY_FAILURE, + SEV_RET_INACTIVE, + SEV_RET_INVALID_ADDRESS, + SEV_RET_BAD_SIGNATURE, + SEV_RET_BAD_MEASUREMENT, + SEV_RET_ASID_OWNED, + SEV_RET_INVALID_ASID, + SEV_RET_WBINVD_REQUIRED, + SEV_RET_DFFLUSH_REQUIRED, + SEV_RET_INVALID_GUEST, + SEV_RET_INVALID_COMMAND, + SEV_RET_ACTIVE, + SEV_RET_HWSEV_RET_PLATFORM, + SEV_RET_HWSEV_RET_UNSAFE, + SEV_RET_UNSUPPORTED, + SEV_RET_MAX, +} sev_ret_code; + +/** + * struct sev_user_data_status - PLATFORM_STATUS command parameters + * + * @major: major API version + * @minor: minor API version + * @state: platform state + * @flags: platform config flags + * @build: firmware build id for API version + * @guest_count: number of active guests + */ +struct sev_user_data_status { + __u8 api_major; /* Out */ + __u8 api_minor; /* Out */ + __u8 state; /* Out */ + __u32 flags; /* Out */ + __u8 build; /* Out */ + __u32 guest_count; /* Out */ +} __attribute__((packed)); + +/** + * struct sev_user_data_pek_csr - PEK_CSR command parameters + * + * @address: PEK certificate chain + * @length: length of certificate + */ +struct sev_user_data_pek_csr { + __u64 address; /* In */ + __u32 length; /* In/Out */ +} __attribute__((packed)); + +/** + * struct sev_user_data_cert_import - PEK_CERT_IMPORT command parameters + * + * @pek_address: PEK certificate chain + * @pek_len: length of PEK certificate + * @oca_address: OCA certificate chain + * @oca_len: length of OCA certificate + */ +struct sev_user_data_pek_cert_import { + __u64 pek_cert_address; /* In */ + __u32 pek_cert_len; /* In */ + __u64 oca_cert_address; /* In */ + __u32 oca_cert_len; /* In */ +} __attribute__((packed)); + +/** + * struct sev_user_data_pdh_cert_export - PDH_CERT_EXPORT command parameters + * + * @pdh_address: PDH certificate address + * @pdh_len: length of PDH certificate + * @cert_chain_address: PDH certificate chain + * @cert_chain_len: length of PDH certificate chain + */ +struct sev_user_data_pdh_cert_export { + __u64 pdh_cert_address; /* In */ + __u32 pdh_cert_len; /* In/Out */ + __u64 cert_chain_address; /* In */ + __u32 cert_chain_len; /* In/Out */ +} __attribute__((packed)); + +/** + * struct sev_issue_cmd - SEV ioctl parameters + * + * @cmd: SEV commands to execute + * @opaque: pointer to the command structure + * @error: SEV FW return code on failure + */ +struct sev_issue_cmd { + __u32 cmd; /* In */ + __u64 data; /* In */ + __u32 error; /* Out */ +} __attribute__((packed)); + +#define SEV_IOC_TYPE 'S' +#define SEV_ISSUE_CMD _IOWR(SEV_IOC_TYPE, 0x0, struct sev_issue_cmd) + +#endif /* __PSP_USER_SEV_H */ diff --git a/linux-headers/linux/vfio.h b/linux-headers/linux/vfio.h index 4312e961ff..3a0a305c8c 100644 --- a/linux-headers/linux/vfio.h +++ b/linux-headers/linux/vfio.h @@ -301,6 +301,16 @@ struct vfio_region_info_cap_type { #define VFIO_REGION_SUBTYPE_INTEL_IGD_HOST_CFG (2) #define VFIO_REGION_SUBTYPE_INTEL_IGD_LPC_CFG (3) +/* + * The MSIX mappable capability informs that MSIX data of a BAR can be mmapped + * which allows direct access to non-MSIX registers which happened to be within + * the same system page. + * + * Even though the userspace gets direct access to the MSIX data, the existing + * VFIO_DEVICE_SET_IRQS interface must still be used for MSIX configuration. + */ +#define VFIO_REGION_INFO_CAP_MSIX_MAPPABLE 3 + /** * VFIO_DEVICE_GET_IRQ_INFO - _IOWR(VFIO_TYPE, VFIO_BASE + 9, * struct vfio_irq_info) @@ -503,6 +513,68 @@ struct vfio_pci_hot_reset { #define VFIO_DEVICE_PCI_HOT_RESET _IO(VFIO_TYPE, VFIO_BASE + 13) +/** + * VFIO_DEVICE_QUERY_GFX_PLANE - _IOW(VFIO_TYPE, VFIO_BASE + 14, + * struct vfio_device_query_gfx_plane) + * + * Set the drm_plane_type and flags, then retrieve the gfx plane info. + * + * flags supported: + * - VFIO_GFX_PLANE_TYPE_PROBE and VFIO_GFX_PLANE_TYPE_DMABUF are set + * to ask if the mdev supports dma-buf. 0 on support, -EINVAL on no + * support for dma-buf. + * - VFIO_GFX_PLANE_TYPE_PROBE and VFIO_GFX_PLANE_TYPE_REGION are set + * to ask if the mdev supports region. 0 on support, -EINVAL on no + * support for region. + * - VFIO_GFX_PLANE_TYPE_DMABUF or VFIO_GFX_PLANE_TYPE_REGION is set + * with each call to query the plane info. + * - Others are invalid and return -EINVAL. + * + * Note: + * 1. Plane could be disabled by guest. In that case, success will be + * returned with zero-initialized drm_format, size, width and height + * fields. + * 2. x_hot/y_hot is set to 0xFFFFFFFF if no hotspot information available + * + * Return: 0 on success, -errno on other failure. + */ +struct vfio_device_gfx_plane_info { + __u32 argsz; + __u32 flags; +#define VFIO_GFX_PLANE_TYPE_PROBE (1 << 0) +#define VFIO_GFX_PLANE_TYPE_DMABUF (1 << 1) +#define VFIO_GFX_PLANE_TYPE_REGION (1 << 2) + /* in */ + __u32 drm_plane_type; /* type of plane: DRM_PLANE_TYPE_* */ + /* out */ + __u32 drm_format; /* drm format of plane */ + __u64 drm_format_mod; /* tiled mode */ + __u32 width; /* width of plane */ + __u32 height; /* height of plane */ + __u32 stride; /* stride of plane */ + __u32 size; /* size of plane in bytes, align on page*/ + __u32 x_pos; /* horizontal position of cursor plane */ + __u32 y_pos; /* vertical position of cursor plane*/ + __u32 x_hot; /* horizontal position of cursor hotspot */ + __u32 y_hot; /* vertical position of cursor hotspot */ + union { + __u32 region_index; /* region index */ + __u32 dmabuf_id; /* dma-buf id */ + }; +}; + +#define VFIO_DEVICE_QUERY_GFX_PLANE _IO(VFIO_TYPE, VFIO_BASE + 14) + +/** + * VFIO_DEVICE_GET_GFX_DMABUF - _IOW(VFIO_TYPE, VFIO_BASE + 15, __u32) + * + * Return a new dma-buf file descriptor for an exposed guest framebuffer + * described by the provided dmabuf_id. The dmabuf_id is returned from VFIO_ + * DEVICE_QUERY_GFX_PLANE as a token of the exposed guest framebuffer. + */ + +#define VFIO_DEVICE_GET_GFX_DMABUF _IO(VFIO_TYPE, VFIO_BASE + 15) + /* -------- API for Type1 VFIO IOMMU -------- */ /** diff --git a/linux-user/elfload.c b/linux-user/elfload.c index fe7a5bc566..4563a3190b 100644 --- a/linux-user/elfload.c +++ b/linux-user/elfload.c @@ -354,7 +354,6 @@ enum { /* The commpage only exists for 32 bit kernels */ -#define TARGET_HAS_VALIDATE_GUEST_SPACE /* Return 1 if the proposed guest space is suitable for the guest. * Return 0 if the proposed guest space isn't suitable, but another * address space should be tried. @@ -363,8 +362,8 @@ enum { * The guest code may leave a page mapped and populate it if the * address is suitable. */ -static int validate_guest_space(unsigned long guest_base, - unsigned long guest_size) +static int init_guest_commpage(unsigned long guest_base, + unsigned long guest_size) { unsigned long real_start, test_page_addr; @@ -375,6 +374,11 @@ static int validate_guest_space(unsigned long guest_base, /* If the commpage lies within the already allocated guest space, * then there is no way we can allocate it. + * + * You may be thinking that that this check is redundant because + * we already validated the guest size against MAX_RESERVED_VA; + * but if qemu_host_page_mask is unusually large, then + * test_page_addr may be lower. */ if (test_page_addr >= guest_base && test_page_addr < (guest_base + guest_size)) { @@ -563,78 +567,6 @@ static uint32_t get_elf_hwcap(void) #endif /* not TARGET_AARCH64 */ #endif /* TARGET_ARM */ -#ifdef TARGET_UNICORE32 - -#define ELF_START_MMAP 0x80000000 - -#define ELF_CLASS ELFCLASS32 -#define ELF_DATA ELFDATA2LSB -#define ELF_ARCH EM_UNICORE32 - -static inline void init_thread(struct target_pt_regs *regs, - struct image_info *infop) -{ - abi_long stack = infop->start_stack; - memset(regs, 0, sizeof(*regs)); - regs->UC32_REG_asr = 0x10; - regs->UC32_REG_pc = infop->entry & 0xfffffffe; - regs->UC32_REG_sp = infop->start_stack; - /* FIXME - what to for failure of get_user()? */ - get_user_ual(regs->UC32_REG_02, stack + 8); /* envp */ - get_user_ual(regs->UC32_REG_01, stack + 4); /* envp */ - /* XXX: it seems that r0 is zeroed after ! */ - regs->UC32_REG_00 = 0; -} - -#define ELF_NREG 34 -typedef target_elf_greg_t target_elf_gregset_t[ELF_NREG]; - -static void elf_core_copy_regs(target_elf_gregset_t *regs, const CPUUniCore32State *env) -{ - (*regs)[0] = env->regs[0]; - (*regs)[1] = env->regs[1]; - (*regs)[2] = env->regs[2]; - (*regs)[3] = env->regs[3]; - (*regs)[4] = env->regs[4]; - (*regs)[5] = env->regs[5]; - (*regs)[6] = env->regs[6]; - (*regs)[7] = env->regs[7]; - (*regs)[8] = env->regs[8]; - (*regs)[9] = env->regs[9]; - (*regs)[10] = env->regs[10]; - (*regs)[11] = env->regs[11]; - (*regs)[12] = env->regs[12]; - (*regs)[13] = env->regs[13]; - (*regs)[14] = env->regs[14]; - (*regs)[15] = env->regs[15]; - (*regs)[16] = env->regs[16]; - (*regs)[17] = env->regs[17]; - (*regs)[18] = env->regs[18]; - (*regs)[19] = env->regs[19]; - (*regs)[20] = env->regs[20]; - (*regs)[21] = env->regs[21]; - (*regs)[22] = env->regs[22]; - (*regs)[23] = env->regs[23]; - (*regs)[24] = env->regs[24]; - (*regs)[25] = env->regs[25]; - (*regs)[26] = env->regs[26]; - (*regs)[27] = env->regs[27]; - (*regs)[28] = env->regs[28]; - (*regs)[29] = env->regs[29]; - (*regs)[30] = env->regs[30]; - (*regs)[31] = env->regs[31]; - - (*regs)[32] = cpu_asr_read((CPUUniCore32State *)env); - (*regs)[33] = env->regs[0]; /* XXX */ -} - -#define USE_ELF_CORE_DUMP -#define ELF_EXEC_PAGESIZE 4096 - -#define ELF_HWCAP (UC32_HWCAP_CMOV | UC32_HWCAP_UCF64) - -#endif - #ifdef TARGET_SPARC #ifdef TARGET_SPARC64 @@ -1927,21 +1859,12 @@ static abi_ulong create_elf_tables(abi_ulong p, int argc, int envc, return sp; } -#ifndef TARGET_HAS_VALIDATE_GUEST_SPACE -/* If the guest doesn't have a validation function just agree */ -static int validate_guest_space(unsigned long guest_base, - unsigned long guest_size) -{ - return 1; -} -#endif - unsigned long init_guest_space(unsigned long host_start, unsigned long host_size, unsigned long guest_start, bool fixed) { - unsigned long current_start, real_start; + unsigned long current_start, aligned_start; int flags; assert(host_start || host_size); @@ -1949,11 +1872,12 @@ unsigned long init_guest_space(unsigned long host_start, /* If just a starting address is given, then just verify that * address. */ if (host_start && !host_size) { - if (validate_guest_space(host_start, host_size) == 1) { - return host_start; - } else { +#if defined(TARGET_ARM) && !defined(TARGET_AARCH64) + if (init_guest_commpage(host_start, host_size) != 1) { return (unsigned long)-1; } +#endif + return host_start; } /* Setup the initial flags and start address. */ @@ -1966,7 +1890,8 @@ unsigned long init_guest_space(unsigned long host_start, /* Otherwise, a non-zero size region of memory needs to be mapped * and validated. */ while (1) { - unsigned long real_size = host_size; + unsigned long real_start, real_size, aligned_size; + aligned_size = real_size = host_size; /* Do not use mmap_find_vma here because that is limited to the * guest address space. We are going to make the @@ -1978,30 +1903,63 @@ unsigned long init_guest_space(unsigned long host_start, return (unsigned long)-1; } + /* Check to see if the address is valid. */ + if (host_start && real_start != current_start) { + goto try_again; + } + /* Ensure the address is properly aligned. */ if (real_start & ~qemu_host_page_mask) { + /* Ideally, we adjust like + * + * pages: [ ][ ][ ][ ][ ] + * old: [ real ] + * [ aligned ] + * new: [ real ] + * [ aligned ] + * + * But if there is something else mapped right after it, + * then obviously it won't have room to grow, and the + * kernel will put the new larger real someplace else with + * unknown alignment (if we made it to here, then + * fixed=false). Which is why we grow real by a full page + * size, instead of by part of one; so that even if we get + * moved, we can still guarantee alignment. But this does + * mean that there is a padding of < 1 page both before + * and after the aligned range; the "after" could could + * cause problems for ARM emulation where it could butt in + * to where we need to put the commpage. + */ munmap((void *)real_start, host_size); - real_size = host_size + qemu_host_page_size; + real_size = aligned_size + qemu_host_page_size; real_start = (unsigned long) mmap((void *)real_start, real_size, PROT_NONE, flags, -1, 0); if (real_start == (unsigned long)-1) { return (unsigned long)-1; } - real_start = HOST_PAGE_ALIGN(real_start); + aligned_start = HOST_PAGE_ALIGN(real_start); + } else { + aligned_start = real_start; } - /* Check to see if the address is valid. */ - if (!host_start || real_start == current_start) { - int valid = validate_guest_space(real_start - guest_start, - real_size); - if (valid == 1) { - break; - } else if (valid == -1) { - return (unsigned long)-1; - } - /* valid == 0, so try again. */ +#if defined(TARGET_ARM) && !defined(TARGET_AARCH64) + /* On 32-bit ARM, we need to also be able to map the commpage. */ + int valid = init_guest_commpage(aligned_start - guest_start, + aligned_size + guest_start); + if (valid == -1) { + munmap((void *)real_start, real_size); + return (unsigned long)-1; + } else if (valid == 0) { + goto try_again; } +#endif + + /* If nothing has said `return -1` or `goto try_again` yet, + * then the address we have is good. + */ + break; + try_again: /* That address didn't work. Unmap and try a different one. * The address the host picked because is typically right at * the top of the host address space and leaves the guest with @@ -2010,8 +1968,12 @@ unsigned long init_guest_space(unsigned long host_start, * happen often. Probably means we got unlucky and host * address space randomization put a shared library somewhere * inconvenient. + * + * This is probably a good strategy if host_start, but is + * probably a bad strategy if not, which means we got here + * because of trouble with ARM commpage setup. */ - munmap((void *)real_start, host_size); + munmap((void *)real_start, real_size); current_start += qemu_host_page_size; if (host_start == current_start) { /* Theoretically possible if host doesn't have any suitably @@ -2023,7 +1985,7 @@ unsigned long init_guest_space(unsigned long host_start, qemu_log_mask(CPU_LOG_PAGE, "Reserved 0x%lx bytes of guest address space\n", host_size); - return real_start; + return aligned_start; } static void probe_guest_base(const char *image_name, diff --git a/linux-user/main.c b/linux-user/main.c index 25b4dfdbcb..108e1f202c 100644 --- a/linux-user/main.c +++ b/linux-user/main.c @@ -884,95 +884,6 @@ void cpu_loop(CPUARMState *env) #endif -#ifdef TARGET_UNICORE32 - -void cpu_loop(CPUUniCore32State *env) -{ - CPUState *cs = CPU(uc32_env_get_cpu(env)); - int trapnr; - unsigned int n, insn; - target_siginfo_t info; - - for (;;) { - cpu_exec_start(cs); - trapnr = cpu_exec(cs); - cpu_exec_end(cs); - process_queued_cpu_work(cs); - - switch (trapnr) { - case UC32_EXCP_PRIV: - { - /* system call */ - get_user_u32(insn, env->regs[31] - 4); - n = insn & 0xffffff; - - if (n >= UC32_SYSCALL_BASE) { - /* linux syscall */ - n -= UC32_SYSCALL_BASE; - if (n == UC32_SYSCALL_NR_set_tls) { - cpu_set_tls(env, env->regs[0]); - env->regs[0] = 0; - } else { - abi_long ret = do_syscall(env, - n, - env->regs[0], - env->regs[1], - env->regs[2], - env->regs[3], - env->regs[4], - env->regs[5], - 0, 0); - if (ret == -TARGET_ERESTARTSYS) { - env->regs[31] -= 4; - } else if (ret != -TARGET_QEMU_ESIGRETURN) { - env->regs[0] = ret; - } - } - } else { - goto error; - } - } - break; - case UC32_EXCP_DTRAP: - case UC32_EXCP_ITRAP: - info.si_signo = TARGET_SIGSEGV; - info.si_errno = 0; - /* XXX: check env->error_code */ - info.si_code = TARGET_SEGV_MAPERR; - info._sifields._sigfault._addr = env->cp0.c4_faultaddr; - queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); - break; - case EXCP_INTERRUPT: - /* just indicate that signals should be handled asap */ - break; - case EXCP_DEBUG: - { - int sig; - - sig = gdb_handlesig(cs, TARGET_SIGTRAP); - if (sig) { - info.si_signo = sig; - info.si_errno = 0; - info.si_code = TARGET_TRAP_BRKPT; - queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); - } - } - break; - case EXCP_ATOMIC: - cpu_exec_step_atomic(cs); - break; - default: - goto error; - } - process_pending_signals(env); - } - -error: - EXCP_DUMP(env, "qemu: unhandled CPU exception 0x%x - aborting\n", trapnr); - abort(); -} -#endif - #ifdef TARGET_SPARC #define SPARC64_STACK_BIAS 2047 @@ -4399,7 +4310,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 + printf("qemu-" TARGET_NAME " version " QEMU_FULL_VERSION "\n" QEMU_COPYRIGHT "\n"); exit(EXIT_SUCCESS); } @@ -4973,14 +4884,6 @@ int main(int argc, char **argv, char **envp) } #endif } -#elif defined(TARGET_UNICORE32) - { - int i; - cpu_asr_write(env, regs->uregs[32], 0xffffffff); - for (i = 0; i < 32; i++) { - env->regs[i] = regs->uregs[i]; - } - } #elif defined(TARGET_SPARC) { int i; @@ -5219,7 +5122,7 @@ int main(int argc, char **argv, char **envp) #error unsupported target CPU #endif -#if defined(TARGET_ARM) || defined(TARGET_M68K) || defined(TARGET_UNICORE32) +#if defined(TARGET_ARM) || defined(TARGET_M68K) ts->stack_base = info->start_stack; ts->heap_base = info->brk; /* This will be filled in on the first SYS_HEAPINFO call. */ diff --git a/linux-user/qemu.h b/linux-user/qemu.h index 23712bbca8..192a0d2fef 100644 --- a/linux-user/qemu.h +++ b/linux-user/qemu.h @@ -100,9 +100,6 @@ typedef struct TaskState { # endif int swi_errno; #endif -#ifdef TARGET_UNICORE32 - int swi_errno; -#endif #if defined(TARGET_I386) && !defined(TARGET_X86_64) abi_ulong target_v86; struct vm86_saved_state vm86_saved_regs; @@ -115,7 +112,7 @@ typedef struct TaskState { int sim_syscalls; abi_ulong tp_value; #endif -#if defined(TARGET_ARM) || defined(TARGET_M68K) || defined(TARGET_UNICORE32) +#if defined(TARGET_ARM) || defined(TARGET_M68K) /* Extra fields for semihosted binaries. */ abi_ulong heap_base; abi_ulong heap_limit; diff --git a/linux-user/signal.c b/linux-user/signal.c index 24b6f38b42..2ea3e0321f 100644 --- a/linux-user/signal.c +++ b/linux-user/signal.c @@ -253,8 +253,7 @@ int do_sigprocmask(int how, const sigset_t *set, sigset_t *oldset) return 0; } -#if !defined(TARGET_OPENRISC) && !defined(TARGET_UNICORE32) && \ - !defined(TARGET_NIOS2) +#if !defined(TARGET_OPENRISC) && !defined(TARGET_NIOS2) /* Just set the guest's signal mask to the specified value; the * caller is assumed to have called block_signals() already. */ @@ -512,7 +511,6 @@ void signal_init(void) } } -#ifndef TARGET_UNICORE32 /* Force a synchronously taken signal. The kernel force_sig() function * also forces the signal to "not blocked, not ignored", but for QEMU * that work is done in process_pending_signals(). @@ -546,7 +544,6 @@ static void force_sigsegv(int oldsig) } force_sig(TARGET_SIGSEGV); } -#endif #endif @@ -7306,32 +7303,7 @@ badframe: } #else - -static void setup_frame(int sig, struct target_sigaction *ka, - target_sigset_t *set, CPUArchState *env) -{ - fprintf(stderr, "setup_frame: not implemented\n"); -} - -static void setup_rt_frame(int sig, struct target_sigaction *ka, - target_siginfo_t *info, - target_sigset_t *set, CPUArchState *env) -{ - fprintf(stderr, "setup_rt_frame: not implemented\n"); -} - -long do_sigreturn(CPUArchState *env) -{ - fprintf(stderr, "do_sigreturn: not implemented\n"); - return -TARGET_ENOSYS; -} - -long do_rt_sigreturn(CPUArchState *env) -{ - fprintf(stderr, "do_rt_sigreturn: not implemented\n"); - return -TARGET_ENOSYS; -} - +#error Target needs to add support for signal handling #endif static void handle_pending_signal(CPUArchState *cpu_env, int sig, diff --git a/linux-user/syscall.c b/linux-user/syscall.c index e864a1d72a..889abbda1e 100644 --- a/linux-user/syscall.c +++ b/linux-user/syscall.c @@ -9556,6 +9556,11 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, __put_user(stfs.f_fsid.__val[1], &target_stfs->f_fsid.val[1]); __put_user(stfs.f_namelen, &target_stfs->f_namelen); __put_user(stfs.f_frsize, &target_stfs->f_frsize); +#ifdef _STATFS_F_FLAGS + __put_user(stfs.f_flags, &target_stfs->f_flags); +#else + __put_user(0, &target_stfs->f_flags); +#endif memset(target_stfs->f_spare, 0, sizeof(target_stfs->f_spare)); unlock_user_struct(target_stfs, arg2, 1); } diff --git a/linux-user/syscall_defs.h b/linux-user/syscall_defs.h index b034fa8d54..13fe840239 100644 --- a/linux-user/syscall_defs.h +++ b/linux-user/syscall_defs.h @@ -69,7 +69,7 @@ #if defined(TARGET_I386) || defined(TARGET_ARM) || defined(TARGET_SH4) \ || defined(TARGET_M68K) || defined(TARGET_CRIS) \ - || defined(TARGET_UNICORE32) || defined(TARGET_S390X) \ + || defined(TARGET_S390X) \ || defined(TARGET_OPENRISC) || defined(TARGET_TILEGX) \ || defined(TARGET_NIOS2) || defined(TARGET_RISCV) \ || defined(TARGET_XTENSA) @@ -353,19 +353,6 @@ typedef struct { int val[2]; } kernel_fsid_t; -struct kernel_statfs { - int f_type; - int f_bsize; - int f_blocks; - int f_bfree; - int f_bavail; - int f_files; - int f_ffree; - kernel_fsid_t f_fsid; - int f_namelen; - int f_spare[6]; -}; - struct target_dirent { abi_long d_ino; abi_long d_off; @@ -434,7 +421,7 @@ int do_sigaction(int sig, const struct target_sigaction *act, #if defined(TARGET_I386) || defined(TARGET_ARM) || defined(TARGET_SPARC) \ || defined(TARGET_PPC) || defined(TARGET_MIPS) || defined(TARGET_SH4) \ || defined(TARGET_M68K) || defined(TARGET_ALPHA) || defined(TARGET_CRIS) \ - || defined(TARGET_MICROBLAZE) || defined(TARGET_UNICORE32) \ + || defined(TARGET_MICROBLAZE) \ || defined(TARGET_S390X) || defined(TARGET_OPENRISC) \ || defined(TARGET_TILEGX) || defined(TARGET_HPPA) || defined(TARGET_NIOS2) \ || defined(TARGET_RISCV) || defined(TARGET_XTENSA) @@ -1422,7 +1409,7 @@ struct target_winsize { #if (defined(TARGET_I386) && defined(TARGET_ABI32)) \ || (defined(TARGET_ARM) && defined(TARGET_ABI32)) \ - || defined(TARGET_CRIS) || defined(TARGET_UNICORE32) + || defined(TARGET_CRIS) struct target_stat { unsigned short st_dev; unsigned short __pad1; @@ -2284,7 +2271,8 @@ struct target_statfs { /* Linux specials */ target_fsid_t f_fsid; int32_t f_namelen; - int32_t f_spare[6]; + int32_t f_flags; + int32_t f_spare[5]; }; #else struct target_statfs { @@ -2300,7 +2288,8 @@ struct target_statfs { /* Linux specials */ target_fsid_t f_fsid; abi_long f_namelen; - abi_long f_spare[6]; + abi_long f_flags; + abi_long f_spare[5]; }; #endif @@ -2316,7 +2305,8 @@ struct target_statfs64 { uint64_t f_bavail; target_fsid_t f_fsid; uint32_t f_namelen; - uint32_t f_spare[6]; + uint32_t f_flags; + uint32_t f_spare[5]; }; #elif (defined(TARGET_PPC64) || defined(TARGET_X86_64) || \ defined(TARGET_SPARC64) || defined(TARGET_AARCH64) || \ @@ -2332,7 +2322,8 @@ struct target_statfs { target_fsid_t f_fsid; abi_long f_namelen; abi_long f_frsize; - abi_long f_spare[5]; + abi_long f_flags; + abi_long f_spare[4]; }; struct target_statfs64 { @@ -2346,7 +2337,8 @@ struct target_statfs64 { target_fsid_t f_fsid; abi_long f_namelen; abi_long f_frsize; - abi_long f_spare[5]; + abi_long f_flags; + abi_long f_spare[4]; }; #elif defined(TARGET_S390X) struct target_statfs { @@ -2360,7 +2352,9 @@ struct target_statfs { kernel_fsid_t f_fsid; int32_t f_namelen; int32_t f_frsize; - int32_t f_spare[5]; + int32_t f_flags; + int32_t f_spare[4]; + }; struct target_statfs64 { @@ -2374,7 +2368,8 @@ struct target_statfs64 { kernel_fsid_t f_fsid; int32_t f_namelen; int32_t f_frsize; - int32_t f_spare[5]; + int32_t f_flags; + int32_t f_spare[4]; }; #else struct target_statfs { @@ -2388,7 +2383,8 @@ struct target_statfs { target_fsid_t f_fsid; uint32_t f_namelen; uint32_t f_frsize; - uint32_t f_spare[5]; + uint32_t f_flags; + uint32_t f_spare[4]; }; struct target_statfs64 { @@ -2402,7 +2398,8 @@ struct target_statfs64 { target_fsid_t f_fsid; uint32_t f_namelen; uint32_t f_frsize; - uint32_t f_spare[5]; + uint32_t f_flags; + uint32_t f_spare[4]; }; #endif diff --git a/linux-user/unicore32/syscall_nr.h b/linux-user/unicore32/syscall_nr.h deleted file mode 100644 index 486b8c45a0..0000000000 --- a/linux-user/unicore32/syscall_nr.h +++ /dev/null @@ -1,371 +0,0 @@ -/* - * This file contains the system call numbers for UniCore32 oldabi. - * - * Copyright (C) 2010-2011 GUAN Xue-tao - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - */ -#define TARGET_NR_restart_syscall 0 -#define TARGET_NR_exit 1 -#define TARGET_NR_fork 2 -#define TARGET_NR_read 3 -#define TARGET_NR_write 4 -#define TARGET_NR_open 5 -#define TARGET_NR_close 6 -#define TARGET_NR_waitpid 7 -#define TARGET_NR_creat 8 -#define TARGET_NR_link 9 -#define TARGET_NR_unlink 10 -#define TARGET_NR_execve 11 -#define TARGET_NR_chdir 12 -#define TARGET_NR_time 13 -#define TARGET_NR_mknod 14 -#define TARGET_NR_chmod 15 -#define TARGET_NR_lchown 16 -#define TARGET_NR_break 17 - /* 18 */ -#define TARGET_NR_lseek 19 -#define TARGET_NR_getpid 20 -#define TARGET_NR_mount 21 -#define TARGET_NR_umount 22 -#define TARGET_NR_setuid 23 -#define TARGET_NR_getuid 24 -#define TARGET_NR_stime 25 -#define TARGET_NR_ptrace 26 -#define TARGET_NR_alarm 27 - /* 28 */ -#define TARGET_NR_pause 29 -#define TARGET_NR_utime 30 -#define TARGET_NR_stty 31 -#define TARGET_NR_gtty 32 -#define TARGET_NR_access 33 -#define TARGET_NR_nice 34 -#define TARGET_NR_ftime 35 -#define TARGET_NR_sync 36 -#define TARGET_NR_kill 37 -#define TARGET_NR_rename 38 -#define TARGET_NR_mkdir 39 -#define TARGET_NR_rmdir 40 -#define TARGET_NR_dup 41 -#define TARGET_NR_pipe 42 -#define TARGET_NR_times 43 -#define TARGET_NR_prof 44 -#define TARGET_NR_brk 45 -#define TARGET_NR_setgid 46 -#define TARGET_NR_getgid 47 -#define TARGET_NR_signal 48 -#define TARGET_NR_geteuid 49 -#define TARGET_NR_getegid 50 -#define TARGET_NR_acct 51 -#define TARGET_NR_umount2 52 -#define TARGET_NR_lock 53 -#define TARGET_NR_ioctl 54 -#define TARGET_NR_fcntl 55 -#define TARGET_NR_mpx 56 -#define TARGET_NR_setpgid 57 -#define TARGET_NR_ulimit 58 - /* 59 */ -#define TARGET_NR_umask 60 -#define TARGET_NR_chroot 61 -#define TARGET_NR_ustat 62 -#define TARGET_NR_dup2 63 -#define TARGET_NR_getppid 64 -#define TARGET_NR_getpgrp 65 -#define TARGET_NR_setsid 66 -#define TARGET_NR_sigaction 67 -#define TARGET_NR_sgetmask 68 -#define TARGET_NR_ssetmask 69 -#define TARGET_NR_setreuid 70 -#define TARGET_NR_setregid 71 -#define TARGET_NR_sigsuspend 72 -#define TARGET_NR_sigpending 73 -#define TARGET_NR_sethostname 74 -#define TARGET_NR_setrlimit 75 -#define TARGET_NR_getrlimit 76 -#define TARGET_NR_getrusage 77 -#define TARGET_NR_gettimeofday 78 -#define TARGET_NR_settimeofday 79 -#define TARGET_NR_getgroups 80 -#define TARGET_NR_setgroups 81 -#define TARGET_NR_select 82 -#define TARGET_NR_symlink 83 - /* 84 */ -#define TARGET_NR_readlink 85 -#define TARGET_NR_uselib 86 -#define TARGET_NR_swapon 87 -#define TARGET_NR_reboot 88 -#define TARGET_NR_readdir 89 -#define TARGET_NR_mmap 90 -#define TARGET_NR_munmap 91 -#define TARGET_NR_truncate 92 -#define TARGET_NR_ftruncate 93 -#define TARGET_NR_fchmod 94 -#define TARGET_NR_fchown 95 -#define TARGET_NR_getpriority 96 -#define TARGET_NR_setpriority 97 -#define TARGET_NR_profil 98 -#define TARGET_NR_statfs 99 -#define TARGET_NR_fstatfs 100 -#define TARGET_NR_ioperm 101 -#define TARGET_NR_socketcall 102 -#define TARGET_NR_syslog 103 -#define TARGET_NR_setitimer 104 -#define TARGET_NR_getitimer 105 -#define TARGET_NR_stat 106 -#define TARGET_NR_lstat 107 -#define TARGET_NR_fstat 108 - /* 109 */ - /* 110 */ -#define TARGET_NR_vhangup 111 -#define TARGET_NR_idle 112 -#define TARGET_NR_syscall 113 -#define TARGET_NR_wait4 114 -#define TARGET_NR_swapoff 115 -#define TARGET_NR_sysinfo 116 -#define TARGET_NR_ipc 117 -#define TARGET_NR_fsync 118 -#define TARGET_NR_sigreturn 119 -#define TARGET_NR_clone 120 -#define TARGET_NR_setdomainname 121 -#define TARGET_NR_uname 122 -#define TARGET_NR_modify_ldt 123 -#define TARGET_NR_adjtimex 124 -#define TARGET_NR_mprotect 125 -#define TARGET_NR_sigprocmask 126 -#define TARGET_NR_create_module 127 -#define TARGET_NR_init_module 128 -#define TARGET_NR_delete_module 129 -#define TARGET_NR_get_kernel_syms 130 -#define TARGET_NR_quotactl 131 -#define TARGET_NR_getpgid 132 -#define TARGET_NR_fchdir 133 -#define TARGET_NR_bdflush 134 -#define TARGET_NR_sysfs 135 -#define TARGET_NR_personality 136 -#define TARGET_NR_afs_syscall 137 -#define TARGET_NR_setfsuid 138 -#define TARGET_NR_setfsgid 139 -#define TARGET_NR__llseek 140 -#define TARGET_NR_getdents 141 -#define TARGET_NR__newselect 142 -#define TARGET_NR_flock 143 -#define TARGET_NR_msync 144 -#define TARGET_NR_readv 145 -#define TARGET_NR_writev 146 -#define TARGET_NR_getsid 147 -#define TARGET_NR_fdatasync 148 -#define TARGET_NR__sysctl 149 -#define TARGET_NR_mlock 150 -#define TARGET_NR_munlock 151 -#define TARGET_NR_mlockall 152 -#define TARGET_NR_munlockall 153 -#define TARGET_NR_sched_setparam 154 -#define TARGET_NR_sched_getparam 155 -#define TARGET_NR_sched_setscheduler 156 -#define TARGET_NR_sched_getscheduler 157 -#define TARGET_NR_sched_yield 158 -#define TARGET_NR_sched_get_priority_max 159 -#define TARGET_NR_sched_get_priority_min 160 -#define TARGET_NR_sched_rr_get_interval 161 -#define TARGET_NR_nanosleep 162 -#define TARGET_NR_mremap 163 -#define TARGET_NR_setresuid 164 -#define TARGET_NR_getresuid 165 -#define TARGET_NR_vm86 166 -#define TARGET_NR_query_module 167 -#define TARGET_NR_poll 168 -#define TARGET_NR_nfsservctl 169 -#define TARGET_NR_setresgid 170 -#define TARGET_NR_getresgid 171 -#define TARGET_NR_prctl 172 -#define TARGET_NR_rt_sigreturn 173 -#define TARGET_NR_rt_sigaction 174 -#define TARGET_NR_rt_sigprocmask 175 -#define TARGET_NR_rt_sigpending 176 -#define TARGET_NR_rt_sigtimedwait 177 -#define TARGET_NR_rt_sigqueueinfo 178 -#define TARGET_NR_rt_sigsuspend 179 -#define TARGET_NR_pread64 180 -#define TARGET_NR_pwrite64 181 -#define TARGET_NR_chown 182 -#define TARGET_NR_getcwd 183 -#define TARGET_NR_capget 184 -#define TARGET_NR_capset 185 -#define TARGET_NR_sigaltstack 186 -#define TARGET_NR_sendfile 187 - /* 188 */ - /* 189 */ -#define TARGET_NR_vfork 190 -#define TARGET_NR_ugetrlimit 191 -#define TARGET_NR_mmap2 192 -#define TARGET_NR_truncate64 193 -#define TARGET_NR_ftruncate64 194 -#define TARGET_NR_stat64 195 -#define TARGET_NR_lstat64 196 -#define TARGET_NR_fstat64 197 -#define TARGET_NR_lchown32 198 -#define TARGET_NR_getuid32 199 -#define TARGET_NR_getgid32 200 -#define TARGET_NR_geteuid32 201 -#define TARGET_NR_getegid32 202 -#define TARGET_NR_setreuid32 203 -#define TARGET_NR_setregid32 204 -#define TARGET_NR_getgroups32 205 -#define TARGET_NR_setgroups32 206 -#define TARGET_NR_fchown32 207 -#define TARGET_NR_setresuid32 208 -#define TARGET_NR_getresuid32 209 -#define TARGET_NR_setresgid32 210 -#define TARGET_NR_getresgid32 211 -#define TARGET_NR_chown32 212 -#define TARGET_NR_setuid32 213 -#define TARGET_NR_setgid32 214 -#define TARGET_NR_setfsuid32 215 -#define TARGET_NR_setfsgid32 216 -#define TARGET_NR_getdents64 217 -#define TARGET_NR_pivot_root 218 -#define TARGET_NR_mincore 219 -#define TARGET_NR_madvise 220 -#define TARGET_NR_fcntl64 221 - /* 222 */ - /* 223 */ -#define TARGET_NR_gettid 224 -#define TARGET_NR_readahead 225 -#define TARGET_NR_setxattr 226 -#define TARGET_NR_lsetxattr 227 -#define TARGET_NR_fsetxattr 228 -#define TARGET_NR_getxattr 229 -#define TARGET_NR_lgetxattr 230 -#define TARGET_NR_fgetxattr 231 -#define TARGET_NR_listxattr 232 -#define TARGET_NR_llistxattr 233 -#define TARGET_NR_flistxattr 234 -#define TARGET_NR_removexattr 235 -#define TARGET_NR_lremovexattr 236 -#define TARGET_NR_fremovexattr 237 -#define TARGET_NR_tkill 238 -#define TARGET_NR_sendfile64 239 -#define TARGET_NR_futex 240 -#define TARGET_NR_sched_setaffinity 241 -#define TARGET_NR_sched_getaffinity 242 -#define TARGET_NR_io_setup 243 -#define TARGET_NR_io_destroy 244 -#define TARGET_NR_io_getevents 245 -#define TARGET_NR_io_submit 246 -#define TARGET_NR_io_cancel 247 -#define TARGET_NR_exit_group 248 -#define TARGET_NR_lookup_dcookie 249 -#define TARGET_NR_epoll_create 250 -#define TARGET_NR_epoll_ctl 251 -#define TARGET_NR_epoll_wait 252 -#define TARGET_NR_remap_file_pages 253 - /* 254 */ - /* 255 */ - /* 256 */ -#define TARGET_NR_set_tid_address 256 -#define TARGET_NR_timer_create 257 -#define TARGET_NR_timer_settime 258 -#define TARGET_NR_timer_gettime 259 -#define TARGET_NR_timer_getoverrun 260 -#define TARGET_NR_timer_delete 261 -#define TARGET_NR_clock_settime 262 -#define TARGET_NR_clock_gettime 263 -#define TARGET_NR_clock_getres 264 -#define TARGET_NR_clock_nanosleep 265 -#define TARGET_NR_statfs64 266 -#define TARGET_NR_fstatfs64 267 -#define TARGET_NR_tgkill 268 -#define TARGET_NR_utimes 269 -#define TARGET_NR_fadvise64_64 270 -#define TARGET_NR_pciconfig_iobase 271 -#define TARGET_NR_pciconfig_read 272 -#define TARGET_NR_pciconfig_write 273 -#define TARGET_NR_mq_open 274 -#define TARGET_NR_mq_unlink 275 -#define TARGET_NR_mq_timedsend 276 -#define TARGET_NR_mq_timedreceive 277 -#define TARGET_NR_mq_notify 278 -#define TARGET_NR_mq_getsetattr 279 -#define TARGET_NR_waitid 280 -#define TARGET_NR_socket 281 -#define TARGET_NR_bind 282 -#define TARGET_NR_connect 283 -#define TARGET_NR_listen 284 -#define TARGET_NR_accept 285 -#define TARGET_NR_getsockname 286 -#define TARGET_NR_getpeername 287 -#define TARGET_NR_socketpair 288 -#define TARGET_NR_send 289 -#define TARGET_NR_sendto 290 -#define TARGET_NR_recv 291 -#define TARGET_NR_recvfrom 292 -#define TARGET_NR_shutdown 293 -#define TARGET_NR_setsockopt 294 -#define TARGET_NR_getsockopt 295 -#define TARGET_NR_sendmsg 296 -#define TARGET_NR_recvmsg 297 -#define TARGET_NR_semop 298 -#define TARGET_NR_semget 299 -#define TARGET_NR_semctl 300 -#define TARGET_NR_msgsnd 301 -#define TARGET_NR_msgrcv 302 -#define TARGET_NR_msgget 303 -#define TARGET_NR_msgctl 304 -#define TARGET_NR_shmat 305 -#define TARGET_NR_shmdt 306 -#define TARGET_NR_shmget 307 -#define TARGET_NR_shmctl 308 -#define TARGET_NR_add_key 309 -#define TARGET_NR_request_key 310 -#define TARGET_NR_keyctl 311 -#define TARGET_NR_semtimedop 312 -#define TARGET_NR_vserver 313 -#define TARGET_NR_ioprio_set 314 -#define TARGET_NR_ioprio_get 315 -#define TARGET_NR_inotify_init 316 -#define TARGET_NR_inotify_add_watch 317 -#define TARGET_NR_inotify_rm_watch 318 -#define TARGET_NR_mbind 319 -#define TARGET_NR_get_mempolicy 320 -#define TARGET_NR_set_mempolicy 321 -#define TARGET_NR_openat 322 -#define TARGET_NR_mkdirat 323 -#define TARGET_NR_mknodat 324 -#define TARGET_NR_fchownat 325 -#define TARGET_NR_futimesat 326 -#define TARGET_NR_fstatat64 327 -#define TARGET_NR_unlinkat 328 -#define TARGET_NR_renameat 329 -#define TARGET_NR_linkat 330 -#define TARGET_NR_symlinkat 331 -#define TARGET_NR_readlinkat 332 -#define TARGET_NR_fchmodat 333 -#define TARGET_NR_faccessat 334 - /* 335 */ - /* 336 */ -#define TARGET_NR_unshare 337 -#define TARGET_NR_set_robust_list 338 -#define TARGET_NR_get_robust_list 339 -#define TARGET_NR_splice 340 -#define TARGET_NR_sync_file_range2 341 -#define TARGET_NR_tee 342 -#define TARGET_NR_vmsplice 343 -#define TARGET_NR_move_pages 344 -#define TARGET_NR_getcpu 345 - /* 346 */ -#define TARGET_NR_kexec_load 347 -#define TARGET_NR_utimensat 348 -#define TARGET_NR_signalfd 349 -#define TARGET_NR_timerfd 350 -#define TARGET_NR_eventfd 351 -#define TARGET_NR_fallocate 352 -#define TARGET_NR_timerfd_settime 353 -#define TARGET_NR_timerfd_gettime 354 -#define TARGET_NR_signalfd4 355 -#define TARGET_NR_eventfd2 356 -#define TARGET_NR_epoll_create1 357 -#define TARGET_NR_dup3 358 -#define TARGET_NR_pipe2 359 -#define TARGET_NR_inotify_init1 360 diff --git a/linux-user/unicore32/target_cpu.h b/linux-user/unicore32/target_cpu.h deleted file mode 100644 index d7d2e7b083..0000000000 --- a/linux-user/unicore32/target_cpu.h +++ /dev/null @@ -1,27 +0,0 @@ -/* - * UniCore32 specific CPU ABI and functions for linux-user - * - * Copyright (C) 2010-2012 Guan Xuetao - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation, or (at your option) any - * later version. See the COPYING file in the top-level directory. - */ -#ifndef UNICORE32_TARGET_CPU_H -#define UNICORE32_TARGET_CPU_H - -static inline void cpu_clone_regs(CPUUniCore32State *env, target_ulong newsp) -{ - if (newsp) { - env->regs[29] = newsp; - } - env->regs[0] = 0; -} - -static inline void cpu_set_tls(CPUUniCore32State *env, target_ulong newtls) -{ - env->regs[16] = newtls; -} - -#endif diff --git a/linux-user/unicore32/target_elf.h b/linux-user/unicore32/target_elf.h deleted file mode 100644 index e2bfcb2ca3..0000000000 --- a/linux-user/unicore32/target_elf.h +++ /dev/null @@ -1,14 +0,0 @@ -/* - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation, or (at your option) any - * later version. See the COPYING file in the top-level directory. - */ - -#ifndef UNICORE32_TARGET_ELF_H -#define UNICORE32_TARGET_ELF_H -static inline const char *cpu_get_model(uint32_t eflags) -{ - return "any"; -} -#endif diff --git a/linux-user/unicore32/target_signal.h b/linux-user/unicore32/target_signal.h deleted file mode 100644 index c6496fb9ea..0000000000 --- a/linux-user/unicore32/target_signal.h +++ /dev/null @@ -1,30 +0,0 @@ -/* - * Copyright (C) 2010-2011 GUAN Xue-tao - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - */ -#ifndef UNICORE32_TARGET_SIGNAL_H -#define UNICORE32_TARGET_SIGNAL_H - -/* this struct defines a stack used during syscall handling */ -typedef struct target_sigaltstack { - abi_ulong ss_sp; - abi_ulong ss_flags; - abi_ulong ss_size; -} target_stack_t; - -/* - * sigaltstack controls - */ -#define TARGET_SS_ONSTACK 1 -#define TARGET_SS_DISABLE 2 - -static inline abi_ulong get_sp_from_cpustate(CPUUniCore32State *state) -{ - return state->regs[29]; -} - - -#endif /* UNICORE32_TARGET_SIGNAL_H */ diff --git a/linux-user/unicore32/target_structs.h b/linux-user/unicore32/target_structs.h deleted file mode 100644 index fbd4fa3f53..0000000000 --- a/linux-user/unicore32/target_structs.h +++ /dev/null @@ -1,58 +0,0 @@ -/* - * UniCore32 specific structures for linux-user - * - * Copyright (c) 2013 Fabrice Bellard - * - * 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/>. - */ -#ifndef UNICORE32_TARGET_STRUCTS_H -#define UNICORE32_TARGET_STRUCTS_H - -struct target_ipc_perm { - abi_int __key; /* Key. */ - abi_uint uid; /* Owner's user ID. */ - abi_uint gid; /* Owner's group ID. */ - abi_uint cuid; /* Creator's user ID. */ - abi_uint cgid; /* Creator's group ID. */ - abi_ushort mode; /* Read/write permission. */ - abi_ushort __pad1; - abi_ushort __seq; /* Sequence number. */ - abi_ushort __pad2; - abi_ulong __unused1; - abi_ulong __unused2; -}; - -struct target_shmid_ds { - struct target_ipc_perm shm_perm; /* operation permission struct */ - abi_long shm_segsz; /* size of segment in bytes */ - abi_ulong shm_atime; /* time of last shmat() */ -#if TARGET_ABI_BITS == 32 - abi_ulong __unused1; -#endif - abi_ulong shm_dtime; /* time of last shmdt() */ -#if TARGET_ABI_BITS == 32 - abi_ulong __unused2; -#endif - abi_ulong shm_ctime; /* time of last change by shmctl() */ -#if TARGET_ABI_BITS == 32 - abi_ulong __unused3; -#endif - abi_int shm_cpid; /* pid of creator */ - abi_int shm_lpid; /* pid of last shmop */ - abi_ulong shm_nattch; /* number of current attaches */ - abi_ulong __unused4; - abi_ulong __unused5; -}; - -#endif diff --git a/linux-user/unicore32/target_syscall.h b/linux-user/unicore32/target_syscall.h deleted file mode 100644 index 346b207700..0000000000 --- a/linux-user/unicore32/target_syscall.h +++ /dev/null @@ -1,62 +0,0 @@ -/* - * Copyright (C) 2010-2011 GUAN Xue-tao - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - */ - -#ifndef UNICORE32_TARGET_SYSCALL_H -#define UNICORE32_TARGET_SYSCALL_H - -struct target_pt_regs { - abi_ulong uregs[34]; -}; - -#define UC32_REG_pc uregs[31] -#define UC32_REG_lr uregs[30] -#define UC32_REG_sp uregs[29] -#define UC32_REG_ip uregs[28] -#define UC32_REG_fp uregs[27] -#define UC32_REG_26 uregs[26] -#define UC32_REG_25 uregs[25] -#define UC32_REG_24 uregs[24] -#define UC32_REG_23 uregs[23] -#define UC32_REG_22 uregs[22] -#define UC32_REG_21 uregs[21] -#define UC32_REG_20 uregs[20] -#define UC32_REG_19 uregs[19] -#define UC32_REG_18 uregs[18] -#define UC32_REG_17 uregs[17] -#define UC32_REG_16 uregs[16] -#define UC32_REG_15 uregs[15] -#define UC32_REG_14 uregs[14] -#define UC32_REG_13 uregs[13] -#define UC32_REG_12 uregs[12] -#define UC32_REG_11 uregs[11] -#define UC32_REG_10 uregs[10] -#define UC32_REG_09 uregs[9] -#define UC32_REG_08 uregs[8] -#define UC32_REG_07 uregs[7] -#define UC32_REG_06 uregs[6] -#define UC32_REG_05 uregs[5] -#define UC32_REG_04 uregs[4] -#define UC32_REG_03 uregs[3] -#define UC32_REG_02 uregs[2] -#define UC32_REG_01 uregs[1] -#define UC32_REG_00 uregs[0] -#define UC32_REG_asr uregs[32] -#define UC32_REG_ORIG_00 uregs[33] - -#define UC32_SYSCALL_BASE 0x900000 -#define UC32_SYSCALL_ARCH_BASE 0xf0000 -#define UC32_SYSCALL_NR_set_tls (UC32_SYSCALL_ARCH_BASE + 5) - -#define UNAME_MACHINE "UniCore-II" -#define UNAME_MINIMUM_RELEASE "2.6.32" - -#define TARGET_MINSIGSTKSZ 2048 -#define TARGET_MLOCKALL_MCL_CURRENT 1 -#define TARGET_MLOCKALL_MCL_FUTURE 2 - -#endif /* UNICORE32_TARGET_SYSCALL_H */ diff --git a/linux-user/unicore32/termbits.h b/linux-user/unicore32/termbits.h deleted file mode 100644 index a5fcd64abf..0000000000 --- a/linux-user/unicore32/termbits.h +++ /dev/null @@ -1,2 +0,0 @@ -/* NOTE: exactly the same as i386 */ -#include "../i386/termbits.h" diff --git a/migration/Makefile.objs b/migration/Makefile.objs index 99e038024d..c83ec47ba8 100644 --- a/migration/Makefile.objs +++ b/migration/Makefile.objs @@ -6,6 +6,7 @@ common-obj-y += qemu-file.o global_state.o common-obj-y += qemu-file-channel.o common-obj-y += xbzrle.o postcopy-ram.o common-obj-y += qjson.o +common-obj-y += block-dirty-bitmap.o common-obj-$(CONFIG_RDMA) += rdma.o diff --git a/migration/block-dirty-bitmap.c b/migration/block-dirty-bitmap.c new file mode 100644 index 0000000000..dd04f102d8 --- /dev/null +++ b/migration/block-dirty-bitmap.c @@ -0,0 +1,746 @@ +/* + * Block dirty bitmap postcopy migration + * + * Copyright IBM, Corp. 2009 + * Copyright (c) 2016-2017 Virtuozzo International GmbH. All rights reserved. + * + * Authors: + * Liran Schour <lirans@il.ibm.com> + * Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com> + * + * This work is licensed under the terms of the GNU GPL, version 2. See + * the COPYING file in the top-level directory. + * This file is derived from migration/block.c, so it's author and IBM copyright + * are here, although content is quite different. + * + * Contributions after 2012-01-13 are licensed under the terms of the + * GNU GPL, version 2 or (at your option) any later version. + * + * *** + * + * Here postcopy migration of dirty bitmaps is realized. Only QMP-addressable + * bitmaps are migrated. + * + * Bitmap migration implies creating bitmap with the same name and granularity + * in destination QEMU. If the bitmap with the same name (for the same node) + * already exists on destination an error will be generated. + * + * format of migration: + * + * # Header (shared for different chunk types) + * 1, 2 or 4 bytes: flags (see qemu_{put,put}_flags) + * [ 1 byte: node name size ] \ flags & DEVICE_NAME + * [ n bytes: node name ] / + * [ 1 byte: bitmap name size ] \ flags & BITMAP_NAME + * [ n bytes: bitmap name ] / + * + * # Start of bitmap migration (flags & START) + * header + * be64: granularity + * 1 byte: bitmap flags (corresponds to BdrvDirtyBitmap) + * bit 0 - bitmap is enabled + * bit 1 - bitmap is persistent + * bit 2 - bitmap is autoloading + * bits 3-7 - reserved, must be zero + * + * # Complete of bitmap migration (flags & COMPLETE) + * header + * + * # Data chunk of bitmap migration + * header + * be64: start sector + * be32: number of sectors + * [ be64: buffer size ] \ ! (flags & ZEROES) + * [ n bytes: buffer ] / + * + * The last chunk in stream should contain flags & EOS. The chunk may skip + * device and/or bitmap names, assuming them to be the same with the previous + * chunk. + */ + +#include "qemu/osdep.h" +#include "block/block.h" +#include "block/block_int.h" +#include "sysemu/block-backend.h" +#include "qemu/main-loop.h" +#include "qemu/error-report.h" +#include "migration/misc.h" +#include "migration/migration.h" +#include "migration/qemu-file.h" +#include "migration/vmstate.h" +#include "migration/register.h" +#include "qemu/hbitmap.h" +#include "sysemu/sysemu.h" +#include "qemu/cutils.h" +#include "qapi/error.h" +#include "trace.h" + +#define CHUNK_SIZE (1 << 10) + +/* Flags occupy one, two or four bytes (Big Endian). The size is determined as + * follows: + * in first (most significant) byte bit 8 is clear --> one byte + * in first byte bit 8 is set --> two or four bytes, depending on second + * byte: + * | in second byte bit 8 is clear --> two bytes + * | in second byte bit 8 is set --> four bytes + */ +#define DIRTY_BITMAP_MIG_FLAG_EOS 0x01 +#define DIRTY_BITMAP_MIG_FLAG_ZEROES 0x02 +#define DIRTY_BITMAP_MIG_FLAG_BITMAP_NAME 0x04 +#define DIRTY_BITMAP_MIG_FLAG_DEVICE_NAME 0x08 +#define DIRTY_BITMAP_MIG_FLAG_START 0x10 +#define DIRTY_BITMAP_MIG_FLAG_COMPLETE 0x20 +#define DIRTY_BITMAP_MIG_FLAG_BITS 0x40 + +#define DIRTY_BITMAP_MIG_EXTRA_FLAGS 0x80 + +#define DIRTY_BITMAP_MIG_START_FLAG_ENABLED 0x01 +#define DIRTY_BITMAP_MIG_START_FLAG_PERSISTENT 0x02 +/* 0x04 was "AUTOLOAD" flags on elder versions, no it is ignored */ +#define DIRTY_BITMAP_MIG_START_FLAG_RESERVED_MASK 0xf8 + +typedef struct DirtyBitmapMigBitmapState { + /* Written during setup phase. */ + BlockDriverState *bs; + const char *node_name; + BdrvDirtyBitmap *bitmap; + uint64_t total_sectors; + uint64_t sectors_per_chunk; + QSIMPLEQ_ENTRY(DirtyBitmapMigBitmapState) entry; + uint8_t flags; + + /* For bulk phase. */ + bool bulk_completed; + uint64_t cur_sector; +} DirtyBitmapMigBitmapState; + +typedef struct DirtyBitmapMigState { + QSIMPLEQ_HEAD(dbms_list, DirtyBitmapMigBitmapState) dbms_list; + + bool bulk_completed; + bool no_bitmaps; + + /* for send_bitmap_bits() */ + BlockDriverState *prev_bs; + BdrvDirtyBitmap *prev_bitmap; +} DirtyBitmapMigState; + +typedef struct DirtyBitmapLoadState { + uint32_t flags; + char node_name[256]; + char bitmap_name[256]; + BlockDriverState *bs; + BdrvDirtyBitmap *bitmap; +} DirtyBitmapLoadState; + +static DirtyBitmapMigState dirty_bitmap_mig_state; + +typedef struct DirtyBitmapLoadBitmapState { + BlockDriverState *bs; + BdrvDirtyBitmap *bitmap; + bool migrated; +} DirtyBitmapLoadBitmapState; +static GSList *enabled_bitmaps; +QemuMutex finish_lock; + +void init_dirty_bitmap_incoming_migration(void) +{ + qemu_mutex_init(&finish_lock); +} + +static uint32_t qemu_get_bitmap_flags(QEMUFile *f) +{ + uint8_t flags = qemu_get_byte(f); + if (flags & DIRTY_BITMAP_MIG_EXTRA_FLAGS) { + flags = flags << 8 | qemu_get_byte(f); + if (flags & DIRTY_BITMAP_MIG_EXTRA_FLAGS) { + flags = flags << 16 | qemu_get_be16(f); + } + } + + return flags; +} + +static void qemu_put_bitmap_flags(QEMUFile *f, uint32_t flags) +{ + /* The code currently do not send flags more than one byte */ + assert(!(flags & (0xffffff00 | DIRTY_BITMAP_MIG_EXTRA_FLAGS))); + + qemu_put_byte(f, flags); +} + +static void send_bitmap_header(QEMUFile *f, DirtyBitmapMigBitmapState *dbms, + uint32_t additional_flags) +{ + BlockDriverState *bs = dbms->bs; + BdrvDirtyBitmap *bitmap = dbms->bitmap; + uint32_t flags = additional_flags; + trace_send_bitmap_header_enter(); + + if (bs != dirty_bitmap_mig_state.prev_bs) { + dirty_bitmap_mig_state.prev_bs = bs; + flags |= DIRTY_BITMAP_MIG_FLAG_DEVICE_NAME; + } + + if (bitmap != dirty_bitmap_mig_state.prev_bitmap) { + dirty_bitmap_mig_state.prev_bitmap = bitmap; + flags |= DIRTY_BITMAP_MIG_FLAG_BITMAP_NAME; + } + + qemu_put_bitmap_flags(f, flags); + + if (flags & DIRTY_BITMAP_MIG_FLAG_DEVICE_NAME) { + qemu_put_counted_string(f, dbms->node_name); + } + + if (flags & DIRTY_BITMAP_MIG_FLAG_BITMAP_NAME) { + qemu_put_counted_string(f, bdrv_dirty_bitmap_name(bitmap)); + } +} + +static void send_bitmap_start(QEMUFile *f, DirtyBitmapMigBitmapState *dbms) +{ + send_bitmap_header(f, dbms, DIRTY_BITMAP_MIG_FLAG_START); + qemu_put_be32(f, bdrv_dirty_bitmap_granularity(dbms->bitmap)); + qemu_put_byte(f, dbms->flags); +} + +static void send_bitmap_complete(QEMUFile *f, DirtyBitmapMigBitmapState *dbms) +{ + send_bitmap_header(f, dbms, DIRTY_BITMAP_MIG_FLAG_COMPLETE); +} + +static void send_bitmap_bits(QEMUFile *f, DirtyBitmapMigBitmapState *dbms, + uint64_t start_sector, uint32_t nr_sectors) +{ + /* align for buffer_is_zero() */ + uint64_t align = 4 * sizeof(long); + uint64_t unaligned_size = + bdrv_dirty_bitmap_serialization_size( + dbms->bitmap, start_sector << BDRV_SECTOR_BITS, + (uint64_t)nr_sectors << BDRV_SECTOR_BITS); + uint64_t buf_size = QEMU_ALIGN_UP(unaligned_size, align); + uint8_t *buf = g_malloc0(buf_size); + uint32_t flags = DIRTY_BITMAP_MIG_FLAG_BITS; + + bdrv_dirty_bitmap_serialize_part( + dbms->bitmap, buf, start_sector << BDRV_SECTOR_BITS, + (uint64_t)nr_sectors << BDRV_SECTOR_BITS); + + if (buffer_is_zero(buf, buf_size)) { + g_free(buf); + buf = NULL; + flags |= DIRTY_BITMAP_MIG_FLAG_ZEROES; + } + + trace_send_bitmap_bits(flags, start_sector, nr_sectors, buf_size); + + send_bitmap_header(f, dbms, flags); + + qemu_put_be64(f, start_sector); + qemu_put_be32(f, nr_sectors); + + /* if a block is zero we need to flush here since the network + * bandwidth is now a lot higher than the storage device bandwidth. + * thus if we queue zero blocks we slow down the migration. */ + if (flags & DIRTY_BITMAP_MIG_FLAG_ZEROES) { + qemu_fflush(f); + } else { + qemu_put_be64(f, buf_size); + qemu_put_buffer(f, buf, buf_size); + } + + g_free(buf); +} + +/* Called with iothread lock taken. */ +static void dirty_bitmap_mig_cleanup(void) +{ + DirtyBitmapMigBitmapState *dbms; + + while ((dbms = QSIMPLEQ_FIRST(&dirty_bitmap_mig_state.dbms_list)) != NULL) { + QSIMPLEQ_REMOVE_HEAD(&dirty_bitmap_mig_state.dbms_list, entry); + bdrv_dirty_bitmap_set_qmp_locked(dbms->bitmap, false); + bdrv_unref(dbms->bs); + g_free(dbms); + } +} + +/* Called with iothread lock taken. */ +static int init_dirty_bitmap_migration(void) +{ + BlockDriverState *bs; + BdrvDirtyBitmap *bitmap; + DirtyBitmapMigBitmapState *dbms; + BdrvNextIterator it; + + dirty_bitmap_mig_state.bulk_completed = false; + dirty_bitmap_mig_state.prev_bs = NULL; + dirty_bitmap_mig_state.prev_bitmap = NULL; + dirty_bitmap_mig_state.no_bitmaps = false; + + for (bs = bdrv_first(&it); bs; bs = bdrv_next(&it)) { + const char *drive_name = bdrv_get_device_or_node_name(bs); + + /* skip automatically inserted nodes */ + while (bs && bs->drv && bs->implicit) { + bs = backing_bs(bs); + } + + for (bitmap = bdrv_dirty_bitmap_next(bs, NULL); bitmap; + bitmap = bdrv_dirty_bitmap_next(bs, bitmap)) + { + if (!bdrv_dirty_bitmap_name(bitmap)) { + continue; + } + + if (drive_name == NULL) { + error_report("Found bitmap '%s' in unnamed node %p. It can't " + "be migrated", bdrv_dirty_bitmap_name(bitmap), bs); + goto fail; + } + + if (bdrv_dirty_bitmap_frozen(bitmap)) { + error_report("Can't migrate frozen dirty bitmap: '%s", + bdrv_dirty_bitmap_name(bitmap)); + goto fail; + } + + if (bdrv_dirty_bitmap_qmp_locked(bitmap)) { + error_report("Can't migrate locked dirty bitmap: '%s", + bdrv_dirty_bitmap_name(bitmap)); + goto fail; + } + + bdrv_ref(bs); + bdrv_dirty_bitmap_set_qmp_locked(bitmap, true); + + dbms = g_new0(DirtyBitmapMigBitmapState, 1); + dbms->bs = bs; + dbms->node_name = drive_name; + dbms->bitmap = bitmap; + dbms->total_sectors = bdrv_nb_sectors(bs); + dbms->sectors_per_chunk = CHUNK_SIZE * 8 * + bdrv_dirty_bitmap_granularity(bitmap) >> BDRV_SECTOR_BITS; + if (bdrv_dirty_bitmap_enabled(bitmap)) { + dbms->flags |= DIRTY_BITMAP_MIG_START_FLAG_ENABLED; + } + if (bdrv_dirty_bitmap_get_persistance(bitmap)) { + dbms->flags |= DIRTY_BITMAP_MIG_START_FLAG_PERSISTENT; + } + + QSIMPLEQ_INSERT_TAIL(&dirty_bitmap_mig_state.dbms_list, + dbms, entry); + } + } + + /* unset persistance here, to not roll back it */ + QSIMPLEQ_FOREACH(dbms, &dirty_bitmap_mig_state.dbms_list, entry) { + bdrv_dirty_bitmap_set_persistance(dbms->bitmap, false); + } + + if (QSIMPLEQ_EMPTY(&dirty_bitmap_mig_state.dbms_list)) { + dirty_bitmap_mig_state.no_bitmaps = true; + } + + return 0; + +fail: + dirty_bitmap_mig_cleanup(); + + return -1; +} + +/* Called with no lock taken. */ +static void bulk_phase_send_chunk(QEMUFile *f, DirtyBitmapMigBitmapState *dbms) +{ + uint32_t nr_sectors = MIN(dbms->total_sectors - dbms->cur_sector, + dbms->sectors_per_chunk); + + send_bitmap_bits(f, dbms, dbms->cur_sector, nr_sectors); + + dbms->cur_sector += nr_sectors; + if (dbms->cur_sector >= dbms->total_sectors) { + dbms->bulk_completed = true; + } +} + +/* Called with no lock taken. */ +static void bulk_phase(QEMUFile *f, bool limit) +{ + DirtyBitmapMigBitmapState *dbms; + + QSIMPLEQ_FOREACH(dbms, &dirty_bitmap_mig_state.dbms_list, entry) { + while (!dbms->bulk_completed) { + bulk_phase_send_chunk(f, dbms); + if (limit && qemu_file_rate_limit(f)) { + return; + } + } + } + + dirty_bitmap_mig_state.bulk_completed = true; +} + +/* for SaveVMHandlers */ +static void dirty_bitmap_save_cleanup(void *opaque) +{ + dirty_bitmap_mig_cleanup(); +} + +static int dirty_bitmap_save_iterate(QEMUFile *f, void *opaque) +{ + trace_dirty_bitmap_save_iterate(migration_in_postcopy()); + + if (migration_in_postcopy() && !dirty_bitmap_mig_state.bulk_completed) { + bulk_phase(f, true); + } + + qemu_put_bitmap_flags(f, DIRTY_BITMAP_MIG_FLAG_EOS); + + return dirty_bitmap_mig_state.bulk_completed; +} + +/* Called with iothread lock taken. */ + +static int dirty_bitmap_save_complete(QEMUFile *f, void *opaque) +{ + DirtyBitmapMigBitmapState *dbms; + trace_dirty_bitmap_save_complete_enter(); + + if (!dirty_bitmap_mig_state.bulk_completed) { + bulk_phase(f, false); + } + + QSIMPLEQ_FOREACH(dbms, &dirty_bitmap_mig_state.dbms_list, entry) { + send_bitmap_complete(f, dbms); + } + + qemu_put_bitmap_flags(f, DIRTY_BITMAP_MIG_FLAG_EOS); + + trace_dirty_bitmap_save_complete_finish(); + + dirty_bitmap_mig_cleanup(); + return 0; +} + +static void dirty_bitmap_save_pending(QEMUFile *f, void *opaque, + uint64_t max_size, + uint64_t *res_precopy_only, + uint64_t *res_compatible, + uint64_t *res_postcopy_only) +{ + DirtyBitmapMigBitmapState *dbms; + uint64_t pending = 0; + + qemu_mutex_lock_iothread(); + + QSIMPLEQ_FOREACH(dbms, &dirty_bitmap_mig_state.dbms_list, entry) { + uint64_t gran = bdrv_dirty_bitmap_granularity(dbms->bitmap); + uint64_t sectors = dbms->bulk_completed ? 0 : + dbms->total_sectors - dbms->cur_sector; + + pending += DIV_ROUND_UP(sectors * BDRV_SECTOR_SIZE, gran); + } + + qemu_mutex_unlock_iothread(); + + trace_dirty_bitmap_save_pending(pending, max_size); + + *res_postcopy_only += pending; +} + +/* First occurrence of this bitmap. It should be created if doesn't exist */ +static int dirty_bitmap_load_start(QEMUFile *f, DirtyBitmapLoadState *s) +{ + Error *local_err = NULL; + uint32_t granularity = qemu_get_be32(f); + uint8_t flags = qemu_get_byte(f); + + if (s->bitmap) { + error_report("Bitmap with the same name ('%s') already exists on " + "destination", bdrv_dirty_bitmap_name(s->bitmap)); + return -EINVAL; + } else { + s->bitmap = bdrv_create_dirty_bitmap(s->bs, granularity, + s->bitmap_name, &local_err); + if (!s->bitmap) { + error_report_err(local_err); + return -EINVAL; + } + } + + if (flags & DIRTY_BITMAP_MIG_START_FLAG_RESERVED_MASK) { + error_report("Unknown flags in migrated dirty bitmap header: %x", + flags); + return -EINVAL; + } + + if (flags & DIRTY_BITMAP_MIG_START_FLAG_PERSISTENT) { + bdrv_dirty_bitmap_set_persistance(s->bitmap, true); + } + + bdrv_disable_dirty_bitmap(s->bitmap); + if (flags & DIRTY_BITMAP_MIG_START_FLAG_ENABLED) { + DirtyBitmapLoadBitmapState *b; + + bdrv_dirty_bitmap_create_successor(s->bs, s->bitmap, &local_err); + if (local_err) { + error_report_err(local_err); + return -EINVAL; + } + + b = g_new(DirtyBitmapLoadBitmapState, 1); + b->bs = s->bs; + b->bitmap = s->bitmap; + b->migrated = false; + enabled_bitmaps = g_slist_prepend(enabled_bitmaps, b); + } + + return 0; +} + +void dirty_bitmap_mig_before_vm_start(void) +{ + GSList *item; + + qemu_mutex_lock(&finish_lock); + + for (item = enabled_bitmaps; item; item = g_slist_next(item)) { + DirtyBitmapLoadBitmapState *b = item->data; + + if (b->migrated) { + bdrv_enable_dirty_bitmap(b->bitmap); + } else { + bdrv_dirty_bitmap_enable_successor(b->bitmap); + } + + g_free(b); + } + + g_slist_free(enabled_bitmaps); + enabled_bitmaps = NULL; + + qemu_mutex_unlock(&finish_lock); +} + +static void dirty_bitmap_load_complete(QEMUFile *f, DirtyBitmapLoadState *s) +{ + GSList *item; + trace_dirty_bitmap_load_complete(); + bdrv_dirty_bitmap_deserialize_finish(s->bitmap); + + qemu_mutex_lock(&finish_lock); + + for (item = enabled_bitmaps; item; item = g_slist_next(item)) { + DirtyBitmapLoadBitmapState *b = item->data; + + if (b->bitmap == s->bitmap) { + b->migrated = true; + break; + } + } + + if (bdrv_dirty_bitmap_frozen(s->bitmap)) { + bdrv_dirty_bitmap_lock(s->bitmap); + if (enabled_bitmaps == NULL) { + /* in postcopy */ + bdrv_reclaim_dirty_bitmap_locked(s->bs, s->bitmap, &error_abort); + bdrv_enable_dirty_bitmap(s->bitmap); + } else { + /* target not started, successor must be empty */ + int64_t count = bdrv_get_dirty_count(s->bitmap); + BdrvDirtyBitmap *ret = bdrv_reclaim_dirty_bitmap_locked(s->bs, + s->bitmap, + NULL); + /* bdrv_reclaim_dirty_bitmap can fail only on no successor (it + * must be) or on merge fail, but merge can't fail when second + * bitmap is empty + */ + assert(ret == s->bitmap && + count == bdrv_get_dirty_count(s->bitmap)); + } + bdrv_dirty_bitmap_unlock(s->bitmap); + } + + qemu_mutex_unlock(&finish_lock); +} + +static int dirty_bitmap_load_bits(QEMUFile *f, DirtyBitmapLoadState *s) +{ + uint64_t first_byte = qemu_get_be64(f) << BDRV_SECTOR_BITS; + uint64_t nr_bytes = (uint64_t)qemu_get_be32(f) << BDRV_SECTOR_BITS; + trace_dirty_bitmap_load_bits_enter(first_byte >> BDRV_SECTOR_BITS, + nr_bytes >> BDRV_SECTOR_BITS); + + if (s->flags & DIRTY_BITMAP_MIG_FLAG_ZEROES) { + trace_dirty_bitmap_load_bits_zeroes(); + bdrv_dirty_bitmap_deserialize_zeroes(s->bitmap, first_byte, nr_bytes, + false); + } else { + size_t ret; + uint8_t *buf; + uint64_t buf_size = qemu_get_be64(f); + uint64_t needed_size = + bdrv_dirty_bitmap_serialization_size(s->bitmap, + first_byte, nr_bytes); + + if (needed_size > buf_size || + buf_size > QEMU_ALIGN_UP(needed_size, 4 * sizeof(long)) + /* Here used same alignment as in send_bitmap_bits */ + ) { + error_report("Migrated bitmap granularity doesn't " + "match the destination bitmap '%s' granularity", + bdrv_dirty_bitmap_name(s->bitmap)); + return -EINVAL; + } + + buf = g_malloc(buf_size); + ret = qemu_get_buffer(f, buf, buf_size); + if (ret != buf_size) { + error_report("Failed to read bitmap bits"); + return -EIO; + } + + bdrv_dirty_bitmap_deserialize_part(s->bitmap, buf, first_byte, nr_bytes, + false); + g_free(buf); + } + + return 0; +} + +static int dirty_bitmap_load_header(QEMUFile *f, DirtyBitmapLoadState *s) +{ + Error *local_err = NULL; + bool nothing; + s->flags = qemu_get_bitmap_flags(f); + trace_dirty_bitmap_load_header(s->flags); + + nothing = s->flags == (s->flags & DIRTY_BITMAP_MIG_FLAG_EOS); + + if (s->flags & DIRTY_BITMAP_MIG_FLAG_DEVICE_NAME) { + if (!qemu_get_counted_string(f, s->node_name)) { + error_report("Unable to read node name string"); + return -EINVAL; + } + s->bs = bdrv_lookup_bs(s->node_name, s->node_name, &local_err); + if (!s->bs) { + error_report_err(local_err); + return -EINVAL; + } + } else if (!s->bs && !nothing) { + error_report("Error: block device name is not set"); + return -EINVAL; + } + + if (s->flags & DIRTY_BITMAP_MIG_FLAG_BITMAP_NAME) { + if (!qemu_get_counted_string(f, s->bitmap_name)) { + error_report("Unable to read bitmap name string"); + return -EINVAL; + } + s->bitmap = bdrv_find_dirty_bitmap(s->bs, s->bitmap_name); + + /* bitmap may be NULL here, it wouldn't be an error if it is the + * first occurrence of the bitmap */ + if (!s->bitmap && !(s->flags & DIRTY_BITMAP_MIG_FLAG_START)) { + error_report("Error: unknown dirty bitmap " + "'%s' for block device '%s'", + s->bitmap_name, s->node_name); + return -EINVAL; + } + } else if (!s->bitmap && !nothing) { + error_report("Error: block device name is not set"); + return -EINVAL; + } + + return 0; +} + +static int dirty_bitmap_load(QEMUFile *f, void *opaque, int version_id) +{ + static DirtyBitmapLoadState s; + int ret = 0; + + trace_dirty_bitmap_load_enter(); + + if (version_id != 1) { + return -EINVAL; + } + + do { + ret = dirty_bitmap_load_header(f, &s); + + if (s.flags & DIRTY_BITMAP_MIG_FLAG_START) { + ret = dirty_bitmap_load_start(f, &s); + } else if (s.flags & DIRTY_BITMAP_MIG_FLAG_COMPLETE) { + dirty_bitmap_load_complete(f, &s); + } else if (s.flags & DIRTY_BITMAP_MIG_FLAG_BITS) { + ret = dirty_bitmap_load_bits(f, &s); + } + + if (!ret) { + ret = qemu_file_get_error(f); + } + + if (ret) { + return ret; + } + } while (!(s.flags & DIRTY_BITMAP_MIG_FLAG_EOS)); + + trace_dirty_bitmap_load_success(); + return 0; +} + +static int dirty_bitmap_save_setup(QEMUFile *f, void *opaque) +{ + DirtyBitmapMigBitmapState *dbms = NULL; + if (init_dirty_bitmap_migration() < 0) { + return -1; + } + + QSIMPLEQ_FOREACH(dbms, &dirty_bitmap_mig_state.dbms_list, entry) { + send_bitmap_start(f, dbms); + } + qemu_put_bitmap_flags(f, DIRTY_BITMAP_MIG_FLAG_EOS); + + return 0; +} + +static bool dirty_bitmap_is_active(void *opaque) +{ + return migrate_dirty_bitmaps() && !dirty_bitmap_mig_state.no_bitmaps; +} + +static bool dirty_bitmap_is_active_iterate(void *opaque) +{ + return dirty_bitmap_is_active(opaque) && !runstate_is_running(); +} + +static bool dirty_bitmap_has_postcopy(void *opaque) +{ + return true; +} + +static SaveVMHandlers savevm_dirty_bitmap_handlers = { + .save_setup = dirty_bitmap_save_setup, + .save_live_complete_postcopy = dirty_bitmap_save_complete, + .save_live_complete_precopy = dirty_bitmap_save_complete, + .has_postcopy = dirty_bitmap_has_postcopy, + .save_live_pending = dirty_bitmap_save_pending, + .save_live_iterate = dirty_bitmap_save_iterate, + .is_active_iterate = dirty_bitmap_is_active_iterate, + .load_state = dirty_bitmap_load, + .save_cleanup = dirty_bitmap_save_cleanup, + .is_active = dirty_bitmap_is_active, +}; + +void dirty_bitmap_mig_init(void) +{ + QSIMPLEQ_INIT(&dirty_bitmap_mig_state.dbms_list); + + register_savevm_live(NULL, "dirty-bitmap", 0, 1, + &savevm_dirty_bitmap_handlers, + &dirty_bitmap_mig_state); +} diff --git a/migration/block.c b/migration/block.c index 41b95d1dd8..5c03632257 100644 --- a/migration/block.c +++ b/migration/block.c @@ -864,8 +864,9 @@ static int block_save_complete(QEMUFile *f, void *opaque) } static void block_save_pending(QEMUFile *f, void *opaque, uint64_t max_size, - uint64_t *non_postcopiable_pending, - uint64_t *postcopiable_pending) + uint64_t *res_precopy_only, + uint64_t *res_compatible, + uint64_t *res_postcopy_only) { /* Estimate pending number of bytes to send */ uint64_t pending; @@ -886,7 +887,7 @@ static void block_save_pending(QEMUFile *f, void *opaque, uint64_t max_size, DPRINTF("Enter save live pending %" PRIu64 "\n", pending); /* We don't do postcopy */ - *non_postcopiable_pending += pending; + *res_precopy_only += pending; } static int block_load(QEMUFile *f, void *opaque, int version_id) diff --git a/migration/migration.c b/migration/migration.c index 6a4780ef6f..623f373326 100644 --- a/migration/migration.c +++ b/migration/migration.c @@ -157,6 +157,9 @@ MigrationIncomingState *migration_incoming_get_current(void) memset(&mis_current, 0, sizeof(MigrationIncomingState)); qemu_mutex_init(&mis_current.rp_mutex); qemu_event_init(&mis_current.main_thread_load_event, false); + + init_dirty_bitmap_incoming_migration(); + once = true; } return &mis_current; @@ -320,6 +323,8 @@ static void process_incoming_migration_bh(void *opaque) state, we need to obey autostart. Any other state is set with runstate_set. */ + dirty_bitmap_mig_before_vm_start(); + if (!global_state_received() || global_state_get_runstate() == RUN_STATE_RUNNING) { if (autostart) { @@ -1022,7 +1027,7 @@ void qmp_migrate_start_postcopy(Error **errp) { MigrationState *s = migrate_get_current(); - if (!migrate_postcopy_ram()) { + if (!migrate_postcopy()) { error_setg(errp, "Enable postcopy with migrate_set_capability before" " the start of migration"); return; @@ -1508,7 +1513,7 @@ bool migrate_postcopy_ram(void) bool migrate_postcopy(void) { - return migrate_postcopy_ram(); + return migrate_postcopy_ram() || migrate_dirty_bitmaps(); } bool migrate_auto_converge(void) @@ -1565,6 +1570,15 @@ int migrate_decompress_threads(void) return s->parameters.decompress_threads; } +bool migrate_dirty_bitmaps(void) +{ + MigrationState *s; + + s = migrate_get_current(); + + return s->enabled_capabilities[MIGRATION_CAPABILITY_DIRTY_BITMAPS]; +} + bool migrate_use_events(void) { MigrationState *s; @@ -2242,20 +2256,20 @@ typedef enum { */ static MigIterateState migration_iteration_run(MigrationState *s) { - uint64_t pending_size, pend_post, pend_nonpost; + uint64_t pending_size, pend_pre, pend_compat, pend_post; bool in_postcopy = s->state == MIGRATION_STATUS_POSTCOPY_ACTIVE; - qemu_savevm_state_pending(s->to_dst_file, s->threshold_size, - &pend_nonpost, &pend_post); - pending_size = pend_nonpost + pend_post; + qemu_savevm_state_pending(s->to_dst_file, s->threshold_size, &pend_pre, + &pend_compat, &pend_post); + pending_size = pend_pre + pend_compat + pend_post; trace_migrate_pending(pending_size, s->threshold_size, - pend_post, pend_nonpost); + pend_pre, pend_compat, pend_post); if (pending_size && pending_size >= s->threshold_size) { /* Still a significant amount to transfer */ if (migrate_postcopy() && !in_postcopy && - pend_nonpost <= s->threshold_size && + pend_pre <= s->threshold_size && atomic_read(&s->start_postcopy)) { if (postcopy_start(s)) { error_report("%s: postcopy failed to start", __func__); diff --git a/migration/migration.h b/migration/migration.h index 08c5d2ded1..a79540b99c 100644 --- a/migration/migration.h +++ b/migration/migration.h @@ -205,6 +205,7 @@ bool migrate_postcopy(void); bool migrate_release_ram(void); bool migrate_postcopy_ram(void); bool migrate_zero_blocks(void); +bool migrate_dirty_bitmaps(void); bool migrate_auto_converge(void); bool migrate_use_multifd(void); @@ -234,4 +235,7 @@ void migrate_send_rp_pong(MigrationIncomingState *mis, int migrate_send_rp_req_pages(MigrationIncomingState *mis, const char* rbname, ram_addr_t start, size_t len); +void dirty_bitmap_mig_before_vm_start(void); +void init_dirty_bitmap_incoming_migration(void); + #endif diff --git a/migration/qemu-file.c b/migration/qemu-file.c index 2ab2bf362d..e85f501f86 100644 --- a/migration/qemu-file.c +++ b/migration/qemu-file.c @@ -734,6 +734,19 @@ size_t qemu_get_counted_string(QEMUFile *f, char buf[256]) } /* + * Put a string with one preceding byte containing its length. The length of + * the string should be less than 256. + */ +void qemu_put_counted_string(QEMUFile *f, const char *str) +{ + size_t len = strlen(str); + + assert(len < 256); + qemu_put_byte(f, len); + qemu_put_buffer(f, (const uint8_t *)str, len); +} + +/* * Set the blocking state of the QEMUFile. * Note: On some transports the OS only keeps a single blocking state for * both directions, and thus changing the blocking on the main diff --git a/migration/qemu-file.h b/migration/qemu-file.h index aae4e5ed36..f4f356ab12 100644 --- a/migration/qemu-file.h +++ b/migration/qemu-file.h @@ -174,4 +174,6 @@ size_t ram_control_save_page(QEMUFile *f, ram_addr_t block_offset, ram_addr_t offset, size_t size, uint64_t *bytes_sent); +void qemu_put_counted_string(QEMUFile *f, const char *name); + #endif diff --git a/migration/ram.c b/migration/ram.c index 7266351fd0..590fceb7e9 100644 --- a/migration/ram.c +++ b/migration/ram.c @@ -2370,8 +2370,9 @@ static int ram_save_complete(QEMUFile *f, void *opaque) } static void ram_save_pending(QEMUFile *f, void *opaque, uint64_t max_size, - uint64_t *non_postcopiable_pending, - uint64_t *postcopiable_pending) + uint64_t *res_precopy_only, + uint64_t *res_compatible, + uint64_t *res_postcopy_only) { RAMState **temp = opaque; RAMState *rs = *temp; @@ -2391,9 +2392,9 @@ static void ram_save_pending(QEMUFile *f, void *opaque, uint64_t max_size, if (migrate_postcopy_ram()) { /* We can do postcopy, and all the data is postcopiable */ - *postcopiable_pending += remaining_size; + *res_compatible += remaining_size; } else { - *non_postcopiable_pending += remaining_size; + *res_precopy_only += remaining_size; } } diff --git a/migration/savevm.c b/migration/savevm.c index 358c5b51e2..f417cef7d5 100644 --- a/migration/savevm.c +++ b/migration/savevm.c @@ -54,6 +54,7 @@ #include "qemu/cutils.h" #include "io/channel-buffer.h" #include "io/channel-file.h" +#include "sysemu/replay.h" #ifndef ETH_P_RARP #define ETH_P_RARP 0x8035 @@ -1028,6 +1029,11 @@ int qemu_savevm_state_iterate(QEMUFile *f, bool postcopy) continue; } } + if (se->ops && se->ops->is_active_iterate) { + if (!se->ops->is_active_iterate(se->opaque)) { + continue; + } + } /* * In the postcopy phase, any device that doesn't know how to * do postcopy should have saved it's state in the _complete @@ -1220,13 +1226,15 @@ int qemu_savevm_state_complete_precopy(QEMUFile *f, bool iterable_only, * for units that can't do postcopy. */ void qemu_savevm_state_pending(QEMUFile *f, uint64_t threshold_size, - uint64_t *res_non_postcopiable, - uint64_t *res_postcopiable) + uint64_t *res_precopy_only, + uint64_t *res_compatible, + uint64_t *res_postcopy_only) { SaveStateEntry *se; - *res_non_postcopiable = 0; - *res_postcopiable = 0; + *res_precopy_only = 0; + *res_compatible = 0; + *res_postcopy_only = 0; QTAILQ_FOREACH(se, &savevm_state.handlers, entry) { @@ -1239,7 +1247,8 @@ void qemu_savevm_state_pending(QEMUFile *f, uint64_t threshold_size, } } se->ops->save_live_pending(f, se->opaque, threshold_size, - res_non_postcopiable, res_postcopiable); + res_precopy_only, res_compatible, + res_postcopy_only); } } @@ -1685,6 +1694,8 @@ static void loadvm_postcopy_handle_run_bh(void *opaque) trace_loadvm_postcopy_handle_run_vmstart(); + dirty_bitmap_mig_before_vm_start(); + if (autostart) { /* Hold onto your hats, starting the CPU */ vm_start(); @@ -2197,6 +2208,12 @@ int save_snapshot(const char *name, Error **errp) struct tm tm; AioContext *aio_context; + if (!replay_can_snapshot()) { + error_report("Record/replay does not allow making snapshot " + "right now. Try once more later."); + return ret; + } + if (!bdrv_all_can_snapshot(&bs)) { error_setg(errp, "Device '%s' is writable but does not support " "snapshots", bdrv_get_device_name(bs)); @@ -2388,6 +2405,12 @@ int load_snapshot(const char *name, Error **errp) AioContext *aio_context; MigrationIncomingState *mis = migration_incoming_get_current(); + if (!replay_can_snapshot()) { + error_report("Record/replay does not allow loading snapshot " + "right now. Try once more later."); + return -EINVAL; + } + if (!bdrv_all_can_snapshot(&bs)) { error_setg(errp, "Device '%s' is writable but does not support snapshots", diff --git a/migration/savevm.h b/migration/savevm.h index 295c4a1f2c..cf4f0d37ca 100644 --- a/migration/savevm.h +++ b/migration/savevm.h @@ -38,8 +38,9 @@ void qemu_savevm_state_complete_postcopy(QEMUFile *f); int qemu_savevm_state_complete_precopy(QEMUFile *f, bool iterable_only, bool inactivate_disks); void qemu_savevm_state_pending(QEMUFile *f, uint64_t max_size, - uint64_t *res_non_postcopiable, - uint64_t *res_postcopiable); + uint64_t *res_precopy_only, + uint64_t *res_compatible, + uint64_t *res_postcopy_only); void qemu_savevm_send_ping(QEMUFile *f, uint32_t value); void qemu_savevm_send_open_return_path(QEMUFile *f); int qemu_savevm_send_packaged(QEMUFile *f, const uint8_t *buf, size_t len); diff --git a/migration/trace-events b/migration/trace-events index 93961dea16..314e1be6bc 100644 --- a/migration/trace-events +++ b/migration/trace-events @@ -86,7 +86,7 @@ migrate_fd_cleanup(void) "" migrate_fd_error(const char *error_desc) "error=%s" migrate_fd_cancel(void) "" migrate_handle_rp_req_pages(const char *rbname, size_t start, size_t len) "in %s at 0x%zx len 0x%zx" -migrate_pending(uint64_t size, uint64_t max, uint64_t post, uint64_t nonpost) "pending size %" PRIu64 " max %" PRIu64 " (post=%" PRIu64 " nonpost=%" PRIu64 ")" +migrate_pending(uint64_t size, uint64_t max, uint64_t pre, uint64_t compat, uint64_t post) "pending size %" PRIu64 " max %" PRIu64 " (pre = %" PRIu64 " compat=%" PRIu64 " post=%" PRIu64 ")" migrate_send_rp_message(int msg_type, uint16_t len) "%d: len %d" migration_completion_file_err(void) "" migration_completion_postcopy_end(void) "" @@ -227,3 +227,17 @@ colo_vm_state_change(const char *old, const char *new) "Change '%s' => '%s'" colo_send_message(const char *msg) "Send '%s' message" colo_receive_message(const char *msg) "Receive '%s' message" colo_failover_set_state(const char *new_state) "new state %s" + +# migration/block-dirty-bitmap.c +send_bitmap_header_enter(void) "" +send_bitmap_bits(uint32_t flags, uint64_t start_sector, uint32_t nr_sectors, uint64_t data_size) "flags: 0x%x, start_sector: %" PRIu64 ", nr_sectors: %" PRIu32 ", data_size: %" PRIu64 +dirty_bitmap_save_iterate(int in_postcopy) "in postcopy: %d" +dirty_bitmap_save_complete_enter(void) "" +dirty_bitmap_save_complete_finish(void) "" +dirty_bitmap_save_pending(uint64_t pending, uint64_t max_size) "pending %" PRIu64 " max: %" PRIu64 +dirty_bitmap_load_complete(void) "" +dirty_bitmap_load_bits_enter(uint64_t first_sector, uint32_t nr_sectors) "chunk: %" PRIu64 " %" PRIu32 +dirty_bitmap_load_bits_zeroes(void) "" +dirty_bitmap_load_header(uint32_t flags) "flags 0x%x" +dirty_bitmap_load_enter(void) "" +dirty_bitmap_load_success(void) "" @@ -983,6 +983,9 @@ static void qmp_unregister_commands_hack(void) #endif #ifndef TARGET_I386 qmp_unregister_command(&qmp_commands, "rtc-reset-reinjection"); + qmp_unregister_command(&qmp_commands, "query-sev"); + qmp_unregister_command(&qmp_commands, "query-sev-launch-measure"); + qmp_unregister_command(&qmp_commands, "query-sev-capabilities"); #endif #ifndef TARGET_S390X qmp_unregister_command(&qmp_commands, "dump-skeys"); @@ -4103,6 +4106,24 @@ void qmp_rtc_reset_reinjection(Error **errp) { error_setg(errp, QERR_FEATURE_DISABLED, "rtc-reset-reinjection"); } + +SevInfo *qmp_query_sev(Error **errp) +{ + error_setg(errp, QERR_FEATURE_DISABLED, "query-sev"); + return NULL; +} + +SevLaunchMeasureInfo *qmp_query_sev_launch_measure(Error **errp) +{ + error_setg(errp, QERR_FEATURE_DISABLED, "query-sev-launch-measure"); + return NULL; +} + +SevCapability *qmp_query_sev_capabilities(Error **errp) +{ + error_setg(errp, QERR_FEATURE_DISABLED, "query-sev-capabilities"); + return NULL; +} #endif #ifndef TARGET_S390X diff --git a/nbd/client.c b/nbd/client.c index dcad23a053..9b9b7f0ea2 100644 --- a/nbd/client.c +++ b/nbd/client.c @@ -595,6 +595,111 @@ static QIOChannel *nbd_receive_starttls(QIOChannel *ioc, return QIO_CHANNEL(tioc); } +/* nbd_negotiate_simple_meta_context: + * Set one meta context. Simple means that reply must contain zero (not + * negotiated) or one (negotiated) contexts. More contexts would be considered + * as a protocol error. It's also implied that meta-data query equals queried + * context name, so, if server replies with something different then @context, + * it considered as error too. + * return 1 for successful negotiation, context_id is set + * 0 if operation is unsupported, + * -1 with errp set for any other error + */ +static int nbd_negotiate_simple_meta_context(QIOChannel *ioc, + const char *export, + const char *context, + uint32_t *context_id, + Error **errp) +{ + int ret; + NBDOptionReply reply; + uint32_t received_id; + bool received; + uint32_t export_len = strlen(export); + uint32_t context_len = strlen(context); + uint32_t data_len = sizeof(export_len) + export_len + + sizeof(uint32_t) + /* number of queries */ + sizeof(context_len) + context_len; + char *data = g_malloc(data_len); + char *p = data; + + stl_be_p(p, export_len); + memcpy(p += sizeof(export_len), export, export_len); + stl_be_p(p += export_len, 1); + stl_be_p(p += sizeof(uint32_t), context_len); + memcpy(p += sizeof(context_len), context, context_len); + + ret = nbd_send_option_request(ioc, NBD_OPT_SET_META_CONTEXT, data_len, data, + errp); + g_free(data); + if (ret < 0) { + return ret; + } + + if (nbd_receive_option_reply(ioc, NBD_OPT_SET_META_CONTEXT, &reply, + errp) < 0) + { + return -1; + } + + ret = nbd_handle_reply_err(ioc, &reply, errp); + if (ret <= 0) { + return ret; + } + + if (reply.type == NBD_REP_META_CONTEXT) { + char *name; + size_t len; + + if (nbd_read(ioc, &received_id, sizeof(received_id), errp) < 0) { + return -1; + } + be32_to_cpus(&received_id); + + len = reply.length - sizeof(received_id); + name = g_malloc(len + 1); + if (nbd_read(ioc, name, len, errp) < 0) { + g_free(name); + return -1; + } + name[len] = '\0'; + if (strcmp(context, name)) { + error_setg(errp, "Failed to negotiate meta context '%s', server " + "answered with different context '%s'", context, + name); + g_free(name); + return -1; + } + g_free(name); + + received = true; + + /* receive NBD_REP_ACK */ + if (nbd_receive_option_reply(ioc, NBD_OPT_SET_META_CONTEXT, &reply, + errp) < 0) + { + return -1; + } + + ret = nbd_handle_reply_err(ioc, &reply, errp); + if (ret <= 0) { + return ret; + } + } + + if (reply.type != NBD_REP_ACK) { + error_setg(errp, "Unexpected reply type %" PRIx32 " expected %x", + reply.type, NBD_REP_ACK); + return -1; + } + + if (received) { + *context_id = received_id; + return 1; + } + + return 0; +} int nbd_receive_negotiate(QIOChannel *ioc, const char *name, QCryptoTLSCreds *tlscreds, const char *hostname, @@ -606,10 +711,12 @@ int nbd_receive_negotiate(QIOChannel *ioc, const char *name, int rc; bool zeroes = true; bool structured_reply = info->structured_reply; + bool base_allocation = info->base_allocation; trace_nbd_receive_negotiate(tlscreds, hostname ? hostname : "<null>"); info->structured_reply = false; + info->base_allocation = false; rc = -EINVAL; if (outioc) { @@ -700,6 +807,16 @@ int nbd_receive_negotiate(QIOChannel *ioc, const char *name, info->structured_reply = result == 1; } + if (info->structured_reply && base_allocation) { + result = nbd_negotiate_simple_meta_context( + ioc, name, "base:allocation", + &info->meta_base_allocation_id, errp); + if (result < 0) { + goto fail; + } + info->base_allocation = result == 1; + } + /* Try NBD_OPT_GO first - if it works, we are done (it * also gives us a good message if the server requires * TLS). If it is not available, fall back to diff --git a/nbd/server.c b/nbd/server.c index e714bfe6a1..cea158913b 100644 --- a/nbd/server.c +++ b/nbd/server.c @@ -22,6 +22,8 @@ #include "trace.h" #include "nbd-internal.h" +#define NBD_META_ID_BASE_ALLOCATION 0 + static int system_errno_to_nbd_errno(int err) { switch (err) { @@ -82,6 +84,16 @@ struct NBDExport { static QTAILQ_HEAD(, NBDExport) exports = QTAILQ_HEAD_INITIALIZER(exports); +/* NBDExportMetaContexts represents a list of contexts to be exported, + * as selected by NBD_OPT_SET_META_CONTEXT. Also used for + * NBD_OPT_LIST_META_CONTEXT. */ +typedef struct NBDExportMetaContexts { + char export_name[NBD_MAX_NAME_SIZE + 1]; + bool valid; /* means that negotiation of the option finished without + errors */ + bool base_allocation; /* export base:allocation context (block status) */ +} NBDExportMetaContexts; + struct NBDClient { int refcount; void (*close_fn)(NBDClient *client, bool negotiated); @@ -102,6 +114,7 @@ struct NBDClient { bool closing; bool structured_reply; + NBDExportMetaContexts export_meta; uint32_t opt; /* Current option being negotiated */ uint32_t optlen; /* remaining length of data in ioc for the option being @@ -218,22 +231,46 @@ nbd_negotiate_send_rep_err(NBDClient *client, uint32_t type, /* Drop remainder of the current option, and send a reply with the * given error type and message. Return -errno on read or write * failure; or 0 if connection is still live. */ -static int GCC_FMT_ATTR(4, 5) -nbd_opt_drop(NBDClient *client, uint32_t type, Error **errp, - const char *fmt, ...) +static int GCC_FMT_ATTR(4, 0) +nbd_opt_vdrop(NBDClient *client, uint32_t type, Error **errp, + const char *fmt, va_list va) { int ret = nbd_drop(client->ioc, client->optlen, errp); - va_list va; client->optlen = 0; if (!ret) { - va_start(va, fmt); ret = nbd_negotiate_send_rep_verr(client, type, errp, fmt, va); - va_end(va); } return ret; } +static int GCC_FMT_ATTR(4, 5) +nbd_opt_drop(NBDClient *client, uint32_t type, Error **errp, + const char *fmt, ...) +{ + int ret; + va_list va; + + va_start(va, fmt); + ret = nbd_opt_vdrop(client, type, errp, fmt, va); + va_end(va); + + return ret; +} + +static int GCC_FMT_ATTR(3, 4) +nbd_opt_invalid(NBDClient *client, Error **errp, const char *fmt, ...) +{ + int ret; + va_list va; + + va_start(va, fmt); + ret = nbd_opt_vdrop(client, NBD_REP_ERR_INVALID, errp, fmt, va); + va_end(va); + + return ret; +} + /* Read size bytes from the unparsed payload of the current option. * Return -errno on I/O error, 0 if option was completely handled by * sending a reply about inconsistent lengths, or 1 on success. */ @@ -241,14 +278,70 @@ static int nbd_opt_read(NBDClient *client, void *buffer, size_t size, Error **errp) { if (size > client->optlen) { - return nbd_opt_drop(client, NBD_REP_ERR_INVALID, errp, - "Inconsistent lengths in option %s", - nbd_opt_lookup(client->opt)); + return nbd_opt_invalid(client, errp, + "Inconsistent lengths in option %s", + nbd_opt_lookup(client->opt)); } client->optlen -= size; return qio_channel_read_all(client->ioc, buffer, size, errp) < 0 ? -EIO : 1; } +/* Drop size bytes from the unparsed payload of the current option. + * Return -errno on I/O error, 0 if option was completely handled by + * sending a reply about inconsistent lengths, or 1 on success. */ +static int nbd_opt_skip(NBDClient *client, size_t size, Error **errp) +{ + if (size > client->optlen) { + return nbd_opt_invalid(client, errp, + "Inconsistent lengths in option %s", + nbd_opt_lookup(client->opt)); + } + client->optlen -= size; + return nbd_drop(client->ioc, size, errp) < 0 ? -EIO : 1; +} + +/* nbd_opt_read_name + * + * Read a string with the format: + * uint32_t len (<= NBD_MAX_NAME_SIZE) + * len bytes string (not 0-terminated) + * + * @name should be enough to store NBD_MAX_NAME_SIZE+1. + * If @length is non-null, it will be set to the actual string length. + * + * Return -errno on I/O error, 0 if option was completely handled by + * sending a reply about inconsistent lengths, or 1 on success. + */ +static int nbd_opt_read_name(NBDClient *client, char *name, uint32_t *length, + Error **errp) +{ + int ret; + uint32_t len; + + ret = nbd_opt_read(client, &len, sizeof(len), errp); + if (ret <= 0) { + return ret; + } + cpu_to_be32s(&len); + + if (len > NBD_MAX_NAME_SIZE) { + return nbd_opt_invalid(client, errp, + "Invalid name length: %" PRIu32, len); + } + + ret = nbd_opt_read(client, name, len, errp); + if (ret <= 0) { + return ret; + } + name[len] = '\0'; + + if (length) { + *length = len; + } + + return 1; +} + /* Send a single NBD_REP_SERVER reply to NBD_OPT_LIST, including payload. * Return -errno on error, 0 on success. */ static int nbd_negotiate_send_rep_list(NBDClient *client, NBDExport *exp, @@ -306,6 +399,12 @@ static int nbd_negotiate_handle_list(NBDClient *client, Error **errp) return nbd_negotiate_send_rep(client, NBD_REP_ACK, errp); } +static void nbd_check_meta_export_name(NBDClient *client) +{ + client->export_meta.valid &= !strcmp(client->exp->name, + client->export_meta.export_name); +} + /* Send a reply to NBD_OPT_EXPORT_NAME. * Return -errno on error, 0 on success. */ static int nbd_negotiate_handle_export_name(NBDClient *client, @@ -357,6 +456,7 @@ static int nbd_negotiate_handle_export_name(NBDClient *client, QTAILQ_INSERT_TAIL(&client->exp->clients, client, next); nbd_export_get(client->exp); + nbd_check_meta_export_name(client); return 0; } @@ -398,9 +498,8 @@ static int nbd_reject_length(NBDClient *client, bool fatal, Error **errp) int ret; assert(client->optlen); - ret = nbd_opt_drop(client, NBD_REP_ERR_INVALID, errp, - "option '%s' has unexpected length", - nbd_opt_lookup(client->opt)); + ret = nbd_opt_invalid(client, errp, "option '%s' has unexpected length", + nbd_opt_lookup(client->opt)); if (fatal && !ret) { error_setg(errp, "option '%s' has unexpected length", nbd_opt_lookup(client->opt)); @@ -432,20 +531,10 @@ static int nbd_negotiate_handle_info(NBDClient *client, uint16_t myflags, 2 bytes: N, number of requests (can be 0) N * 2 bytes: N requests */ - rc = nbd_opt_read(client, &namelen, sizeof(namelen), errp); - if (rc <= 0) { - return rc; - } - be32_to_cpus(&namelen); - if (namelen >= sizeof(name)) { - return nbd_opt_drop(client, NBD_REP_ERR_INVALID, errp, - "name too long for qemu"); - } - rc = nbd_opt_read(client, name, namelen, errp); + rc = nbd_opt_read_name(client, name, &namelen, errp); if (rc <= 0) { return rc; } - name[namelen] = '\0'; trace_nbd_negotiate_handle_export_name_request(name); rc = nbd_opt_read(client, &requests, sizeof(requests), errp); @@ -561,6 +650,7 @@ static int nbd_negotiate_handle_info(NBDClient *client, uint16_t myflags, client->exp = exp; QTAILQ_INSERT_TAIL(&client->exp->clients, client, next); nbd_export_get(client->exp); + nbd_check_meta_export_name(client); rc = 1; } return rc; @@ -615,6 +705,189 @@ static QIOChannel *nbd_negotiate_handle_starttls(NBDClient *client, return QIO_CHANNEL(tioc); } +/* nbd_negotiate_send_meta_context + * + * Send one chunk of reply to NBD_OPT_{LIST,SET}_META_CONTEXT + * + * For NBD_OPT_LIST_META_CONTEXT @context_id is ignored, 0 is used instead. + */ +static int nbd_negotiate_send_meta_context(NBDClient *client, + const char *context, + uint32_t context_id, + Error **errp) +{ + NBDOptionReplyMetaContext opt; + struct iovec iov[] = { + {.iov_base = &opt, .iov_len = sizeof(opt)}, + {.iov_base = (void *)context, .iov_len = strlen(context)} + }; + + if (client->opt == NBD_OPT_LIST_META_CONTEXT) { + context_id = 0; + } + + set_be_option_rep(&opt.h, client->opt, NBD_REP_META_CONTEXT, + sizeof(opt) - sizeof(opt.h) + iov[1].iov_len); + stl_be_p(&opt.context_id, context_id); + + return qio_channel_writev_all(client->ioc, iov, 2, errp) < 0 ? -EIO : 0; +} + +/* nbd_meta_base_query + * + * Handle query to 'base' namespace. For now, only base:allocation context is + * available in it. 'len' is the amount of text remaining to be read from + * the current name, after the 'base:' portion has been stripped. + * + * Return -errno on I/O error, 0 if option was completely handled by + * sending a reply about inconsistent lengths, or 1 on success. */ +static int nbd_meta_base_query(NBDClient *client, NBDExportMetaContexts *meta, + uint32_t len, Error **errp) +{ + int ret; + char query[sizeof("allocation") - 1]; + size_t alen = strlen("allocation"); + + if (len == 0) { + if (client->opt == NBD_OPT_LIST_META_CONTEXT) { + meta->base_allocation = true; + } + return 1; + } + + if (len != alen) { + return nbd_opt_skip(client, len, errp); + } + + ret = nbd_opt_read(client, query, len, errp); + if (ret <= 0) { + return ret; + } + + if (strncmp(query, "allocation", alen) == 0) { + meta->base_allocation = true; + } + + return 1; +} + +/* nbd_negotiate_meta_query + * + * Parse namespace name and call corresponding function to parse body of the + * query. + * + * The only supported namespace now is 'base'. + * + * The function aims not wasting time and memory to read long unknown namespace + * names. + * + * Return -errno on I/O error, 0 if option was completely handled by + * sending a reply about inconsistent lengths, or 1 on success. */ +static int nbd_negotiate_meta_query(NBDClient *client, + NBDExportMetaContexts *meta, Error **errp) +{ + int ret; + char query[sizeof("base:") - 1]; + size_t baselen = strlen("base:"); + uint32_t len; + + ret = nbd_opt_read(client, &len, sizeof(len), errp); + if (ret <= 0) { + return ret; + } + cpu_to_be32s(&len); + + /* The only supported namespace for now is 'base'. So query should start + * with 'base:'. Otherwise, we can ignore it and skip the remainder. */ + if (len < baselen) { + return nbd_opt_skip(client, len, errp); + } + + len -= baselen; + ret = nbd_opt_read(client, query, baselen, errp); + if (ret <= 0) { + return ret; + } + if (strncmp(query, "base:", baselen) != 0) { + return nbd_opt_skip(client, len, errp); + } + + return nbd_meta_base_query(client, meta, len, errp); +} + +/* nbd_negotiate_meta_queries + * Handle NBD_OPT_LIST_META_CONTEXT and NBD_OPT_SET_META_CONTEXT + * + * Return -errno on I/O error, or 0 if option was completely handled. */ +static int nbd_negotiate_meta_queries(NBDClient *client, + NBDExportMetaContexts *meta, Error **errp) +{ + int ret; + NBDExport *exp; + NBDExportMetaContexts local_meta; + uint32_t nb_queries; + int i; + + if (!client->structured_reply) { + return nbd_opt_invalid(client, errp, + "request option '%s' when structured reply " + "is not negotiated", + nbd_opt_lookup(client->opt)); + } + + if (client->opt == NBD_OPT_LIST_META_CONTEXT) { + /* Only change the caller's meta on SET. */ + meta = &local_meta; + } + + memset(meta, 0, sizeof(*meta)); + + ret = nbd_opt_read_name(client, meta->export_name, NULL, errp); + if (ret <= 0) { + return ret; + } + + exp = nbd_export_find(meta->export_name); + if (exp == NULL) { + return nbd_opt_drop(client, NBD_REP_ERR_UNKNOWN, errp, + "export '%s' not present", meta->export_name); + } + + ret = nbd_opt_read(client, &nb_queries, sizeof(nb_queries), errp); + if (ret <= 0) { + return ret; + } + cpu_to_be32s(&nb_queries); + + if (client->opt == NBD_OPT_LIST_META_CONTEXT && !nb_queries) { + /* enable all known contexts */ + meta->base_allocation = true; + } else { + for (i = 0; i < nb_queries; ++i) { + ret = nbd_negotiate_meta_query(client, meta, errp); + if (ret <= 0) { + return ret; + } + } + } + + if (meta->base_allocation) { + ret = nbd_negotiate_send_meta_context(client, "base:allocation", + NBD_META_ID_BASE_ALLOCATION, + errp); + if (ret < 0) { + return ret; + } + } + + ret = nbd_negotiate_send_rep(client, NBD_REP_ACK, errp); + if (ret == 0) { + meta->valid = true; + } + + return ret; +} + /* nbd_negotiate_options * Process all NBD_OPT_* client option commands, during fixed newstyle * negotiation. @@ -805,6 +1078,12 @@ static int nbd_negotiate_options(NBDClient *client, uint16_t myflags, } break; + case NBD_OPT_LIST_META_CONTEXT: + case NBD_OPT_SET_META_CONTEXT: + ret = nbd_negotiate_meta_queries(client, &client->export_meta, + errp); + break; + default: ret = nbd_opt_drop(client, NBD_REP_ERR_UNSUP, errp, "Unsupported option %" PRIu32 " (%s)", @@ -1342,6 +1621,34 @@ static int coroutine_fn nbd_co_send_structured_read(NBDClient *client, return nbd_co_send_iov(client, iov, 2, errp); } +static int coroutine_fn nbd_co_send_structured_error(NBDClient *client, + uint64_t handle, + uint32_t error, + const char *msg, + Error **errp) +{ + NBDStructuredError chunk; + int nbd_err = system_errno_to_nbd_errno(error); + struct iovec iov[] = { + {.iov_base = &chunk, .iov_len = sizeof(chunk)}, + {.iov_base = (char *)msg, .iov_len = msg ? strlen(msg) : 0}, + }; + + assert(nbd_err); + trace_nbd_co_send_structured_error(handle, nbd_err, + nbd_err_lookup(nbd_err), msg ? msg : ""); + set_be_chunk(&chunk.h, NBD_REPLY_FLAG_DONE, NBD_REPLY_TYPE_ERROR, handle, + sizeof(chunk) - sizeof(chunk.h) + iov[1].iov_len); + stl_be_p(&chunk.error, nbd_err); + stw_be_p(&chunk.message_length, iov[1].iov_len); + + return nbd_co_send_iov(client, iov, 1 + !!iov[1].iov_len, errp); +} + +/* Do a sparse read and send the structured reply to the client. + * Returns -errno if sending fails. bdrv_block_status_above() failure is + * reported to the client, at which point this function succeeds. + */ static int coroutine_fn nbd_co_send_sparse_read(NBDClient *client, uint64_t handle, uint64_t offset, @@ -1362,8 +1669,13 @@ static int coroutine_fn nbd_co_send_sparse_read(NBDClient *client, bool final; if (status < 0) { - error_setg_errno(errp, -status, "unable to check for holes"); - return status; + char *msg = g_strdup_printf("unable to check for holes: %s", + strerror(-status)); + + ret = nbd_co_send_structured_error(client, handle, -status, msg, + errp); + g_free(msg); + return ret; } assert(pnum && pnum <= size - progress); final = progress + pnum == size; @@ -1401,28 +1713,77 @@ static int coroutine_fn nbd_co_send_sparse_read(NBDClient *client, return ret; } -static int coroutine_fn nbd_co_send_structured_error(NBDClient *client, - uint64_t handle, - uint32_t error, - const char *msg, - Error **errp) +static int blockstatus_to_extent_be(BlockDriverState *bs, uint64_t offset, + uint64_t bytes, NBDExtent *extent) { - NBDStructuredError chunk; - int nbd_err = system_errno_to_nbd_errno(error); + uint64_t remaining_bytes = bytes; + + while (remaining_bytes) { + uint32_t flags; + int64_t num; + int ret = bdrv_block_status_above(bs, NULL, offset, remaining_bytes, + &num, NULL, NULL); + if (ret < 0) { + return ret; + } + + flags = (ret & BDRV_BLOCK_ALLOCATED ? 0 : NBD_STATE_HOLE) | + (ret & BDRV_BLOCK_ZERO ? NBD_STATE_ZERO : 0); + + if (remaining_bytes == bytes) { + extent->flags = flags; + } + + if (flags != extent->flags) { + break; + } + + offset += num; + remaining_bytes -= num; + } + + cpu_to_be32s(&extent->flags); + extent->length = cpu_to_be32(bytes - remaining_bytes); + + return 0; +} + +/* nbd_co_send_extents + * @extents should be in big-endian */ +static int nbd_co_send_extents(NBDClient *client, uint64_t handle, + NBDExtent *extents, unsigned nb_extents, + uint32_t context_id, Error **errp) +{ + NBDStructuredMeta chunk; + struct iovec iov[] = { {.iov_base = &chunk, .iov_len = sizeof(chunk)}, - {.iov_base = (char *)msg, .iov_len = msg ? strlen(msg) : 0}, + {.iov_base = extents, .iov_len = nb_extents * sizeof(extents[0])} }; - assert(nbd_err); - trace_nbd_co_send_structured_error(handle, nbd_err, - nbd_err_lookup(nbd_err), msg ? msg : ""); - set_be_chunk(&chunk.h, NBD_REPLY_FLAG_DONE, NBD_REPLY_TYPE_ERROR, handle, - sizeof(chunk) - sizeof(chunk.h) + iov[1].iov_len); - stl_be_p(&chunk.error, nbd_err); - stw_be_p(&chunk.message_length, iov[1].iov_len); + set_be_chunk(&chunk.h, NBD_REPLY_FLAG_DONE, NBD_REPLY_TYPE_BLOCK_STATUS, + handle, sizeof(chunk) - sizeof(chunk.h) + iov[1].iov_len); + stl_be_p(&chunk.context_id, context_id); - return nbd_co_send_iov(client, iov, 1 + !!iov[1].iov_len, errp); + return nbd_co_send_iov(client, iov, 2, errp); +} + +/* Get block status from the exported device and send it to the client */ +static int nbd_co_send_block_status(NBDClient *client, uint64_t handle, + BlockDriverState *bs, uint64_t offset, + uint64_t length, uint32_t context_id, + Error **errp) +{ + int ret; + NBDExtent extent; + + ret = blockstatus_to_extent_be(bs, offset, length, &extent); + if (ret < 0) { + return nbd_co_send_structured_error( + client, handle, -ret, "can't get block status", errp); + } + + return nbd_co_send_extents(client, handle, &extent, 1, context_id, errp); } /* nbd_co_receive_request @@ -1502,6 +1863,8 @@ static int nbd_co_receive_request(NBDRequestData *req, NBDRequest *request, valid_flags |= NBD_CMD_FLAG_DF; } else if (request->type == NBD_CMD_WRITE_ZEROES) { valid_flags |= NBD_CMD_FLAG_NO_HOLE; + } else if (request->type == NBD_CMD_BLOCK_STATUS) { + valid_flags |= NBD_CMD_FLAG_REQ_ONE; } if (request->flags & ~valid_flags) { error_setg(errp, "unsupported flags for command %s (got 0x%x)", @@ -1512,159 +1875,195 @@ static int nbd_co_receive_request(NBDRequestData *req, NBDRequest *request, return 0; } -/* Owns a reference to the NBDClient passed as opaque. */ -static coroutine_fn void nbd_trip(void *opaque) +/* Send simple reply without a payload, or a structured error + * @error_msg is ignored if @ret >= 0 + * Returns 0 if connection is still live, -errno on failure to talk to client + */ +static coroutine_fn int nbd_send_generic_reply(NBDClient *client, + uint64_t handle, + int ret, + const char *error_msg, + Error **errp) +{ + if (client->structured_reply && ret < 0) { + return nbd_co_send_structured_error(client, handle, -ret, error_msg, + errp); + } else { + return nbd_co_send_simple_reply(client, handle, ret < 0 ? -ret : 0, + NULL, 0, errp); + } +} + +/* Handle NBD_CMD_READ request. + * Return -errno if sending fails. Other errors are reported directly to the + * client as an error reply. */ +static coroutine_fn int nbd_do_cmd_read(NBDClient *client, NBDRequest *request, + uint8_t *data, Error **errp) { - NBDClient *client = opaque; - NBDExport *exp = client->exp; - NBDRequestData *req; - NBDRequest request = { 0 }; /* GCC thinks it can be used uninitialized */ int ret; - int flags; - int reply_data_len = 0; - Error *local_err = NULL; - char *msg = NULL; + NBDExport *exp = client->exp; - trace_nbd_trip(); - if (client->closing) { - nbd_client_put(client); - return; - } + assert(request->type == NBD_CMD_READ); - req = nbd_request_get(client); - ret = nbd_co_receive_request(req, &request, &local_err); - client->recv_coroutine = NULL; - nbd_client_receive_next_request(client); - if (ret == -EIO) { - goto disconnect; + /* XXX: NBD Protocol only documents use of FUA with WRITE */ + if (request->flags & NBD_CMD_FLAG_FUA) { + ret = blk_co_flush(exp->blk); + if (ret < 0) { + return nbd_send_generic_reply(client, request->handle, ret, + "flush failed", errp); + } } - if (ret < 0) { - goto reply; + if (client->structured_reply && !(request->flags & NBD_CMD_FLAG_DF) && + request->len) { + return nbd_co_send_sparse_read(client, request->handle, request->from, + data, request->len, errp); } - if (client->closing) { - /* - * The client may be closed when we are blocked in - * nbd_co_receive_request() - */ - goto done; + ret = blk_pread(exp->blk, request->from + exp->dev_offset, data, + request->len); + if (ret < 0) { + return nbd_send_generic_reply(client, request->handle, ret, + "reading from file failed", errp); } - switch (request.type) { - case NBD_CMD_READ: - /* XXX: NBD Protocol only documents use of FUA with WRITE */ - if (request.flags & NBD_CMD_FLAG_FUA) { - ret = blk_co_flush(exp->blk); - if (ret < 0) { - error_setg_errno(&local_err, -ret, "flush failed"); - break; - } - } - - if (client->structured_reply && !(request.flags & NBD_CMD_FLAG_DF) && - request.len) { - ret = nbd_co_send_sparse_read(req->client, request.handle, - request.from, req->data, request.len, - &local_err); - if (ret < 0) { - goto reply; - } - goto done; + if (client->structured_reply) { + if (request->len) { + return nbd_co_send_structured_read(client, request->handle, + request->from, data, + request->len, true, errp); + } else { + return nbd_co_send_structured_done(client, request->handle, errp); } + } else { + return nbd_co_send_simple_reply(client, request->handle, 0, + data, request->len, errp); + } +} - ret = blk_pread(exp->blk, request.from + exp->dev_offset, - req->data, request.len); - if (ret < 0) { - error_setg_errno(&local_err, -ret, "reading from file failed"); - break; - } +/* Handle NBD request. + * Return -errno if sending fails. Other errors are reported directly to the + * client as an error reply. */ +static coroutine_fn int nbd_handle_request(NBDClient *client, + NBDRequest *request, + uint8_t *data, Error **errp) +{ + int ret; + int flags; + NBDExport *exp = client->exp; + char *msg; - reply_data_len = request.len; + switch (request->type) { + case NBD_CMD_READ: + return nbd_do_cmd_read(client, request, data, errp); - break; case NBD_CMD_WRITE: flags = 0; - if (request.flags & NBD_CMD_FLAG_FUA) { + if (request->flags & NBD_CMD_FLAG_FUA) { flags |= BDRV_REQ_FUA; } - ret = blk_pwrite(exp->blk, request.from + exp->dev_offset, - req->data, request.len, flags); - if (ret < 0) { - error_setg_errno(&local_err, -ret, "writing to file failed"); - } + ret = blk_pwrite(exp->blk, request->from + exp->dev_offset, + data, request->len, flags); + return nbd_send_generic_reply(client, request->handle, ret, + "writing to file failed", errp); - break; case NBD_CMD_WRITE_ZEROES: flags = 0; - if (request.flags & NBD_CMD_FLAG_FUA) { + if (request->flags & NBD_CMD_FLAG_FUA) { flags |= BDRV_REQ_FUA; } - if (!(request.flags & NBD_CMD_FLAG_NO_HOLE)) { + if (!(request->flags & NBD_CMD_FLAG_NO_HOLE)) { flags |= BDRV_REQ_MAY_UNMAP; } - ret = blk_pwrite_zeroes(exp->blk, request.from + exp->dev_offset, - request.len, flags); - if (ret < 0) { - error_setg_errno(&local_err, -ret, "writing to file failed"); - } + ret = blk_pwrite_zeroes(exp->blk, request->from + exp->dev_offset, + request->len, flags); + return nbd_send_generic_reply(client, request->handle, ret, + "writing to file failed", errp); - break; case NBD_CMD_DISC: /* unreachable, thanks to special case in nbd_co_receive_request() */ abort(); case NBD_CMD_FLUSH: ret = blk_co_flush(exp->blk); - if (ret < 0) { - error_setg_errno(&local_err, -ret, "flush failed"); - } + return nbd_send_generic_reply(client, request->handle, ret, + "flush failed", errp); - break; case NBD_CMD_TRIM: - ret = blk_co_pdiscard(exp->blk, request.from + exp->dev_offset, - request.len); - if (ret < 0) { - error_setg_errno(&local_err, -ret, "discard failed"); + ret = blk_co_pdiscard(exp->blk, request->from + exp->dev_offset, + request->len); + if (ret == 0 && request->flags & NBD_CMD_FLAG_FUA) { + ret = blk_co_flush(exp->blk); + } + return nbd_send_generic_reply(client, request->handle, ret, + "discard failed", errp); + + case NBD_CMD_BLOCK_STATUS: + if (client->export_meta.valid && client->export_meta.base_allocation) { + return nbd_co_send_block_status(client, request->handle, + blk_bs(exp->blk), request->from, + request->len, + NBD_META_ID_BASE_ALLOCATION, errp); + } else { + return nbd_send_generic_reply(client, request->handle, -EINVAL, + "CMD_BLOCK_STATUS not negotiated", + errp); } - break; default: - error_setg(&local_err, "invalid request type (%" PRIu32 ") received", - request.type); - ret = -EINVAL; + msg = g_strdup_printf("invalid request type (%" PRIu32 ") received", + request->type); + ret = nbd_send_generic_reply(client, request->handle, -EINVAL, msg, + errp); + g_free(msg); + return ret; } +} -reply: - if (local_err) { - /* If we get here, local_err was not a fatal error, and should be sent - * to the client. */ - assert(ret < 0); - msg = g_strdup(error_get_pretty(local_err)); - error_report_err(local_err); - local_err = NULL; +/* Owns a reference to the NBDClient passed as opaque. */ +static coroutine_fn void nbd_trip(void *opaque) +{ + NBDClient *client = opaque; + NBDRequestData *req; + NBDRequest request = { 0 }; /* GCC thinks it can be used uninitialized */ + int ret; + Error *local_err = NULL; + + trace_nbd_trip(); + if (client->closing) { + nbd_client_put(client); + return; } - if (client->structured_reply && - (ret < 0 || request.type == NBD_CMD_READ)) { - if (ret < 0) { - ret = nbd_co_send_structured_error(req->client, request.handle, - -ret, msg, &local_err); - } else if (reply_data_len) { - ret = nbd_co_send_structured_read(req->client, request.handle, - request.from, req->data, - reply_data_len, true, - &local_err); - } else { - ret = nbd_co_send_structured_done(req->client, request.handle, - &local_err); - } + req = nbd_request_get(client); + ret = nbd_co_receive_request(req, &request, &local_err); + client->recv_coroutine = NULL; + + if (client->closing) { + /* + * The client may be closed when we are blocked in + * nbd_co_receive_request() + */ + goto done; + } + + nbd_client_receive_next_request(client); + if (ret == -EIO) { + goto disconnect; + } + + if (ret < 0) { + /* It wans't -EIO, so, according to nbd_co_receive_request() + * semantics, we should return the error to the client. */ + Error *export_err = local_err; + + local_err = NULL; + ret = nbd_send_generic_reply(client, request.handle, -EINVAL, + error_get_pretty(export_err), &local_err); + error_free(export_err); } else { - ret = nbd_co_send_simple_reply(req->client, request.handle, - ret < 0 ? -ret : 0, - req->data, reply_data_len, &local_err); + ret = nbd_handle_request(client, &request, req->data, &local_err); } - g_free(msg); if (ret < 0) { error_prepend(&local_err, "Failed to send reply: "); goto disconnect; diff --git a/qapi/block-core.json b/qapi/block-core.json index 524d51567a..2b378f510a 100644 --- a/qapi/block-core.json +++ b/qapi/block-core.json @@ -426,10 +426,13 @@ # @active: The bitmap is actively monitoring for new writes, and can be cleared, # deleted, or used for backup operations. # +# @locked: The bitmap is currently in-use by some operation and can not be +# cleared, deleted, or used for backup operations. (Since 2.12) +# # Since: 2.4 ## { 'enum': 'DirtyBitmapStatus', - 'data': ['active', 'disabled', 'frozen'] } + 'data': ['active', 'disabled', 'frozen', 'locked'] } ## # @BlockDirtyInfo: diff --git a/qapi/migration.json b/qapi/migration.json index 7f465a1902..9d0bf82cf4 100644 --- a/qapi/migration.json +++ b/qapi/migration.json @@ -354,12 +354,16 @@ # # @x-multifd: Use more than one fd for migration (since 2.11) # +# @dirty-bitmaps: If enabled, QEMU will migrate named dirty bitmaps. +# (since 2.12) +# # Since: 1.2 ## { 'enum': 'MigrationCapability', 'data': ['xbzrle', 'rdma-pin-all', 'auto-converge', 'zero-blocks', 'compress', 'events', 'postcopy-ram', 'x-colo', 'release-ram', - 'block', 'return-path', 'pause-before-switchover', 'x-multifd' ] } + 'block', 'return-path', 'pause-before-switchover', 'x-multifd', + 'dirty-bitmaps' ] } ## # @MigrationCapabilityStatus: diff --git a/qapi/misc.json b/qapi/misc.json index bcd5d10778..6150b9a003 100644 --- a/qapi/misc.json +++ b/qapi/misc.json @@ -3216,3 +3216,151 @@ # Since: 2.9 ## { 'command': 'query-vm-generation-id', 'returns': 'GuidInfo' } + + +## +# @SevState: +# +# An enumeration of SEV state information used during @query-sev. +# +# @uninit: The guest is uninitialized. +# +# @launch-update: The guest is currently being launched; plaintext data and +# register state is being imported. +# +# @launch-secret: The guest is currently being launched; ciphertext data +# is being imported. +# +# @running: The guest is fully launched or migrated in. +# +# @send-update: The guest is currently being migrated out to another machine. +# +# @receive-update: The guest is currently being migrated from another machine. +# +# Since: 2.12 +## +{ 'enum': 'SevState', + 'data': ['uninit', 'launch-update', 'launch-secret', 'running', + 'send-update', 'receive-update' ] } + +## +# @SevInfo: +# +# Information about Secure Encrypted Virtualization (SEV) support +# +# @enabled: true if SEV is active +# +# @api-major: SEV API major version +# +# @api-minor: SEV API minor version +# +# @build-id: SEV FW build id +# +# @policy: SEV policy value +# +# @state: SEV guest state +# +# @handle: SEV firmware handle +# +# Since: 2.12 +## +{ 'struct': 'SevInfo', + 'data': { 'enabled': 'bool', + 'api-major': 'uint8', + 'api-minor' : 'uint8', + 'build-id' : 'uint8', + 'policy' : 'uint32', + 'state' : 'SevState', + 'handle' : 'uint32' + } +} + +## +# @query-sev: +# +# Returns information about SEV +# +# Returns: @SevInfo +# +# Since: 2.12 +# +# Example: +# +# -> { "execute": "query-sev" } +# <- { "return": { "enabled": true, "api-major" : 0, "api-minor" : 0, +# "build-id" : 0, "policy" : 0, "state" : "running", +# "handle" : 1 } } +# +## +{ 'command': 'query-sev', 'returns': 'SevInfo' } + +## +# @SevLaunchMeasureInfo: +# +# SEV Guest Launch measurement information +# +# @data: the measurement value encoded in base64 +# +# Since: 2.12 +# +## +{ 'struct': 'SevLaunchMeasureInfo', 'data': {'data': 'str'} } + +## +# @query-sev-launch-measure: +# +# Query the SEV guest launch information. +# +# Returns: The @SevLaunchMeasureInfo for the guest +# +# Since: 2.12 +# +# Example: +# +# -> { "execute": "query-sev-launch-measure" } +# <- { "return": { "data": "4l8LXeNlSPUDlXPJG5966/8%YZ" } } +# +## +{ 'command': 'query-sev-launch-measure', 'returns': 'SevLaunchMeasureInfo' } + +## +# @SevCapability: +# +# The struct describes capability for a Secure Encrypted Virtualization +# feature. +# +# @pdh: Platform Diffie-Hellman key (base64 encoded) +# +# @cert-chain: PDH certificate chain (base64 encoded) +# +# @cbitpos: C-bit location in page table entry +# +# @reduced-phys-bits: Number of physical Address bit reduction when SEV is +# enabled +# +# Since: 2.12 +## +{ 'struct': 'SevCapability', + 'data': { 'pdh': 'str', + 'cert-chain': 'str', + 'cbitpos': 'int', + 'reduced-phys-bits': 'int'} } + +## +# @query-sev-capabilities: +# +# This command is used to get the SEV capabilities, and is supported on AMD +# X86 platforms only. +# +# Returns: SevCapability objects. +# +# Since: 2.12 +# +# Example: +# +# -> { "execute": "query-sev-capabilities" } +# <- { "return": { "pdh": "8CCDD8DDD", "cert-chain": "888CCCDDDEE", +# "cbitpos": 47, "reduced-phys-bits": 5}} +# +## +{ 'command': 'query-sev-capabilities', 'returns': 'SevCapability' } diff --git a/qapi/sockets.json b/qapi/sockets.json index ac022c6ad0..fc81d8d5e8 100644 --- a/qapi/sockets.json +++ b/qapi/sockets.json @@ -123,6 +123,13 @@ # # @unix: Unix domain socket # +# @vsock: VMCI address +# +# @fd: decimal is for file descriptor number, otherwise a file descriptor name. +# Named file descriptors are permitted in monitor commands, in combination +# with the 'getfd' command. Decimal file descriptors are permitted at +# startup or other contexts where no monitor context is active. +# # Since: 2.9 ## { 'enum': 'SocketAddressType', diff --git a/qdev-monitor.c b/qdev-monitor.c index b7e3291f8b..61e0300991 100644 --- a/qdev-monitor.c +++ b/qdev-monitor.c @@ -122,12 +122,6 @@ static void qdev_print_devinfo(DeviceClass *dc) error_printf("\n"); } -static gint devinfo_cmp(gconstpointer a, gconstpointer b) -{ - return strcasecmp(object_class_get_name((ObjectClass *)a), - object_class_get_name((ObjectClass *)b)); -} - static void qdev_print_devinfos(bool show_no_user) { static const char *cat_name[DEVICE_CATEGORY_MAX + 1] = { @@ -146,8 +140,7 @@ static void qdev_print_devinfos(bool show_no_user) int i; bool cat_printed; - list = g_slist_sort(object_class_get_list(TYPE_DEVICE, false), - devinfo_cmp); + list = object_class_get_list_sorted(TYPE_DEVICE, false); for (i = 0; i <= DEVICE_CATEGORY_MAX; i++) { cat_printed = false; diff --git a/qemu-doc.texi b/qemu-doc.texi index 39e38c87ec..f8da9b8135 100644 --- a/qemu-doc.texi +++ b/qemu-doc.texi @@ -2643,42 +2643,46 @@ combined with ``-vnc tls-creds=tls0' @subsection -tftp (since 2.6.0) -The ``-tftp /some/dir'' argument is replaced by -``-netdev user,id=x,tftp=/some/dir'', either accompanied with -``-device ...,netdev=x'' (for pluggable NICs) or ``-net nic,netdev=x'' +The ``-tftp /some/dir'' argument is replaced by either +``-netdev user,id=x,tftp=/some/dir '' (for pluggable NICs, accompanied +with ``-device ...,netdev=x''), or ``-nic user,tftp=/some/dir'' (for embedded NICs). The new syntax allows different settings to be provided per NIC. @subsection -bootp (since 2.6.0) -The ``-bootp /some/file'' argument is replaced by -``-netdev user,id=x,bootp=/some/file'', either accompanied with -``-device ...,netdev=x'' (for pluggable NICs) or ``-net nic,netdev=x'' +The ``-bootp /some/file'' argument is replaced by either +``-netdev user,id=x,bootp=/some/file '' (for pluggable NICs, accompanied +with ``-device ...,netdev=x''), or ``-nic user,bootp=/some/file'' (for embedded NICs). The new syntax allows different settings to be provided per NIC. @subsection -redir (since 2.6.0) The ``-redir [tcp|udp]:hostport:[guestaddr]:guestport'' argument is -replaced by ``-netdev -user,id=x,hostfwd=[tcp|udp]:[hostaddr]:hostport-[guestaddr]:guestport'', -either accompanied with ``-device ...,netdev=x'' (for pluggable NICs) or -``-net nic,netdev=x'' (for embedded NICs). The new syntax allows different -settings to be provided per NIC. +replaced by either +``-netdev user,id=x,hostfwd=[tcp|udp]:[hostaddr]:hostport-[guestaddr]:guestport'' +(for pluggable NICs, accompanied with ``-device ...,netdev=x'') or +``-nic user,hostfwd=[tcp|udp]:[hostaddr]:hostport-[guestaddr]:guestport'' +(for embedded NICs). The new syntax allows different settings to be +provided per NIC. @subsection -smb (since 2.6.0) -The ``-smb /some/dir'' argument is replaced by -``-netdev user,id=x,smb=/some/dir'', either accompanied with -``-device ...,netdev=x'' (for pluggable NICs) or ``-net nic,netdev=x'' +The ``-smb /some/dir'' argument is replaced by either +``-netdev user,id=x,smb=/some/dir '' (for pluggable NICs, accompanied +with ``-device ...,netdev=x''), or ``-nic user,smb=/some/dir'' (for embedded NICs). The new syntax allows different settings to be provided per NIC. @subsection -net vlan (since 2.9.0) -The ``-net vlan=NN'' argument is partially replaced with the -new ``-netdev'' argument. The remaining use cases will no -longer be directly supported in QEMU. +The ``-net vlan=NN'' argument was mostly used to attach separate +network backends to different virtual NICs. This is the default +behavior for ``-netdev'' and ``-nic''. You can connect multiple +``-netdev'' and ``-nic'' devices to the same network using the +"hubport" network backend, created with ``-netdev hubport,hubid=NN,...'' +and ``-nic hubport,hubid=NN''. @subsection -drive cyls=...,heads=...,secs=...,trans=... (since 2.10.0) @@ -2728,6 +2732,12 @@ filesystem test suite. Also it requires the CAP_DAC_READ_SEARCH capability, which is not the recommended way to run QEMU. This backend should not be used and it will be removed with no replacement. +@subsection -no-frame (since 2.12.0) + +The @code{--no-frame} argument works with SDL 1.2 only. The other user +interfaces never implemented this in the first place. So this will be +removed together with SDL 1.2 support. + @subsection -rtc-td-hack (since 2.12.0) The @code{-rtc-td-hack} option has been replaced by diff --git a/qemu-img.c b/qemu-img.c index 088d89043e..855fa52514 100644 --- a/qemu-img.c +++ b/qemu-img.c @@ -46,7 +46,7 @@ #include "crypto/init.h" #include "trace/control.h" -#define QEMU_IMG_VERSION "qemu-img version " QEMU_VERSION QEMU_PKGVERSION \ +#define QEMU_IMG_VERSION "qemu-img version " QEMU_FULL_VERSION \ "\n" QEMU_COPYRIGHT "\n" typedef struct img_cmd_t { @@ -558,7 +558,7 @@ int main(int argc, char **argv) trace_file = trace_opt_parse(optarg); break; case 'V': - printf("%s version " QEMU_VERSION QEMU_PKGVERSION "\n" + printf("%s version " QEMU_FULL_VERSION "\n" QEMU_COPYRIGHT "\n", progname); exit(0); case 'h': diff --git a/qemu-nbd.c b/qemu-nbd.c index ed5d9b5062..0af0560ad1 100644 --- a/qemu-nbd.c +++ b/qemu-nbd.c @@ -130,7 +130,7 @@ QEMU_HELP_BOTTOM "\n" static void version(const char *name) { printf( -"%s " QEMU_VERSION QEMU_PKGVERSION "\n" +"%s " QEMU_FULL_VERSION "\n" "Written by Anthony Liguori.\n" "\n" QEMU_COPYRIGHT "\n" diff --git a/qemu-options.hx b/qemu-options.hx index 6585058c6c..6113bce08a 100644 --- a/qemu-options.hx +++ b/qemu-options.hx @@ -43,7 +43,8 @@ DEF("machine", HAS_ARG, QEMU_OPTION_machine, \ " suppress-vmdesc=on|off disables self-describing migration (default=off)\n" " nvdimm=on|off controls NVDIMM support (default=off)\n" " enforce-config-section=on|off enforce configuration section migration (default=off)\n" - " s390-squash-mcss=on|off (deprecated) controls support for squashing into default css (default=off)\n", + " s390-squash-mcss=on|off (deprecated) controls support for squashing into default css (default=off)\n" + " memory-encryption=@var{} memory encryption object to use (default=none)\n", QEMU_ARCH_ALL) STEXI @item -machine [type=]@var{name}[,prop=@var{value}[,...]] @@ -110,6 +111,8 @@ code to send configuration section even if the machine-type sets the @option{migration.send-configuration} property to @var{off}. NOTE: this parameter is deprecated. Please use @option{-global} @option{migration.send-configuration}=@var{on|off} instead. +@item memory-encryption=@var{} +Memory encryption object to use. The default is none. @end table ETEXI @@ -4350,6 +4353,50 @@ contents of @code{iv.b64} to the second secret data=$SECRET,iv=$(<iv.b64) @end example +@item -object sev-guest,id=@var{id},cbitpos=@var{cbitpos},reduced-phys-bits=@var{val},[sev-device=@var{string},policy=@var{policy},handle=@var{handle},dh-cert-file=@var{file},session-file=@var{file}] + +Create a Secure Encrypted Virtualization (SEV) guest object, which can be used +to provide the guest memory encryption support on AMD processors. + +When memory encryption is enabled, one of the physical address bit (aka the +C-bit) is utilized to mark if a memory page is protected. The @option{cbitpos} +is used to provide the C-bit position. The C-bit position is Host family dependent +hence user must provide this value. On EPYC, the value should be 47. + +When memory encryption is enabled, we loose certain bits in physical address space. +The @option{reduced-phys-bits} is used to provide the number of bits we loose in +physical address space. Similar to C-bit, the value is Host family dependent. +On EPYC, the value should be 5. + +The @option{sev-device} provides the device file to use for communicating with +the SEV firmware running inside AMD Secure Processor. The default device is +'/dev/sev'. If hardware supports memory encryption then /dev/sev devices are +created by CCP driver. + +The @option{policy} provides the guest policy to be enforced by the SEV firmware +and restrict what configuration and operational commands can be performed on this +guest by the hypervisor. The policy should be provided by the guest owner and is +bound to the guest and cannot be changed throughout the lifetime of the guest. +The default is 0. + +If guest @option{policy} allows sharing the key with another SEV guest then +@option{handle} can be use to provide handle of the guest from which to share +the key. + +The @option{dh-cert-file} and @option{session-file} provides the guest owner's +Public Diffie-Hillman key defined in SEV spec. The PDH and session parameters +are used for establishing a cryptographic session with the guest owner to +negotiate keys used for attestation. The file must be encoded in base64. + +e.g to launch a SEV guest +@example + # $QEMU \ + ...... + -object sev-guest,id=sev0,cbitpos=47,reduced-phys-bits=5 \ + -machine ...,memory-encryption=sev0 + ..... + +@end example @end table ETEXI diff --git a/qga/main.c b/qga/main.c index f9c83050c5..837a75a46a 100644 --- a/qga/main.c +++ b/qga/main.c @@ -218,7 +218,7 @@ static void usage(const char *cmd) { printf( "Usage: %s [-m <method> -p <path>] [<options>]\n" -"QEMU Guest Agent " QEMU_VERSION QEMU_PKGVERSION "\n" +"QEMU Guest Agent " QEMU_FULL_VERSION "\n" QEMU_COPYRIGHT "\n" "\n" " -m, --method transport method: one of unix-listen, virtio-serial,\n" diff --git a/qom/object.c b/qom/object.c index 755ad03819..6088f55943 100644 --- a/qom/object.c +++ b/qom/object.c @@ -891,6 +891,19 @@ GSList *object_class_get_list(const char *implements_type, return list; } +static gint object_class_cmp(gconstpointer a, gconstpointer b) +{ + return strcasecmp(object_class_get_name((ObjectClass *)a), + object_class_get_name((ObjectClass *)b)); +} + +GSList *object_class_get_list_sorted(const char *implements_type, + bool include_abstract) +{ + return g_slist_sort(object_class_get_list(implements_type, include_abstract), + object_class_cmp); +} + void object_ref(Object *obj) { if (!obj) { diff --git a/replay/replay-audio.c b/replay/replay-audio.c index 3d837434d4..b113836de4 100644 --- a/replay/replay-audio.c +++ b/replay/replay-audio.c @@ -19,20 +19,17 @@ void replay_audio_out(int *played) { if (replay_mode == REPLAY_MODE_RECORD) { + g_assert(replay_mutex_locked()); replay_save_instructions(); - replay_mutex_lock(); replay_put_event(EVENT_AUDIO_OUT); replay_put_dword(*played); - replay_mutex_unlock(); } else if (replay_mode == REPLAY_MODE_PLAY) { + g_assert(replay_mutex_locked()); replay_account_executed_instructions(); - replay_mutex_lock(); if (replay_next_event_is(EVENT_AUDIO_OUT)) { *played = replay_get_dword(); replay_finish_event(); - replay_mutex_unlock(); } else { - replay_mutex_unlock(); error_report("Missing audio out event in the replay log"); abort(); } @@ -44,8 +41,8 @@ void replay_audio_in(int *recorded, void *samples, int *wpos, int size) int pos; uint64_t left, right; if (replay_mode == REPLAY_MODE_RECORD) { + g_assert(replay_mutex_locked()); replay_save_instructions(); - replay_mutex_lock(); replay_put_event(EVENT_AUDIO_IN); replay_put_dword(*recorded); replay_put_dword(*wpos); @@ -55,10 +52,9 @@ void replay_audio_in(int *recorded, void *samples, int *wpos, int size) replay_put_qword(left); replay_put_qword(right); } - replay_mutex_unlock(); } else if (replay_mode == REPLAY_MODE_PLAY) { + g_assert(replay_mutex_locked()); replay_account_executed_instructions(); - replay_mutex_lock(); if (replay_next_event_is(EVENT_AUDIO_IN)) { *recorded = replay_get_dword(); *wpos = replay_get_dword(); @@ -69,9 +65,7 @@ void replay_audio_in(int *recorded, void *samples, int *wpos, int size) audio_sample_from_uint64(samples, pos, left, right); } replay_finish_event(); - replay_mutex_unlock(); } else { - replay_mutex_unlock(); error_report("Missing audio in event in the replay log"); abort(); } diff --git a/replay/replay-char.c b/replay/replay-char.c index cbf7c04a9f..736cc8c2e6 100755 --- a/replay/replay-char.c +++ b/replay/replay-char.c @@ -96,25 +96,24 @@ void *replay_event_char_read_load(void) void replay_char_write_event_save(int res, int offset) { + g_assert(replay_mutex_locked()); + replay_save_instructions(); - replay_mutex_lock(); replay_put_event(EVENT_CHAR_WRITE); replay_put_dword(res); replay_put_dword(offset); - replay_mutex_unlock(); } void replay_char_write_event_load(int *res, int *offset) { + g_assert(replay_mutex_locked()); + replay_account_executed_instructions(); - replay_mutex_lock(); if (replay_next_event_is(EVENT_CHAR_WRITE)) { *res = replay_get_dword(); *offset = replay_get_dword(); replay_finish_event(); - replay_mutex_unlock(); } else { - replay_mutex_unlock(); error_report("Missing character write event in the replay log"); exit(1); } @@ -122,23 +121,21 @@ void replay_char_write_event_load(int *res, int *offset) int replay_char_read_all_load(uint8_t *buf) { - replay_mutex_lock(); + g_assert(replay_mutex_locked()); + if (replay_next_event_is(EVENT_CHAR_READ_ALL)) { size_t size; int res; replay_get_array(buf, &size); replay_finish_event(); - replay_mutex_unlock(); res = (int)size; assert(res >= 0); return res; } else if (replay_next_event_is(EVENT_CHAR_READ_ALL_ERROR)) { int res = replay_get_dword(); replay_finish_event(); - replay_mutex_unlock(); return res; } else { - replay_mutex_unlock(); error_report("Missing character read all event in the replay log"); exit(1); } @@ -146,19 +143,17 @@ int replay_char_read_all_load(uint8_t *buf) void replay_char_read_all_save_error(int res) { + g_assert(replay_mutex_locked()); assert(res < 0); replay_save_instructions(); - replay_mutex_lock(); replay_put_event(EVENT_CHAR_READ_ALL_ERROR); replay_put_dword(res); - replay_mutex_unlock(); } void replay_char_read_all_save_buf(uint8_t *buf, int offset) { + g_assert(replay_mutex_locked()); replay_save_instructions(); - replay_mutex_lock(); replay_put_event(EVENT_CHAR_READ_ALL); replay_put_array(buf, offset); - replay_mutex_unlock(); } diff --git a/replay/replay-events.c b/replay/replay-events.c index 94a6dcccfc..707de3867b 100644 --- a/replay/replay-events.c +++ b/replay/replay-events.c @@ -27,10 +27,6 @@ typedef struct Event { } Event; static QTAILQ_HEAD(, Event) events_list = QTAILQ_HEAD_INITIALIZER(events_list); -static unsigned int read_event_kind = -1; -static uint64_t read_id = -1; -static int read_checkpoint = -1; - static bool events_enabled; /* Functions */ @@ -67,7 +63,9 @@ static void replay_run_event(Event *event) void replay_enable_events(void) { - events_enabled = true; + if (replay_mode != REPLAY_MODE_NONE) { + events_enabled = true; + } } bool replay_has_events(void) @@ -77,16 +75,14 @@ bool replay_has_events(void) void replay_flush_events(void) { - replay_mutex_lock(); + g_assert(replay_mutex_locked()); + while (!QTAILQ_EMPTY(&events_list)) { Event *event = QTAILQ_FIRST(&events_list); - replay_mutex_unlock(); replay_run_event(event); - replay_mutex_lock(); QTAILQ_REMOVE(&events_list, event, events); g_free(event); } - replay_mutex_unlock(); } void replay_disable_events(void) @@ -100,14 +96,14 @@ void replay_disable_events(void) void replay_clear_events(void) { - replay_mutex_lock(); + g_assert(replay_mutex_locked()); + while (!QTAILQ_EMPTY(&events_list)) { Event *event = QTAILQ_FIRST(&events_list); QTAILQ_REMOVE(&events_list, event, events); g_free(event); } - replay_mutex_unlock(); } /*! Adds specified async event to the queue */ @@ -134,14 +130,13 @@ void replay_add_event(ReplayAsyncEventKind event_kind, event->opaque2 = opaque2; event->id = id; - replay_mutex_lock(); + g_assert(replay_mutex_locked()); QTAILQ_INSERT_TAIL(&events_list, event, events); - replay_mutex_unlock(); } void replay_bh_schedule_event(QEMUBH *bh) { - if (replay_mode != REPLAY_MODE_NONE && events_enabled) { + if (events_enabled) { uint64_t id = replay_get_current_step(); replay_add_event(REPLAY_ASYNC_EVENT_BH, bh, NULL, id); } else { @@ -161,7 +156,7 @@ void replay_add_input_sync_event(void) void replay_block_event(QEMUBH *bh, uint64_t id) { - if (replay_mode != REPLAY_MODE_NONE && events_enabled) { + if (events_enabled) { replay_add_event(REPLAY_ASYNC_EVENT_BLOCK, bh, NULL, id); } else { qemu_bh_schedule(bh); @@ -205,13 +200,12 @@ static void replay_save_event(Event *event, int checkpoint) /* Called with replay mutex locked */ void replay_save_events(int checkpoint) { + g_assert(replay_mutex_locked()); + g_assert(checkpoint != CHECKPOINT_CLOCK_WARP_START); while (!QTAILQ_EMPTY(&events_list)) { Event *event = QTAILQ_FIRST(&events_list); replay_save_event(event, checkpoint); - - replay_mutex_unlock(); replay_run_event(event); - replay_mutex_lock(); QTAILQ_REMOVE(&events_list, event, events); g_free(event); } @@ -220,58 +214,60 @@ void replay_save_events(int checkpoint) static Event *replay_read_event(int checkpoint) { Event *event; - if (read_event_kind == -1) { - read_checkpoint = replay_get_byte(); - read_event_kind = replay_get_byte(); - read_id = -1; + if (replay_state.read_event_kind == -1) { + replay_state.read_event_checkpoint = replay_get_byte(); + replay_state.read_event_kind = replay_get_byte(); + replay_state.read_event_id = -1; replay_check_error(); } - if (checkpoint != read_checkpoint) { + if (checkpoint != replay_state.read_event_checkpoint) { return NULL; } /* Events that has not to be in the queue */ - switch (read_event_kind) { + switch (replay_state.read_event_kind) { case REPLAY_ASYNC_EVENT_BH: - if (read_id == -1) { - read_id = replay_get_qword(); + if (replay_state.read_event_id == -1) { + replay_state.read_event_id = replay_get_qword(); } break; case REPLAY_ASYNC_EVENT_INPUT: event = g_malloc0(sizeof(Event)); - event->event_kind = read_event_kind; + event->event_kind = replay_state.read_event_kind; event->opaque = replay_read_input_event(); return event; case REPLAY_ASYNC_EVENT_INPUT_SYNC: event = g_malloc0(sizeof(Event)); - event->event_kind = read_event_kind; + event->event_kind = replay_state.read_event_kind; event->opaque = 0; return event; case REPLAY_ASYNC_EVENT_CHAR_READ: event = g_malloc0(sizeof(Event)); - event->event_kind = read_event_kind; + event->event_kind = replay_state.read_event_kind; event->opaque = replay_event_char_read_load(); return event; case REPLAY_ASYNC_EVENT_BLOCK: - if (read_id == -1) { - read_id = replay_get_qword(); + if (replay_state.read_event_id == -1) { + replay_state.read_event_id = replay_get_qword(); } break; case REPLAY_ASYNC_EVENT_NET: event = g_malloc0(sizeof(Event)); - event->event_kind = read_event_kind; + event->event_kind = replay_state.read_event_kind; event->opaque = replay_event_net_load(); return event; default: - error_report("Unknown ID %d of replay event", read_event_kind); + error_report("Unknown ID %d of replay event", + replay_state.read_event_kind); exit(1); break; } QTAILQ_FOREACH(event, &events_list, events) { - if (event->event_kind == read_event_kind - && (read_id == -1 || read_id == event->id)) { + if (event->event_kind == replay_state.read_event_kind + && (replay_state.read_event_id == -1 + || replay_state.read_event_id == event->id)) { break; } } @@ -290,24 +286,23 @@ static Event *replay_read_event(int checkpoint) /* Called with replay mutex locked */ void replay_read_events(int checkpoint) { + g_assert(replay_mutex_locked()); while (replay_state.data_kind == EVENT_ASYNC) { Event *event = replay_read_event(checkpoint); if (!event) { break; } - replay_mutex_unlock(); + replay_finish_event(); + replay_state.read_event_kind = -1; replay_run_event(event); - replay_mutex_lock(); g_free(event); - replay_finish_event(); - read_event_kind = -1; } } void replay_init_events(void) { - read_event_kind = -1; + replay_state.read_event_kind = -1; } void replay_finish_events(void) diff --git a/replay/replay-internal.c b/replay/replay-internal.c index fca8514012..b077cb5fd5 100644 --- a/replay/replay-internal.c +++ b/replay/replay-internal.c @@ -24,12 +24,23 @@ static QemuMutex lock; /* File for replay writing */ +static bool write_error; FILE *replay_file; +static void replay_write_error(void) +{ + if (!write_error) { + error_report("replay write error"); + write_error = true; + } +} + void replay_put_byte(uint8_t byte) { if (replay_file) { - putc(byte, replay_file); + if (putc(byte, replay_file) == EOF) { + replay_write_error(); + } } } @@ -62,7 +73,9 @@ void replay_put_array(const uint8_t *buf, size_t size) { if (replay_file) { replay_put_dword(size); - fwrite(buf, 1, size, replay_file); + if (fwrite(buf, 1, size, replay_file) != size) { + replay_write_error(); + } } } @@ -169,31 +182,46 @@ void replay_finish_event(void) replay_fetch_data_kind(); } +static __thread bool replay_locked; + void replay_mutex_init(void) { qemu_mutex_init(&lock); + /* Hold the mutex while we start-up */ + qemu_mutex_lock(&lock); + replay_locked = true; } -void replay_mutex_destroy(void) +bool replay_mutex_locked(void) { - qemu_mutex_destroy(&lock); + return replay_locked; } +/* Ordering constraints, replay_lock must be taken before BQL */ void replay_mutex_lock(void) { - qemu_mutex_lock(&lock); + if (replay_mode != REPLAY_MODE_NONE) { + g_assert(!qemu_mutex_iothread_locked()); + g_assert(!replay_mutex_locked()); + qemu_mutex_lock(&lock); + replay_locked = true; + } } void replay_mutex_unlock(void) { - qemu_mutex_unlock(&lock); + if (replay_mode != REPLAY_MODE_NONE) { + g_assert(replay_mutex_locked()); + replay_locked = false; + qemu_mutex_unlock(&lock); + } } /*! Saves cached instructions. */ void replay_save_instructions(void) { if (replay_file && replay_mode == REPLAY_MODE_RECORD) { - replay_mutex_lock(); + g_assert(replay_mutex_locked()); int diff = (int)(replay_get_current_step() - replay_state.current_step); /* Time can only go forward */ @@ -204,6 +232,5 @@ void replay_save_instructions(void) replay_put_dword(diff); replay_state.current_step += diff; } - replay_mutex_unlock(); } } diff --git a/replay/replay-internal.h b/replay/replay-internal.h index 3ebb19912a..ac4b27b674 100644 --- a/replay/replay-internal.h +++ b/replay/replay-internal.h @@ -12,7 +12,7 @@ * */ - +/* Any changes to order/number of events will need to bump REPLAY_VERSION */ enum ReplayEvents { /* for instruction event */ EVENT_INSTRUCTION, @@ -78,6 +78,14 @@ typedef struct ReplayState { This counter is global, because requests from different block devices should not get overlapping ids. */ uint64_t block_request_id; + /*! Prior value of the host clock */ + uint64_t host_clock_last; + /*! Asynchronous event type read from the log */ + int32_t read_event_kind; + /*! Asynchronous event id read from the log */ + uint64_t read_event_id; + /*! Asynchronous event checkpoint id read from the log */ + int32_t read_event_checkpoint; } ReplayState; extern ReplayState replay_state; @@ -98,12 +106,11 @@ int64_t replay_get_qword(void); void replay_get_array(uint8_t *buf, size_t *size); void replay_get_array_alloc(uint8_t **buf, size_t *size); -/* Mutex functions for protecting replay log file */ +/* Mutex functions for protecting replay log file and ensuring + * synchronisation between vCPU and main-loop threads. */ void replay_mutex_init(void); -void replay_mutex_destroy(void); -void replay_mutex_lock(void); -void replay_mutex_unlock(void); +bool replay_mutex_locked(void); /*! Checks error status of the file. */ void replay_check_error(void); diff --git a/replay/replay-snapshot.c b/replay/replay-snapshot.c index b2e10769a6..2ab85cfc60 100644 --- a/replay/replay-snapshot.c +++ b/replay/replay-snapshot.c @@ -25,6 +25,7 @@ static int replay_pre_save(void *opaque) { ReplayState *state = opaque; state->file_offset = ftell(replay_file); + state->host_clock_last = qemu_clock_get_last(QEMU_CLOCK_HOST); return 0; } @@ -33,6 +34,7 @@ static int replay_post_load(void *opaque, int version_id) { ReplayState *state = opaque; fseek(replay_file, state->file_offset, SEEK_SET); + qemu_clock_set_last(QEMU_CLOCK_HOST, state->host_clock_last); /* If this was a vmstate, saved in recording mode, we need to initialize replay data fields. */ replay_fetch_data_kind(); @@ -54,6 +56,10 @@ static const VMStateDescription vmstate_replay = { VMSTATE_UINT32(has_unread_data, ReplayState), VMSTATE_UINT64(file_offset, ReplayState), VMSTATE_UINT64(block_request_id, ReplayState), + VMSTATE_UINT64(host_clock_last, ReplayState), + VMSTATE_INT32(read_event_kind, ReplayState), + VMSTATE_UINT64(read_event_id, ReplayState), + VMSTATE_INT32(read_event_checkpoint, ReplayState), VMSTATE_END_OF_LIST() }, }; @@ -83,3 +89,9 @@ void replay_vmstate_init(void) } } } + +bool replay_can_snapshot(void) +{ + return replay_mode == REPLAY_MODE_NONE + || !replay_has_events(); +} diff --git a/replay/replay-time.c b/replay/replay-time.c index f70382a88f..6a7565ec8d 100644 --- a/replay/replay-time.c +++ b/replay/replay-time.c @@ -17,13 +17,13 @@ int64_t replay_save_clock(ReplayClockKind kind, int64_t clock) { - replay_save_instructions(); if (replay_file) { - replay_mutex_lock(); + g_assert(replay_mutex_locked()); + + replay_save_instructions(); replay_put_event(EVENT_CLOCK + kind); replay_put_qword(clock); - replay_mutex_unlock(); } return clock; @@ -46,16 +46,16 @@ void replay_read_next_clock(ReplayClockKind kind) /*! Reads next clock event from the input. */ int64_t replay_read_clock(ReplayClockKind kind) { + g_assert(replay_file && replay_mutex_locked()); + replay_account_executed_instructions(); if (replay_file) { int64_t ret; - replay_mutex_lock(); if (replay_next_event_is(EVENT_CLOCK + kind)) { replay_read_next_clock(kind); } ret = replay_state.cached_clock[kind]; - replay_mutex_unlock(); return ret; } diff --git a/replay/replay.c b/replay/replay.c index 7a23c62d61..8228261401 100644 --- a/replay/replay.c +++ b/replay/replay.c @@ -22,7 +22,7 @@ /* Current version of the replay mechanism. Increase it when file format changes. */ -#define REPLAY_VERSION 0xe02006 +#define REPLAY_VERSION 0xe02007 /* Size of replay log header */ #define HEADER_SIZE (sizeof(uint32_t) + sizeof(uint64_t)) @@ -81,7 +81,7 @@ int replay_get_instructions(void) void replay_account_executed_instructions(void) { if (replay_mode == REPLAY_MODE_PLAY) { - replay_mutex_lock(); + g_assert(replay_mutex_locked()); if (replay_state.instructions_count > 0) { int count = (int)(replay_get_current_step() - replay_state.current_step); @@ -100,24 +100,22 @@ void replay_account_executed_instructions(void) qemu_notify_event(); } } - replay_mutex_unlock(); } } bool replay_exception(void) { + if (replay_mode == REPLAY_MODE_RECORD) { + g_assert(replay_mutex_locked()); replay_save_instructions(); - replay_mutex_lock(); replay_put_event(EVENT_EXCEPTION); - replay_mutex_unlock(); return true; } else if (replay_mode == REPLAY_MODE_PLAY) { + g_assert(replay_mutex_locked()); bool res = replay_has_exception(); if (res) { - replay_mutex_lock(); replay_finish_event(); - replay_mutex_unlock(); } return res; } @@ -129,10 +127,9 @@ bool replay_has_exception(void) { bool res = false; if (replay_mode == REPLAY_MODE_PLAY) { + g_assert(replay_mutex_locked()); replay_account_executed_instructions(); - replay_mutex_lock(); res = replay_next_event_is(EVENT_EXCEPTION); - replay_mutex_unlock(); } return res; @@ -141,17 +138,15 @@ bool replay_has_exception(void) bool replay_interrupt(void) { if (replay_mode == REPLAY_MODE_RECORD) { + g_assert(replay_mutex_locked()); replay_save_instructions(); - replay_mutex_lock(); replay_put_event(EVENT_INTERRUPT); - replay_mutex_unlock(); return true; } else if (replay_mode == REPLAY_MODE_PLAY) { + g_assert(replay_mutex_locked()); bool res = replay_has_interrupt(); if (res) { - replay_mutex_lock(); replay_finish_event(); - replay_mutex_unlock(); } return res; } @@ -163,10 +158,9 @@ bool replay_has_interrupt(void) { bool res = false; if (replay_mode == REPLAY_MODE_PLAY) { + g_assert(replay_mutex_locked()); replay_account_executed_instructions(); - replay_mutex_lock(); res = replay_next_event_is(EVENT_INTERRUPT); - replay_mutex_unlock(); } return res; } @@ -174,25 +168,35 @@ bool replay_has_interrupt(void) void replay_shutdown_request(ShutdownCause cause) { if (replay_mode == REPLAY_MODE_RECORD) { - replay_mutex_lock(); + g_assert(replay_mutex_locked()); replay_put_event(EVENT_SHUTDOWN + cause); - replay_mutex_unlock(); } } bool replay_checkpoint(ReplayCheckpoint checkpoint) { bool res = false; + static bool in_checkpoint; assert(EVENT_CHECKPOINT + checkpoint <= EVENT_CHECKPOINT_LAST); - replay_save_instructions(); if (!replay_file) { return true; } - replay_mutex_lock(); + if (in_checkpoint) { + /* If we are already in checkpoint, then there is no need + for additional synchronization. + Recursion occurs when HW event modifies timers. + Timer modification may invoke the checkpoint and + proceed to recursion. */ + return true; + } + in_checkpoint = true; + + replay_save_instructions(); if (replay_mode == REPLAY_MODE_PLAY) { + g_assert(replay_mutex_locked()); if (replay_next_event_is(EVENT_CHECKPOINT + checkpoint)) { replay_finish_event(); } else if (replay_state.data_kind != EVENT_ASYNC) { @@ -205,12 +209,18 @@ bool replay_checkpoint(ReplayCheckpoint checkpoint) checkpoint were processed */ res = replay_state.data_kind != EVENT_ASYNC; } else if (replay_mode == REPLAY_MODE_RECORD) { + g_assert(replay_mutex_locked()); replay_put_event(EVENT_CHECKPOINT + checkpoint); - replay_save_events(checkpoint); + /* This checkpoint belongs to several threads. + Processing events from different threads is + non-deterministic */ + if (checkpoint != CHECKPOINT_CLOCK_WARP_START) { + replay_save_events(checkpoint); + } res = true; } out: - replay_mutex_unlock(); + in_checkpoint = false; return res; } @@ -233,8 +243,6 @@ static void replay_enable(const char *fname, int mode) atexit(replay_finish); - replay_mutex_init(); - replay_file = fopen(fname, fmode); if (replay_file == NULL) { fprintf(stderr, "Replay: open %s: %s\n", fname, strerror(errno)); @@ -242,8 +250,9 @@ static void replay_enable(const char *fname, int mode) } replay_filename = g_strdup(fname); - replay_mode = mode; + replay_mutex_init(); + replay_state.data_kind = -1; replay_state.instructions_count = 0; replay_state.current_step = 0; @@ -358,7 +367,6 @@ void replay_finish(void) replay_snapshot = NULL; replay_finish_events(); - replay_mutex_destroy(); } void replay_add_blocker(Error *reason) diff --git a/scripts/checkpatch.pl b/scripts/checkpatch.pl index d1fe79bcc4..57daae05ea 100755 --- a/scripts/checkpatch.pl +++ b/scripts/checkpatch.pl @@ -1447,9 +1447,10 @@ sub process { # check we are in a valid source file if not then ignore this hunk next if ($realfile !~ /$SrcFile/); -#90 column limit +#90 column limit; exempt URLs, if no other words on line if ($line =~ /^\+/ && !($line =~ /^\+\s*"[^"]*"\s*(?:\s*|,|\)\s*;)\s*$/) && + !($rawline =~ /^[^[:alnum:]]*https?:\S*$/) && $length > 80) { if ($length > 90) { diff --git a/scripts/qemu-binfmt-conf.sh b/scripts/qemu-binfmt-conf.sh index 5a0562f28d..f39ad344fc 100755 --- a/scripts/qemu-binfmt-conf.sh +++ b/scripts/qemu-binfmt-conf.sh @@ -162,7 +162,8 @@ Usage: qemu-binfmt-conf.sh [--qemu-path PATH][--debian][--systemd CPU] instead generate update-binfmts templates --systemd: don't write into /proc, instead generate file for systemd-binfmt.service - for the given CPU + for the given CPU. If CPU is "ALL", generate a + file for all known cpus --exportdir: define where to write configuration files (default: $SYSTEMDDIR or $DEBIANDIR) --credential: if yes, credential and security tokens are @@ -309,18 +310,20 @@ while true ; do EXPORTDIR=${EXPORTDIR:-$SYSTEMDDIR} shift # check given cpu is in the supported CPU list - for cpu in ${qemu_target_list} ; do + if [ "$1" != "ALL" ] ; then + for cpu in ${qemu_target_list} ; do + if [ "$cpu" = "$1" ] ; then + break + fi + done + if [ "$cpu" = "$1" ] ; then - break + qemu_target_list="$1" + else + echo "ERROR: unknown CPU \"$1\"" 1>&2 + usage + exit 1 fi - done - - if [ "$cpu" = "$1" ] ; then - qemu_target_list="$1" - else - echo "ERROR: unknown CPU \"$1\"" 1>&2 - usage - exit 1 fi ;; -Q|--qemu-path) diff --git a/scripts/replay-dump.py b/scripts/replay-dump.py new file mode 100755 index 0000000000..e274086277 --- /dev/null +++ b/scripts/replay-dump.py @@ -0,0 +1,308 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +# +# Dump the contents of a recorded execution stream +# +# Copyright (c) 2017 Alex Bennée <alex.bennee@linaro.org> +# +# 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/>. + +import argparse +import struct +from collections import namedtuple + +# This mirrors some of the global replay state which some of the +# stream loading refers to. Some decoders may read the next event so +# we need handle that case. Calling reuse_event will ensure the next +# event is read from the cache rather than advancing the file. + +class ReplayState(object): + def __init__(self): + self.event = -1 + self.event_count = 0 + self.already_read = False + self.current_checkpoint = 0 + self.checkpoint = 0 + + def set_event(self, ev): + self.event = ev + self.event_count += 1 + + def get_event(self): + self.already_read = False + return self.event + + def reuse_event(self, ev): + self.event = ev + self.already_read = True + + def set_checkpoint(self): + self.checkpoint = self.event - self.checkpoint_start + + def get_checkpoint(self): + return self.checkpoint + +replay_state = ReplayState() + +# Simple read functions that mirror replay-internal.c +# The file-stream is big-endian and manually written out a byte at a time. + +def read_byte(fin): + "Read a single byte" + return struct.unpack('>B', fin.read(1))[0] + +def read_event(fin): + "Read a single byte event, but save some state" + if replay_state.already_read: + return replay_state.get_event() + else: + replay_state.set_event(read_byte(fin)) + return replay_state.event + +def read_word(fin): + "Read a 16 bit word" + return struct.unpack('>H', fin.read(2))[0] + +def read_dword(fin): + "Read a 32 bit word" + return struct.unpack('>I', fin.read(4))[0] + +def read_qword(fin): + "Read a 64 bit word" + return struct.unpack('>Q', fin.read(8))[0] + +# Generic decoder structure +Decoder = namedtuple("Decoder", "eid name fn") + +def call_decode(table, index, dumpfile): + "Search decode table for next step" + decoder = next((d for d in table if d.eid == index), None) + if not decoder: + print "Could not decode index: %d" % (index) + print "Entry is: %s" % (decoder) + print "Decode Table is:\n%s" % (table) + return False + else: + return decoder.fn(decoder.eid, decoder.name, dumpfile) + +# Print event +def print_event(eid, name, string=None, event_count=None): + "Print event with count" + if not event_count: + event_count = replay_state.event_count + + if string: + print "%d:%s(%d) %s" % (event_count, name, eid, string) + else: + print "%d:%s(%d)" % (event_count, name, eid) + + +# Decoders for each event type + +def decode_unimp(eid, name, _unused_dumpfile): + "Unimplimented decoder, will trigger exit" + print "%s not handled - will now stop" % (name) + return False + +# Checkpoint decoder +def swallow_async_qword(eid, name, dumpfile): + "Swallow a qword of data without looking at it" + step_id = read_qword(dumpfile) + print " %s(%d) @ %d" % (name, eid, step_id) + return True + +async_decode_table = [ Decoder(0, "REPLAY_ASYNC_EVENT_BH", swallow_async_qword), + Decoder(1, "REPLAY_ASYNC_INPUT", decode_unimp), + Decoder(2, "REPLAY_ASYNC_INPUT_SYNC", decode_unimp), + Decoder(3, "REPLAY_ASYNC_CHAR_READ", decode_unimp), + Decoder(4, "REPLAY_ASYNC_EVENT_BLOCK", decode_unimp), + Decoder(5, "REPLAY_ASYNC_EVENT_NET", decode_unimp), +] +# See replay_read_events/replay_read_event +def decode_async(eid, name, dumpfile): + """Decode an ASYNC event""" + + print_event(eid, name) + + async_event_kind = read_byte(dumpfile) + async_event_checkpoint = read_byte(dumpfile) + + if async_event_checkpoint != replay_state.current_checkpoint: + print " mismatch between checkpoint %d and async data %d" % ( + replay_state.current_checkpoint, async_event_checkpoint) + return True + + return call_decode(async_decode_table, async_event_kind, dumpfile) + + +def decode_instruction(eid, name, dumpfile): + ins_diff = read_dword(dumpfile) + print_event(eid, name, "0x%x" % (ins_diff)) + return True + +def decode_audio_out(eid, name, dumpfile): + audio_data = read_dword(dumpfile) + print_event(eid, name, "%d" % (audio_data)) + return True + +def decode_checkpoint(eid, name, dumpfile): + """Decode a checkpoint. + + Checkpoints contain a series of async events with their own specific data. + """ + replay_state.set_checkpoint() + # save event count as we peek ahead + event_number = replay_state.event_count + next_event = read_event(dumpfile) + + # if the next event is EVENT_ASYNC there are a bunch of + # async events to read, otherwise we are done + if next_event != 3: + print_event(eid, name, "no additional data", event_number) + else: + print_event(eid, name, "more data follows", event_number) + + replay_state.reuse_event(next_event) + return True + +def decode_checkpoint_init(eid, name, dumpfile): + print_event(eid, name) + return True + +def decode_interrupt(eid, name, dumpfile): + print_event(eid, name) + return True + +def decode_clock(eid, name, dumpfile): + clock_data = read_qword(dumpfile) + print_event(eid, name, "0x%x" % (clock_data)) + return True + + +# pre-MTTCG merge +v5_event_table = [Decoder(0, "EVENT_INSTRUCTION", decode_instruction), + Decoder(1, "EVENT_INTERRUPT", decode_interrupt), + Decoder(2, "EVENT_EXCEPTION", decode_unimp), + Decoder(3, "EVENT_ASYNC", decode_async), + Decoder(4, "EVENT_SHUTDOWN", decode_unimp), + Decoder(5, "EVENT_CHAR_WRITE", decode_unimp), + Decoder(6, "EVENT_CHAR_READ_ALL", decode_unimp), + Decoder(7, "EVENT_CHAR_READ_ALL_ERROR", decode_unimp), + Decoder(8, "EVENT_CLOCK_HOST", decode_clock), + Decoder(9, "EVENT_CLOCK_VIRTUAL_RT", decode_clock), + Decoder(10, "EVENT_CP_CLOCK_WARP_START", decode_checkpoint), + Decoder(11, "EVENT_CP_CLOCK_WARP_ACCOUNT", decode_checkpoint), + Decoder(12, "EVENT_CP_RESET_REQUESTED", decode_checkpoint), + Decoder(13, "EVENT_CP_SUSPEND_REQUESTED", decode_checkpoint), + Decoder(14, "EVENT_CP_CLOCK_VIRTUAL", decode_checkpoint), + Decoder(15, "EVENT_CP_CLOCK_HOST", decode_checkpoint), + Decoder(16, "EVENT_CP_CLOCK_VIRTUAL_RT", decode_checkpoint), + Decoder(17, "EVENT_CP_INIT", decode_checkpoint_init), + Decoder(18, "EVENT_CP_RESET", decode_checkpoint), +] + +# post-MTTCG merge, AUDIO support added +v6_event_table = [Decoder(0, "EVENT_INSTRUCTION", decode_instruction), + Decoder(1, "EVENT_INTERRUPT", decode_interrupt), + Decoder(2, "EVENT_EXCEPTION", decode_unimp), + Decoder(3, "EVENT_ASYNC", decode_async), + Decoder(4, "EVENT_SHUTDOWN", decode_unimp), + Decoder(5, "EVENT_CHAR_WRITE", decode_unimp), + Decoder(6, "EVENT_CHAR_READ_ALL", decode_unimp), + Decoder(7, "EVENT_CHAR_READ_ALL_ERROR", decode_unimp), + Decoder(8, "EVENT_AUDIO_OUT", decode_audio_out), + Decoder(9, "EVENT_AUDIO_IN", decode_unimp), + Decoder(10, "EVENT_CLOCK_HOST", decode_clock), + Decoder(11, "EVENT_CLOCK_VIRTUAL_RT", decode_clock), + Decoder(12, "EVENT_CP_CLOCK_WARP_START", decode_checkpoint), + Decoder(13, "EVENT_CP_CLOCK_WARP_ACCOUNT", decode_checkpoint), + Decoder(14, "EVENT_CP_RESET_REQUESTED", decode_checkpoint), + Decoder(15, "EVENT_CP_SUSPEND_REQUESTED", decode_checkpoint), + Decoder(16, "EVENT_CP_CLOCK_VIRTUAL", decode_checkpoint), + Decoder(17, "EVENT_CP_CLOCK_HOST", decode_checkpoint), + Decoder(18, "EVENT_CP_CLOCK_VIRTUAL_RT", decode_checkpoint), + Decoder(19, "EVENT_CP_INIT", decode_checkpoint_init), + Decoder(20, "EVENT_CP_RESET", decode_checkpoint), +] + +# Shutdown cause added +v7_event_table = [Decoder(0, "EVENT_INSTRUCTION", decode_instruction), + Decoder(1, "EVENT_INTERRUPT", decode_interrupt), + Decoder(2, "EVENT_EXCEPTION", decode_unimp), + Decoder(3, "EVENT_ASYNC", decode_async), + Decoder(4, "EVENT_SHUTDOWN", decode_unimp), + Decoder(5, "EVENT_SHUTDOWN_HOST_ERR", decode_unimp), + Decoder(6, "EVENT_SHUTDOWN_HOST_QMP", decode_unimp), + Decoder(7, "EVENT_SHUTDOWN_HOST_SIGNAL", decode_unimp), + Decoder(8, "EVENT_SHUTDOWN_HOST_UI", decode_unimp), + Decoder(9, "EVENT_SHUTDOWN_GUEST_SHUTDOWN", decode_unimp), + Decoder(10, "EVENT_SHUTDOWN_GUEST_RESET", decode_unimp), + Decoder(11, "EVENT_SHUTDOWN_GUEST_PANIC", decode_unimp), + Decoder(12, "EVENT_SHUTDOWN___MAX", decode_unimp), + Decoder(13, "EVENT_CHAR_WRITE", decode_unimp), + Decoder(14, "EVENT_CHAR_READ_ALL", decode_unimp), + Decoder(15, "EVENT_CHAR_READ_ALL_ERROR", decode_unimp), + Decoder(16, "EVENT_AUDIO_OUT", decode_audio_out), + Decoder(17, "EVENT_AUDIO_IN", decode_unimp), + Decoder(18, "EVENT_CLOCK_HOST", decode_clock), + Decoder(19, "EVENT_CLOCK_VIRTUAL_RT", decode_clock), + Decoder(20, "EVENT_CP_CLOCK_WARP_START", decode_checkpoint), + Decoder(21, "EVENT_CP_CLOCK_WARP_ACCOUNT", decode_checkpoint), + Decoder(22, "EVENT_CP_RESET_REQUESTED", decode_checkpoint), + Decoder(23, "EVENT_CP_SUSPEND_REQUESTED", decode_checkpoint), + Decoder(24, "EVENT_CP_CLOCK_VIRTUAL", decode_checkpoint), + Decoder(25, "EVENT_CP_CLOCK_HOST", decode_checkpoint), + Decoder(26, "EVENT_CP_CLOCK_VIRTUAL_RT", decode_checkpoint), + Decoder(27, "EVENT_CP_INIT", decode_checkpoint_init), + Decoder(28, "EVENT_CP_RESET", decode_checkpoint), +] + +def parse_arguments(): + "Grab arguments for script" + parser = argparse.ArgumentParser() + parser.add_argument("-f", "--file", help='record/replay dump to read from', + required=True) + return parser.parse_args() + +def decode_file(filename): + "Decode a record/replay dump" + dumpfile = open(filename, "rb") + + # read and throwaway the header + version = read_dword(dumpfile) + junk = read_qword(dumpfile) + + print "HEADER: version 0x%x" % (version) + + if version == 0xe02007: + event_decode_table = v7_event_table + replay_state.checkpoint_start = 12 + elif version == 0xe02006: + event_decode_table = v6_event_table + replay_state.checkpoint_start = 12 + else: + event_decode_table = v5_event_table + replay_state.checkpoint_start = 10 + + try: + decode_ok = True + while decode_ok: + event = read_event(dumpfile) + decode_ok = call_decode(event_decode_table, event, dumpfile) + finally: + dumpfile.close() + +if __name__ == "__main__": + args = parse_arguments() + decode_file(args.file) diff --git a/scripts/update-linux-headers.sh b/scripts/update-linux-headers.sh index be065704df..da9030a01c 100755 --- a/scripts/update-linux-headers.sh +++ b/scripts/update-linux-headers.sh @@ -39,6 +39,7 @@ cp_portable() { -e 'input-event-codes' \ -e 'sys/' \ -e 'pvrdma_verbs' \ + -e 'drm.h' \ > /dev/null then echo "Unexpected #include in input file $f". @@ -56,6 +57,8 @@ cp_portable() { -e 's/__bitwise//' \ -e 's/__attribute__((packed))/QEMU_PACKED/' \ -e 's/__inline__/inline/' \ + -e 's/__BITS_PER_LONG/HOST_LONG_BITS/' \ + -e '/\"drm.h\"/d' \ -e '/sys\/ioctl.h/d' \ -e 's/SW_MAX/SW_MAX_/' \ -e 's/atomic_t/int/' \ @@ -99,6 +102,8 @@ for arch in $ARCHLIST; do mkdir -p "$output/include/standard-headers/asm-$arch" if [ $arch = s390 ]; then cp_portable "$tmpdir/include/asm/virtio-ccw.h" "$output/include/standard-headers/asm-s390/" + cp "$tmpdir/include/asm/unistd_32.h" "$output/linux-headers/asm-s390/" + cp "$tmpdir/include/asm/unistd_64.h" "$output/linux-headers/asm-s390/" fi if [ $arch = arm ]; then cp "$tmpdir/include/asm/unistd-eabi.h" "$output/linux-headers/asm-arm/" @@ -118,7 +123,7 @@ done rm -rf "$output/linux-headers/linux" mkdir -p "$output/linux-headers/linux" for header in kvm.h kvm_para.h vfio.h vfio_ccw.h vhost.h \ - psci.h userfaultfd.h; do + psci.h psp-sev.h userfaultfd.h; do cp "$tmpdir/include/linux/$header" "$output/linux-headers/linux" done rm -rf "$output/linux-headers/asm-generic" @@ -149,6 +154,9 @@ for i in "$tmpdir"/include/linux/*virtio*.h "$tmpdir/include/linux/input.h" \ "$tmpdir/include/linux/pci_regs.h"; do cp_portable "$i" "$output/include/standard-headers/linux" done +mkdir -p "$output/include/standard-headers/drm" +cp_portable "$tmpdir/include/drm/drm_fourcc.h" \ + "$output/include/standard-headers/drm" rm -rf "$output/include/standard-headers/drivers/infiniband/hw/vmw_pvrdma" mkdir -p "$output/include/standard-headers/drivers/infiniband/hw/vmw_pvrdma" diff --git a/scsi/qemu-pr-helper.c b/scsi/qemu-pr-helper.c index 9fe615c73c..3facbba170 100644 --- a/scsi/qemu-pr-helper.c +++ b/scsi/qemu-pr-helper.c @@ -102,7 +102,7 @@ QEMU_HELP_BOTTOM "\n" static void version(const char *name) { printf( -"%s " QEMU_VERSION QEMU_PKGVERSION "\n" +"%s " QEMU_FULL_VERSION "\n" "Written by Paolo Bonzini.\n" "\n" QEMU_COPYRIGHT "\n" diff --git a/stubs/machine-init-done.c b/stubs/machine-init-done.c index 9a0d62514f..4121f1709b 100644 --- a/stubs/machine-init-done.c +++ b/stubs/machine-init-done.c @@ -2,6 +2,8 @@ #include "qemu-common.h" #include "sysemu/sysemu.h" +bool machine_init_done = true; + void qemu_add_machine_init_done_notifier(Notifier *notify) { } diff --git a/stubs/replay.c b/stubs/replay.c index 9c8aa48c9c..04279abb2c 100644 --- a/stubs/replay.c +++ b/stubs/replay.c @@ -72,3 +72,11 @@ uint64_t blkreplay_next_id(void) { return 0; } + +void replay_mutex_lock(void) +{ +} + +void replay_mutex_unlock(void) +{ +} diff --git a/target/alpha/cpu.c b/target/alpha/cpu.c index 55675ce419..b08078e7fc 100644 --- a/target/alpha/cpu.c +++ b/target/alpha/cpu.c @@ -71,18 +71,6 @@ static void alpha_cpu_realizefn(DeviceState *dev, Error **errp) acc->parent_realize(dev, errp); } -/* Sort alphabetically by type name. */ -static gint alpha_cpu_list_compare(gconstpointer a, gconstpointer b) -{ - ObjectClass *class_a = (ObjectClass *)a; - ObjectClass *class_b = (ObjectClass *)b; - const char *name_a, *name_b; - - name_a = object_class_get_name(class_a); - name_b = object_class_get_name(class_b); - return strcmp(name_a, name_b); -} - static void alpha_cpu_list_entry(gpointer data, gpointer user_data) { ObjectClass *oc = data; @@ -100,8 +88,7 @@ void alpha_cpu_list(FILE *f, fprintf_function cpu_fprintf) }; GSList *list; - list = object_class_get_list(TYPE_ALPHA_CPU, false); - list = g_slist_sort(list, alpha_cpu_list_compare); + list = object_class_get_list_sorted(TYPE_ALPHA_CPU, false); (*cpu_fprintf)(f, "Available CPUs:\n"); g_slist_foreach(list, alpha_cpu_list_entry, &s); g_slist_free(list); diff --git a/target/hppa/cpu.c b/target/hppa/cpu.c index 969f628f0a..c261b6b090 100644 --- a/target/hppa/cpu.c +++ b/target/hppa/cpu.c @@ -110,18 +110,6 @@ static void hppa_cpu_realizefn(DeviceState *dev, Error **errp) #endif } -/* Sort hppabetically by type name. */ -static gint hppa_cpu_list_compare(gconstpointer a, gconstpointer b) -{ - ObjectClass *class_a = (ObjectClass *)a; - ObjectClass *class_b = (ObjectClass *)b; - const char *name_a, *name_b; - - name_a = object_class_get_name(class_a); - name_b = object_class_get_name(class_b); - return strcmp(name_a, name_b); -} - static void hppa_cpu_list_entry(gpointer data, gpointer user_data) { ObjectClass *oc = data; @@ -138,8 +126,7 @@ void hppa_cpu_list(FILE *f, fprintf_function cpu_fprintf) }; GSList *list; - list = object_class_get_list(TYPE_HPPA_CPU, false); - list = g_slist_sort(list, hppa_cpu_list_compare); + list = object_class_get_list_sorted(TYPE_HPPA_CPU, false); (*cpu_fprintf)(f, "Available CPUs:\n"); g_slist_foreach(list, hppa_cpu_list_entry, &s); g_slist_free(list); diff --git a/target/i386/Makefile.objs b/target/i386/Makefile.objs index f5c6ef20a7..04678f5503 100644 --- a/target/i386/Makefile.objs +++ b/target/i386/Makefile.objs @@ -5,7 +5,9 @@ obj-$(CONFIG_TCG) += int_helper.o mem_helper.o misc_helper.o mpx_helper.o obj-$(CONFIG_TCG) += seg_helper.o smm_helper.o svm_helper.o obj-$(CONFIG_SOFTMMU) += machine.o arch_memory_mapping.o arch_dump.o monitor.o obj-$(CONFIG_KVM) += kvm.o hyperv.o +obj-$(CONFIG_SEV) += sev.o obj-$(call lnot,$(CONFIG_KVM)) += kvm-stub.o +obj-$(call lnot,$(CONFIG_SEV)) += sev-stub.o # HAX support ifdef CONFIG_WIN32 obj-$(CONFIG_HAX) += hax-all.o hax-mem.o hax-windows.o diff --git a/target/i386/cpu.c b/target/i386/cpu.c index ec1efd3a3c..6bb4ce8719 100644 --- a/target/i386/cpu.c +++ b/target/i386/cpu.c @@ -26,6 +26,7 @@ #include "sysemu/hvf.h" #include "sysemu/cpus.h" #include "kvm_i386.h" +#include "sev_i386.h" #include "qemu/error-report.h" #include "qemu/option.h" @@ -3672,6 +3673,13 @@ void cpu_x86_cpuid(CPUX86State *env, uint32_t index, uint32_t count, *ecx = 0; *edx = 0; break; + case 0x8000001F: + *eax = sev_enabled() ? 0x2 : 0; + *ebx = sev_get_cbit_position(); + *ebx |= sev_get_reduced_phys_bits() << 6; + *ecx = 0; + *edx = 0; + break; default: /* reserved values: zero */ *eax = 0; @@ -3705,6 +3713,7 @@ static void x86_cpu_reset(CPUState *s) cpu_x86_update_cr0(env, 0x60000010); env->a20_mask = ~0x0; env->smbase = 0x30000; + env->msr_smi_count = 0; env->idt.limit = 0xffff; env->gdt.limit = 0xffff; @@ -4101,6 +4110,11 @@ static void x86_cpu_expand_features(X86CPU *cpu, Error **errp) if (env->features[FEAT_8000_0001_ECX] & CPUID_EXT3_SVM) { x86_cpu_adjust_level(cpu, &env->cpuid_min_xlevel, 0x8000000A); } + + /* SEV requires CPUID[0x8000001F] */ + if (sev_enabled()) { + x86_cpu_adjust_level(cpu, &env->cpuid_min_xlevel, 0x8000001F); + } } /* Set cpuid_*level* based on cpuid_min_*level, if not explicitly set */ diff --git a/target/i386/cpu.h b/target/i386/cpu.h index 0c3f51445e..2e2bab5ff3 100644 --- a/target/i386/cpu.h +++ b/target/i386/cpu.h @@ -1,3 +1,4 @@ + /* * i386 virtual CPU header * @@ -359,6 +360,7 @@ typedef enum X86Seg { #define MSR_P6_PERFCTR0 0xc1 #define MSR_IA32_SMBASE 0x9e +#define MSR_SMI_COUNT 0x34 #define MSR_MTRRcap 0xfe #define MSR_MTRRcap_VCNT 8 #define MSR_MTRRcap_FIXRANGE_SUPPORT (1 << 8) @@ -1142,6 +1144,7 @@ typedef struct CPUX86State { uint64_t pat; uint32_t smbase; + uint64_t msr_smi_count; uint32_t pkru; diff --git a/target/i386/kvm.c b/target/i386/kvm.c index d996cca68b..d23fff12f5 100644 --- a/target/i386/kvm.c +++ b/target/i386/kvm.c @@ -92,6 +92,7 @@ static bool has_msr_hv_stimer; static bool has_msr_hv_frequencies; static bool has_msr_xss; static bool has_msr_spec_ctrl; +static bool has_msr_smi_count; static uint32_t has_architectural_pmu_version; static uint32_t num_architectural_pmu_gp_counters; @@ -1151,6 +1152,9 @@ static int kvm_get_supported_msrs(KVMState *s) case MSR_IA32_SMBASE: has_msr_smbase = true; break; + case MSR_SMI_COUNT: + has_msr_smi_count = true; + break; case MSR_IA32_MISC_ENABLE: has_msr_misc_enable = true; break; @@ -1660,6 +1664,9 @@ static int kvm_put_msrs(X86CPU *cpu, int level) if (has_msr_smbase) { kvm_msr_entry_add(cpu, MSR_IA32_SMBASE, env->smbase); } + if (has_msr_smi_count) { + kvm_msr_entry_add(cpu, MSR_SMI_COUNT, env->msr_smi_count); + } if (has_msr_bndcfgs) { kvm_msr_entry_add(cpu, MSR_IA32_BNDCFGS, env->msr_bndcfgs); } @@ -2025,6 +2032,9 @@ static int kvm_get_msrs(X86CPU *cpu) if (has_msr_smbase) { kvm_msr_entry_add(cpu, MSR_IA32_SMBASE, 0); } + if (has_msr_smi_count) { + kvm_msr_entry_add(cpu, MSR_SMI_COUNT, 0); + } if (has_msr_feature_control) { kvm_msr_entry_add(cpu, MSR_IA32_FEATURE_CONTROL, 0); } @@ -2265,6 +2275,9 @@ static int kvm_get_msrs(X86CPU *cpu) case MSR_IA32_SMBASE: env->smbase = msrs[i].data; break; + case MSR_SMI_COUNT: + env->msr_smi_count = msrs[i].data; + break; case MSR_IA32_FEATURE_CONTROL: env->msr_ia32_feature_control = msrs[i].data; break; diff --git a/target/i386/machine.c b/target/i386/machine.c index c05fe6fb1a..bd2d82e91b 100644 --- a/target/i386/machine.c +++ b/target/i386/machine.c @@ -395,6 +395,25 @@ static const VMStateDescription vmstate_msr_tsc_adjust = { } }; +static bool msr_smi_count_needed(void *opaque) +{ + X86CPU *cpu = opaque; + CPUX86State *env = &cpu->env; + + return env->msr_smi_count != 0; +} + +static const VMStateDescription vmstate_msr_smi_count = { + .name = "cpu/msr_smi_count", + .version_id = 1, + .minimum_version_id = 1, + .needed = msr_smi_count_needed, + .fields = (VMStateField[]) { + VMSTATE_UINT64(env.msr_smi_count, X86CPU), + VMSTATE_END_OF_LIST() + } +}; + static bool tscdeadline_needed(void *opaque) { X86CPU *cpu = opaque; @@ -989,6 +1008,7 @@ VMStateDescription vmstate_x86_cpu = { &vmstate_avx512, &vmstate_xss, &vmstate_tsc_khz, + &vmstate_msr_smi_count, #ifdef TARGET_X86_64 &vmstate_pkru, #endif diff --git a/target/i386/monitor.c b/target/i386/monitor.c index 75429129fd..011419eba2 100644 --- a/target/i386/monitor.c +++ b/target/i386/monitor.c @@ -29,7 +29,11 @@ #include "qapi/qmp/qdict.h" #include "hw/i386/pc.h" #include "sysemu/kvm.h" +#include "sysemu/sev.h" #include "hmp.h" +#include "qapi/error.h" +#include "sev_i386.h" +#include "qapi/qapi-commands-misc.h" static void print_pte(Monitor *mon, CPUArchState *env, hwaddr addr, @@ -661,3 +665,65 @@ void hmp_info_io_apic(Monitor *mon, const QDict *qdict) ioapic_dump_state(mon, qdict); } } + +SevInfo *qmp_query_sev(Error **errp) +{ + SevInfo *info; + + info = sev_get_info(); + if (!info) { + error_setg(errp, "SEV feature is not available"); + return NULL; + } + + return info; +} + +void hmp_info_sev(Monitor *mon, const QDict *qdict) +{ + SevInfo *info = sev_get_info(); + + if (info && info->enabled) { + monitor_printf(mon, "handle: %d\n", info->handle); + monitor_printf(mon, "state: %s\n", SevState_str(info->state)); + monitor_printf(mon, "build: %d\n", info->build_id); + monitor_printf(mon, "api version: %d.%d\n", + info->api_major, info->api_minor); + monitor_printf(mon, "debug: %s\n", + info->policy & SEV_POLICY_NODBG ? "off" : "on"); + monitor_printf(mon, "key-sharing: %s\n", + info->policy & SEV_POLICY_NOKS ? "off" : "on"); + } else { + monitor_printf(mon, "SEV is not enabled\n"); + } +} + +SevLaunchMeasureInfo *qmp_query_sev_launch_measure(Error **errp) +{ + char *data; + SevLaunchMeasureInfo *info; + + data = sev_get_launch_measurement(); + if (!data) { + error_setg(errp, "Measurement is not available"); + return NULL; + } + + info = g_malloc0(sizeof(*info)); + info->data = data; + + return info; +} + +SevCapability *qmp_query_sev_capabilities(Error **errp) +{ + SevCapability *data; + + data = sev_get_capabilities(); + if (!data) { + error_setg(errp, "SEV feature is not available"); + return NULL; + } + + return data; +} diff --git a/target/i386/sev-stub.c b/target/i386/sev-stub.c new file mode 100644 index 0000000000..59a003a4eb --- /dev/null +++ b/target/i386/sev-stub.c @@ -0,0 +1,51 @@ +/* + * QEMU SEV stub + * + * Copyright Advanced Micro Devices 2018 + * + * Authors: + * Brijesh Singh <brijesh.singh@amd.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-common.h" +#include "sev_i386.h" + +SevInfo *sev_get_info(void) +{ + return NULL; +} + +bool sev_enabled(void) +{ + return false; +} + +uint64_t sev_get_me_mask(void) +{ + return ~0; +} + +uint32_t sev_get_cbit_position(void) +{ + return 0; +} + +uint32_t sev_get_reduced_phys_bits(void) +{ + return 0; +} + +char *sev_get_launch_measurement(void) +{ + return NULL; +} + +SevCapability *sev_get_capabilities(void) +{ + return NULL; +} diff --git a/target/i386/sev.c b/target/i386/sev.c new file mode 100644 index 0000000000..019d84cef2 --- /dev/null +++ b/target/i386/sev.c @@ -0,0 +1,811 @@ +/* + * QEMU SEV support + * + * Copyright Advanced Micro Devices 2016-2018 + * + * Author: + * Brijesh Singh <brijesh.singh@amd.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 <linux/kvm.h> +#include <linux/psp-sev.h> + +#include <sys/ioctl.h> + +#include "qemu/osdep.h" +#include "qapi/error.h" +#include "qom/object_interfaces.h" +#include "qemu/base64.h" +#include "sysemu/kvm.h" +#include "sev_i386.h" +#include "sysemu/sysemu.h" +#include "trace.h" +#include "migration/blocker.h" + +#define DEFAULT_GUEST_POLICY 0x1 /* disable debug */ +#define DEFAULT_SEV_DEVICE "/dev/sev" + +static SEVState *sev_state; +static Error *sev_mig_blocker; + +static const char *const sev_fw_errlist[] = { + "", + "Platform state is invalid", + "Guest state is invalid", + "Platform configuration is invalid", + "Buffer too small", + "Platform is already owned", + "Certificate is invalid", + "Policy is not allowed", + "Guest is not active", + "Invalid address", + "Bad signature", + "Bad measurement", + "Asid is already owned", + "Invalid ASID", + "WBINVD is required", + "DF_FLUSH is required", + "Guest handle is invalid", + "Invalid command", + "Guest is active", + "Hardware error", + "Hardware unsafe", + "Feature not supported", + "Invalid parameter" +}; + +#define SEV_FW_MAX_ERROR ARRAY_SIZE(sev_fw_errlist) + +static int +sev_ioctl(int fd, int cmd, void *data, int *error) +{ + int r; + struct kvm_sev_cmd input; + + memset(&input, 0x0, sizeof(input)); + + input.id = cmd; + input.sev_fd = fd; + input.data = (__u64)(unsigned long)data; + + r = kvm_vm_ioctl(kvm_state, KVM_MEMORY_ENCRYPT_OP, &input); + + if (error) { + *error = input.error; + } + + return r; +} + +static int +sev_platform_ioctl(int fd, int cmd, void *data, int *error) +{ + int r; + struct sev_issue_cmd arg; + + arg.cmd = cmd; + arg.data = (unsigned long)data; + r = ioctl(fd, SEV_ISSUE_CMD, &arg); + if (error) { + *error = arg.error; + } + + return r; +} + +static const char * +fw_error_to_str(int code) +{ + if (code < 0 || code >= SEV_FW_MAX_ERROR) { + return "unknown error"; + } + + return sev_fw_errlist[code]; +} + +static bool +sev_check_state(SevState state) +{ + assert(sev_state); + return sev_state->state == state ? true : false; +} + +static void +sev_set_guest_state(SevState new_state) +{ + assert(new_state < SEV_STATE__MAX); + assert(sev_state); + + trace_kvm_sev_change_state(SevState_str(sev_state->state), + SevState_str(new_state)); + sev_state->state = new_state; +} + +static void +sev_ram_block_added(RAMBlockNotifier *n, void *host, size_t size) +{ + int r; + struct kvm_enc_region range; + + range.addr = (__u64)(unsigned long)host; + range.size = size; + + trace_kvm_memcrypt_register_region(host, size); + r = kvm_vm_ioctl(kvm_state, KVM_MEMORY_ENCRYPT_REG_REGION, &range); + if (r) { + error_report("%s: failed to register region (%p+%#zx) error '%s'", + __func__, host, size, strerror(errno)); + exit(1); + } +} + +static void +sev_ram_block_removed(RAMBlockNotifier *n, void *host, size_t size) +{ + int r; + struct kvm_enc_region range; + + range.addr = (__u64)(unsigned long)host; + range.size = size; + + trace_kvm_memcrypt_unregister_region(host, size); + r = kvm_vm_ioctl(kvm_state, KVM_MEMORY_ENCRYPT_UNREG_REGION, &range); + if (r) { + error_report("%s: failed to unregister region (%p+%#zx)", + __func__, host, size); + } +} + +static struct RAMBlockNotifier sev_ram_notifier = { + .ram_block_added = sev_ram_block_added, + .ram_block_removed = sev_ram_block_removed, +}; + +static void +qsev_guest_finalize(Object *obj) +{ +} + +static char * +qsev_guest_get_session_file(Object *obj, Error **errp) +{ + QSevGuestInfo *s = QSEV_GUEST_INFO(obj); + + return s->session_file ? g_strdup(s->session_file) : NULL; +} + +static void +qsev_guest_set_session_file(Object *obj, const char *value, Error **errp) +{ + QSevGuestInfo *s = QSEV_GUEST_INFO(obj); + + s->session_file = g_strdup(value); +} + +static char * +qsev_guest_get_dh_cert_file(Object *obj, Error **errp) +{ + QSevGuestInfo *s = QSEV_GUEST_INFO(obj); + + return g_strdup(s->dh_cert_file); +} + +static void +qsev_guest_set_dh_cert_file(Object *obj, const char *value, Error **errp) +{ + QSevGuestInfo *s = QSEV_GUEST_INFO(obj); + + s->dh_cert_file = g_strdup(value); +} + +static char * +qsev_guest_get_sev_device(Object *obj, Error **errp) +{ + QSevGuestInfo *sev = QSEV_GUEST_INFO(obj); + + return g_strdup(sev->sev_device); +} + +static void +qsev_guest_set_sev_device(Object *obj, const char *value, Error **errp) +{ + QSevGuestInfo *sev = QSEV_GUEST_INFO(obj); + + sev->sev_device = g_strdup(value); +} + +static void +qsev_guest_class_init(ObjectClass *oc, void *data) +{ + object_class_property_add_str(oc, "sev-device", + qsev_guest_get_sev_device, + qsev_guest_set_sev_device, + NULL); + object_class_property_set_description(oc, "sev-device", + "SEV device to use", NULL); + object_class_property_add_str(oc, "dh-cert-file", + qsev_guest_get_dh_cert_file, + qsev_guest_set_dh_cert_file, + NULL); + object_class_property_set_description(oc, "dh-cert-file", + "guest owners DH certificate (encoded with base64)", NULL); + object_class_property_add_str(oc, "session-file", + qsev_guest_get_session_file, + qsev_guest_set_session_file, + NULL); + object_class_property_set_description(oc, "session-file", + "guest owners session parameters (encoded with base64)", NULL); +} + +static void +qsev_guest_set_handle(Object *obj, Visitor *v, const char *name, + void *opaque, Error **errp) +{ + QSevGuestInfo *sev = QSEV_GUEST_INFO(obj); + uint32_t value; + + visit_type_uint32(v, name, &value, errp); + sev->handle = value; +} + +static void +qsev_guest_set_policy(Object *obj, Visitor *v, const char *name, + void *opaque, Error **errp) +{ + QSevGuestInfo *sev = QSEV_GUEST_INFO(obj); + uint32_t value; + + visit_type_uint32(v, name, &value, errp); + sev->policy = value; +} + +static void +qsev_guest_set_cbitpos(Object *obj, Visitor *v, const char *name, + void *opaque, Error **errp) +{ + QSevGuestInfo *sev = QSEV_GUEST_INFO(obj); + uint32_t value; + + visit_type_uint32(v, name, &value, errp); + sev->cbitpos = value; +} + +static void +qsev_guest_set_reduced_phys_bits(Object *obj, Visitor *v, const char *name, + void *opaque, Error **errp) +{ + QSevGuestInfo *sev = QSEV_GUEST_INFO(obj); + uint32_t value; + + visit_type_uint32(v, name, &value, errp); + sev->reduced_phys_bits = value; +} + +static void +qsev_guest_get_policy(Object *obj, Visitor *v, const char *name, + void *opaque, Error **errp) +{ + uint32_t value; + QSevGuestInfo *sev = QSEV_GUEST_INFO(obj); + + value = sev->policy; + visit_type_uint32(v, name, &value, errp); +} + +static void +qsev_guest_get_handle(Object *obj, Visitor *v, const char *name, + void *opaque, Error **errp) +{ + uint32_t value; + QSevGuestInfo *sev = QSEV_GUEST_INFO(obj); + + value = sev->handle; + visit_type_uint32(v, name, &value, errp); +} + +static void +qsev_guest_get_cbitpos(Object *obj, Visitor *v, const char *name, + void *opaque, Error **errp) +{ + uint32_t value; + QSevGuestInfo *sev = QSEV_GUEST_INFO(obj); + + value = sev->cbitpos; + visit_type_uint32(v, name, &value, errp); +} + +static void +qsev_guest_get_reduced_phys_bits(Object *obj, Visitor *v, const char *name, + void *opaque, Error **errp) +{ + uint32_t value; + QSevGuestInfo *sev = QSEV_GUEST_INFO(obj); + + value = sev->reduced_phys_bits; + visit_type_uint32(v, name, &value, errp); +} + +static void +qsev_guest_init(Object *obj) +{ + QSevGuestInfo *sev = QSEV_GUEST_INFO(obj); + + sev->sev_device = g_strdup(DEFAULT_SEV_DEVICE); + sev->policy = DEFAULT_GUEST_POLICY; + object_property_add(obj, "policy", "uint32", qsev_guest_get_policy, + qsev_guest_set_policy, NULL, NULL, NULL); + object_property_add(obj, "handle", "uint32", qsev_guest_get_handle, + qsev_guest_set_handle, NULL, NULL, NULL); + object_property_add(obj, "cbitpos", "uint32", qsev_guest_get_cbitpos, + qsev_guest_set_cbitpos, NULL, NULL, NULL); + object_property_add(obj, "reduced-phys-bits", "uint32", + qsev_guest_get_reduced_phys_bits, + qsev_guest_set_reduced_phys_bits, NULL, NULL, NULL); +} + +/* sev guest info */ +static const TypeInfo qsev_guest_info = { + .parent = TYPE_OBJECT, + .name = TYPE_QSEV_GUEST_INFO, + .instance_size = sizeof(QSevGuestInfo), + .instance_finalize = qsev_guest_finalize, + .class_size = sizeof(QSevGuestInfoClass), + .class_init = qsev_guest_class_init, + .instance_init = qsev_guest_init, + .interfaces = (InterfaceInfo[]) { + { TYPE_USER_CREATABLE }, + { } + } +}; + +static QSevGuestInfo * +lookup_sev_guest_info(const char *id) +{ + Object *obj; + QSevGuestInfo *info; + + obj = object_resolve_path_component(object_get_objects_root(), id); + if (!obj) { + return NULL; + } + + info = (QSevGuestInfo *) + object_dynamic_cast(obj, TYPE_QSEV_GUEST_INFO); + if (!info) { + return NULL; + } + + return info; +} + +bool +sev_enabled(void) +{ + return sev_state ? true : false; +} + +uint64_t +sev_get_me_mask(void) +{ + return sev_state ? sev_state->me_mask : ~0; +} + +uint32_t +sev_get_cbit_position(void) +{ + return sev_state ? sev_state->cbitpos : 0; +} + +uint32_t +sev_get_reduced_phys_bits(void) +{ + return sev_state ? sev_state->reduced_phys_bits : 0; +} + +SevInfo * +sev_get_info(void) +{ + SevInfo *info; + + info = g_new0(SevInfo, 1); + info->enabled = sev_state ? true : false; + + if (info->enabled) { + info->api_major = sev_state->api_major; + info->api_minor = sev_state->api_minor; + info->build_id = sev_state->build_id; + info->policy = sev_state->policy; + info->state = sev_state->state; + info->handle = sev_state->handle; + } + + return info; +} + +static int +sev_get_pdh_info(int fd, guchar **pdh, size_t *pdh_len, guchar **cert_chain, + size_t *cert_chain_len) +{ + guchar *pdh_data, *cert_chain_data; + struct sev_user_data_pdh_cert_export export = {}; + int err, r; + + /* query the certificate length */ + r = sev_platform_ioctl(fd, SEV_PDH_CERT_EXPORT, &export, &err); + if (r < 0) { + if (err != SEV_RET_INVALID_LEN) { + error_report("failed to export PDH cert ret=%d fw_err=%d (%s)", + r, err, fw_error_to_str(err)); + return 1; + } + } + + pdh_data = g_new(guchar, export.pdh_cert_len); + cert_chain_data = g_new(guchar, export.cert_chain_len); + export.pdh_cert_address = (unsigned long)pdh_data; + export.cert_chain_address = (unsigned long)cert_chain_data; + + r = sev_platform_ioctl(fd, SEV_PDH_CERT_EXPORT, &export, &err); + if (r < 0) { + error_report("failed to export PDH cert ret=%d fw_err=%d (%s)", + r, err, fw_error_to_str(err)); + goto e_free; + } + + *pdh = pdh_data; + *pdh_len = export.pdh_cert_len; + *cert_chain = cert_chain_data; + *cert_chain_len = export.cert_chain_len; + return 0; + +e_free: + g_free(pdh_data); + g_free(cert_chain_data); + return 1; +} + +SevCapability * +sev_get_capabilities(void) +{ + SevCapability *cap; + guchar *pdh_data, *cert_chain_data; + size_t pdh_len = 0, cert_chain_len = 0; + uint32_t ebx; + int fd; + + fd = open(DEFAULT_SEV_DEVICE, O_RDWR); + if (fd < 0) { + error_report("%s: Failed to open %s '%s'", __func__, + DEFAULT_SEV_DEVICE, strerror(errno)); + return NULL; + } + + if (sev_get_pdh_info(fd, &pdh_data, &pdh_len, + &cert_chain_data, &cert_chain_len)) { + return NULL; + } + + cap = g_new0(SevCapability, 1); + cap->pdh = g_base64_encode(pdh_data, pdh_len); + cap->cert_chain = g_base64_encode(cert_chain_data, cert_chain_len); + + host_cpuid(0x8000001F, 0, NULL, &ebx, NULL, NULL); + cap->cbitpos = ebx & 0x3f; + + /* + * When SEV feature is enabled, we loose one bit in guest physical + * addressing. + */ + cap->reduced_phys_bits = 1; + + g_free(pdh_data); + g_free(cert_chain_data); + + close(fd); + return cap; +} + +static int +sev_read_file_base64(const char *filename, guchar **data, gsize *len) +{ + gsize sz; + gchar *base64; + GError *error = NULL; + + if (!g_file_get_contents(filename, &base64, &sz, &error)) { + error_report("failed to read '%s' (%s)", filename, error->message); + return -1; + } + + *data = g_base64_decode(base64, len); + return 0; +} + +static int +sev_launch_start(SEVState *s) +{ + gsize sz; + int ret = 1; + int fw_error; + QSevGuestInfo *sev = s->sev_info; + struct kvm_sev_launch_start *start; + guchar *session = NULL, *dh_cert = NULL; + + start = g_new0(struct kvm_sev_launch_start, 1); + + start->handle = object_property_get_int(OBJECT(sev), "handle", + &error_abort); + start->policy = object_property_get_int(OBJECT(sev), "policy", + &error_abort); + if (sev->session_file) { + if (sev_read_file_base64(sev->session_file, &session, &sz) < 0) { + return 1; + } + start->session_uaddr = (unsigned long)session; + start->session_len = sz; + } + + if (sev->dh_cert_file) { + if (sev_read_file_base64(sev->dh_cert_file, &dh_cert, &sz) < 0) { + return 1; + } + start->dh_uaddr = (unsigned long)dh_cert; + start->dh_len = sz; + } + + trace_kvm_sev_launch_start(start->policy, session, dh_cert); + ret = sev_ioctl(s->sev_fd, KVM_SEV_LAUNCH_START, start, &fw_error); + if (ret < 0) { + error_report("%s: LAUNCH_START ret=%d fw_error=%d '%s'", + __func__, ret, fw_error, fw_error_to_str(fw_error)); + return 1; + } + + object_property_set_int(OBJECT(sev), start->handle, "handle", + &error_abort); + sev_set_guest_state(SEV_STATE_LAUNCH_UPDATE); + s->handle = start->handle; + s->policy = start->policy; + + g_free(start); + g_free(session); + g_free(dh_cert); + + return 0; +} + +static int +sev_launch_update_data(uint8_t *addr, uint64_t len) +{ + int ret, fw_error; + struct kvm_sev_launch_update_data update; + + if (!addr || !len) { + return 1; + } + + update.uaddr = (__u64)(unsigned long)addr; + update.len = len; + trace_kvm_sev_launch_update_data(addr, len); + ret = sev_ioctl(sev_state->sev_fd, KVM_SEV_LAUNCH_UPDATE_DATA, + &update, &fw_error); + if (ret) { + error_report("%s: LAUNCH_UPDATE ret=%d fw_error=%d '%s'", + __func__, ret, fw_error, fw_error_to_str(fw_error)); + } + + return ret; +} + +static void +sev_launch_get_measure(Notifier *notifier, void *unused) +{ + int ret, error; + guchar *data; + SEVState *s = sev_state; + struct kvm_sev_launch_measure *measurement; + + if (!sev_check_state(SEV_STATE_LAUNCH_UPDATE)) { + return; + } + + measurement = g_new0(struct kvm_sev_launch_measure, 1); + + /* query the measurement blob length */ + ret = sev_ioctl(sev_state->sev_fd, KVM_SEV_LAUNCH_MEASURE, + measurement, &error); + if (!measurement->len) { + error_report("%s: LAUNCH_MEASURE ret=%d fw_error=%d '%s'", + __func__, ret, error, fw_error_to_str(errno)); + goto free_measurement; + } + + data = g_new0(guchar, measurement->len); + measurement->uaddr = (unsigned long)data; + + /* get the measurement blob */ + ret = sev_ioctl(sev_state->sev_fd, KVM_SEV_LAUNCH_MEASURE, + measurement, &error); + if (ret) { + error_report("%s: LAUNCH_MEASURE ret=%d fw_error=%d '%s'", + __func__, ret, error, fw_error_to_str(errno)); + goto free_data; + } + + sev_set_guest_state(SEV_STATE_LAUNCH_SECRET); + + /* encode the measurement value and emit the event */ + s->measurement = g_base64_encode(data, measurement->len); + trace_kvm_sev_launch_measurement(s->measurement); + +free_data: + g_free(data); +free_measurement: + g_free(measurement); +} + +char * +sev_get_launch_measurement(void) +{ + if (sev_state && + sev_state->state >= SEV_STATE_LAUNCH_SECRET) { + return g_strdup(sev_state->measurement); + } + + return NULL; +} + +static Notifier sev_machine_done_notify = { + .notify = sev_launch_get_measure, +}; + +static void +sev_launch_finish(SEVState *s) +{ + int ret, error; + Error *local_err = NULL; + + trace_kvm_sev_launch_finish(); + ret = sev_ioctl(sev_state->sev_fd, KVM_SEV_LAUNCH_FINISH, 0, &error); + if (ret) { + error_report("%s: LAUNCH_FINISH ret=%d fw_error=%d '%s'", + __func__, ret, error, fw_error_to_str(error)); + exit(1); + } + + sev_set_guest_state(SEV_STATE_RUNNING); + + /* add migration blocker */ + error_setg(&sev_mig_blocker, + "SEV: Migration is not implemented"); + ret = migrate_add_blocker(sev_mig_blocker, &local_err); + if (local_err) { + error_report_err(local_err); + error_free(sev_mig_blocker); + exit(1); + } +} + +static void +sev_vm_state_change(void *opaque, int running, RunState state) +{ + SEVState *s = opaque; + + if (running) { + if (!sev_check_state(SEV_STATE_RUNNING)) { + sev_launch_finish(s); + } + } +} + +void * +sev_guest_init(const char *id) +{ + SEVState *s; + char *devname; + int ret, fw_error; + uint32_t ebx; + uint32_t host_cbitpos; + struct sev_user_data_status status = {}; + + s = g_new0(SEVState, 1); + s->sev_info = lookup_sev_guest_info(id); + if (!s->sev_info) { + error_report("%s: '%s' is not a valid '%s' object", + __func__, id, TYPE_QSEV_GUEST_INFO); + goto err; + } + + sev_state = s; + s->state = SEV_STATE_UNINIT; + + host_cpuid(0x8000001F, 0, NULL, &ebx, NULL, NULL); + host_cbitpos = ebx & 0x3f; + + s->cbitpos = object_property_get_int(OBJECT(s->sev_info), "cbitpos", NULL); + if (host_cbitpos != s->cbitpos) { + error_report("%s: cbitpos check failed, host '%d' requested '%d'", + __func__, host_cbitpos, s->cbitpos); + goto err; + } + + s->reduced_phys_bits = object_property_get_int(OBJECT(s->sev_info), + "reduced-phys-bits", NULL); + if (s->reduced_phys_bits < 1) { + error_report("%s: reduced_phys_bits check failed, it should be >=1," + "' requested '%d'", __func__, s->reduced_phys_bits); + goto err; + } + + s->me_mask = ~(1UL << s->cbitpos); + + devname = object_property_get_str(OBJECT(s->sev_info), "sev-device", NULL); + s->sev_fd = open(devname, O_RDWR); + if (s->sev_fd < 0) { + error_report("%s: Failed to open %s '%s'", __func__, + devname, strerror(errno)); + goto err; + } + g_free(devname); + + ret = sev_platform_ioctl(s->sev_fd, SEV_PLATFORM_STATUS, &status, + &fw_error); + if (ret) { + error_report("%s: failed to get platform status ret=%d" + "fw_error='%d: %s'", __func__, ret, fw_error, + fw_error_to_str(fw_error)); + goto err; + } + s->build_id = status.build; + s->api_major = status.api_major; + s->api_minor = status.api_minor; + + trace_kvm_sev_init(); + ret = sev_ioctl(s->sev_fd, KVM_SEV_INIT, NULL, &fw_error); + if (ret) { + error_report("%s: failed to initialize ret=%d fw_error=%d '%s'", + __func__, ret, fw_error, fw_error_to_str(fw_error)); + goto err; + } + + ret = sev_launch_start(s); + if (ret) { + error_report("%s: failed to create encryption context", __func__); + goto err; + } + + ram_block_notifier_add(&sev_ram_notifier); + qemu_add_machine_init_done_notifier(&sev_machine_done_notify); + qemu_add_vm_change_state_handler(sev_vm_state_change, s); + + return s; +err: + g_free(sev_state); + sev_state = NULL; + return NULL; +} + +int +sev_encrypt_data(void *handle, uint8_t *ptr, uint64_t len) +{ + assert(handle); + + /* if SEV is in update state then encrypt the data else do nothing */ + if (sev_check_state(SEV_STATE_LAUNCH_UPDATE)) { + return sev_launch_update_data(ptr, len); + } + + return 0; +} + +static void +sev_register_types(void) +{ + type_register_static(&qsev_guest_info); +} + +type_init(sev_register_types); diff --git a/target/i386/sev_i386.h b/target/i386/sev_i386.h new file mode 100644 index 0000000000..b8622dfb1e --- /dev/null +++ b/target/i386/sev_i386.h @@ -0,0 +1,88 @@ +/* + * QEMU Secure Encrypted Virutualization (SEV) support + * + * Copyright: Advanced Micro Devices, 2016-2018 + * + * Authors: + * Brijesh Singh <brijesh.singh@amd.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. + * + */ + +#ifndef QEMU_SEV_I386_H +#define QEMU_SEV_I386_H + +#include "qom/object.h" +#include "qapi/error.h" +#include "sysemu/kvm.h" +#include "sysemu/sev.h" +#include "qemu/error-report.h" +#include "qapi/qapi-commands-misc.h" + +#define SEV_POLICY_NODBG 0x1 +#define SEV_POLICY_NOKS 0x2 +#define SEV_POLICY_ES 0x4 +#define SEV_POLICY_NOSEND 0x8 +#define SEV_POLICY_DOMAIN 0x10 +#define SEV_POLICY_SEV 0x20 + +#define TYPE_QSEV_GUEST_INFO "sev-guest" +#define QSEV_GUEST_INFO(obj) \ + OBJECT_CHECK(QSevGuestInfo, (obj), TYPE_QSEV_GUEST_INFO) + +extern bool sev_enabled(void); +extern uint64_t sev_get_me_mask(void); +extern SevInfo *sev_get_info(void); +extern uint32_t sev_get_cbit_position(void); +extern uint32_t sev_get_reduced_phys_bits(void); +extern char *sev_get_launch_measurement(void); +extern SevCapability *sev_get_capabilities(void); + +typedef struct QSevGuestInfo QSevGuestInfo; +typedef struct QSevGuestInfoClass QSevGuestInfoClass; + +/** + * QSevGuestInfo: + * + * The QSevGuestInfo object is used for creating a SEV guest. + * + * # $QEMU \ + * -object sev-guest,id=sev0 \ + * -machine ...,memory-encryption=sev0 + */ +struct QSevGuestInfo { + Object parent_obj; + + char *sev_device; + uint32_t policy; + uint32_t handle; + char *dh_cert_file; + char *session_file; + uint32_t cbitpos; + uint32_t reduced_phys_bits; +}; + +struct QSevGuestInfoClass { + ObjectClass parent_class; +}; + +struct SEVState { + QSevGuestInfo *sev_info; + uint8_t api_major; + uint8_t api_minor; + uint8_t build_id; + uint32_t policy; + uint64_t me_mask; + uint32_t cbitpos; + uint32_t reduced_phys_bits; + uint32_t handle; + int sev_fd; + SevState state; + gchar *measurement; +}; + +typedef struct SEVState SEVState; + +#endif diff --git a/target/i386/trace-events b/target/i386/trace-events index 3153fd4454..6a19a69af5 100644 --- a/target/i386/trace-events +++ b/target/i386/trace-events @@ -5,3 +5,13 @@ kvm_x86_fixup_msi_error(uint32_t gsi) "VT-d failed to remap interrupt for GSI %" kvm_x86_add_msi_route(int virq) "Adding route entry for virq %d" kvm_x86_remove_msi_route(int virq) "Removing route entry for virq %d" kvm_x86_update_msi_routes(int num) "Updated %d MSI routes" + +# target/i386/sev.c +kvm_sev_init(void) "" +kvm_memcrypt_register_region(void *addr, size_t len) "addr %p len 0x%zu" +kvm_memcrypt_unregister_region(void *addr, size_t len) "addr %p len 0x%zu" +kvm_sev_change_state(const char *old, const char *new) "%s -> %s" +kvm_sev_launch_start(int policy, void *session, void *pdh) "policy 0x%x session %p pdh %p" +kvm_sev_launch_update_data(void *addr, uint64_t len) "addr %p len 0x%" PRIu64 +kvm_sev_launch_measurement(const char *value) "data %s" +kvm_sev_launch_finish(void) "" diff --git a/target/lm32/cpu.c b/target/lm32/cpu.c index 96c2499d0b..0003152469 100644 --- a/target/lm32/cpu.c +++ b/target/lm32/cpu.c @@ -32,18 +32,6 @@ static void lm32_cpu_set_pc(CPUState *cs, vaddr value) cpu->env.pc = value; } -/* Sort alphabetically by type name. */ -static gint lm32_cpu_list_compare(gconstpointer a, gconstpointer b) -{ - ObjectClass *class_a = (ObjectClass *)a; - ObjectClass *class_b = (ObjectClass *)b; - const char *name_a, *name_b; - - name_a = object_class_get_name(class_a); - name_b = object_class_get_name(class_b); - return strcmp(name_a, name_b); -} - static void lm32_cpu_list_entry(gpointer data, gpointer user_data) { ObjectClass *oc = data; @@ -65,8 +53,7 @@ void lm32_cpu_list(FILE *f, fprintf_function cpu_fprintf) }; GSList *list; - list = object_class_get_list(TYPE_LM32_CPU, false); - list = g_slist_sort(list, lm32_cpu_list_compare); + list = object_class_get_list_sorted(TYPE_LM32_CPU, false); (*cpu_fprintf)(f, "Available CPUs:\n"); g_slist_foreach(list, lm32_cpu_list_entry, &s); g_slist_free(list); diff --git a/target/m68k/fpu_helper.c b/target/m68k/fpu_helper.c index 62cbb0dff1..6eeffdf9bb 100644 --- a/target/m68k/fpu_helper.c +++ b/target/m68k/fpu_helper.c @@ -592,3 +592,64 @@ void HELPER(ftentox)(CPUM68KState *env, FPReg *res, FPReg *val) { res->d = floatx80_tentox(val->d, &env->fp_status); } + +void HELPER(ftan)(CPUM68KState *env, FPReg *res, FPReg *val) +{ + res->d = floatx80_tan(val->d, &env->fp_status); +} + +void HELPER(fsin)(CPUM68KState *env, FPReg *res, FPReg *val) +{ + res->d = floatx80_sin(val->d, &env->fp_status); +} + +void HELPER(fcos)(CPUM68KState *env, FPReg *res, FPReg *val) +{ + res->d = floatx80_cos(val->d, &env->fp_status); +} + +void HELPER(fsincos)(CPUM68KState *env, FPReg *res0, FPReg *res1, FPReg *val) +{ + floatx80 a = val->d; + /* If res0 and res1 specify the same floating-point data register, + * the sine result is stored in the register, and the cosine + * result is discarded. + */ + res1->d = floatx80_cos(a, &env->fp_status); + res0->d = floatx80_sin(a, &env->fp_status); +} + +void HELPER(fatan)(CPUM68KState *env, FPReg *res, FPReg *val) +{ + res->d = floatx80_atan(val->d, &env->fp_status); +} + +void HELPER(fasin)(CPUM68KState *env, FPReg *res, FPReg *val) +{ + res->d = floatx80_asin(val->d, &env->fp_status); +} + +void HELPER(facos)(CPUM68KState *env, FPReg *res, FPReg *val) +{ + res->d = floatx80_acos(val->d, &env->fp_status); +} + +void HELPER(fatanh)(CPUM68KState *env, FPReg *res, FPReg *val) +{ + res->d = floatx80_atanh(val->d, &env->fp_status); +} + +void HELPER(ftanh)(CPUM68KState *env, FPReg *res, FPReg *val) +{ + res->d = floatx80_tanh(val->d, &env->fp_status); +} + +void HELPER(fsinh)(CPUM68KState *env, FPReg *res, FPReg *val) +{ + res->d = floatx80_sinh(val->d, &env->fp_status); +} + +void HELPER(fcosh)(CPUM68KState *env, FPReg *res, FPReg *val) +{ + res->d = floatx80_cosh(val->d, &env->fp_status); +} diff --git a/target/m68k/helper.h b/target/m68k/helper.h index 9a9734c196..feee7be626 100644 --- a/target/m68k/helper.h +++ b/target/m68k/helper.h @@ -75,6 +75,17 @@ DEF_HELPER_3(flog2, void, env, fp, fp) DEF_HELPER_3(fetox, void, env, fp, fp) DEF_HELPER_3(ftwotox, void, env, fp, fp) DEF_HELPER_3(ftentox, void, env, fp, fp) +DEF_HELPER_3(ftan, void, env, fp, fp) +DEF_HELPER_3(fsin, void, env, fp, fp) +DEF_HELPER_3(fcos, void, env, fp, fp) +DEF_HELPER_4(fsincos, void, env, fp, fp, fp) +DEF_HELPER_3(fatan, void, env, fp, fp) +DEF_HELPER_3(fasin, void, env, fp, fp) +DEF_HELPER_3(facos, void, env, fp, fp) +DEF_HELPER_3(fatanh, void, env, fp, fp) +DEF_HELPER_3(ftanh, void, env, fp, fp) +DEF_HELPER_3(fsinh, void, env, fp, fp) +DEF_HELPER_3(fcosh, void, env, fp, fp) DEF_HELPER_3(mac_move, void, env, i32, i32) DEF_HELPER_3(macmulf, i64, env, i32, i32) diff --git a/target/m68k/softfloat.c b/target/m68k/softfloat.c index 4bd5b9e6b7..dffb371c71 100644 --- a/target/m68k/softfloat.c +++ b/target/m68k/softfloat.c @@ -23,6 +23,10 @@ #include "fpu/softfloat-macros.h" #include "softfloat_fpsp_tables.h" +#define pi_exp 0x4000 +#define piby2_exp 0x3FFF +#define pi_sig LIT64(0xc90fdaa22168c235) + static floatx80 propagateFloatx80NaNOneArg(floatx80 a, float_status *status) { if (floatx80_is_signaling_nan(a, status)) { @@ -1266,3 +1270,1636 @@ floatx80 floatx80_tentox(floatx80 a, float_status *status) return a; } } + +/*---------------------------------------------------------------------------- + | Tangent + *----------------------------------------------------------------------------*/ + +floatx80 floatx80_tan(floatx80 a, float_status *status) +{ + flag aSign, xSign; + int32_t aExp, xExp; + uint64_t aSig, xSig; + + int8_t user_rnd_mode, user_rnd_prec; + + int32_t compact, l, n, j; + floatx80 fp0, fp1, fp2, fp3, fp4, fp5, invtwopi, twopi1, twopi2; + float32 twoto63; + flag endflag; + + aSig = extractFloatx80Frac(a); + aExp = extractFloatx80Exp(a); + aSign = extractFloatx80Sign(a); + + if (aExp == 0x7FFF) { + if ((uint64_t) (aSig << 1)) { + return propagateFloatx80NaNOneArg(a, status); + } + float_raise(float_flag_invalid, status); + return floatx80_default_nan(status); + } + + if (aExp == 0 && aSig == 0) { + return packFloatx80(aSign, 0, 0); + } + + user_rnd_mode = status->float_rounding_mode; + user_rnd_prec = status->floatx80_rounding_precision; + status->float_rounding_mode = float_round_nearest_even; + status->floatx80_rounding_precision = 80; + + compact = floatx80_make_compact(aExp, aSig); + + fp0 = a; + + if (compact < 0x3FD78000 || compact > 0x4004BC7E) { + /* 2^(-40) > |X| > 15 PI */ + if (compact > 0x3FFF8000) { /* |X| >= 15 PI */ + /* REDUCEX */ + fp1 = packFloatx80(0, 0, 0); + if (compact == 0x7FFEFFFF) { + twopi1 = packFloatx80(aSign ^ 1, 0x7FFE, + LIT64(0xC90FDAA200000000)); + twopi2 = packFloatx80(aSign ^ 1, 0x7FDC, + LIT64(0x85A308D300000000)); + fp0 = floatx80_add(fp0, twopi1, status); + fp1 = fp0; + fp0 = floatx80_add(fp0, twopi2, status); + fp1 = floatx80_sub(fp1, fp0, status); + fp1 = floatx80_add(fp1, twopi2, status); + } + loop: + xSign = extractFloatx80Sign(fp0); + xExp = extractFloatx80Exp(fp0); + xExp -= 0x3FFF; + if (xExp <= 28) { + l = 0; + endflag = 1; + } else { + l = xExp - 27; + endflag = 0; + } + invtwopi = packFloatx80(0, 0x3FFE - l, + LIT64(0xA2F9836E4E44152A)); /* INVTWOPI */ + twopi1 = packFloatx80(0, 0x3FFF + l, LIT64(0xC90FDAA200000000)); + twopi2 = packFloatx80(0, 0x3FDD + l, LIT64(0x85A308D300000000)); + + /* SIGN(INARG)*2^63 IN SGL */ + twoto63 = packFloat32(xSign, 0xBE, 0); + + fp2 = floatx80_mul(fp0, invtwopi, status); + fp2 = floatx80_add(fp2, float32_to_floatx80(twoto63, status), + status); /* THE FRACT PART OF FP2 IS ROUNDED */ + fp2 = floatx80_sub(fp2, float32_to_floatx80(twoto63, status), + status); /* FP2 is N */ + fp4 = floatx80_mul(twopi1, fp2, status); /* W = N*P1 */ + fp5 = floatx80_mul(twopi2, fp2, status); /* w = N*P2 */ + fp3 = floatx80_add(fp4, fp5, status); /* FP3 is P */ + fp4 = floatx80_sub(fp4, fp3, status); /* W-P */ + fp0 = floatx80_sub(fp0, fp3, status); /* FP0 is A := R - P */ + fp4 = floatx80_add(fp4, fp5, status); /* FP4 is p = (W-P)+w */ + fp3 = fp0; /* FP3 is A */ + fp1 = floatx80_sub(fp1, fp4, status); /* FP1 is a := r - p */ + fp0 = floatx80_add(fp0, fp1, status); /* FP0 is R := A+a */ + + if (endflag > 0) { + n = floatx80_to_int32(fp2, status); + goto tancont; + } + fp3 = floatx80_sub(fp3, fp0, status); /* A-R */ + fp1 = floatx80_add(fp1, fp3, status); /* FP1 is r := (A-R)+a */ + goto loop; + } else { + status->float_rounding_mode = user_rnd_mode; + status->floatx80_rounding_precision = user_rnd_prec; + + a = floatx80_move(a, status); + + float_raise(float_flag_inexact, status); + + return a; + } + } else { + fp1 = floatx80_mul(fp0, float64_to_floatx80( + make_float64(0x3FE45F306DC9C883), status), + status); /* X*2/PI */ + + n = floatx80_to_int32(fp1, status); + j = 32 + n; + + fp0 = floatx80_sub(fp0, pi_tbl[j], status); /* X-Y1 */ + fp0 = floatx80_sub(fp0, float32_to_floatx80(pi_tbl2[j], status), + status); /* FP0 IS R = (X-Y1)-Y2 */ + + tancont: + if (n & 1) { + /* NODD */ + fp1 = fp0; /* R */ + fp0 = floatx80_mul(fp0, fp0, status); /* S = R*R */ + fp3 = float64_to_floatx80(make_float64(0x3EA0B759F50F8688), + status); /* Q4 */ + fp2 = float64_to_floatx80(make_float64(0xBEF2BAA5A8924F04), + status); /* P3 */ + fp3 = floatx80_mul(fp3, fp0, status); /* SQ4 */ + fp2 = floatx80_mul(fp2, fp0, status); /* SP3 */ + fp3 = floatx80_add(fp3, float64_to_floatx80( + make_float64(0xBF346F59B39BA65F), status), + status); /* Q3+SQ4 */ + fp4 = packFloatx80(0, 0x3FF6, LIT64(0xE073D3FC199C4A00)); + fp2 = floatx80_add(fp2, fp4, status); /* P2+SP3 */ + fp3 = floatx80_mul(fp3, fp0, status); /* S(Q3+SQ4) */ + fp2 = floatx80_mul(fp2, fp0, status); /* S(P2+SP3) */ + fp4 = packFloatx80(0, 0x3FF9, LIT64(0xD23CD68415D95FA1)); + fp3 = floatx80_add(fp3, fp4, status); /* Q2+S(Q3+SQ4) */ + fp4 = packFloatx80(1, 0x3FFC, LIT64(0x8895A6C5FB423BCA)); + fp2 = floatx80_add(fp2, fp4, status); /* P1+S(P2+SP3) */ + fp3 = floatx80_mul(fp3, fp0, status); /* S(Q2+S(Q3+SQ4)) */ + fp2 = floatx80_mul(fp2, fp0, status); /* S(P1+S(P2+SP3)) */ + fp4 = packFloatx80(1, 0x3FFD, LIT64(0xEEF57E0DA84BC8CE)); + fp3 = floatx80_add(fp3, fp4, status); /* Q1+S(Q2+S(Q3+SQ4)) */ + fp2 = floatx80_mul(fp2, fp1, status); /* RS(P1+S(P2+SP3)) */ + fp0 = floatx80_mul(fp0, fp3, status); /* S(Q1+S(Q2+S(Q3+SQ4))) */ + fp1 = floatx80_add(fp1, fp2, status); /* R+RS(P1+S(P2+SP3)) */ + fp0 = floatx80_add(fp0, float32_to_floatx80( + make_float32(0x3F800000), status), + status); /* 1+S(Q1+S(Q2+S(Q3+SQ4))) */ + + xSign = extractFloatx80Sign(fp1); + xExp = extractFloatx80Exp(fp1); + xSig = extractFloatx80Frac(fp1); + xSign ^= 1; + fp1 = packFloatx80(xSign, xExp, xSig); + + status->float_rounding_mode = user_rnd_mode; + status->floatx80_rounding_precision = user_rnd_prec; + + a = floatx80_div(fp0, fp1, status); + + float_raise(float_flag_inexact, status); + + return a; + } else { + fp1 = floatx80_mul(fp0, fp0, status); /* S = R*R */ + fp3 = float64_to_floatx80(make_float64(0x3EA0B759F50F8688), + status); /* Q4 */ + fp2 = float64_to_floatx80(make_float64(0xBEF2BAA5A8924F04), + status); /* P3 */ + fp3 = floatx80_mul(fp3, fp1, status); /* SQ4 */ + fp2 = floatx80_mul(fp2, fp1, status); /* SP3 */ + fp3 = floatx80_add(fp3, float64_to_floatx80( + make_float64(0xBF346F59B39BA65F), status), + status); /* Q3+SQ4 */ + fp4 = packFloatx80(0, 0x3FF6, LIT64(0xE073D3FC199C4A00)); + fp2 = floatx80_add(fp2, fp4, status); /* P2+SP3 */ + fp3 = floatx80_mul(fp3, fp1, status); /* S(Q3+SQ4) */ + fp2 = floatx80_mul(fp2, fp1, status); /* S(P2+SP3) */ + fp4 = packFloatx80(0, 0x3FF9, LIT64(0xD23CD68415D95FA1)); + fp3 = floatx80_add(fp3, fp4, status); /* Q2+S(Q3+SQ4) */ + fp4 = packFloatx80(1, 0x3FFC, LIT64(0x8895A6C5FB423BCA)); + fp2 = floatx80_add(fp2, fp4, status); /* P1+S(P2+SP3) */ + fp3 = floatx80_mul(fp3, fp1, status); /* S(Q2+S(Q3+SQ4)) */ + fp2 = floatx80_mul(fp2, fp1, status); /* S(P1+S(P2+SP3)) */ + fp4 = packFloatx80(1, 0x3FFD, LIT64(0xEEF57E0DA84BC8CE)); + fp3 = floatx80_add(fp3, fp4, status); /* Q1+S(Q2+S(Q3+SQ4)) */ + fp2 = floatx80_mul(fp2, fp0, status); /* RS(P1+S(P2+SP3)) */ + fp1 = floatx80_mul(fp1, fp3, status); /* S(Q1+S(Q2+S(Q3+SQ4))) */ + fp0 = floatx80_add(fp0, fp2, status); /* R+RS(P1+S(P2+SP3)) */ + fp1 = floatx80_add(fp1, float32_to_floatx80( + make_float32(0x3F800000), status), + status); /* 1+S(Q1+S(Q2+S(Q3+SQ4))) */ + + status->float_rounding_mode = user_rnd_mode; + status->floatx80_rounding_precision = user_rnd_prec; + + a = floatx80_div(fp0, fp1, status); + + float_raise(float_flag_inexact, status); + + return a; + } + } +} + +/*---------------------------------------------------------------------------- + | Sine + *----------------------------------------------------------------------------*/ + +floatx80 floatx80_sin(floatx80 a, float_status *status) +{ + flag aSign, xSign; + int32_t aExp, xExp; + uint64_t aSig, xSig; + + int8_t user_rnd_mode, user_rnd_prec; + + int32_t compact, l, n, j; + floatx80 fp0, fp1, fp2, fp3, fp4, fp5, x, invtwopi, twopi1, twopi2; + float32 posneg1, twoto63; + flag adjn, endflag; + + aSig = extractFloatx80Frac(a); + aExp = extractFloatx80Exp(a); + aSign = extractFloatx80Sign(a); + + if (aExp == 0x7FFF) { + if ((uint64_t) (aSig << 1)) { + return propagateFloatx80NaNOneArg(a, status); + } + float_raise(float_flag_invalid, status); + return floatx80_default_nan(status); + } + + if (aExp == 0 && aSig == 0) { + return packFloatx80(aSign, 0, 0); + } + + adjn = 0; + + user_rnd_mode = status->float_rounding_mode; + user_rnd_prec = status->floatx80_rounding_precision; + status->float_rounding_mode = float_round_nearest_even; + status->floatx80_rounding_precision = 80; + + compact = floatx80_make_compact(aExp, aSig); + + fp0 = a; + + if (compact < 0x3FD78000 || compact > 0x4004BC7E) { + /* 2^(-40) > |X| > 15 PI */ + if (compact > 0x3FFF8000) { /* |X| >= 15 PI */ + /* REDUCEX */ + fp1 = packFloatx80(0, 0, 0); + if (compact == 0x7FFEFFFF) { + twopi1 = packFloatx80(aSign ^ 1, 0x7FFE, + LIT64(0xC90FDAA200000000)); + twopi2 = packFloatx80(aSign ^ 1, 0x7FDC, + LIT64(0x85A308D300000000)); + fp0 = floatx80_add(fp0, twopi1, status); + fp1 = fp0; + fp0 = floatx80_add(fp0, twopi2, status); + fp1 = floatx80_sub(fp1, fp0, status); + fp1 = floatx80_add(fp1, twopi2, status); + } + loop: + xSign = extractFloatx80Sign(fp0); + xExp = extractFloatx80Exp(fp0); + xExp -= 0x3FFF; + if (xExp <= 28) { + l = 0; + endflag = 1; + } else { + l = xExp - 27; + endflag = 0; + } + invtwopi = packFloatx80(0, 0x3FFE - l, + LIT64(0xA2F9836E4E44152A)); /* INVTWOPI */ + twopi1 = packFloatx80(0, 0x3FFF + l, LIT64(0xC90FDAA200000000)); + twopi2 = packFloatx80(0, 0x3FDD + l, LIT64(0x85A308D300000000)); + + /* SIGN(INARG)*2^63 IN SGL */ + twoto63 = packFloat32(xSign, 0xBE, 0); + + fp2 = floatx80_mul(fp0, invtwopi, status); + fp2 = floatx80_add(fp2, float32_to_floatx80(twoto63, status), + status); /* THE FRACT PART OF FP2 IS ROUNDED */ + fp2 = floatx80_sub(fp2, float32_to_floatx80(twoto63, status), + status); /* FP2 is N */ + fp4 = floatx80_mul(twopi1, fp2, status); /* W = N*P1 */ + fp5 = floatx80_mul(twopi2, fp2, status); /* w = N*P2 */ + fp3 = floatx80_add(fp4, fp5, status); /* FP3 is P */ + fp4 = floatx80_sub(fp4, fp3, status); /* W-P */ + fp0 = floatx80_sub(fp0, fp3, status); /* FP0 is A := R - P */ + fp4 = floatx80_add(fp4, fp5, status); /* FP4 is p = (W-P)+w */ + fp3 = fp0; /* FP3 is A */ + fp1 = floatx80_sub(fp1, fp4, status); /* FP1 is a := r - p */ + fp0 = floatx80_add(fp0, fp1, status); /* FP0 is R := A+a */ + + if (endflag > 0) { + n = floatx80_to_int32(fp2, status); + goto sincont; + } + fp3 = floatx80_sub(fp3, fp0, status); /* A-R */ + fp1 = floatx80_add(fp1, fp3, status); /* FP1 is r := (A-R)+a */ + goto loop; + } else { + /* SINSM */ + fp0 = float32_to_floatx80(make_float32(0x3F800000), + status); /* 1 */ + + status->float_rounding_mode = user_rnd_mode; + status->floatx80_rounding_precision = user_rnd_prec; + + if (adjn) { + /* COSTINY */ + a = floatx80_sub(fp0, float32_to_floatx80( + make_float32(0x00800000), status), status); + } else { + /* SINTINY */ + a = floatx80_move(a, status); + } + float_raise(float_flag_inexact, status); + + return a; + } + } else { + fp1 = floatx80_mul(fp0, float64_to_floatx80( + make_float64(0x3FE45F306DC9C883), status), + status); /* X*2/PI */ + + n = floatx80_to_int32(fp1, status); + j = 32 + n; + + fp0 = floatx80_sub(fp0, pi_tbl[j], status); /* X-Y1 */ + fp0 = floatx80_sub(fp0, float32_to_floatx80(pi_tbl2[j], status), + status); /* FP0 IS R = (X-Y1)-Y2 */ + + sincont: + if ((n + adjn) & 1) { + /* COSPOLY */ + fp0 = floatx80_mul(fp0, fp0, status); /* FP0 IS S */ + fp1 = floatx80_mul(fp0, fp0, status); /* FP1 IS T */ + fp2 = float64_to_floatx80(make_float64(0x3D2AC4D0D6011EE3), + status); /* B8 */ + fp3 = float64_to_floatx80(make_float64(0xBDA9396F9F45AC19), + status); /* B7 */ + + xSign = extractFloatx80Sign(fp0); /* X IS S */ + xExp = extractFloatx80Exp(fp0); + xSig = extractFloatx80Frac(fp0); + + if (((n + adjn) >> 1) & 1) { + xSign ^= 1; + posneg1 = make_float32(0xBF800000); /* -1 */ + } else { + xSign ^= 0; + posneg1 = make_float32(0x3F800000); /* 1 */ + } /* X IS NOW R'= SGN*R */ + + fp2 = floatx80_mul(fp2, fp1, status); /* TB8 */ + fp3 = floatx80_mul(fp3, fp1, status); /* TB7 */ + fp2 = floatx80_add(fp2, float64_to_floatx80( + make_float64(0x3E21EED90612C972), status), + status); /* B6+TB8 */ + fp3 = floatx80_add(fp3, float64_to_floatx80( + make_float64(0xBE927E4FB79D9FCF), status), + status); /* B5+TB7 */ + fp2 = floatx80_mul(fp2, fp1, status); /* T(B6+TB8) */ + fp3 = floatx80_mul(fp3, fp1, status); /* T(B5+TB7) */ + fp2 = floatx80_add(fp2, float64_to_floatx80( + make_float64(0x3EFA01A01A01D423), status), + status); /* B4+T(B6+TB8) */ + fp4 = packFloatx80(1, 0x3FF5, LIT64(0xB60B60B60B61D438)); + fp3 = floatx80_add(fp3, fp4, status); /* B3+T(B5+TB7) */ + fp2 = floatx80_mul(fp2, fp1, status); /* T(B4+T(B6+TB8)) */ + fp1 = floatx80_mul(fp1, fp3, status); /* T(B3+T(B5+TB7)) */ + fp4 = packFloatx80(0, 0x3FFA, LIT64(0xAAAAAAAAAAAAAB5E)); + fp2 = floatx80_add(fp2, fp4, status); /* B2+T(B4+T(B6+TB8)) */ + fp1 = floatx80_add(fp1, float32_to_floatx80( + make_float32(0xBF000000), status), + status); /* B1+T(B3+T(B5+TB7)) */ + fp0 = floatx80_mul(fp0, fp2, status); /* S(B2+T(B4+T(B6+TB8))) */ + fp0 = floatx80_add(fp0, fp1, status); /* [B1+T(B3+T(B5+TB7))]+ + * [S(B2+T(B4+T(B6+TB8)))] + */ + + x = packFloatx80(xSign, xExp, xSig); + fp0 = floatx80_mul(fp0, x, status); + + status->float_rounding_mode = user_rnd_mode; + status->floatx80_rounding_precision = user_rnd_prec; + + a = floatx80_add(fp0, float32_to_floatx80(posneg1, status), status); + + float_raise(float_flag_inexact, status); + + return a; + } else { + /* SINPOLY */ + xSign = extractFloatx80Sign(fp0); /* X IS R */ + xExp = extractFloatx80Exp(fp0); + xSig = extractFloatx80Frac(fp0); + + xSign ^= ((n + adjn) >> 1) & 1; /* X IS NOW R'= SGN*R */ + + fp0 = floatx80_mul(fp0, fp0, status); /* FP0 IS S */ + fp1 = floatx80_mul(fp0, fp0, status); /* FP1 IS T */ + fp3 = float64_to_floatx80(make_float64(0xBD6AAA77CCC994F5), + status); /* A7 */ + fp2 = float64_to_floatx80(make_float64(0x3DE612097AAE8DA1), + status); /* A6 */ + fp3 = floatx80_mul(fp3, fp1, status); /* T*A7 */ + fp2 = floatx80_mul(fp2, fp1, status); /* T*A6 */ + fp3 = floatx80_add(fp3, float64_to_floatx80( + make_float64(0xBE5AE6452A118AE4), status), + status); /* A5+T*A7 */ + fp2 = floatx80_add(fp2, float64_to_floatx80( + make_float64(0x3EC71DE3A5341531), status), + status); /* A4+T*A6 */ + fp3 = floatx80_mul(fp3, fp1, status); /* T(A5+TA7) */ + fp2 = floatx80_mul(fp2, fp1, status); /* T(A4+TA6) */ + fp3 = floatx80_add(fp3, float64_to_floatx80( + make_float64(0xBF2A01A01A018B59), status), + status); /* A3+T(A5+TA7) */ + fp4 = packFloatx80(0, 0x3FF8, LIT64(0x88888888888859AF)); + fp2 = floatx80_add(fp2, fp4, status); /* A2+T(A4+TA6) */ + fp1 = floatx80_mul(fp1, fp3, status); /* T(A3+T(A5+TA7)) */ + fp2 = floatx80_mul(fp2, fp0, status); /* S(A2+T(A4+TA6)) */ + fp4 = packFloatx80(1, 0x3FFC, LIT64(0xAAAAAAAAAAAAAA99)); + fp1 = floatx80_add(fp1, fp4, status); /* A1+T(A3+T(A5+TA7)) */ + fp1 = floatx80_add(fp1, fp2, + status); /* [A1+T(A3+T(A5+TA7))]+ + * [S(A2+T(A4+TA6))] + */ + + x = packFloatx80(xSign, xExp, xSig); + fp0 = floatx80_mul(fp0, x, status); /* R'*S */ + fp0 = floatx80_mul(fp0, fp1, status); /* SIN(R')-R' */ + + status->float_rounding_mode = user_rnd_mode; + status->floatx80_rounding_precision = user_rnd_prec; + + a = floatx80_add(fp0, x, status); + + float_raise(float_flag_inexact, status); + + return a; + } + } +} + +/*---------------------------------------------------------------------------- + | Cosine + *----------------------------------------------------------------------------*/ + +floatx80 floatx80_cos(floatx80 a, float_status *status) +{ + flag aSign, xSign; + int32_t aExp, xExp; + uint64_t aSig, xSig; + + int8_t user_rnd_mode, user_rnd_prec; + + int32_t compact, l, n, j; + floatx80 fp0, fp1, fp2, fp3, fp4, fp5, x, invtwopi, twopi1, twopi2; + float32 posneg1, twoto63; + flag adjn, endflag; + + aSig = extractFloatx80Frac(a); + aExp = extractFloatx80Exp(a); + aSign = extractFloatx80Sign(a); + + if (aExp == 0x7FFF) { + if ((uint64_t) (aSig << 1)) { + return propagateFloatx80NaNOneArg(a, status); + } + float_raise(float_flag_invalid, status); + return floatx80_default_nan(status); + } + + if (aExp == 0 && aSig == 0) { + return packFloatx80(0, one_exp, one_sig); + } + + adjn = 1; + + user_rnd_mode = status->float_rounding_mode; + user_rnd_prec = status->floatx80_rounding_precision; + status->float_rounding_mode = float_round_nearest_even; + status->floatx80_rounding_precision = 80; + + compact = floatx80_make_compact(aExp, aSig); + + fp0 = a; + + if (compact < 0x3FD78000 || compact > 0x4004BC7E) { + /* 2^(-40) > |X| > 15 PI */ + if (compact > 0x3FFF8000) { /* |X| >= 15 PI */ + /* REDUCEX */ + fp1 = packFloatx80(0, 0, 0); + if (compact == 0x7FFEFFFF) { + twopi1 = packFloatx80(aSign ^ 1, 0x7FFE, + LIT64(0xC90FDAA200000000)); + twopi2 = packFloatx80(aSign ^ 1, 0x7FDC, + LIT64(0x85A308D300000000)); + fp0 = floatx80_add(fp0, twopi1, status); + fp1 = fp0; + fp0 = floatx80_add(fp0, twopi2, status); + fp1 = floatx80_sub(fp1, fp0, status); + fp1 = floatx80_add(fp1, twopi2, status); + } + loop: + xSign = extractFloatx80Sign(fp0); + xExp = extractFloatx80Exp(fp0); + xExp -= 0x3FFF; + if (xExp <= 28) { + l = 0; + endflag = 1; + } else { + l = xExp - 27; + endflag = 0; + } + invtwopi = packFloatx80(0, 0x3FFE - l, + LIT64(0xA2F9836E4E44152A)); /* INVTWOPI */ + twopi1 = packFloatx80(0, 0x3FFF + l, LIT64(0xC90FDAA200000000)); + twopi2 = packFloatx80(0, 0x3FDD + l, LIT64(0x85A308D300000000)); + + /* SIGN(INARG)*2^63 IN SGL */ + twoto63 = packFloat32(xSign, 0xBE, 0); + + fp2 = floatx80_mul(fp0, invtwopi, status); + fp2 = floatx80_add(fp2, float32_to_floatx80(twoto63, status), + status); /* THE FRACT PART OF FP2 IS ROUNDED */ + fp2 = floatx80_sub(fp2, float32_to_floatx80(twoto63, status), + status); /* FP2 is N */ + fp4 = floatx80_mul(twopi1, fp2, status); /* W = N*P1 */ + fp5 = floatx80_mul(twopi2, fp2, status); /* w = N*P2 */ + fp3 = floatx80_add(fp4, fp5, status); /* FP3 is P */ + fp4 = floatx80_sub(fp4, fp3, status); /* W-P */ + fp0 = floatx80_sub(fp0, fp3, status); /* FP0 is A := R - P */ + fp4 = floatx80_add(fp4, fp5, status); /* FP4 is p = (W-P)+w */ + fp3 = fp0; /* FP3 is A */ + fp1 = floatx80_sub(fp1, fp4, status); /* FP1 is a := r - p */ + fp0 = floatx80_add(fp0, fp1, status); /* FP0 is R := A+a */ + + if (endflag > 0) { + n = floatx80_to_int32(fp2, status); + goto sincont; + } + fp3 = floatx80_sub(fp3, fp0, status); /* A-R */ + fp1 = floatx80_add(fp1, fp3, status); /* FP1 is r := (A-R)+a */ + goto loop; + } else { + /* SINSM */ + fp0 = float32_to_floatx80(make_float32(0x3F800000), status); /* 1 */ + + status->float_rounding_mode = user_rnd_mode; + status->floatx80_rounding_precision = user_rnd_prec; + + if (adjn) { + /* COSTINY */ + a = floatx80_sub(fp0, float32_to_floatx80( + make_float32(0x00800000), status), + status); + } else { + /* SINTINY */ + a = floatx80_move(a, status); + } + float_raise(float_flag_inexact, status); + + return a; + } + } else { + fp1 = floatx80_mul(fp0, float64_to_floatx80( + make_float64(0x3FE45F306DC9C883), status), + status); /* X*2/PI */ + + n = floatx80_to_int32(fp1, status); + j = 32 + n; + + fp0 = floatx80_sub(fp0, pi_tbl[j], status); /* X-Y1 */ + fp0 = floatx80_sub(fp0, float32_to_floatx80(pi_tbl2[j], status), + status); /* FP0 IS R = (X-Y1)-Y2 */ + + sincont: + if ((n + adjn) & 1) { + /* COSPOLY */ + fp0 = floatx80_mul(fp0, fp0, status); /* FP0 IS S */ + fp1 = floatx80_mul(fp0, fp0, status); /* FP1 IS T */ + fp2 = float64_to_floatx80(make_float64(0x3D2AC4D0D6011EE3), + status); /* B8 */ + fp3 = float64_to_floatx80(make_float64(0xBDA9396F9F45AC19), + status); /* B7 */ + + xSign = extractFloatx80Sign(fp0); /* X IS S */ + xExp = extractFloatx80Exp(fp0); + xSig = extractFloatx80Frac(fp0); + + if (((n + adjn) >> 1) & 1) { + xSign ^= 1; + posneg1 = make_float32(0xBF800000); /* -1 */ + } else { + xSign ^= 0; + posneg1 = make_float32(0x3F800000); /* 1 */ + } /* X IS NOW R'= SGN*R */ + + fp2 = floatx80_mul(fp2, fp1, status); /* TB8 */ + fp3 = floatx80_mul(fp3, fp1, status); /* TB7 */ + fp2 = floatx80_add(fp2, float64_to_floatx80( + make_float64(0x3E21EED90612C972), status), + status); /* B6+TB8 */ + fp3 = floatx80_add(fp3, float64_to_floatx80( + make_float64(0xBE927E4FB79D9FCF), status), + status); /* B5+TB7 */ + fp2 = floatx80_mul(fp2, fp1, status); /* T(B6+TB8) */ + fp3 = floatx80_mul(fp3, fp1, status); /* T(B5+TB7) */ + fp2 = floatx80_add(fp2, float64_to_floatx80( + make_float64(0x3EFA01A01A01D423), status), + status); /* B4+T(B6+TB8) */ + fp4 = packFloatx80(1, 0x3FF5, LIT64(0xB60B60B60B61D438)); + fp3 = floatx80_add(fp3, fp4, status); /* B3+T(B5+TB7) */ + fp2 = floatx80_mul(fp2, fp1, status); /* T(B4+T(B6+TB8)) */ + fp1 = floatx80_mul(fp1, fp3, status); /* T(B3+T(B5+TB7)) */ + fp4 = packFloatx80(0, 0x3FFA, LIT64(0xAAAAAAAAAAAAAB5E)); + fp2 = floatx80_add(fp2, fp4, status); /* B2+T(B4+T(B6+TB8)) */ + fp1 = floatx80_add(fp1, float32_to_floatx80( + make_float32(0xBF000000), status), + status); /* B1+T(B3+T(B5+TB7)) */ + fp0 = floatx80_mul(fp0, fp2, status); /* S(B2+T(B4+T(B6+TB8))) */ + fp0 = floatx80_add(fp0, fp1, status); + /* [B1+T(B3+T(B5+TB7))]+[S(B2+T(B4+T(B6+TB8)))] */ + + x = packFloatx80(xSign, xExp, xSig); + fp0 = floatx80_mul(fp0, x, status); + + status->float_rounding_mode = user_rnd_mode; + status->floatx80_rounding_precision = user_rnd_prec; + + a = floatx80_add(fp0, float32_to_floatx80(posneg1, status), status); + + float_raise(float_flag_inexact, status); + + return a; + } else { + /* SINPOLY */ + xSign = extractFloatx80Sign(fp0); /* X IS R */ + xExp = extractFloatx80Exp(fp0); + xSig = extractFloatx80Frac(fp0); + + xSign ^= ((n + adjn) >> 1) & 1; /* X IS NOW R'= SGN*R */ + + fp0 = floatx80_mul(fp0, fp0, status); /* FP0 IS S */ + fp1 = floatx80_mul(fp0, fp0, status); /* FP1 IS T */ + fp3 = float64_to_floatx80(make_float64(0xBD6AAA77CCC994F5), + status); /* A7 */ + fp2 = float64_to_floatx80(make_float64(0x3DE612097AAE8DA1), + status); /* A6 */ + fp3 = floatx80_mul(fp3, fp1, status); /* T*A7 */ + fp2 = floatx80_mul(fp2, fp1, status); /* T*A6 */ + fp3 = floatx80_add(fp3, float64_to_floatx80( + make_float64(0xBE5AE6452A118AE4), status), + status); /* A5+T*A7 */ + fp2 = floatx80_add(fp2, float64_to_floatx80( + make_float64(0x3EC71DE3A5341531), status), + status); /* A4+T*A6 */ + fp3 = floatx80_mul(fp3, fp1, status); /* T(A5+TA7) */ + fp2 = floatx80_mul(fp2, fp1, status); /* T(A4+TA6) */ + fp3 = floatx80_add(fp3, float64_to_floatx80( + make_float64(0xBF2A01A01A018B59), status), + status); /* A3+T(A5+TA7) */ + fp4 = packFloatx80(0, 0x3FF8, LIT64(0x88888888888859AF)); + fp2 = floatx80_add(fp2, fp4, status); /* A2+T(A4+TA6) */ + fp1 = floatx80_mul(fp1, fp3, status); /* T(A3+T(A5+TA7)) */ + fp2 = floatx80_mul(fp2, fp0, status); /* S(A2+T(A4+TA6)) */ + fp4 = packFloatx80(1, 0x3FFC, LIT64(0xAAAAAAAAAAAAAA99)); + fp1 = floatx80_add(fp1, fp4, status); /* A1+T(A3+T(A5+TA7)) */ + fp1 = floatx80_add(fp1, fp2, status); + /* [A1+T(A3+T(A5+TA7))]+[S(A2+T(A4+TA6))] */ + + x = packFloatx80(xSign, xExp, xSig); + fp0 = floatx80_mul(fp0, x, status); /* R'*S */ + fp0 = floatx80_mul(fp0, fp1, status); /* SIN(R')-R' */ + + status->float_rounding_mode = user_rnd_mode; + status->floatx80_rounding_precision = user_rnd_prec; + + a = floatx80_add(fp0, x, status); + + float_raise(float_flag_inexact, status); + + return a; + } + } +} + +/*---------------------------------------------------------------------------- + | Arc tangent + *----------------------------------------------------------------------------*/ + +floatx80 floatx80_atan(floatx80 a, float_status *status) +{ + flag aSign; + int32_t aExp; + uint64_t aSig; + + int8_t user_rnd_mode, user_rnd_prec; + + int32_t compact, tbl_index; + floatx80 fp0, fp1, fp2, fp3, xsave; + + aSig = extractFloatx80Frac(a); + aExp = extractFloatx80Exp(a); + aSign = extractFloatx80Sign(a); + + if (aExp == 0x7FFF) { + if ((uint64_t) (aSig << 1)) { + return propagateFloatx80NaNOneArg(a, status); + } + a = packFloatx80(aSign, piby2_exp, pi_sig); + float_raise(float_flag_inexact, status); + return floatx80_move(a, status); + } + + if (aExp == 0 && aSig == 0) { + return packFloatx80(aSign, 0, 0); + } + + compact = floatx80_make_compact(aExp, aSig); + + user_rnd_mode = status->float_rounding_mode; + user_rnd_prec = status->floatx80_rounding_precision; + status->float_rounding_mode = float_round_nearest_even; + status->floatx80_rounding_precision = 80; + + if (compact < 0x3FFB8000 || compact > 0x4002FFFF) { + /* |X| >= 16 or |X| < 1/16 */ + if (compact > 0x3FFF8000) { /* |X| >= 16 */ + if (compact > 0x40638000) { /* |X| > 2^(100) */ + fp0 = packFloatx80(aSign, piby2_exp, pi_sig); + fp1 = packFloatx80(aSign, 0x0001, one_sig); + + status->float_rounding_mode = user_rnd_mode; + status->floatx80_rounding_precision = user_rnd_prec; + + a = floatx80_sub(fp0, fp1, status); + + float_raise(float_flag_inexact, status); + + return a; + } else { + fp0 = a; + fp1 = packFloatx80(1, one_exp, one_sig); /* -1 */ + fp1 = floatx80_div(fp1, fp0, status); /* X' = -1/X */ + xsave = fp1; + fp0 = floatx80_mul(fp1, fp1, status); /* Y = X'*X' */ + fp1 = floatx80_mul(fp0, fp0, status); /* Z = Y*Y */ + fp3 = float64_to_floatx80(make_float64(0xBFB70BF398539E6A), + status); /* C5 */ + fp2 = float64_to_floatx80(make_float64(0x3FBC7187962D1D7D), + status); /* C4 */ + fp3 = floatx80_mul(fp3, fp1, status); /* Z*C5 */ + fp2 = floatx80_mul(fp2, fp1, status); /* Z*C4 */ + fp3 = floatx80_add(fp3, float64_to_floatx80( + make_float64(0xBFC24924827107B8), status), + status); /* C3+Z*C5 */ + fp2 = floatx80_add(fp2, float64_to_floatx80( + make_float64(0x3FC999999996263E), status), + status); /* C2+Z*C4 */ + fp1 = floatx80_mul(fp1, fp3, status); /* Z*(C3+Z*C5) */ + fp2 = floatx80_mul(fp2, fp0, status); /* Y*(C2+Z*C4) */ + fp1 = floatx80_add(fp1, float64_to_floatx80( + make_float64(0xBFD5555555555536), status), + status); /* C1+Z*(C3+Z*C5) */ + fp0 = floatx80_mul(fp0, xsave, status); /* X'*Y */ + /* [Y*(C2+Z*C4)]+[C1+Z*(C3+Z*C5)] */ + fp1 = floatx80_add(fp1, fp2, status); + /* X'*Y*([B1+Z*(B3+Z*B5)]+[Y*(B2+Z*(B4+Z*B6))]) ?? */ + fp0 = floatx80_mul(fp0, fp1, status); + fp0 = floatx80_add(fp0, xsave, status); + fp1 = packFloatx80(aSign, piby2_exp, pi_sig); + + status->float_rounding_mode = user_rnd_mode; + status->floatx80_rounding_precision = user_rnd_prec; + + a = floatx80_add(fp0, fp1, status); + + float_raise(float_flag_inexact, status); + + return a; + } + } else { /* |X| < 1/16 */ + if (compact < 0x3FD78000) { /* |X| < 2^(-40) */ + status->float_rounding_mode = user_rnd_mode; + status->floatx80_rounding_precision = user_rnd_prec; + + a = floatx80_move(a, status); + + float_raise(float_flag_inexact, status); + + return a; + } else { + fp0 = a; + xsave = a; + fp0 = floatx80_mul(fp0, fp0, status); /* Y = X*X */ + fp1 = floatx80_mul(fp0, fp0, status); /* Z = Y*Y */ + fp2 = float64_to_floatx80(make_float64(0x3FB344447F876989), + status); /* B6 */ + fp3 = float64_to_floatx80(make_float64(0xBFB744EE7FAF45DB), + status); /* B5 */ + fp2 = floatx80_mul(fp2, fp1, status); /* Z*B6 */ + fp3 = floatx80_mul(fp3, fp1, status); /* Z*B5 */ + fp2 = floatx80_add(fp2, float64_to_floatx80( + make_float64(0x3FBC71C646940220), status), + status); /* B4+Z*B6 */ + fp3 = floatx80_add(fp3, float64_to_floatx80( + make_float64(0xBFC24924921872F9), + status), status); /* B3+Z*B5 */ + fp2 = floatx80_mul(fp2, fp1, status); /* Z*(B4+Z*B6) */ + fp1 = floatx80_mul(fp1, fp3, status); /* Z*(B3+Z*B5) */ + fp2 = floatx80_add(fp2, float64_to_floatx80( + make_float64(0x3FC9999999998FA9), status), + status); /* B2+Z*(B4+Z*B6) */ + fp1 = floatx80_add(fp1, float64_to_floatx80( + make_float64(0xBFD5555555555555), status), + status); /* B1+Z*(B3+Z*B5) */ + fp2 = floatx80_mul(fp2, fp0, status); /* Y*(B2+Z*(B4+Z*B6)) */ + fp0 = floatx80_mul(fp0, xsave, status); /* X*Y */ + /* [B1+Z*(B3+Z*B5)]+[Y*(B2+Z*(B4+Z*B6))] */ + fp1 = floatx80_add(fp1, fp2, status); + /* X*Y*([B1+Z*(B3+Z*B5)]+[Y*(B2+Z*(B4+Z*B6))]) */ + fp0 = floatx80_mul(fp0, fp1, status); + + status->float_rounding_mode = user_rnd_mode; + status->floatx80_rounding_precision = user_rnd_prec; + + a = floatx80_add(fp0, xsave, status); + + float_raise(float_flag_inexact, status); + + return a; + } + } + } else { + aSig &= LIT64(0xF800000000000000); + aSig |= LIT64(0x0400000000000000); + xsave = packFloatx80(aSign, aExp, aSig); /* F */ + fp0 = a; + fp1 = a; /* X */ + fp2 = packFloatx80(0, one_exp, one_sig); /* 1 */ + fp1 = floatx80_mul(fp1, xsave, status); /* X*F */ + fp0 = floatx80_sub(fp0, xsave, status); /* X-F */ + fp1 = floatx80_add(fp1, fp2, status); /* 1 + X*F */ + fp0 = floatx80_div(fp0, fp1, status); /* U = (X-F)/(1+X*F) */ + + tbl_index = compact; + + tbl_index &= 0x7FFF0000; + tbl_index -= 0x3FFB0000; + tbl_index >>= 1; + tbl_index += compact & 0x00007800; + tbl_index >>= 11; + + fp3 = atan_tbl[tbl_index]; + + fp3.high |= aSign ? 0x8000 : 0; /* ATAN(F) */ + + fp1 = floatx80_mul(fp0, fp0, status); /* V = U*U */ + fp2 = float64_to_floatx80(make_float64(0xBFF6687E314987D8), + status); /* A3 */ + fp2 = floatx80_add(fp2, fp1, status); /* A3+V */ + fp2 = floatx80_mul(fp2, fp1, status); /* V*(A3+V) */ + fp1 = floatx80_mul(fp1, fp0, status); /* U*V */ + fp2 = floatx80_add(fp2, float64_to_floatx80( + make_float64(0x4002AC6934A26DB3), status), + status); /* A2+V*(A3+V) */ + fp1 = floatx80_mul(fp1, float64_to_floatx80( + make_float64(0xBFC2476F4E1DA28E), status), + status); /* A1+U*V */ + fp1 = floatx80_mul(fp1, fp2, status); /* A1*U*V*(A2+V*(A3+V)) */ + fp0 = floatx80_add(fp0, fp1, status); /* ATAN(U) */ + + status->float_rounding_mode = user_rnd_mode; + status->floatx80_rounding_precision = user_rnd_prec; + + a = floatx80_add(fp0, fp3, status); /* ATAN(X) */ + + float_raise(float_flag_inexact, status); + + return a; + } +} + +/*---------------------------------------------------------------------------- + | Arc sine + *----------------------------------------------------------------------------*/ + +floatx80 floatx80_asin(floatx80 a, float_status *status) +{ + flag aSign; + int32_t aExp; + uint64_t aSig; + + int8_t user_rnd_mode, user_rnd_prec; + + int32_t compact; + floatx80 fp0, fp1, fp2, one; + + aSig = extractFloatx80Frac(a); + aExp = extractFloatx80Exp(a); + aSign = extractFloatx80Sign(a); + + if (aExp == 0x7FFF && (uint64_t) (aSig << 1)) { + return propagateFloatx80NaNOneArg(a, status); + } + + if (aExp == 0 && aSig == 0) { + return packFloatx80(aSign, 0, 0); + } + + compact = floatx80_make_compact(aExp, aSig); + + if (compact >= 0x3FFF8000) { /* |X| >= 1 */ + if (aExp == one_exp && aSig == one_sig) { /* |X| == 1 */ + float_raise(float_flag_inexact, status); + a = packFloatx80(aSign, piby2_exp, pi_sig); + return floatx80_move(a, status); + } else { /* |X| > 1 */ + float_raise(float_flag_invalid, status); + return floatx80_default_nan(status); + } + + } /* |X| < 1 */ + + user_rnd_mode = status->float_rounding_mode; + user_rnd_prec = status->floatx80_rounding_precision; + status->float_rounding_mode = float_round_nearest_even; + status->floatx80_rounding_precision = 80; + + one = packFloatx80(0, one_exp, one_sig); + fp0 = a; + + fp1 = floatx80_sub(one, fp0, status); /* 1 - X */ + fp2 = floatx80_add(one, fp0, status); /* 1 + X */ + fp1 = floatx80_mul(fp2, fp1, status); /* (1+X)*(1-X) */ + fp1 = floatx80_sqrt(fp1, status); /* SQRT((1+X)*(1-X)) */ + fp0 = floatx80_div(fp0, fp1, status); /* X/SQRT((1+X)*(1-X)) */ + + status->float_rounding_mode = user_rnd_mode; + status->floatx80_rounding_precision = user_rnd_prec; + + a = floatx80_atan(fp0, status); /* ATAN(X/SQRT((1+X)*(1-X))) */ + + float_raise(float_flag_inexact, status); + + return a; +} + +/*---------------------------------------------------------------------------- + | Arc cosine + *----------------------------------------------------------------------------*/ + +floatx80 floatx80_acos(floatx80 a, float_status *status) +{ + flag aSign; + int32_t aExp; + uint64_t aSig; + + int8_t user_rnd_mode, user_rnd_prec; + + int32_t compact; + floatx80 fp0, fp1, one; + + aSig = extractFloatx80Frac(a); + aExp = extractFloatx80Exp(a); + aSign = extractFloatx80Sign(a); + + if (aExp == 0x7FFF && (uint64_t) (aSig << 1)) { + return propagateFloatx80NaNOneArg(a, status); + } + if (aExp == 0 && aSig == 0) { + float_raise(float_flag_inexact, status); + return roundAndPackFloatx80(status->floatx80_rounding_precision, 0, + piby2_exp, pi_sig, 0, status); + } + + compact = floatx80_make_compact(aExp, aSig); + + if (compact >= 0x3FFF8000) { /* |X| >= 1 */ + if (aExp == one_exp && aSig == one_sig) { /* |X| == 1 */ + if (aSign) { /* X == -1 */ + a = packFloatx80(0, pi_exp, pi_sig); + float_raise(float_flag_inexact, status); + return floatx80_move(a, status); + } else { /* X == +1 */ + return packFloatx80(0, 0, 0); + } + } else { /* |X| > 1 */ + float_raise(float_flag_invalid, status); + return floatx80_default_nan(status); + } + } /* |X| < 1 */ + + user_rnd_mode = status->float_rounding_mode; + user_rnd_prec = status->floatx80_rounding_precision; + status->float_rounding_mode = float_round_nearest_even; + status->floatx80_rounding_precision = 80; + + one = packFloatx80(0, one_exp, one_sig); + fp0 = a; + + fp1 = floatx80_add(one, fp0, status); /* 1 + X */ + fp0 = floatx80_sub(one, fp0, status); /* 1 - X */ + fp0 = floatx80_div(fp0, fp1, status); /* (1-X)/(1+X) */ + fp0 = floatx80_sqrt(fp0, status); /* SQRT((1-X)/(1+X)) */ + fp0 = floatx80_atan(fp0, status); /* ATAN(SQRT((1-X)/(1+X))) */ + + status->float_rounding_mode = user_rnd_mode; + status->floatx80_rounding_precision = user_rnd_prec; + + a = floatx80_add(fp0, fp0, status); /* 2 * ATAN(SQRT((1-X)/(1+X))) */ + + float_raise(float_flag_inexact, status); + + return a; +} + +/*---------------------------------------------------------------------------- + | Hyperbolic arc tangent + *----------------------------------------------------------------------------*/ + +floatx80 floatx80_atanh(floatx80 a, float_status *status) +{ + flag aSign; + int32_t aExp; + uint64_t aSig; + + int8_t user_rnd_mode, user_rnd_prec; + + int32_t compact; + floatx80 fp0, fp1, fp2, one; + + aSig = extractFloatx80Frac(a); + aExp = extractFloatx80Exp(a); + aSign = extractFloatx80Sign(a); + + if (aExp == 0x7FFF && (uint64_t) (aSig << 1)) { + return propagateFloatx80NaNOneArg(a, status); + } + + if (aExp == 0 && aSig == 0) { + return packFloatx80(aSign, 0, 0); + } + + compact = floatx80_make_compact(aExp, aSig); + + if (compact >= 0x3FFF8000) { /* |X| >= 1 */ + if (aExp == one_exp && aSig == one_sig) { /* |X| == 1 */ + float_raise(float_flag_divbyzero, status); + return packFloatx80(aSign, floatx80_infinity.high, + floatx80_infinity.low); + } else { /* |X| > 1 */ + float_raise(float_flag_invalid, status); + return floatx80_default_nan(status); + } + } /* |X| < 1 */ + + user_rnd_mode = status->float_rounding_mode; + user_rnd_prec = status->floatx80_rounding_precision; + status->float_rounding_mode = float_round_nearest_even; + status->floatx80_rounding_precision = 80; + + one = packFloatx80(0, one_exp, one_sig); + fp2 = packFloatx80(aSign, 0x3FFE, one_sig); /* SIGN(X) * (1/2) */ + fp0 = packFloatx80(0, aExp, aSig); /* Y = |X| */ + fp1 = packFloatx80(1, aExp, aSig); /* -Y */ + fp0 = floatx80_add(fp0, fp0, status); /* 2Y */ + fp1 = floatx80_add(fp1, one, status); /* 1-Y */ + fp0 = floatx80_div(fp0, fp1, status); /* Z = 2Y/(1-Y) */ + fp0 = floatx80_lognp1(fp0, status); /* LOG1P(Z) */ + + status->float_rounding_mode = user_rnd_mode; + status->floatx80_rounding_precision = user_rnd_prec; + + a = floatx80_mul(fp0, fp2, + status); /* ATANH(X) = SIGN(X) * (1/2) * LOG1P(Z) */ + + float_raise(float_flag_inexact, status); + + return a; +} + +/*---------------------------------------------------------------------------- + | e to x minus 1 + *----------------------------------------------------------------------------*/ + +floatx80 floatx80_etoxm1(floatx80 a, float_status *status) +{ + flag aSign; + int32_t aExp; + uint64_t aSig; + + int8_t user_rnd_mode, user_rnd_prec; + + int32_t compact, n, j, m, m1; + floatx80 fp0, fp1, fp2, fp3, l2, sc, onebysc; + + aSig = extractFloatx80Frac(a); + aExp = extractFloatx80Exp(a); + aSign = extractFloatx80Sign(a); + + if (aExp == 0x7FFF) { + if ((uint64_t) (aSig << 1)) { + return propagateFloatx80NaNOneArg(a, status); + } + if (aSign) { + return packFloatx80(aSign, one_exp, one_sig); + } + return packFloatx80(0, floatx80_infinity.high, + floatx80_infinity.low); + } + + if (aExp == 0 && aSig == 0) { + return packFloatx80(aSign, 0, 0); + } + + user_rnd_mode = status->float_rounding_mode; + user_rnd_prec = status->floatx80_rounding_precision; + status->float_rounding_mode = float_round_nearest_even; + status->floatx80_rounding_precision = 80; + + if (aExp >= 0x3FFD) { /* |X| >= 1/4 */ + compact = floatx80_make_compact(aExp, aSig); + + if (compact <= 0x4004C215) { /* |X| <= 70 log2 */ + fp0 = a; + fp1 = a; + fp0 = floatx80_mul(fp0, float32_to_floatx80( + make_float32(0x42B8AA3B), status), + status); /* 64/log2 * X */ + n = floatx80_to_int32(fp0, status); /* int(64/log2*X) */ + fp0 = int32_to_floatx80(n, status); + + j = n & 0x3F; /* J = N mod 64 */ + m = n / 64; /* NOTE: this is really arithmetic right shift by 6 */ + if (n < 0 && j) { + /* arithmetic right shift is division and + * round towards minus infinity + */ + m--; + } + m1 = -m; + /*m += 0x3FFF; // biased exponent of 2^(M) */ + /*m1 += 0x3FFF; // biased exponent of -2^(-M) */ + + fp2 = fp0; /* N */ + fp0 = floatx80_mul(fp0, float32_to_floatx80( + make_float32(0xBC317218), status), + status); /* N * L1, L1 = lead(-log2/64) */ + l2 = packFloatx80(0, 0x3FDC, LIT64(0x82E308654361C4C6)); + fp2 = floatx80_mul(fp2, l2, status); /* N * L2, L1+L2 = -log2/64 */ + fp0 = floatx80_add(fp0, fp1, status); /* X + N*L1 */ + fp0 = floatx80_add(fp0, fp2, status); /* R */ + + fp1 = floatx80_mul(fp0, fp0, status); /* S = R*R */ + fp2 = float32_to_floatx80(make_float32(0x3950097B), + status); /* A6 */ + fp2 = floatx80_mul(fp2, fp1, status); /* fp2 is S*A6 */ + fp3 = floatx80_mul(float32_to_floatx80(make_float32(0x3AB60B6A), + status), fp1, status); /* fp3 is S*A5 */ + fp2 = floatx80_add(fp2, float64_to_floatx80( + make_float64(0x3F81111111174385), status), + status); /* fp2 IS A4+S*A6 */ + fp3 = floatx80_add(fp3, float64_to_floatx80( + make_float64(0x3FA5555555554F5A), status), + status); /* fp3 is A3+S*A5 */ + fp2 = floatx80_mul(fp2, fp1, status); /* fp2 IS S*(A4+S*A6) */ + fp3 = floatx80_mul(fp3, fp1, status); /* fp3 IS S*(A3+S*A5) */ + fp2 = floatx80_add(fp2, float64_to_floatx80( + make_float64(0x3FC5555555555555), status), + status); /* fp2 IS A2+S*(A4+S*A6) */ + fp3 = floatx80_add(fp3, float32_to_floatx80( + make_float32(0x3F000000), status), + status); /* fp3 IS A1+S*(A3+S*A5) */ + fp2 = floatx80_mul(fp2, fp1, + status); /* fp2 IS S*(A2+S*(A4+S*A6)) */ + fp1 = floatx80_mul(fp1, fp3, + status); /* fp1 IS S*(A1+S*(A3+S*A5)) */ + fp2 = floatx80_mul(fp2, fp0, + status); /* fp2 IS R*S*(A2+S*(A4+S*A6)) */ + fp0 = floatx80_add(fp0, fp1, + status); /* fp0 IS R+S*(A1+S*(A3+S*A5)) */ + fp0 = floatx80_add(fp0, fp2, status); /* fp0 IS EXP(R) - 1 */ + + fp0 = floatx80_mul(fp0, exp_tbl[j], + status); /* 2^(J/64)*(Exp(R)-1) */ + + if (m >= 64) { + fp1 = float32_to_floatx80(exp_tbl2[j], status); + onebysc = packFloatx80(1, m1 + 0x3FFF, one_sig); /* -2^(-M) */ + fp1 = floatx80_add(fp1, onebysc, status); + fp0 = floatx80_add(fp0, fp1, status); + fp0 = floatx80_add(fp0, exp_tbl[j], status); + } else if (m < -3) { + fp0 = floatx80_add(fp0, float32_to_floatx80(exp_tbl2[j], + status), status); + fp0 = floatx80_add(fp0, exp_tbl[j], status); + onebysc = packFloatx80(1, m1 + 0x3FFF, one_sig); /* -2^(-M) */ + fp0 = floatx80_add(fp0, onebysc, status); + } else { /* -3 <= m <= 63 */ + fp1 = exp_tbl[j]; + fp0 = floatx80_add(fp0, float32_to_floatx80(exp_tbl2[j], + status), status); + onebysc = packFloatx80(1, m1 + 0x3FFF, one_sig); /* -2^(-M) */ + fp1 = floatx80_add(fp1, onebysc, status); + fp0 = floatx80_add(fp0, fp1, status); + } + + sc = packFloatx80(0, m + 0x3FFF, one_sig); + + status->float_rounding_mode = user_rnd_mode; + status->floatx80_rounding_precision = user_rnd_prec; + + a = floatx80_mul(fp0, sc, status); + + float_raise(float_flag_inexact, status); + + return a; + } else { /* |X| > 70 log2 */ + if (aSign) { + fp0 = float32_to_floatx80(make_float32(0xBF800000), + status); /* -1 */ + + status->float_rounding_mode = user_rnd_mode; + status->floatx80_rounding_precision = user_rnd_prec; + + a = floatx80_add(fp0, float32_to_floatx80( + make_float32(0x00800000), status), + status); /* -1 + 2^(-126) */ + + float_raise(float_flag_inexact, status); + + return a; + } else { + status->float_rounding_mode = user_rnd_mode; + status->floatx80_rounding_precision = user_rnd_prec; + + return floatx80_etox(a, status); + } + } + } else { /* |X| < 1/4 */ + if (aExp >= 0x3FBE) { + fp0 = a; + fp0 = floatx80_mul(fp0, fp0, status); /* S = X*X */ + fp1 = float32_to_floatx80(make_float32(0x2F30CAA8), + status); /* B12 */ + fp1 = floatx80_mul(fp1, fp0, status); /* S * B12 */ + fp2 = float32_to_floatx80(make_float32(0x310F8290), + status); /* B11 */ + fp1 = floatx80_add(fp1, float32_to_floatx80( + make_float32(0x32D73220), status), + status); /* B10 */ + fp2 = floatx80_mul(fp2, fp0, status); + fp1 = floatx80_mul(fp1, fp0, status); + fp2 = floatx80_add(fp2, float32_to_floatx80( + make_float32(0x3493F281), status), + status); /* B9 */ + fp1 = floatx80_add(fp1, float64_to_floatx80( + make_float64(0x3EC71DE3A5774682), status), + status); /* B8 */ + fp2 = floatx80_mul(fp2, fp0, status); + fp1 = floatx80_mul(fp1, fp0, status); + fp2 = floatx80_add(fp2, float64_to_floatx80( + make_float64(0x3EFA01A019D7CB68), status), + status); /* B7 */ + fp1 = floatx80_add(fp1, float64_to_floatx80( + make_float64(0x3F2A01A01A019DF3), status), + status); /* B6 */ + fp2 = floatx80_mul(fp2, fp0, status); + fp1 = floatx80_mul(fp1, fp0, status); + fp2 = floatx80_add(fp2, float64_to_floatx80( + make_float64(0x3F56C16C16C170E2), status), + status); /* B5 */ + fp1 = floatx80_add(fp1, float64_to_floatx80( + make_float64(0x3F81111111111111), status), + status); /* B4 */ + fp2 = floatx80_mul(fp2, fp0, status); + fp1 = floatx80_mul(fp1, fp0, status); + fp2 = floatx80_add(fp2, float64_to_floatx80( + make_float64(0x3FA5555555555555), status), + status); /* B3 */ + fp3 = packFloatx80(0, 0x3FFC, LIT64(0xAAAAAAAAAAAAAAAB)); + fp1 = floatx80_add(fp1, fp3, status); /* B2 */ + fp2 = floatx80_mul(fp2, fp0, status); + fp1 = floatx80_mul(fp1, fp0, status); + + fp2 = floatx80_mul(fp2, fp0, status); + fp1 = floatx80_mul(fp1, a, status); + + fp0 = floatx80_mul(fp0, float32_to_floatx80( + make_float32(0x3F000000), status), + status); /* S*B1 */ + fp1 = floatx80_add(fp1, fp2, status); /* Q */ + fp0 = floatx80_add(fp0, fp1, status); /* S*B1+Q */ + + status->float_rounding_mode = user_rnd_mode; + status->floatx80_rounding_precision = user_rnd_prec; + + a = floatx80_add(fp0, a, status); + + float_raise(float_flag_inexact, status); + + return a; + } else { /* |X| < 2^(-65) */ + sc = packFloatx80(1, 1, one_sig); + fp0 = a; + + if (aExp < 0x0033) { /* |X| < 2^(-16382) */ + fp0 = floatx80_mul(fp0, float64_to_floatx80( + make_float64(0x48B0000000000000), status), + status); + fp0 = floatx80_add(fp0, sc, status); + + status->float_rounding_mode = user_rnd_mode; + status->floatx80_rounding_precision = user_rnd_prec; + + a = floatx80_mul(fp0, float64_to_floatx80( + make_float64(0x3730000000000000), status), + status); + } else { + status->float_rounding_mode = user_rnd_mode; + status->floatx80_rounding_precision = user_rnd_prec; + + a = floatx80_add(fp0, sc, status); + } + + float_raise(float_flag_inexact, status); + + return a; + } + } +} + +/*---------------------------------------------------------------------------- + | Hyperbolic tangent + *----------------------------------------------------------------------------*/ + +floatx80 floatx80_tanh(floatx80 a, float_status *status) +{ + flag aSign, vSign; + int32_t aExp, vExp; + uint64_t aSig, vSig; + + int8_t user_rnd_mode, user_rnd_prec; + + int32_t compact; + floatx80 fp0, fp1; + uint32_t sign; + + aSig = extractFloatx80Frac(a); + aExp = extractFloatx80Exp(a); + aSign = extractFloatx80Sign(a); + + if (aExp == 0x7FFF) { + if ((uint64_t) (aSig << 1)) { + return propagateFloatx80NaNOneArg(a, status); + } + return packFloatx80(aSign, one_exp, one_sig); + } + + if (aExp == 0 && aSig == 0) { + return packFloatx80(aSign, 0, 0); + } + + user_rnd_mode = status->float_rounding_mode; + user_rnd_prec = status->floatx80_rounding_precision; + status->float_rounding_mode = float_round_nearest_even; + status->floatx80_rounding_precision = 80; + + compact = floatx80_make_compact(aExp, aSig); + + if (compact < 0x3FD78000 || compact > 0x3FFFDDCE) { + /* TANHBORS */ + if (compact < 0x3FFF8000) { + /* TANHSM */ + status->float_rounding_mode = user_rnd_mode; + status->floatx80_rounding_precision = user_rnd_prec; + + a = floatx80_move(a, status); + + float_raise(float_flag_inexact, status); + + return a; + } else { + if (compact > 0x40048AA1) { + /* TANHHUGE */ + sign = 0x3F800000; + sign |= aSign ? 0x80000000 : 0x00000000; + fp0 = float32_to_floatx80(make_float32(sign), status); + sign &= 0x80000000; + sign ^= 0x80800000; /* -SIGN(X)*EPS */ + + status->float_rounding_mode = user_rnd_mode; + status->floatx80_rounding_precision = user_rnd_prec; + + a = floatx80_add(fp0, float32_to_floatx80(make_float32(sign), + status), status); + + float_raise(float_flag_inexact, status); + + return a; + } else { + fp0 = packFloatx80(0, aExp + 1, aSig); /* Y = 2|X| */ + fp0 = floatx80_etox(fp0, status); /* FP0 IS EXP(Y) */ + fp0 = floatx80_add(fp0, float32_to_floatx80( + make_float32(0x3F800000), + status), status); /* EXP(Y)+1 */ + sign = aSign ? 0x80000000 : 0x00000000; + fp1 = floatx80_div(float32_to_floatx80(make_float32( + sign ^ 0xC0000000), status), fp0, + status); /* -SIGN(X)*2 / [EXP(Y)+1] */ + fp0 = float32_to_floatx80(make_float32(sign | 0x3F800000), + status); /* SIGN */ + + status->float_rounding_mode = user_rnd_mode; + status->floatx80_rounding_precision = user_rnd_prec; + + a = floatx80_add(fp1, fp0, status); + + float_raise(float_flag_inexact, status); + + return a; + } + } + } else { /* 2**(-40) < |X| < (5/2)LOG2 */ + fp0 = packFloatx80(0, aExp + 1, aSig); /* Y = 2|X| */ + fp0 = floatx80_etoxm1(fp0, status); /* FP0 IS Z = EXPM1(Y) */ + fp1 = floatx80_add(fp0, float32_to_floatx80(make_float32(0x40000000), + status), + status); /* Z+2 */ + + vSign = extractFloatx80Sign(fp1); + vExp = extractFloatx80Exp(fp1); + vSig = extractFloatx80Frac(fp1); + + fp1 = packFloatx80(vSign ^ aSign, vExp, vSig); + + status->float_rounding_mode = user_rnd_mode; + status->floatx80_rounding_precision = user_rnd_prec; + + a = floatx80_div(fp0, fp1, status); + + float_raise(float_flag_inexact, status); + + return a; + } +} + +/*---------------------------------------------------------------------------- + | Hyperbolic sine + *----------------------------------------------------------------------------*/ + +floatx80 floatx80_sinh(floatx80 a, float_status *status) +{ + flag aSign; + int32_t aExp; + uint64_t aSig; + + int8_t user_rnd_mode, user_rnd_prec; + + int32_t compact; + floatx80 fp0, fp1, fp2; + float32 fact; + + aSig = extractFloatx80Frac(a); + aExp = extractFloatx80Exp(a); + aSign = extractFloatx80Sign(a); + + if (aExp == 0x7FFF) { + if ((uint64_t) (aSig << 1)) { + return propagateFloatx80NaNOneArg(a, status); + } + return packFloatx80(aSign, floatx80_infinity.high, + floatx80_infinity.low); + } + + if (aExp == 0 && aSig == 0) { + return packFloatx80(aSign, 0, 0); + } + + user_rnd_mode = status->float_rounding_mode; + user_rnd_prec = status->floatx80_rounding_precision; + status->float_rounding_mode = float_round_nearest_even; + status->floatx80_rounding_precision = 80; + + compact = floatx80_make_compact(aExp, aSig); + + if (compact > 0x400CB167) { + /* SINHBIG */ + if (compact > 0x400CB2B3) { + status->float_rounding_mode = user_rnd_mode; + status->floatx80_rounding_precision = user_rnd_prec; + + return roundAndPackFloatx80(status->floatx80_rounding_precision, + aSign, 0x8000, aSig, 0, status); + } else { + fp0 = floatx80_abs(a); /* Y = |X| */ + fp0 = floatx80_sub(fp0, float64_to_floatx80( + make_float64(0x40C62D38D3D64634), status), + status); /* (|X|-16381LOG2_LEAD) */ + fp0 = floatx80_sub(fp0, float64_to_floatx80( + make_float64(0x3D6F90AEB1E75CC7), status), + status); /* |X| - 16381 LOG2, ACCURATE */ + fp0 = floatx80_etox(fp0, status); + fp2 = packFloatx80(aSign, 0x7FFB, one_sig); + + status->float_rounding_mode = user_rnd_mode; + status->floatx80_rounding_precision = user_rnd_prec; + + a = floatx80_mul(fp0, fp2, status); + + float_raise(float_flag_inexact, status); + + return a; + } + } else { /* |X| < 16380 LOG2 */ + fp0 = floatx80_abs(a); /* Y = |X| */ + fp0 = floatx80_etoxm1(fp0, status); /* FP0 IS Z = EXPM1(Y) */ + fp1 = floatx80_add(fp0, float32_to_floatx80(make_float32(0x3F800000), + status), status); /* 1+Z */ + fp2 = fp0; + fp0 = floatx80_div(fp0, fp1, status); /* Z/(1+Z) */ + fp0 = floatx80_add(fp0, fp2, status); + + fact = packFloat32(aSign, 0x7E, 0); + + status->float_rounding_mode = user_rnd_mode; + status->floatx80_rounding_precision = user_rnd_prec; + + a = floatx80_mul(fp0, float32_to_floatx80(fact, status), status); + + float_raise(float_flag_inexact, status); + + return a; + } +} + +/*---------------------------------------------------------------------------- + | Hyperbolic cosine + *----------------------------------------------------------------------------*/ + +floatx80 floatx80_cosh(floatx80 a, float_status *status) +{ + int32_t aExp; + uint64_t aSig; + + int8_t user_rnd_mode, user_rnd_prec; + + int32_t compact; + floatx80 fp0, fp1; + + aSig = extractFloatx80Frac(a); + aExp = extractFloatx80Exp(a); + + if (aExp == 0x7FFF) { + if ((uint64_t) (aSig << 1)) { + return propagateFloatx80NaNOneArg(a, status); + } + return packFloatx80(0, floatx80_infinity.high, + floatx80_infinity.low); + } + + if (aExp == 0 && aSig == 0) { + return packFloatx80(0, one_exp, one_sig); + } + + user_rnd_mode = status->float_rounding_mode; + user_rnd_prec = status->floatx80_rounding_precision; + status->float_rounding_mode = float_round_nearest_even; + status->floatx80_rounding_precision = 80; + + compact = floatx80_make_compact(aExp, aSig); + + if (compact > 0x400CB167) { + if (compact > 0x400CB2B3) { + status->float_rounding_mode = user_rnd_mode; + status->floatx80_rounding_precision = user_rnd_prec; + return roundAndPackFloatx80(status->floatx80_rounding_precision, 0, + 0x8000, one_sig, 0, status); + } else { + fp0 = packFloatx80(0, aExp, aSig); + fp0 = floatx80_sub(fp0, float64_to_floatx80( + make_float64(0x40C62D38D3D64634), status), + status); + fp0 = floatx80_sub(fp0, float64_to_floatx80( + make_float64(0x3D6F90AEB1E75CC7), status), + status); + fp0 = floatx80_etox(fp0, status); + fp1 = packFloatx80(0, 0x7FFB, one_sig); + + status->float_rounding_mode = user_rnd_mode; + status->floatx80_rounding_precision = user_rnd_prec; + + a = floatx80_mul(fp0, fp1, status); + + float_raise(float_flag_inexact, status); + + return a; + } + } + + fp0 = packFloatx80(0, aExp, aSig); /* |X| */ + fp0 = floatx80_etox(fp0, status); /* EXP(|X|) */ + fp0 = floatx80_mul(fp0, float32_to_floatx80(make_float32(0x3F000000), + status), status); /* (1/2)*EXP(|X|) */ + fp1 = float32_to_floatx80(make_float32(0x3E800000), status); /* 1/4 */ + fp1 = floatx80_div(fp1, fp0, status); /* 1/(2*EXP(|X|)) */ + + status->float_rounding_mode = user_rnd_mode; + status->floatx80_rounding_precision = user_rnd_prec; + + a = floatx80_add(fp0, fp1, status); + + float_raise(float_flag_inexact, status); + + return a; +} diff --git a/target/m68k/softfloat.h b/target/m68k/softfloat.h index d28e49fe9f..602661d5a8 100644 --- a/target/m68k/softfloat.h +++ b/target/m68k/softfloat.h @@ -34,4 +34,15 @@ floatx80 floatx80_log2(floatx80 a, float_status *status); floatx80 floatx80_etox(floatx80 a, float_status *status); floatx80 floatx80_twotox(floatx80 a, float_status *status); floatx80 floatx80_tentox(floatx80 a, float_status *status); +floatx80 floatx80_tan(floatx80 a, float_status *status); +floatx80 floatx80_sin(floatx80 a, float_status *status); +floatx80 floatx80_cos(floatx80 a, float_status *status); +floatx80 floatx80_atan(floatx80 a, float_status *status); +floatx80 floatx80_asin(floatx80 a, float_status *status); +floatx80 floatx80_acos(floatx80 a, float_status *status); +floatx80 floatx80_atanh(floatx80 a, float_status *status); +floatx80 floatx80_etoxm1(floatx80 a, float_status *status); +floatx80 floatx80_tanh(floatx80 a, float_status *status); +floatx80 floatx80_sinh(floatx80 a, float_status *status); +floatx80 floatx80_cosh(floatx80 a, float_status *status); #endif diff --git a/target/m68k/softfloat_fpsp_tables.h b/target/m68k/softfloat_fpsp_tables.h index dd76dc0373..3f1419ee6e 100644 --- a/target/m68k/softfloat_fpsp_tables.h +++ b/target/m68k/softfloat_fpsp_tables.h @@ -371,4 +371,271 @@ static const uint32_t exp2_tbl2[64] = { 0xBFBDBF4A, 0x3FBEC01A, 0x3FBE8CAC, 0xBFBCBB3F, 0x3FBEF73A, 0xBFB8B795, 0x3FBEF84B, 0xBFBEF581 }; + +static const floatx80 pi_tbl[65] = { + make_floatx80_init(0xC004, 0xC90FDAA22168C235), + make_floatx80_init(0xC004, 0xC2C75BCD105D7C23), + make_floatx80_init(0xC004, 0xBC7EDCF7FF523611), + make_floatx80_init(0xC004, 0xB6365E22EE46F000), + make_floatx80_init(0xC004, 0xAFEDDF4DDD3BA9EE), + make_floatx80_init(0xC004, 0xA9A56078CC3063DD), + make_floatx80_init(0xC004, 0xA35CE1A3BB251DCB), + make_floatx80_init(0xC004, 0x9D1462CEAA19D7B9), + make_floatx80_init(0xC004, 0x96CBE3F9990E91A8), + make_floatx80_init(0xC004, 0x9083652488034B96), + make_floatx80_init(0xC004, 0x8A3AE64F76F80584), + make_floatx80_init(0xC004, 0x83F2677A65ECBF73), + make_floatx80_init(0xC003, 0xFB53D14AA9C2F2C2), + make_floatx80_init(0xC003, 0xEEC2D3A087AC669F), + make_floatx80_init(0xC003, 0xE231D5F66595DA7B), + make_floatx80_init(0xC003, 0xD5A0D84C437F4E58), + make_floatx80_init(0xC003, 0xC90FDAA22168C235), + make_floatx80_init(0xC003, 0xBC7EDCF7FF523611), + make_floatx80_init(0xC003, 0xAFEDDF4DDD3BA9EE), + make_floatx80_init(0xC003, 0xA35CE1A3BB251DCB), + make_floatx80_init(0xC003, 0x96CBE3F9990E91A8), + make_floatx80_init(0xC003, 0x8A3AE64F76F80584), + make_floatx80_init(0xC002, 0xFB53D14AA9C2F2C2), + make_floatx80_init(0xC002, 0xE231D5F66595DA7B), + make_floatx80_init(0xC002, 0xC90FDAA22168C235), + make_floatx80_init(0xC002, 0xAFEDDF4DDD3BA9EE), + make_floatx80_init(0xC002, 0x96CBE3F9990E91A8), + make_floatx80_init(0xC001, 0xFB53D14AA9C2F2C2), + make_floatx80_init(0xC001, 0xC90FDAA22168C235), + make_floatx80_init(0xC001, 0x96CBE3F9990E91A8), + make_floatx80_init(0xC000, 0xC90FDAA22168C235), + make_floatx80_init(0xBFFF, 0xC90FDAA22168C235), + make_floatx80_init(0x0000, 0x0000000000000000), + make_floatx80_init(0x3FFF, 0xC90FDAA22168C235), + make_floatx80_init(0x4000, 0xC90FDAA22168C235), + make_floatx80_init(0x4001, 0x96CBE3F9990E91A8), + make_floatx80_init(0x4001, 0xC90FDAA22168C235), + make_floatx80_init(0x4001, 0xFB53D14AA9C2F2C2), + make_floatx80_init(0x4002, 0x96CBE3F9990E91A8), + make_floatx80_init(0x4002, 0xAFEDDF4DDD3BA9EE), + make_floatx80_init(0x4002, 0xC90FDAA22168C235), + make_floatx80_init(0x4002, 0xE231D5F66595DA7B), + make_floatx80_init(0x4002, 0xFB53D14AA9C2F2C2), + make_floatx80_init(0x4003, 0x8A3AE64F76F80584), + make_floatx80_init(0x4003, 0x96CBE3F9990E91A8), + make_floatx80_init(0x4003, 0xA35CE1A3BB251DCB), + make_floatx80_init(0x4003, 0xAFEDDF4DDD3BA9EE), + make_floatx80_init(0x4003, 0xBC7EDCF7FF523611), + make_floatx80_init(0x4003, 0xC90FDAA22168C235), + make_floatx80_init(0x4003, 0xD5A0D84C437F4E58), + make_floatx80_init(0x4003, 0xE231D5F66595DA7B), + make_floatx80_init(0x4003, 0xEEC2D3A087AC669F), + make_floatx80_init(0x4003, 0xFB53D14AA9C2F2C2), + make_floatx80_init(0x4004, 0x83F2677A65ECBF73), + make_floatx80_init(0x4004, 0x8A3AE64F76F80584), + make_floatx80_init(0x4004, 0x9083652488034B96), + make_floatx80_init(0x4004, 0x96CBE3F9990E91A8), + make_floatx80_init(0x4004, 0x9D1462CEAA19D7B9), + make_floatx80_init(0x4004, 0xA35CE1A3BB251DCB), + make_floatx80_init(0x4004, 0xA9A56078CC3063DD), + make_floatx80_init(0x4004, 0xAFEDDF4DDD3BA9EE), + make_floatx80_init(0x4004, 0xB6365E22EE46F000), + make_floatx80_init(0x4004, 0xBC7EDCF7FF523611), + make_floatx80_init(0x4004, 0xC2C75BCD105D7C23), + make_floatx80_init(0x4004, 0xC90FDAA22168C235) +}; + +static const float32 pi_tbl2[65] = { + const_float32(0x21800000), + const_float32(0xA0D00000), + const_float32(0xA1E80000), + const_float32(0x21480000), + const_float32(0xA1200000), + const_float32(0x21FC0000), + const_float32(0x21100000), + const_float32(0xA1580000), + const_float32(0x21E00000), + const_float32(0x20B00000), + const_float32(0xA1880000), + const_float32(0x21C40000), + const_float32(0x20000000), + const_float32(0x21380000), + const_float32(0xA1300000), + const_float32(0x9FC00000), + const_float32(0x21000000), + const_float32(0xA1680000), + const_float32(0xA0A00000), + const_float32(0x20900000), + const_float32(0x21600000), + const_float32(0xA1080000), + const_float32(0x1F800000), + const_float32(0xA0B00000), + const_float32(0x20800000), + const_float32(0xA0200000), + const_float32(0x20E00000), + const_float32(0x1F000000), + const_float32(0x20000000), + const_float32(0x20600000), + const_float32(0x1F800000), + const_float32(0x1F000000), + const_float32(0x00000000), + const_float32(0x9F000000), + const_float32(0x9F800000), + const_float32(0xA0600000), + const_float32(0xA0000000), + const_float32(0x9F000000), + const_float32(0xA0E00000), + const_float32(0x20200000), + const_float32(0xA0800000), + const_float32(0x20B00000), + const_float32(0x9F800000), + const_float32(0x21080000), + const_float32(0xA1600000), + const_float32(0xA0900000), + const_float32(0x20A00000), + const_float32(0x21680000), + const_float32(0xA1000000), + const_float32(0x1FC00000), + const_float32(0x21300000), + const_float32(0xA1380000), + const_float32(0xA0000000), + const_float32(0xA1C40000), + const_float32(0x21880000), + const_float32(0xA0B00000), + const_float32(0xA1E00000), + const_float32(0x21580000), + const_float32(0xA1100000), + const_float32(0xA1FC0000), + const_float32(0x21200000), + const_float32(0xA1480000), + const_float32(0x21E80000), + const_float32(0x20D00000), + const_float32(0xA1800000), +}; + +static const floatx80 atan_tbl[128] = { + make_floatx80_init(0x3FFB, 0x83D152C5060B7A51), + make_floatx80_init(0x3FFB, 0x8BC8544565498B8B), + make_floatx80_init(0x3FFB, 0x93BE406017626B0D), + make_floatx80_init(0x3FFB, 0x9BB3078D35AEC202), + make_floatx80_init(0x3FFB, 0xA3A69A525DDCE7DE), + make_floatx80_init(0x3FFB, 0xAB98E94362765619), + make_floatx80_init(0x3FFB, 0xB389E502F9C59862), + make_floatx80_init(0x3FFB, 0xBB797E436B09E6FB), + make_floatx80_init(0x3FFB, 0xC367A5C739E5F446), + make_floatx80_init(0x3FFB, 0xCB544C61CFF7D5C6), + make_floatx80_init(0x3FFB, 0xD33F62F82488533E), + make_floatx80_init(0x3FFB, 0xDB28DA8162404C77), + make_floatx80_init(0x3FFB, 0xE310A4078AD34F18), + make_floatx80_init(0x3FFB, 0xEAF6B0A8188EE1EB), + make_floatx80_init(0x3FFB, 0xF2DAF1949DBE79D5), + make_floatx80_init(0x3FFB, 0xFABD581361D47E3E), + make_floatx80_init(0x3FFC, 0x8346AC210959ECC4), + make_floatx80_init(0x3FFC, 0x8B232A08304282D8), + make_floatx80_init(0x3FFC, 0x92FB70B8D29AE2F9), + make_floatx80_init(0x3FFC, 0x9ACF476F5CCD1CB4), + make_floatx80_init(0x3FFC, 0xA29E76304954F23F), + make_floatx80_init(0x3FFC, 0xAA68C5D08AB85230), + make_floatx80_init(0x3FFC, 0xB22DFFFD9D539F83), + make_floatx80_init(0x3FFC, 0xB9EDEF453E900EA5), + make_floatx80_init(0x3FFC, 0xC1A85F1CC75E3EA5), + make_floatx80_init(0x3FFC, 0xC95D1BE828138DE6), + make_floatx80_init(0x3FFC, 0xD10BF300840D2DE4), + make_floatx80_init(0x3FFC, 0xD8B4B2BA6BC05E7A), + make_floatx80_init(0x3FFC, 0xE0572A6BB42335F6), + make_floatx80_init(0x3FFC, 0xE7F32A70EA9CAA8F), + make_floatx80_init(0x3FFC, 0xEF88843264ECEFAA), + make_floatx80_init(0x3FFC, 0xF7170A28ECC06666), + make_floatx80_init(0x3FFD, 0x812FD288332DAD32), + make_floatx80_init(0x3FFD, 0x88A8D1B1218E4D64), + make_floatx80_init(0x3FFD, 0x9012AB3F23E4AEE8), + make_floatx80_init(0x3FFD, 0x976CC3D411E7F1B9), + make_floatx80_init(0x3FFD, 0x9EB689493889A227), + make_floatx80_init(0x3FFD, 0xA5EF72C34487361B), + make_floatx80_init(0x3FFD, 0xAD1700BAF07A7227), + make_floatx80_init(0x3FFD, 0xB42CBCFAFD37EFB7), + make_floatx80_init(0x3FFD, 0xBB303A940BA80F89), + make_floatx80_init(0x3FFD, 0xC22115C6FCAEBBAF), + make_floatx80_init(0x3FFD, 0xC8FEF3E686331221), + make_floatx80_init(0x3FFD, 0xCFC98330B4000C70), + make_floatx80_init(0x3FFD, 0xD6807AA1102C5BF9), + make_floatx80_init(0x3FFD, 0xDD2399BC31252AA3), + make_floatx80_init(0x3FFD, 0xE3B2A8556B8FC517), + make_floatx80_init(0x3FFD, 0xEA2D764F64315989), + make_floatx80_init(0x3FFD, 0xF3BF5BF8BAD1A21D), + make_floatx80_init(0x3FFE, 0x801CE39E0D205C9A), + make_floatx80_init(0x3FFE, 0x8630A2DADA1ED066), + make_floatx80_init(0x3FFE, 0x8C1AD445F3E09B8C), + make_floatx80_init(0x3FFE, 0x91DB8F1664F350E2), + make_floatx80_init(0x3FFE, 0x97731420365E538C), + make_floatx80_init(0x3FFE, 0x9CE1C8E6A0B8CDBA), + make_floatx80_init(0x3FFE, 0xA22832DBCADAAE09), + make_floatx80_init(0x3FFE, 0xA746F2DDB7602294), + make_floatx80_init(0x3FFE, 0xAC3EC0FB997DD6A2), + make_floatx80_init(0x3FFE, 0xB110688AEBDC6F6A), + make_floatx80_init(0x3FFE, 0xB5BCC49059ECC4B0), + make_floatx80_init(0x3FFE, 0xBA44BC7DD470782F), + make_floatx80_init(0x3FFE, 0xBEA94144FD049AAC), + make_floatx80_init(0x3FFE, 0xC2EB4ABB661628B6), + make_floatx80_init(0x3FFE, 0xC70BD54CE602EE14), + make_floatx80_init(0x3FFE, 0xCD000549ADEC7159), + make_floatx80_init(0x3FFE, 0xD48457D2D8EA4EA3), + make_floatx80_init(0x3FFE, 0xDB948DA712DECE3B), + make_floatx80_init(0x3FFE, 0xE23855F969E8096A), + make_floatx80_init(0x3FFE, 0xE8771129C4353259), + make_floatx80_init(0x3FFE, 0xEE57C16E0D379C0D), + make_floatx80_init(0x3FFE, 0xF3E10211A87C3779), + make_floatx80_init(0x3FFE, 0xF919039D758B8D41), + make_floatx80_init(0x3FFE, 0xFE058B8F64935FB3), + make_floatx80_init(0x3FFF, 0x8155FB497B685D04), + make_floatx80_init(0x3FFF, 0x83889E3549D108E1), + make_floatx80_init(0x3FFF, 0x859CFA76511D724B), + make_floatx80_init(0x3FFF, 0x87952ECFFF8131E7), + make_floatx80_init(0x3FFF, 0x89732FD19557641B), + make_floatx80_init(0x3FFF, 0x8B38CAD101932A35), + make_floatx80_init(0x3FFF, 0x8CE7A8D8301EE6B5), + make_floatx80_init(0x3FFF, 0x8F46A39E2EAE5281), + make_floatx80_init(0x3FFF, 0x922DA7D791888487), + make_floatx80_init(0x3FFF, 0x94D19FCBDEDF5241), + make_floatx80_init(0x3FFF, 0x973AB94419D2A08B), + make_floatx80_init(0x3FFF, 0x996FF00E08E10B96), + make_floatx80_init(0x3FFF, 0x9B773F9512321DA7), + make_floatx80_init(0x3FFF, 0x9D55CC320F935624), + make_floatx80_init(0x3FFF, 0x9F100575006CC571), + make_floatx80_init(0x3FFF, 0xA0A9C290D97CC06C), + make_floatx80_init(0x3FFF, 0xA22659EBEBC0630A), + make_floatx80_init(0x3FFF, 0xA388B4AFF6EF0EC9), + make_floatx80_init(0x3FFF, 0xA4D35F1061D292C4), + make_floatx80_init(0x3FFF, 0xA60895DCFBE3187E), + make_floatx80_init(0x3FFF, 0xA72A51DC7367BEAC), + make_floatx80_init(0x3FFF, 0xA83A51530956168F), + make_floatx80_init(0x3FFF, 0xA93A20077539546E), + make_floatx80_init(0x3FFF, 0xAA9E7245023B2605), + make_floatx80_init(0x3FFF, 0xAC4C84BA6FE4D58F), + make_floatx80_init(0x3FFF, 0xADCE4A4A606B9712), + make_floatx80_init(0x3FFF, 0xAF2A2DCD8D263C9C), + make_floatx80_init(0x3FFF, 0xB0656F81F22265C7), + make_floatx80_init(0x3FFF, 0xB18465150F71496A), + make_floatx80_init(0x3FFF, 0xB28AAA156F9ADA35), + make_floatx80_init(0x3FFF, 0xB37B44FF3766B895), + make_floatx80_init(0x3FFF, 0xB458C3DCE9630433), + make_floatx80_init(0x3FFF, 0xB525529D562246BD), + make_floatx80_init(0x3FFF, 0xB5E2CCA95F9D88CC), + make_floatx80_init(0x3FFF, 0xB692CADA7ACA1ADA), + make_floatx80_init(0x3FFF, 0xB736AEA7A6925838), + make_floatx80_init(0x3FFF, 0xB7CFAB287E9F7B36), + make_floatx80_init(0x3FFF, 0xB85ECC66CB219835), + make_floatx80_init(0x3FFF, 0xB8E4FD5A20A593DA), + make_floatx80_init(0x3FFF, 0xB99F41F64AFF9BB5), + make_floatx80_init(0x3FFF, 0xBA7F1E17842BBE7B), + make_floatx80_init(0x3FFF, 0xBB4712857637E17D), + make_floatx80_init(0x3FFF, 0xBBFABE8A4788DF6F), + make_floatx80_init(0x3FFF, 0xBC9D0FAD2B689D79), + make_floatx80_init(0x3FFF, 0xBD306A39471ECD86), + make_floatx80_init(0x3FFF, 0xBDB6C731856AF18A), + make_floatx80_init(0x3FFF, 0xBE31CAC502E80D70), + make_floatx80_init(0x3FFF, 0xBEA2D55CE33194E2), + make_floatx80_init(0x3FFF, 0xBF0B10B7C03128F0), + make_floatx80_init(0x3FFF, 0xBF6B7A18DACB778D), + make_floatx80_init(0x3FFF, 0xBFC4EA4663FA18F6), + make_floatx80_init(0x3FFF, 0xC0181BDE8B89A454), + make_floatx80_init(0x3FFF, 0xC065B066CFBF6439), + make_floatx80_init(0x3FFF, 0xC0AE345F56340AE6), + make_floatx80_init(0x3FFF, 0xC0F222919CB9E6A7) +}; #endif diff --git a/target/m68k/translate.c b/target/m68k/translate.c index 6d5bde0777..cef6f663ad 100644 --- a/target/m68k/translate.c +++ b/target/m68k/translate.c @@ -5042,6 +5042,9 @@ DISAS_INSN(fpu) case 1: /* fint */ gen_helper_firound(cpu_env, cpu_dest, cpu_src); break; + case 2: /* fsinh */ + gen_helper_fsinh(cpu_env, cpu_dest, cpu_src); + break; case 3: /* fintrz */ gen_helper_fitrunc(cpu_env, cpu_dest, cpu_src); break; @@ -5057,6 +5060,24 @@ DISAS_INSN(fpu) case 0x06: /* flognp1 */ gen_helper_flognp1(cpu_env, cpu_dest, cpu_src); break; + case 0x09: /* ftanh */ + gen_helper_ftanh(cpu_env, cpu_dest, cpu_src); + break; + case 0x0a: /* fatan */ + gen_helper_fatan(cpu_env, cpu_dest, cpu_src); + break; + case 0x0c: /* fasin */ + gen_helper_fasin(cpu_env, cpu_dest, cpu_src); + break; + case 0x0d: /* fatanh */ + gen_helper_fatanh(cpu_env, cpu_dest, cpu_src); + break; + case 0x0e: /* fsin */ + gen_helper_fsin(cpu_env, cpu_dest, cpu_src); + break; + case 0x0f: /* ftan */ + gen_helper_ftan(cpu_env, cpu_dest, cpu_src); + break; case 0x10: /* fetox */ gen_helper_fetox(cpu_env, cpu_dest, cpu_src); break; @@ -5084,6 +5105,9 @@ DISAS_INSN(fpu) case 0x5c: /* fdabs */ gen_helper_fdabs(cpu_env, cpu_dest, cpu_src); break; + case 0x19: /* fcosh */ + gen_helper_fcosh(cpu_env, cpu_dest, cpu_src); + break; case 0x1a: /* fneg */ gen_helper_fneg(cpu_env, cpu_dest, cpu_src); break; @@ -5093,6 +5117,12 @@ DISAS_INSN(fpu) case 0x5e: /* fdneg */ gen_helper_fdneg(cpu_env, cpu_dest, cpu_src); break; + case 0x1c: /* facos */ + gen_helper_facos(cpu_env, cpu_dest, cpu_src); + break; + case 0x1d: /* fcos */ + gen_helper_fcos(cpu_env, cpu_dest, cpu_src); + break; case 0x1e: /* fgetexp */ gen_helper_fgetexp(cpu_env, cpu_dest, cpu_src); break; @@ -5150,6 +5180,14 @@ DISAS_INSN(fpu) case 0x6c: /* fdsub */ gen_helper_fdsub(cpu_env, cpu_dest, cpu_src, cpu_dest); break; + case 0x30: case 0x31: case 0x32: + case 0x33: case 0x34: case 0x35: + case 0x36: case 0x37: { + TCGv_ptr cpu_dest2 = gen_fp_ptr(REG(ext, 0)); + gen_helper_fsincos(cpu_env, cpu_dest, cpu_dest2, cpu_src); + tcg_temp_free_ptr(cpu_dest2); + } + break; case 0x38: /* fcmp */ gen_helper_fcmp(cpu_env, cpu_src, cpu_dest); return; diff --git a/target/sh4/cpu.c b/target/sh4/cpu.c index 6302cfda3a..541ffc2d97 100644 --- a/target/sh4/cpu.c +++ b/target/sh4/cpu.c @@ -85,18 +85,6 @@ typedef struct SuperHCPUListState { FILE *file; } SuperHCPUListState; -/* Sort alphabetically by type name. */ -static gint superh_cpu_list_compare(gconstpointer a, gconstpointer b) -{ - ObjectClass *class_a = (ObjectClass *)a; - ObjectClass *class_b = (ObjectClass *)b; - const char *name_a, *name_b; - - name_a = object_class_get_name(class_a); - name_b = object_class_get_name(class_b); - return strcmp(name_a, name_b); -} - static void superh_cpu_list_entry(gpointer data, gpointer user_data) { SuperHCPUListState *s = user_data; @@ -114,8 +102,7 @@ void sh4_cpu_list(FILE *f, fprintf_function cpu_fprintf) }; GSList *list; - list = object_class_get_list(TYPE_SUPERH_CPU, false); - list = g_slist_sort(list, superh_cpu_list_compare); + list = object_class_get_list_sorted(TYPE_SUPERH_CPU, false); g_slist_foreach(list, superh_cpu_list_entry, &s); g_slist_free(list); } diff --git a/target/tricore/helper.c b/target/tricore/helper.c index 45276d3782..dad7eea085 100644 --- a/target/tricore/helper.c +++ b/target/tricore/helper.c @@ -101,7 +101,7 @@ void tricore_cpu_list(FILE *f, fprintf_function cpu_fprintf) }; GSList *list; - list = object_class_get_list(TYPE_TRICORE_CPU, false); + list = object_class_get_list_sorted(TYPE_TRICORE_CPU, false); (*cpu_fprintf)(f, "Available CPUs:\n"); g_slist_foreach(list, tricore_cpu_list_entry, &s); g_slist_free(list); diff --git a/tcg/i386/tcg-target.inc.c b/tcg/i386/tcg-target.inc.c index fc05909d1d..d7e59e79c5 100644 --- a/tcg/i386/tcg-target.inc.c +++ b/tcg/i386/tcg-target.inc.c @@ -2696,6 +2696,12 @@ static void tcg_out_vec_op(TCGContext *s, TCGOpcode opc, case INDEX_op_x86_packus_vec: insn = packus_insn[vece]; goto gen_simd; +#if TCG_TARGET_REG_BITS == 32 + case INDEX_op_dup2_vec: + /* Constraints have already placed both 32-bit inputs in xmm regs. */ + insn = OPC_PUNPCKLDQ; + goto gen_simd; +#endif gen_simd: tcg_debug_assert(insn != OPC_UD2); if (type == TCG_TYPE_V256) { @@ -3045,6 +3051,9 @@ static const TCGTargetOpDef *tcg_target_op_def(TCGOpcode op) case INDEX_op_x86_vperm2i128_vec: case INDEX_op_x86_punpckl_vec: case INDEX_op_x86_punpckh_vec: +#if TCG_TARGET_REG_BITS == 32 + case INDEX_op_dup2_vec: +#endif return &x_x_x; case INDEX_op_dup_vec: case INDEX_op_shli_vec: diff --git a/tcg/tcg-op-gvec.c b/tcg/tcg-op-gvec.c index bfe44bba81..22db1590d5 100644 --- a/tcg/tcg-op-gvec.c +++ b/tcg/tcg-op-gvec.c @@ -351,6 +351,42 @@ static void gen_dup_i64(unsigned vece, TCGv_i64 out, TCGv_i64 in) } } +/* Select a supported vector type for implementing an operation on SIZE + * bytes. If OP is 0, assume that the real operation to be performed is + * required by all backends. Otherwise, make sure than OP can be performed + * on elements of size VECE in the selected type. Do not select V64 if + * PREFER_I64 is true. Return 0 if no vector type is selected. + */ +static TCGType choose_vector_type(TCGOpcode op, unsigned vece, uint32_t size, + bool prefer_i64) +{ + if (TCG_TARGET_HAS_v256 && check_size_impl(size, 32)) { + if (op == 0) { + return TCG_TYPE_V256; + } + /* Recall that ARM SVE allows vector sizes that are not a + * power of 2, but always a multiple of 16. The intent is + * that e.g. size == 80 would be expanded with 2x32 + 1x16. + * It is hard to imagine a case in which v256 is supported + * but v128 is not, but check anyway. + */ + if (tcg_can_emit_vec_op(op, TCG_TYPE_V256, vece) + && (size % 32 == 0 + || tcg_can_emit_vec_op(op, TCG_TYPE_V128, vece))) { + return TCG_TYPE_V256; + } + } + if (TCG_TARGET_HAS_v128 && check_size_impl(size, 16) + && (op == 0 || tcg_can_emit_vec_op(op, TCG_TYPE_V128, vece))) { + return TCG_TYPE_V128; + } + if (TCG_TARGET_HAS_v64 && !prefer_i64 && check_size_impl(size, 8) + && (op == 0 || tcg_can_emit_vec_op(op, TCG_TYPE_V64, vece))) { + return TCG_TYPE_V64; + } + return 0; +} + /* Set OPRSZ bytes at DOFS to replications of IN_32, IN_64 or IN_C. * Only one of IN_32 or IN_64 may be set; * IN_C is used if IN_32 and IN_64 are unset. @@ -376,19 +412,12 @@ static void do_dup(unsigned vece, uint32_t dofs, uint32_t oprsz, } } - type = 0; - if (TCG_TARGET_HAS_v256 && check_size_impl(oprsz, 32)) { - type = TCG_TYPE_V256; - } else if (TCG_TARGET_HAS_v128 && check_size_impl(oprsz, 16)) { - type = TCG_TYPE_V128; - } else if (TCG_TARGET_HAS_v64 && check_size_impl(oprsz, 8) - /* Prefer integer when 64-bit host and no variable dup. */ - && !(TCG_TARGET_REG_BITS == 64 && in_32 == NULL - && (in_64 == NULL || vece == MO_64))) { - type = TCG_TYPE_V64; - } - - /* Implement inline with a vector type, if possible. */ + /* Implement inline with a vector type, if possible. + * Prefer integer when 64-bit host and no variable dup. + */ + type = choose_vector_type(0, vece, oprsz, + (TCG_TARGET_REG_BITS == 64 && in_32 == NULL + && (in_64 == NULL || vece == MO_64))); if (type != 0) { TCGv_vec t_vec = tcg_temp_new_vec(type); @@ -414,21 +443,30 @@ static void do_dup(unsigned vece, uint32_t dofs, uint32_t oprsz, } i = 0; - if (TCG_TARGET_HAS_v256) { + switch (type) { + case TCG_TYPE_V256: + /* Recall that ARM SVE allows vector sizes that are not a + * power of 2, but always a multiple of 16. The intent is + * that e.g. size == 80 would be expanded with 2x32 + 1x16. + */ for (; i + 32 <= oprsz; i += 32) { tcg_gen_stl_vec(t_vec, cpu_env, dofs + i, TCG_TYPE_V256); } - } - if (TCG_TARGET_HAS_v128) { + /* fallthru */ + case TCG_TYPE_V128: for (; i + 16 <= oprsz; i += 16) { tcg_gen_stl_vec(t_vec, cpu_env, dofs + i, TCG_TYPE_V128); } - } - if (TCG_TARGET_HAS_v64) { + break; + case TCG_TYPE_V64: for (; i < oprsz; i += 8) { tcg_gen_stl_vec(t_vec, cpu_env, dofs + i, TCG_TYPE_V64); } + break; + default: + g_assert_not_reached(); } + tcg_temp_free_vec(t_vec); goto done; } @@ -484,7 +522,7 @@ static void do_dup(unsigned vece, uint32_t dofs, uint32_t oprsz, } tcg_temp_free_i64(t_64); goto done; - } + } } /* Otherwise implement out of line. */ @@ -866,49 +904,55 @@ static void expand_4_vec(unsigned vece, uint32_t dofs, uint32_t aofs, void tcg_gen_gvec_2(uint32_t dofs, uint32_t aofs, uint32_t oprsz, uint32_t maxsz, const GVecGen2 *g) { + TCGType type; + uint32_t some; + check_size_align(oprsz, maxsz, dofs | aofs); check_overlap_2(dofs, aofs, maxsz); - /* Recall that ARM SVE allows vector sizes that are not a power of 2. - Expand with successively smaller host vector sizes. The intent is - that e.g. oprsz == 80 would be expanded with 2x32 + 1x16. */ - /* ??? For maxsz > oprsz, the host may be able to use an opr-sized - operation, zeroing the balance of the register. We can then - use a max-sized store to implement the clearing without an extra - store operation. This is true for aarch64 and x86_64 hosts. */ - - if (TCG_TARGET_HAS_v256 && g->fniv && check_size_impl(oprsz, 32) - && (!g->opc || tcg_can_emit_vec_op(g->opc, TCG_TYPE_V256, g->vece))) { - uint32_t some = QEMU_ALIGN_DOWN(oprsz, 32); + type = 0; + if (g->fniv) { + type = choose_vector_type(g->opc, g->vece, oprsz, g->prefer_i64); + } + switch (type) { + case TCG_TYPE_V256: + /* Recall that ARM SVE allows vector sizes that are not a + * power of 2, but always a multiple of 16. The intent is + * that e.g. size == 80 would be expanded with 2x32 + 1x16. + */ + some = QEMU_ALIGN_DOWN(oprsz, 32); expand_2_vec(g->vece, dofs, aofs, some, 32, TCG_TYPE_V256, g->fniv); if (some == oprsz) { - goto done; + break; } dofs += some; aofs += some; oprsz -= some; maxsz -= some; - } - - if (TCG_TARGET_HAS_v128 && g->fniv && check_size_impl(oprsz, 16) - && (!g->opc || tcg_can_emit_vec_op(g->opc, TCG_TYPE_V128, g->vece))) { + /* fallthru */ + case TCG_TYPE_V128: expand_2_vec(g->vece, dofs, aofs, oprsz, 16, TCG_TYPE_V128, g->fniv); - } else if (TCG_TARGET_HAS_v64 && !g->prefer_i64 - && g->fniv && check_size_impl(oprsz, 8) - && (!g->opc - || tcg_can_emit_vec_op(g->opc, TCG_TYPE_V64, g->vece))) { + break; + case TCG_TYPE_V64: expand_2_vec(g->vece, dofs, aofs, oprsz, 8, TCG_TYPE_V64, g->fniv); - } else if (g->fni8 && check_size_impl(oprsz, 8)) { - expand_2_i64(dofs, aofs, oprsz, g->fni8); - } else if (g->fni4 && check_size_impl(oprsz, 4)) { - expand_2_i32(dofs, aofs, oprsz, g->fni4); - } else { - assert(g->fno != NULL); - tcg_gen_gvec_2_ool(dofs, aofs, oprsz, maxsz, g->data, g->fno); - return; + break; + + case 0: + if (g->fni8 && check_size_impl(oprsz, 8)) { + expand_2_i64(dofs, aofs, oprsz, g->fni8); + } else if (g->fni4 && check_size_impl(oprsz, 4)) { + expand_2_i32(dofs, aofs, oprsz, g->fni4); + } else { + assert(g->fno != NULL); + tcg_gen_gvec_2_ool(dofs, aofs, oprsz, maxsz, g->data, g->fno); + return; + } + break; + + default: + g_assert_not_reached(); } - done: if (oprsz < maxsz) { expand_clr(dofs + oprsz, maxsz - oprsz); } @@ -918,53 +962,64 @@ void tcg_gen_gvec_2(uint32_t dofs, uint32_t aofs, void tcg_gen_gvec_2i(uint32_t dofs, uint32_t aofs, uint32_t oprsz, uint32_t maxsz, int64_t c, const GVecGen2i *g) { + TCGType type; + uint32_t some; + check_size_align(oprsz, maxsz, dofs | aofs); check_overlap_2(dofs, aofs, maxsz); - /* Recall that ARM SVE allows vector sizes that are not a power of 2. - Expand with successively smaller host vector sizes. The intent is - that e.g. oprsz == 80 would be expanded with 2x32 + 1x16. */ - - if (TCG_TARGET_HAS_v256 && g->fniv && check_size_impl(oprsz, 32) - && (!g->opc || tcg_can_emit_vec_op(g->opc, TCG_TYPE_V256, g->vece))) { - uint32_t some = QEMU_ALIGN_DOWN(oprsz, 32); + type = 0; + if (g->fniv) { + type = choose_vector_type(g->opc, g->vece, oprsz, g->prefer_i64); + } + switch (type) { + case TCG_TYPE_V256: + /* Recall that ARM SVE allows vector sizes that are not a + * power of 2, but always a multiple of 16. The intent is + * that e.g. size == 80 would be expanded with 2x32 + 1x16. + */ + some = QEMU_ALIGN_DOWN(oprsz, 32); expand_2i_vec(g->vece, dofs, aofs, some, 32, TCG_TYPE_V256, c, g->load_dest, g->fniv); if (some == oprsz) { - goto done; + break; } dofs += some; aofs += some; oprsz -= some; maxsz -= some; - } - - if (TCG_TARGET_HAS_v128 && g->fniv && check_size_impl(oprsz, 16) - && (!g->opc || tcg_can_emit_vec_op(g->opc, TCG_TYPE_V128, g->vece))) { + /* fallthru */ + case TCG_TYPE_V128: expand_2i_vec(g->vece, dofs, aofs, oprsz, 16, TCG_TYPE_V128, c, g->load_dest, g->fniv); - } else if (TCG_TARGET_HAS_v64 && !g->prefer_i64 - && g->fniv && check_size_impl(oprsz, 8) - && (!g->opc - || tcg_can_emit_vec_op(g->opc, TCG_TYPE_V64, g->vece))) { + break; + case TCG_TYPE_V64: expand_2i_vec(g->vece, dofs, aofs, oprsz, 8, TCG_TYPE_V64, c, g->load_dest, g->fniv); - } else if (g->fni8 && check_size_impl(oprsz, 8)) { - expand_2i_i64(dofs, aofs, oprsz, c, g->load_dest, g->fni8); - } else if (g->fni4 && check_size_impl(oprsz, 4)) { - expand_2i_i32(dofs, aofs, oprsz, c, g->load_dest, g->fni4); - } else { - if (g->fno) { - tcg_gen_gvec_2_ool(dofs, aofs, oprsz, maxsz, c, g->fno); + break; + + case 0: + if (g->fni8 && check_size_impl(oprsz, 8)) { + expand_2i_i64(dofs, aofs, oprsz, c, g->load_dest, g->fni8); + } else if (g->fni4 && check_size_impl(oprsz, 4)) { + expand_2i_i32(dofs, aofs, oprsz, c, g->load_dest, g->fni4); } else { - TCGv_i64 tcg_c = tcg_const_i64(c); - tcg_gen_gvec_2i_ool(dofs, aofs, tcg_c, oprsz, maxsz, c, g->fnoi); - tcg_temp_free_i64(tcg_c); + if (g->fno) { + tcg_gen_gvec_2_ool(dofs, aofs, oprsz, maxsz, c, g->fno); + } else { + TCGv_i64 tcg_c = tcg_const_i64(c); + tcg_gen_gvec_2i_ool(dofs, aofs, tcg_c, oprsz, + maxsz, c, g->fnoi); + tcg_temp_free_i64(tcg_c); + } + return; } - return; + break; + + default: + g_assert_not_reached(); } - done: if (oprsz < maxsz) { expand_clr(dofs + oprsz, maxsz - oprsz); } @@ -981,37 +1036,30 @@ void tcg_gen_gvec_2s(uint32_t dofs, uint32_t aofs, uint32_t oprsz, type = 0; if (g->fniv) { - if (TCG_TARGET_HAS_v256 && check_size_impl(oprsz, 32)) { - type = TCG_TYPE_V256; - } else if (TCG_TARGET_HAS_v128 && check_size_impl(oprsz, 16)) { - type = TCG_TYPE_V128; - } else if (TCG_TARGET_HAS_v64 && !g->prefer_i64 - && check_size_impl(oprsz, 8)) { - type = TCG_TYPE_V64; - } + type = choose_vector_type(g->opc, g->vece, oprsz, g->prefer_i64); } if (type != 0) { TCGv_vec t_vec = tcg_temp_new_vec(type); + uint32_t some; tcg_gen_dup_i64_vec(g->vece, t_vec, c); - /* Recall that ARM SVE allows vector sizes that are not a power of 2. - Expand with successively smaller host vector sizes. The intent is - that e.g. oprsz == 80 would be expanded with 2x32 + 1x16. */ switch (type) { case TCG_TYPE_V256: - { - uint32_t some = QEMU_ALIGN_DOWN(oprsz, 32); - expand_2s_vec(g->vece, dofs, aofs, some, 32, TCG_TYPE_V256, - t_vec, g->scalar_first, g->fniv); - if (some == oprsz) { - break; - } - dofs += some; - aofs += some; - oprsz -= some; - maxsz -= some; + /* Recall that ARM SVE allows vector sizes that are not a + * power of 2, but always a multiple of 16. The intent is + * that e.g. size == 80 would be expanded with 2x32 + 1x16. + */ + some = QEMU_ALIGN_DOWN(oprsz, 32); + expand_2s_vec(g->vece, dofs, aofs, some, 32, TCG_TYPE_V256, + t_vec, g->scalar_first, g->fniv); + if (some == oprsz) { + break; } + dofs += some; + aofs += some; + oprsz -= some; + maxsz -= some; /* fallthru */ case TCG_TYPE_V128: @@ -1055,48 +1103,60 @@ void tcg_gen_gvec_2s(uint32_t dofs, uint32_t aofs, uint32_t oprsz, void tcg_gen_gvec_3(uint32_t dofs, uint32_t aofs, uint32_t bofs, uint32_t oprsz, uint32_t maxsz, const GVecGen3 *g) { + TCGType type; + uint32_t some; + check_size_align(oprsz, maxsz, dofs | aofs | bofs); check_overlap_3(dofs, aofs, bofs, maxsz); - /* Recall that ARM SVE allows vector sizes that are not a power of 2. - Expand with successively smaller host vector sizes. The intent is - that e.g. oprsz == 80 would be expanded with 2x32 + 1x16. */ - - if (TCG_TARGET_HAS_v256 && g->fniv && check_size_impl(oprsz, 32) - && (!g->opc || tcg_can_emit_vec_op(g->opc, TCG_TYPE_V256, g->vece))) { - uint32_t some = QEMU_ALIGN_DOWN(oprsz, 32); + type = 0; + if (g->fniv) { + type = choose_vector_type(g->opc, g->vece, oprsz, g->prefer_i64); + } + switch (type) { + case TCG_TYPE_V256: + /* Recall that ARM SVE allows vector sizes that are not a + * power of 2, but always a multiple of 16. The intent is + * that e.g. size == 80 would be expanded with 2x32 + 1x16. + */ + some = QEMU_ALIGN_DOWN(oprsz, 32); expand_3_vec(g->vece, dofs, aofs, bofs, some, 32, TCG_TYPE_V256, g->load_dest, g->fniv); if (some == oprsz) { - goto done; + break; } dofs += some; aofs += some; bofs += some; oprsz -= some; maxsz -= some; - } - - if (TCG_TARGET_HAS_v128 && g->fniv && check_size_impl(oprsz, 16) - && (!g->opc || tcg_can_emit_vec_op(g->opc, TCG_TYPE_V128, g->vece))) { + /* fallthru */ + case TCG_TYPE_V128: expand_3_vec(g->vece, dofs, aofs, bofs, oprsz, 16, TCG_TYPE_V128, g->load_dest, g->fniv); - } else if (TCG_TARGET_HAS_v64 && !g->prefer_i64 - && g->fniv && check_size_impl(oprsz, 8) - && (!g->opc - || tcg_can_emit_vec_op(g->opc, TCG_TYPE_V64, g->vece))) { + break; + case TCG_TYPE_V64: expand_3_vec(g->vece, dofs, aofs, bofs, oprsz, 8, TCG_TYPE_V64, g->load_dest, g->fniv); - } else if (g->fni8 && check_size_impl(oprsz, 8)) { - expand_3_i64(dofs, aofs, bofs, oprsz, g->load_dest, g->fni8); - } else if (g->fni4 && check_size_impl(oprsz, 4)) { - expand_3_i32(dofs, aofs, bofs, oprsz, g->load_dest, g->fni4); - } else { - assert(g->fno != NULL); - tcg_gen_gvec_3_ool(dofs, aofs, bofs, oprsz, maxsz, g->data, g->fno); + break; + + case 0: + if (g->fni8 && check_size_impl(oprsz, 8)) { + expand_3_i64(dofs, aofs, bofs, oprsz, g->load_dest, g->fni8); + } else if (g->fni4 && check_size_impl(oprsz, 4)) { + expand_3_i32(dofs, aofs, bofs, oprsz, g->load_dest, g->fni4); + } else { + assert(g->fno != NULL); + tcg_gen_gvec_3_ool(dofs, aofs, bofs, oprsz, + maxsz, g->data, g->fno); + return; + } + break; + + default: + g_assert_not_reached(); } - done: if (oprsz < maxsz) { expand_clr(dofs + oprsz, maxsz - oprsz); } @@ -1106,20 +1166,27 @@ void tcg_gen_gvec_3(uint32_t dofs, uint32_t aofs, uint32_t bofs, void tcg_gen_gvec_4(uint32_t dofs, uint32_t aofs, uint32_t bofs, uint32_t cofs, uint32_t oprsz, uint32_t maxsz, const GVecGen4 *g) { + TCGType type; + uint32_t some; + check_size_align(oprsz, maxsz, dofs | aofs | bofs | cofs); check_overlap_4(dofs, aofs, bofs, cofs, maxsz); - /* Recall that ARM SVE allows vector sizes that are not a power of 2. - Expand with successively smaller host vector sizes. The intent is - that e.g. oprsz == 80 would be expanded with 2x32 + 1x16. */ - - if (TCG_TARGET_HAS_v256 && g->fniv && check_size_impl(oprsz, 32) - && (!g->opc || tcg_can_emit_vec_op(g->opc, TCG_TYPE_V256, g->vece))) { - uint32_t some = QEMU_ALIGN_DOWN(oprsz, 32); + type = 0; + if (g->fniv) { + type = choose_vector_type(g->opc, g->vece, oprsz, g->prefer_i64); + } + switch (type) { + case TCG_TYPE_V256: + /* Recall that ARM SVE allows vector sizes that are not a + * power of 2, but always a multiple of 16. The intent is + * that e.g. size == 80 would be expanded with 2x32 + 1x16. + */ + some = QEMU_ALIGN_DOWN(oprsz, 32); expand_4_vec(g->vece, dofs, aofs, bofs, cofs, some, 32, TCG_TYPE_V256, g->fniv); if (some == oprsz) { - goto done; + break; } dofs += some; aofs += some; @@ -1127,30 +1194,33 @@ void tcg_gen_gvec_4(uint32_t dofs, uint32_t aofs, uint32_t bofs, uint32_t cofs, cofs += some; oprsz -= some; maxsz -= some; - } - - if (TCG_TARGET_HAS_v128 && g->fniv && check_size_impl(oprsz, 16) - && (!g->opc || tcg_can_emit_vec_op(g->opc, TCG_TYPE_V128, g->vece))) { + /* fallthru */ + case TCG_TYPE_V128: expand_4_vec(g->vece, dofs, aofs, bofs, cofs, oprsz, 16, TCG_TYPE_V128, g->fniv); - } else if (TCG_TARGET_HAS_v64 && !g->prefer_i64 - && g->fniv && check_size_impl(oprsz, 8) - && (!g->opc - || tcg_can_emit_vec_op(g->opc, TCG_TYPE_V64, g->vece))) { + break; + case TCG_TYPE_V64: expand_4_vec(g->vece, dofs, aofs, bofs, cofs, oprsz, 8, TCG_TYPE_V64, g->fniv); - } else if (g->fni8 && check_size_impl(oprsz, 8)) { - expand_4_i64(dofs, aofs, bofs, cofs, oprsz, g->fni8); - } else if (g->fni4 && check_size_impl(oprsz, 4)) { - expand_4_i32(dofs, aofs, bofs, cofs, oprsz, g->fni4); - } else { - assert(g->fno != NULL); - tcg_gen_gvec_4_ool(dofs, aofs, bofs, cofs, - oprsz, maxsz, g->data, g->fno); - return; + break; + + case 0: + if (g->fni8 && check_size_impl(oprsz, 8)) { + expand_4_i64(dofs, aofs, bofs, cofs, oprsz, g->fni8); + } else if (g->fni4 && check_size_impl(oprsz, 4)) { + expand_4_i32(dofs, aofs, bofs, cofs, oprsz, g->fni4); + } else { + assert(g->fno != NULL); + tcg_gen_gvec_4_ool(dofs, aofs, bofs, cofs, + oprsz, maxsz, g->data, g->fno); + return; + } + break; + + default: + g_assert_not_reached(); } - done: if (oprsz < maxsz) { expand_clr(dofs + oprsz, maxsz - oprsz); } @@ -2155,6 +2225,8 @@ void tcg_gen_gvec_cmp(TCGCond cond, unsigned vece, uint32_t dofs, [TCG_COND_LTU] = ltu_fn, [TCG_COND_LEU] = leu_fn, }; + TCGType type; + uint32_t some; check_size_align(oprsz, maxsz, dofs | aofs | bofs); check_overlap_3(dofs, aofs, bofs, maxsz); @@ -2165,51 +2237,59 @@ void tcg_gen_gvec_cmp(TCGCond cond, unsigned vece, uint32_t dofs, return; } - /* Recall that ARM SVE allows vector sizes that are not a power of 2. - Expand with successively smaller host vector sizes. The intent is - that e.g. oprsz == 80 would be expanded with 2x32 + 1x16. */ - - if (TCG_TARGET_HAS_v256 && check_size_impl(oprsz, 32) - && tcg_can_emit_vec_op(INDEX_op_cmp_vec, TCG_TYPE_V256, vece)) { - uint32_t some = QEMU_ALIGN_DOWN(oprsz, 32); + /* Implement inline with a vector type, if possible. + * Prefer integer when 64-bit host and 64-bit comparison. + */ + type = choose_vector_type(INDEX_op_cmp_vec, vece, oprsz, + TCG_TARGET_REG_BITS == 64 && vece == MO_64); + switch (type) { + case TCG_TYPE_V256: + /* Recall that ARM SVE allows vector sizes that are not a + * power of 2, but always a multiple of 16. The intent is + * that e.g. size == 80 would be expanded with 2x32 + 1x16. + */ + some = QEMU_ALIGN_DOWN(oprsz, 32); expand_cmp_vec(vece, dofs, aofs, bofs, some, 32, TCG_TYPE_V256, cond); if (some == oprsz) { - goto done; + break; } dofs += some; aofs += some; bofs += some; oprsz -= some; maxsz -= some; - } - - if (TCG_TARGET_HAS_v128 && check_size_impl(oprsz, 16) - && tcg_can_emit_vec_op(INDEX_op_cmp_vec, TCG_TYPE_V128, vece)) { + /* fallthru */ + case TCG_TYPE_V128: expand_cmp_vec(vece, dofs, aofs, bofs, oprsz, 16, TCG_TYPE_V128, cond); - } else if (TCG_TARGET_HAS_v64 - && check_size_impl(oprsz, 8) - && (TCG_TARGET_REG_BITS == 32 || vece != MO_64) - && tcg_can_emit_vec_op(INDEX_op_cmp_vec, TCG_TYPE_V64, vece)) { + break; + case TCG_TYPE_V64: expand_cmp_vec(vece, dofs, aofs, bofs, oprsz, 8, TCG_TYPE_V64, cond); - } else if (vece == MO_64 && check_size_impl(oprsz, 8)) { - expand_cmp_i64(dofs, aofs, bofs, oprsz, cond); - } else if (vece == MO_32 && check_size_impl(oprsz, 4)) { - expand_cmp_i32(dofs, aofs, bofs, oprsz, cond); - } else { - gen_helper_gvec_3 * const *fn = fns[cond]; - - if (fn == NULL) { - uint32_t tmp; - tmp = aofs, aofs = bofs, bofs = tmp; - cond = tcg_swap_cond(cond); - fn = fns[cond]; - assert(fn != NULL); + break; + + case 0: + if (vece == MO_64 && check_size_impl(oprsz, 8)) { + expand_cmp_i64(dofs, aofs, bofs, oprsz, cond); + } else if (vece == MO_32 && check_size_impl(oprsz, 4)) { + expand_cmp_i32(dofs, aofs, bofs, oprsz, cond); + } else { + gen_helper_gvec_3 * const *fn = fns[cond]; + + if (fn == NULL) { + uint32_t tmp; + tmp = aofs, aofs = bofs, bofs = tmp; + cond = tcg_swap_cond(cond); + fn = fns[cond]; + assert(fn != NULL); + } + tcg_gen_gvec_3_ool(dofs, aofs, bofs, oprsz, maxsz, 0, fn[vece]); + return; } - tcg_gen_gvec_3_ool(dofs, aofs, bofs, oprsz, maxsz, 0, fn[vece]); - return; + break; + + default: + g_assert_not_reached(); } - done: if (oprsz < maxsz) { expand_clr(dofs + oprsz, maxsz - oprsz); } diff --git a/tcg/tcg-op.c b/tcg/tcg-op.c index 3467787323..34b96d68f3 100644 --- a/tcg/tcg-op.c +++ b/tcg/tcg-op.c @@ -277,9 +277,15 @@ void tcg_gen_setcondi_i32(TCGCond cond, TCGv_i32 ret, void tcg_gen_muli_i32(TCGv_i32 ret, TCGv_i32 arg1, int32_t arg2) { - TCGv_i32 t0 = tcg_const_i32(arg2); - tcg_gen_mul_i32(ret, arg1, t0); - tcg_temp_free_i32(t0); + if (arg2 == 0) { + tcg_gen_movi_i32(ret, 0); + } else if (is_power_of_2(arg2)) { + tcg_gen_shli_i32(ret, arg1, ctz32(arg2)); + } else { + TCGv_i32 t0 = tcg_const_i32(arg2); + tcg_gen_mul_i32(ret, arg1, t0); + tcg_temp_free_i32(t0); + } } void tcg_gen_div_i32(TCGv_i32 ret, TCGv_i32 arg1, TCGv_i32 arg2) @@ -1430,9 +1436,15 @@ void tcg_gen_setcondi_i64(TCGCond cond, TCGv_i64 ret, void tcg_gen_muli_i64(TCGv_i64 ret, TCGv_i64 arg1, int64_t arg2) { - TCGv_i64 t0 = tcg_const_i64(arg2); - tcg_gen_mul_i64(ret, arg1, t0); - tcg_temp_free_i64(t0); + if (arg2 == 0) { + tcg_gen_movi_i64(ret, 0); + } else if (is_power_of_2(arg2)) { + tcg_gen_shli_i64(ret, arg1, ctz64(arg2)); + } else { + TCGv_i64 t0 = tcg_const_i64(arg2); + tcg_gen_mul_i64(ret, arg1, t0); + tcg_temp_free_i64(t0); + } } void tcg_gen_div_i64(TCGv_i64 ret, TCGv_i64 arg1, TCGv_i64 arg2) diff --git a/tests/.gitignore b/tests/.gitignore index 18e58b2183..fb62d2299b 100644 --- a/tests/.gitignore +++ b/tests/.gitignore @@ -86,6 +86,7 @@ test-thread-pool test-throttle test-timed-average test-uuid +test-util-sockets test-visitor-serialization test-vmstate test-write-threshold diff --git a/tests/Makefile.include b/tests/Makefile.include index ef9b88c369..42fd426cd0 100644 --- a/tests/Makefile.include +++ b/tests/Makefile.include @@ -146,6 +146,7 @@ ifneq (,$(findstring qemu-ga,$(TOOLS))) check-unit-$(CONFIG_LINUX) += tests/test-qga$(EXESUF) endif check-unit-y += tests/test-timed-average$(EXESUF) +check-unit-y += tests/test-util-sockets$(EXESUF) check-unit-y += tests/test-io-task$(EXESUF) check-unit-y += tests/test-io-channel-socket$(EXESUF) check-unit-y += tests/test-io-channel-file$(EXESUF) @@ -713,9 +714,11 @@ tests/test-crypto-tlscredsx509$(EXESUF): tests/test-crypto-tlscredsx509.o \ tests/test-crypto-tlssession.o-cflags := $(TASN1_CFLAGS) tests/test-crypto-tlssession$(EXESUF): tests/test-crypto-tlssession.o \ tests/crypto-tls-x509-helpers.o tests/pkix_asn1_tab.o $(test-crypto-obj-y) +tests/test-util-sockets$(EXESUF): tests/test-util-sockets.o \ + tests/socket-helpers.o $(test-util-obj-y) tests/test-io-task$(EXESUF): tests/test-io-task.o $(test-io-obj-y) tests/test-io-channel-socket$(EXESUF): tests/test-io-channel-socket.o \ - tests/io-channel-helpers.o $(test-io-obj-y) + tests/io-channel-helpers.o tests/socket-helpers.o $(test-io-obj-y) tests/tpm-crb-test$(EXESUF): tests/tpm-crb-test.o tests/tpm-emu.o $(test-io-obj-y) tests/tpm-tis-test$(EXESUF): tests/tpm-tis-test.o tests/tpm-emu.o $(test-io-obj-y) tests/test-io-channel-file$(EXESUF): tests/test-io-channel-file.o \ diff --git a/tests/qemu-iotests/033 b/tests/qemu-iotests/033 index a1d8357331..ee8a1338bb 100755 --- a/tests/qemu-iotests/033 +++ b/tests/qemu-iotests/033 @@ -105,6 +105,7 @@ for align in 512 4k; do done done +_cleanup_test_img # Trigger truncate that would shrink qcow2 L1 table, which is done by # clearing one entry (8 bytes) with bdrv_co_pwrite_zeroes() diff --git a/tests/qemu-iotests/169 b/tests/qemu-iotests/169 new file mode 100755 index 0000000000..3a8db91f6f --- /dev/null +++ b/tests/qemu-iotests/169 @@ -0,0 +1,156 @@ +#!/usr/bin/env python +# +# Tests for dirty bitmaps migration. +# +# Copyright (c) 2016-2017 Virtuozzo International GmbH. All rights reserved. +# +# 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 iotests +import time +import itertools +import operator +import new +from iotests import qemu_img + + +disk_a = os.path.join(iotests.test_dir, 'disk_a') +disk_b = os.path.join(iotests.test_dir, 'disk_b') +size = '1M' +mig_file = os.path.join(iotests.test_dir, 'mig_file') + + +class TestDirtyBitmapMigration(iotests.QMPTestCase): + def tearDown(self): + self.vm_a.shutdown() + self.vm_b.shutdown() + os.remove(disk_a) + os.remove(disk_b) + os.remove(mig_file) + + def setUp(self): + qemu_img('create', '-f', iotests.imgfmt, disk_a, size) + qemu_img('create', '-f', iotests.imgfmt, disk_b, size) + + self.vm_a = iotests.VM(path_suffix='a').add_drive(disk_a) + self.vm_a.launch() + + self.vm_b = iotests.VM(path_suffix='b') + self.vm_b.add_incoming("exec: cat '" + mig_file + "'") + + def add_bitmap(self, vm, granularity, persistent): + params = {'node': 'drive0', + 'name': 'bitmap0', + 'granularity': granularity} + if persistent: + params['persistent'] = True + params['autoload'] = True + + result = vm.qmp('block-dirty-bitmap-add', **params) + self.assert_qmp(result, 'return', {}); + + def get_bitmap_hash(self, vm): + result = vm.qmp('x-debug-block-dirty-bitmap-sha256', + node='drive0', name='bitmap0') + return result['return']['sha256'] + + def check_bitmap(self, vm, sha256): + result = vm.qmp('x-debug-block-dirty-bitmap-sha256', + node='drive0', name='bitmap0') + if sha256: + self.assert_qmp(result, 'return/sha256', sha256); + else: + self.assert_qmp(result, 'error/desc', + "Dirty bitmap 'bitmap0' not found"); + + def do_test_migration(self, persistent, migrate_bitmaps, online, + shared_storage): + granularity = 512 + + # regions = ((start, count), ...) + regions = ((0, 0x10000), + (0xf0000, 0x10000), + (0xa0201, 0x1000)) + + should_migrate = migrate_bitmaps or persistent and shared_storage + + self.vm_b.add_drive(disk_a if shared_storage else disk_b) + + if online: + os.mkfifo(mig_file) + self.vm_b.launch() + + self.add_bitmap(self.vm_a, granularity, persistent) + for r in regions: + self.vm_a.hmp_qemu_io('drive0', 'write %d %d' % r) + sha256 = self.get_bitmap_hash(self.vm_a) + + if migrate_bitmaps: + capabilities = [{'capability': 'dirty-bitmaps', 'state': True}] + + result = self.vm_a.qmp('migrate-set-capabilities', + capabilities=capabilities) + self.assert_qmp(result, 'return', {}) + + if online: + result = self.vm_b.qmp('migrate-set-capabilities', + capabilities=capabilities) + self.assert_qmp(result, 'return', {}) + + result = self.vm_a.qmp('migrate-set-capabilities', + capabilities=[{'capability': 'events', + 'state': True}]) + self.assert_qmp(result, 'return', {}) + + result = self.vm_a.qmp('migrate', uri='exec:cat>' + mig_file) + while True: + event = self.vm_a.event_wait('MIGRATION') + if event['data']['status'] == 'completed': + break + + if not online: + self.vm_a.shutdown() + self.vm_b.launch() + # TODO enable bitmap capability for vm_b in this case + + self.vm_b.event_wait("RESUME", timeout=10.0) + + self.check_bitmap(self.vm_b, sha256 if should_migrate else False) + + if should_migrate: + self.vm_b.shutdown() + self.vm_b.launch() + self.check_bitmap(self.vm_b, sha256 if persistent else False) + + +def inject_test_case(klass, name, method, *args, **kwargs): + mc = operator.methodcaller(method, *args, **kwargs) + setattr(klass, 'test_' + name, new.instancemethod(mc, None, klass)) + +for cmb in list(itertools.product((True, False), repeat=3)): + name = ('_' if cmb[0] else '_not_') + 'persistent_' + name += ('_' if cmb[1] else '_not_') + 'migbitmap_' + name += '_online' if cmb[2] else '_offline' + + # TODO fix shared-storage bitmap migration and enable cases for it + args = list(cmb) + [False] + + inject_test_case(TestDirtyBitmapMigration, name, 'do_test_migration', + *args) + + +if __name__ == '__main__': + iotests.main(supported_fmts=['qcow2']) diff --git a/tests/qemu-iotests/169.out b/tests/qemu-iotests/169.out new file mode 100644 index 0000000000..594c16f49f --- /dev/null +++ b/tests/qemu-iotests/169.out @@ -0,0 +1,5 @@ +........ +---------------------------------------------------------------------- +Ran 8 tests + +OK diff --git a/tests/qemu-iotests/199 b/tests/qemu-iotests/199 new file mode 100755 index 0000000000..651e8df5d9 --- /dev/null +++ b/tests/qemu-iotests/199 @@ -0,0 +1,118 @@ +#!/usr/bin/env python +# +# Tests for dirty bitmaps postcopy migration. +# +# Copyright (c) 2016-2017 Virtuozzo International GmbH. All rights reserved. +# +# 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 iotests +import time +from iotests import qemu_img + +disk_a = os.path.join(iotests.test_dir, 'disk_a') +disk_b = os.path.join(iotests.test_dir, 'disk_b') +size = '256G' +fifo = os.path.join(iotests.test_dir, 'mig_fifo') + +class TestDirtyBitmapPostcopyMigration(iotests.QMPTestCase): + + def tearDown(self): + self.vm_a.shutdown() + self.vm_b.shutdown() + os.remove(disk_a) + os.remove(disk_b) + os.remove(fifo) + + def setUp(self): + os.mkfifo(fifo) + qemu_img('create', '-f', iotests.imgfmt, disk_a, size) + qemu_img('create', '-f', iotests.imgfmt, disk_b, size) + self.vm_a = iotests.VM(path_suffix='a').add_drive(disk_a) + self.vm_b = iotests.VM(path_suffix='b').add_drive(disk_b) + self.vm_b.add_incoming("exec: cat '" + fifo + "'") + self.vm_a.launch() + self.vm_b.launch() + + def test_postcopy(self): + write_size = 0x40000000 + granularity = 512 + chunk = 4096 + + result = self.vm_a.qmp('block-dirty-bitmap-add', node='drive0', + name='bitmap', granularity=granularity) + self.assert_qmp(result, 'return', {}); + + s = 0 + while s < write_size: + self.vm_a.hmp_qemu_io('drive0', 'write %d %d' % (s, chunk)) + s += 0x10000 + s = 0x8000 + while s < write_size: + self.vm_a.hmp_qemu_io('drive0', 'write %d %d' % (s, chunk)) + s += 0x10000 + + result = self.vm_a.qmp('x-debug-block-dirty-bitmap-sha256', + node='drive0', name='bitmap') + sha256 = result['return']['sha256'] + + result = self.vm_a.qmp('block-dirty-bitmap-clear', node='drive0', + name='bitmap') + self.assert_qmp(result, 'return', {}); + s = 0 + while s < write_size: + self.vm_a.hmp_qemu_io('drive0', 'write %d %d' % (s, chunk)) + s += 0x10000 + + bitmaps_cap = {'capability': 'dirty-bitmaps', 'state': True} + events_cap = {'capability': 'events', 'state': True} + + result = self.vm_a.qmp('migrate-set-capabilities', + capabilities=[bitmaps_cap, events_cap]) + self.assert_qmp(result, 'return', {}) + + result = self.vm_b.qmp('migrate-set-capabilities', + capabilities=[bitmaps_cap]) + self.assert_qmp(result, 'return', {}) + + result = self.vm_a.qmp('migrate', uri='exec:cat>' + fifo) + self.assert_qmp(result, 'return', {}) + + result = self.vm_a.qmp('migrate-start-postcopy') + self.assert_qmp(result, 'return', {}) + + while True: + event = self.vm_a.event_wait('MIGRATION') + if event['data']['status'] == 'completed': + break + + s = 0x8000 + while s < write_size: + self.vm_b.hmp_qemu_io('drive0', 'write %d %d' % (s, chunk)) + s += 0x10000 + + result = self.vm_b.qmp('query-block'); + while len(result['return'][0]['dirty-bitmaps']) > 1: + time.sleep(2) + result = self.vm_b.qmp('query-block'); + + result = self.vm_b.qmp('x-debug-block-dirty-bitmap-sha256', + node='drive0', name='bitmap') + + self.assert_qmp(result, 'return/sha256', sha256); + +if __name__ == '__main__': + iotests.main(supported_fmts=['qcow2'], supported_cache_modes=['none']) diff --git a/tests/qemu-iotests/199.out b/tests/qemu-iotests/199.out new file mode 100644 index 0000000000..ae1213e6f8 --- /dev/null +++ b/tests/qemu-iotests/199.out @@ -0,0 +1,5 @@ +. +---------------------------------------------------------------------- +Ran 1 tests + +OK diff --git a/tests/qemu-iotests/208 b/tests/qemu-iotests/208 new file mode 100755 index 0000000000..4e82b96c82 --- /dev/null +++ b/tests/qemu-iotests/208 @@ -0,0 +1,55 @@ +#!/usr/bin/env python +# +# Copyright (C) 2018 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: Stefan Hajnoczi <stefanha@redhat.com> +# +# Check that the runtime NBD server does not crash when stopped after +# blockdev-snapshot-sync. + +import iotests + +with iotests.FilePath('disk.img') as disk_img_path, \ + iotests.FilePath('disk-snapshot.img') as disk_snapshot_img_path, \ + iotests.FilePath('nbd.sock') as nbd_sock_path, \ + iotests.VM() as vm: + + img_size = '10M' + iotests.qemu_img_pipe('create', '-f', iotests.imgfmt, disk_img_path, img_size) + + iotests.log('Launching VM...') + (vm.add_drive(disk_img_path, 'node-name=drive0-node', interface='none') + .launch()) + + iotests.log('Starting NBD server...') + iotests.log(vm.qmp('nbd-server-start', addr={ + "type": "unix", + "data": { + "path": nbd_sock_path, + } + })) + + iotests.log('Adding NBD export...') + iotests.log(vm.qmp('nbd-server-add', device='drive0-node', writable=True)) + + iotests.log('Creating external snapshot...') + iotests.log(vm.qmp('blockdev-snapshot-sync', + node_name='drive0-node', + snapshot_node_name='drive0-snapshot-node', + snapshot_file=disk_snapshot_img_path)) + + iotests.log('Stopping NBD server...') + iotests.log(vm.qmp('nbd-server-stop')) diff --git a/tests/qemu-iotests/208.out b/tests/qemu-iotests/208.out new file mode 100644 index 0000000000..3687e9d0dd --- /dev/null +++ b/tests/qemu-iotests/208.out @@ -0,0 +1,9 @@ +Launching VM... +Starting NBD server... +{u'return': {}} +Adding NBD export... +{u'return': {}} +Creating external snapshot... +{u'return': {}} +Stopping NBD server... +{u'return': {}} diff --git a/tests/qemu-iotests/209 b/tests/qemu-iotests/209 new file mode 100755 index 0000000000..259e991ec6 --- /dev/null +++ b/tests/qemu-iotests/209 @@ -0,0 +1,34 @@ +#!/usr/bin/env python +# +# Tests for NBD BLOCK_STATUS extension +# +# Copyright (c) 2018 Virtuozzo International GmbH +# +# 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 iotests +from iotests import qemu_img_create, qemu_io, qemu_img_verbose, qemu_nbd, \ + file_path + +iotests.verify_image_format(supported_fmts=['qcow2']) + +disk, nbd_sock = file_path('disk', 'nbd-sock') +nbd_uri = 'nbd+unix:///exp?socket=' + nbd_sock + +qemu_img_create('-f', iotests.imgfmt, disk, '1M') +qemu_io('-f', iotests.imgfmt, '-c', 'write 0 512K', disk) + +qemu_nbd('-k', nbd_sock, '-x', 'exp', '-f', iotests.imgfmt, disk) +qemu_img_verbose('map', '-f', 'raw', '--output=json', nbd_uri) diff --git a/tests/qemu-iotests/209.out b/tests/qemu-iotests/209.out new file mode 100644 index 0000000000..0d29724e84 --- /dev/null +++ b/tests/qemu-iotests/209.out @@ -0,0 +1,2 @@ +[{ "start": 0, "length": 524288, "depth": 0, "zero": false, "data": true}, +{ "start": 524288, "length": 524288, "depth": 0, "zero": true, "data": false}] diff --git a/tests/qemu-iotests/group b/tests/qemu-iotests/group index c401791fcd..0f912e17b0 100644 --- a/tests/qemu-iotests/group +++ b/tests/qemu-iotests/group @@ -169,6 +169,7 @@ 162 auto quick 163 rw auto quick 165 rw auto quick +169 rw auto quick 170 rw auto quick 171 rw auto quick 172 auto @@ -196,6 +197,7 @@ 196 rw auto quick 197 rw auto quick 198 rw auto +199 rw auto 200 rw auto 201 rw auto migration 202 rw auto quick @@ -204,3 +206,5 @@ 205 rw auto quick 206 rw auto 207 rw auto +208 rw auto quick +209 rw auto quick diff --git a/tests/qemu-iotests/iotests.py b/tests/qemu-iotests/iotests.py index 1bcc9ca57d..845be3ad01 100644 --- a/tests/qemu-iotests/iotests.py +++ b/tests/qemu-iotests/iotests.py @@ -23,12 +23,14 @@ import subprocess import string import unittest import sys -sys.path.append(os.path.join(os.path.dirname(__file__), '..', '..', 'scripts')) -import qtest import struct import json import signal import logging +import atexit + +sys.path.append(os.path.join(os.path.dirname(__file__), '..', '..', 'scripts')) +import qtest # This will not work if arguments contain spaces but is necessary if we @@ -249,6 +251,37 @@ class FilePath(object): return False +def file_path_remover(): + for path in reversed(file_path_remover.paths): + try: + os.remove(path) + except OSError: + pass + + +def file_path(*names): + ''' Another way to get auto-generated filename that cleans itself up. + + Use is as simple as: + + img_a, img_b = file_path('a.img', 'b.img') + sock = file_path('socket') + ''' + + if not hasattr(file_path_remover, 'paths'): + file_path_remover.paths = [] + atexit.register(file_path_remover) + + paths = [] + for name in names: + filename = '{0}-{1}'.format(os.getpid(), name) + path = os.path.join(test_dir, filename) + file_path_remover.paths.append(path) + paths.append(path) + + return paths[0] if len(paths) == 1 else paths + + class VM(qtest.QEMUQtestMachine): '''A QEMU VM''' @@ -504,6 +537,10 @@ def verify_platform(supported_oses=['linux']): if True not in [sys.platform.startswith(x) for x in supported_oses]: notrun('not suitable for this OS: %s' % sys.platform) +def verify_cache_mode(supported_cache_modes=[]): + if supported_cache_modes and (cachemode not in supported_cache_modes): + notrun('not suitable for this cache mode: %s' % cachemode) + def supports_quorum(): return 'quorum' in qemu_img_pipe('--help') @@ -512,7 +549,7 @@ def verify_quorum(): if not supports_quorum(): notrun('quorum support missing') -def main(supported_fmts=[], supported_oses=['linux']): +def main(supported_fmts=[], supported_oses=['linux'], supported_cache_modes=[]): '''Run tests''' global debug @@ -529,6 +566,7 @@ def main(supported_fmts=[], supported_oses=['linux']): verbosity = 1 verify_image_format(supported_fmts) verify_platform(supported_oses) + verify_cache_mode(supported_cache_modes) # We need to filter out the time taken from the output so that qemu-iotest # can reliably diff the results against master output. diff --git a/tests/qmp-test.c b/tests/qmp-test.c index 22445d9ec2..7470c6b754 100644 --- a/tests/qmp-test.c +++ b/tests/qmp-test.c @@ -204,6 +204,11 @@ static bool query_is_blacklisted(const char *cmd) "query-gic-capabilities", /* arm */ /* Success depends on target-specific build configuration: */ "query-pci", /* CONFIG_PCI */ + /* Success depends on launching SEV guest */ + "query-sev-launch-measure", + /* Success depends on Host or Hypervisor SEV support */ + "query-sev", + "query-sev-capabilities", NULL }; int i; diff --git a/tests/rcutorture.c b/tests/rcutorture.c index 4002ecf123..49311c82ea 100644 --- a/tests/rcutorture.c +++ b/tests/rcutorture.c @@ -238,7 +238,6 @@ long long rcu_stress_count[RCU_STRESS_PIPE_LEN + 1]; static void *rcu_read_stress_test(void *arg) { int i; - int itercnt = 0; struct rcu_stress *p; int pc; long long n_reads_local = 0; @@ -269,9 +268,6 @@ static void *rcu_read_stress_test(void *arg) } rcu_stress_local[pc]++; n_reads_local++; - if ((++itercnt % 0x1000) == 0) { - synchronize_rcu(); - } } qemu_mutex_lock(&counts_mutex); n_reads += n_reads_local; diff --git a/tests/socket-helpers.c b/tests/socket-helpers.c new file mode 100644 index 0000000000..8112763f5b --- /dev/null +++ b/tests/socket-helpers.c @@ -0,0 +1,149 @@ +/* + * Helper functions for tests using sockets + * + * Copyright 2015-2018 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 or + * (at your option) version 3 of the License. + * + * 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/>. + * + */ + +#include "qemu/osdep.h" +#include "qemu-common.h" +#include "qemu/sockets.h" +#include "socket-helpers.h" + +#ifndef AI_ADDRCONFIG +# define AI_ADDRCONFIG 0 +#endif +#ifndef EAI_ADDRFAMILY +# define EAI_ADDRFAMILY 0 +#endif + +int socket_can_bind_connect(const char *hostname) +{ + int lfd = -1, cfd = -1, afd = -1; + struct addrinfo ai, *res = NULL; + struct sockaddr_storage ss; + socklen_t sslen = sizeof(ss); + int soerr; + socklen_t soerrlen = sizeof(soerr); + bool check_soerr = false; + int rc; + int ret = -1; + + memset(&ai, 0, sizeof(ai)); + ai.ai_flags = AI_CANONNAME | AI_ADDRCONFIG; + ai.ai_family = AF_UNSPEC; + ai.ai_socktype = SOCK_STREAM; + + /* lookup */ + rc = getaddrinfo(hostname, NULL, &ai, &res); + if (rc != 0) { + if (rc == EAI_ADDRFAMILY || + rc == EAI_FAMILY) { + errno = EADDRNOTAVAIL; + } else { + errno = EINVAL; + } + goto cleanup; + } + + lfd = qemu_socket(res->ai_family, res->ai_socktype, res->ai_protocol); + if (lfd < 0) { + goto cleanup; + } + + cfd = qemu_socket(res->ai_family, res->ai_socktype, res->ai_protocol); + if (cfd < 0) { + goto cleanup; + } + + if (bind(lfd, res->ai_addr, res->ai_addrlen) < 0) { + goto cleanup; + } + + if (listen(lfd, 1) < 0) { + goto cleanup; + } + + if (getsockname(lfd, (struct sockaddr *)&ss, &sslen) < 0) { + goto cleanup; + } + + qemu_set_nonblock(cfd); + if (connect(cfd, (struct sockaddr *)&ss, sslen) < 0) { + if (errno == EINPROGRESS) { + check_soerr = true; + } else { + goto cleanup; + } + } + + sslen = sizeof(ss); + afd = accept(lfd, (struct sockaddr *)&ss, &sslen); + if (afd < 0) { + goto cleanup; + } + + if (check_soerr) { + if (qemu_getsockopt(cfd, SOL_SOCKET, SO_ERROR, &soerr, &soerrlen) < 0) { + goto cleanup; + } + if (soerr) { + errno = soerr; + goto cleanup; + } + } + + ret = 0; + + cleanup: + if (afd != -1) { + close(afd); + } + if (cfd != -1) { + close(cfd); + } + if (lfd != -1) { + close(lfd); + } + if (res) { + freeaddrinfo(res); + } + return ret; +} + + +int socket_check_protocol_support(bool *has_ipv4, bool *has_ipv6) +{ + *has_ipv4 = *has_ipv6 = false; + + if (socket_can_bind_connect("127.0.0.1") < 0) { + if (errno != EADDRNOTAVAIL) { + return -1; + } + } else { + *has_ipv4 = true; + } + + if (socket_can_bind_connect("::1") < 0) { + if (errno != EADDRNOTAVAIL) { + return -1; + } + } else { + *has_ipv6 = true; + } + + return 0; +} diff --git a/tests/socket-helpers.h b/tests/socket-helpers.h new file mode 100644 index 0000000000..1c07d6d656 --- /dev/null +++ b/tests/socket-helpers.h @@ -0,0 +1,42 @@ +/* + * Helper functions for tests using sockets + * + * Copyright 2015-2018 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 or + * (at your option) version 3 of the License. + * + * 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/>. + * + */ + +/* + * @hostname: a DNS name or numeric IP address + * + * Check whether it is possible to bind & connect to ports + * on the DNS name or IP address @hostname. If an IP address + * is used, it must not be a wildcard address. + * + * Returns 0 on success, -1 on error with errno set + */ +int socket_can_bind_connect(const char *hostname); + +/* + * @has_ipv4: set to true on return if IPv4 is available + * @has_ipv6: set to true on return if IPv6 is available + * + * Check whether IPv4 and/or IPv6 are available for use. + * On success, @has_ipv4 and @has_ipv6 will be set to + * indicate whether the respective protocols are available. + * + * Returns 0 on success, -1 on fatal error + */ +int socket_check_protocol_support(bool *has_ipv4, bool *has_ipv6); diff --git a/tests/test-char.c b/tests/test-char.c index b3a77af085..df939cc5c4 100644 --- a/tests/test-char.c +++ b/tests/test-char.c @@ -166,7 +166,6 @@ static void char_mux_test(void) FeHandler h1 = { 0, }, h2 = { 0, }; CharBackend chr_be1, chr_be2; - muxes_realized = true; /* done after machine init */ opts = qemu_opts_create(qemu_find_opts("chardev"), "mux-label", 1, &error_abort); qemu_opt_set(opts, "backend", "ringbuf", &error_abort); @@ -302,9 +301,8 @@ static int socket_can_read_hello(void *opaque) return 10; } -static void char_socket_test(void) +static void char_socket_test_common(Chardev *chr) { - Chardev *chr = qemu_chr_new("server", "tcp:127.0.0.1:0,server,nowait"); Chardev *chr_client; QObject *addr; QDict *qdict; @@ -359,6 +357,47 @@ static void char_socket_test(void) object_unparent(OBJECT(chr)); } + +static void char_socket_basic_test(void) +{ + Chardev *chr = qemu_chr_new("server", "tcp:127.0.0.1:0,server,nowait"); + + char_socket_test_common(chr); +} + + +static void char_socket_fdpass_test(void) +{ + Chardev *chr; + char *optstr; + QemuOpts *opts; + int fd; + SocketAddress *addr = g_new0(SocketAddress, 1); + + addr->type = SOCKET_ADDRESS_TYPE_INET; + addr->u.inet.host = g_strdup("127.0.0.1"); + addr->u.inet.port = g_strdup("0"); + + fd = socket_listen(addr, &error_abort); + g_assert(fd >= 0); + + qapi_free_SocketAddress(addr); + + optstr = g_strdup_printf("socket,id=cdev,fd=%d,server,nowait", fd); + + opts = qemu_opts_parse_noisily(qemu_find_opts("chardev"), + optstr, true); + g_free(optstr); + g_assert_nonnull(opts); + + chr = qemu_chr_new_from_opts(opts, &error_abort); + + qemu_opts_del(opts); + + char_socket_test_common(chr); +} + + #ifndef _WIN32 static void char_pipe_test(void) { @@ -775,7 +814,8 @@ int main(int argc, char **argv) #ifndef _WIN32 g_test_add_func("/char/file-fifo", char_file_fifo_test); #endif - g_test_add_func("/char/socket", char_socket_test); + g_test_add_func("/char/socket/basic", char_socket_basic_test); + g_test_add_func("/char/socket/fdpass", char_socket_fdpass_test); g_test_add_func("/char/udp", char_udp_test); #ifdef HAVE_CHARDEV_SERIAL g_test_add_func("/char/serial", char_serial_test); diff --git a/tests/test-cutils.c b/tests/test-cutils.c index f64a49b7fb..64a489c2e9 100644 --- a/tests/test-cutils.c +++ b/tests/test-cutils.c @@ -223,6 +223,583 @@ static void test_parse_uint_full_correct(void) g_assert_cmpint(i, ==, 123); } +static void test_qemu_strtoi_correct(void) +{ + const char *str = "12345 foo"; + char f = 'X'; + const char *endptr = &f; + int res = 999; + int err; + + err = qemu_strtoi(str, &endptr, 0, &res); + + g_assert_cmpint(err, ==, 0); + g_assert_cmpint(res, ==, 12345); + g_assert(endptr == str + 5); +} + +static void test_qemu_strtoi_null(void) +{ + char f = 'X'; + const char *endptr = &f; + int res = 999; + int err; + + err = qemu_strtoi(NULL, &endptr, 0, &res); + + g_assert_cmpint(err, ==, -EINVAL); + g_assert(endptr == NULL); +} + +static void test_qemu_strtoi_empty(void) +{ + const char *str = ""; + char f = 'X'; + const char *endptr = &f; + int res = 999; + int err; + + err = qemu_strtoi(str, &endptr, 0, &res); + + g_assert_cmpint(err, ==, -EINVAL); + g_assert(endptr == str); +} + +static void test_qemu_strtoi_whitespace(void) +{ + const char *str = " \t "; + char f = 'X'; + const char *endptr = &f; + int res = 999; + int err; + + err = qemu_strtoi(str, &endptr, 0, &res); + + g_assert_cmpint(err, ==, -EINVAL); + g_assert(endptr == str); +} + +static void test_qemu_strtoi_invalid(void) +{ + const char *str = " xxxx \t abc"; + char f = 'X'; + const char *endptr = &f; + int res = 999; + int err; + + err = qemu_strtoi(str, &endptr, 0, &res); + + g_assert_cmpint(err, ==, -EINVAL); + g_assert(endptr == str); +} + +static void test_qemu_strtoi_trailing(void) +{ + const char *str = "123xxx"; + char f = 'X'; + const char *endptr = &f; + int res = 999; + int err; + + err = qemu_strtoi(str, &endptr, 0, &res); + + g_assert_cmpint(err, ==, 0); + g_assert_cmpint(res, ==, 123); + g_assert(endptr == str + 3); +} + +static void test_qemu_strtoi_octal(void) +{ + const char *str = "0123"; + char f = 'X'; + const char *endptr = &f; + int res = 999; + int err; + + err = qemu_strtoi(str, &endptr, 8, &res); + + g_assert_cmpint(err, ==, 0); + g_assert_cmpint(res, ==, 0123); + g_assert(endptr == str + strlen(str)); + + res = 999; + endptr = &f; + err = qemu_strtoi(str, &endptr, 0, &res); + + g_assert_cmpint(err, ==, 0); + g_assert_cmpint(res, ==, 0123); + g_assert(endptr == str + strlen(str)); +} + +static void test_qemu_strtoi_decimal(void) +{ + const char *str = "0123"; + char f = 'X'; + const char *endptr = &f; + int res = 999; + int err; + + err = qemu_strtoi(str, &endptr, 10, &res); + + g_assert_cmpint(err, ==, 0); + g_assert_cmpint(res, ==, 123); + g_assert(endptr == str + strlen(str)); + + str = "123"; + res = 999; + endptr = &f; + err = qemu_strtoi(str, &endptr, 0, &res); + + g_assert_cmpint(err, ==, 0); + g_assert_cmpint(res, ==, 123); + g_assert(endptr == str + strlen(str)); +} + +static void test_qemu_strtoi_hex(void) +{ + const char *str = "0123"; + char f = 'X'; + const char *endptr = &f; + int res = 999; + int err; + + err = qemu_strtoi(str, &endptr, 16, &res); + + g_assert_cmpint(err, ==, 0); + g_assert_cmpint(res, ==, 0x123); + g_assert(endptr == str + strlen(str)); + + str = "0x123"; + res = 999; + endptr = &f; + err = qemu_strtoi(str, &endptr, 0, &res); + + g_assert_cmpint(err, ==, 0); + g_assert_cmpint(res, ==, 0x123); + g_assert(endptr == str + strlen(str)); +} + +static void test_qemu_strtoi_max(void) +{ + char *str = g_strdup_printf("%d", INT_MAX); + char f = 'X'; + const char *endptr = &f; + int res = 999; + int err; + + err = qemu_strtoi(str, &endptr, 0, &res); + + g_assert_cmpint(err, ==, 0); + g_assert_cmpint(res, ==, INT_MAX); + g_assert(endptr == str + strlen(str)); + g_free(str); +} + +static void test_qemu_strtoi_overflow(void) +{ + char *str = g_strdup_printf("%lld", (long long)INT_MAX + 1ll); + char f = 'X'; + const char *endptr = &f; + int res = 999; + int err; + + err = qemu_strtoi(str, &endptr, 0, &res); + + g_assert_cmpint(err, ==, -ERANGE); + g_assert_cmpint(res, ==, INT_MAX); + g_assert(endptr == str + strlen(str)); + g_free(str); +} + +static void test_qemu_strtoi_underflow(void) +{ + char *str = g_strdup_printf("%lld", (long long)INT_MIN - 1ll); + char f = 'X'; + const char *endptr = &f; + int res = 999; + int err; + + err = qemu_strtoi(str, &endptr, 0, &res); + + g_assert_cmpint(err, ==, -ERANGE); + g_assert_cmpint(res, ==, INT_MIN); + g_assert(endptr == str + strlen(str)); + g_free(str); +} + +static void test_qemu_strtoi_negative(void) +{ + const char *str = " \t -321"; + char f = 'X'; + const char *endptr = &f; + int res = 999; + int err; + + err = qemu_strtoi(str, &endptr, 0, &res); + + g_assert_cmpint(err, ==, 0); + g_assert_cmpint(res, ==, -321); + g_assert(endptr == str + strlen(str)); +} + +static void test_qemu_strtoi_full_correct(void) +{ + const char *str = "123"; + int res = 999; + int err; + + err = qemu_strtoi(str, NULL, 0, &res); + + g_assert_cmpint(err, ==, 0); + g_assert_cmpint(res, ==, 123); +} + +static void test_qemu_strtoi_full_null(void) +{ + char f = 'X'; + const char *endptr = &f; + int res = 999; + int err; + + err = qemu_strtoi(NULL, &endptr, 0, &res); + + g_assert_cmpint(err, ==, -EINVAL); + g_assert(endptr == NULL); +} + +static void test_qemu_strtoi_full_empty(void) +{ + const char *str = ""; + int res = 999L; + int err; + + err = qemu_strtoi(str, NULL, 0, &res); + + g_assert_cmpint(err, ==, -EINVAL); +} + +static void test_qemu_strtoi_full_negative(void) +{ + const char *str = " \t -321"; + int res = 999; + int err; + + err = qemu_strtoi(str, NULL, 0, &res); + + g_assert_cmpint(err, ==, 0); + g_assert_cmpint(res, ==, -321); +} + +static void test_qemu_strtoi_full_trailing(void) +{ + const char *str = "123xxx"; + int res; + int err; + + err = qemu_strtoi(str, NULL, 0, &res); + + g_assert_cmpint(err, ==, -EINVAL); +} + +static void test_qemu_strtoi_full_max(void) +{ + char *str = g_strdup_printf("%d", INT_MAX); + int res; + int err; + + err = qemu_strtoi(str, NULL, 0, &res); + + g_assert_cmpint(err, ==, 0); + g_assert_cmpint(res, ==, INT_MAX); + g_free(str); +} + +static void test_qemu_strtoui_correct(void) +{ + const char *str = "12345 foo"; + char f = 'X'; + const char *endptr = &f; + unsigned int res = 999; + int err; + + err = qemu_strtoui(str, &endptr, 0, &res); + + g_assert_cmpint(err, ==, 0); + g_assert_cmpuint(res, ==, 12345); + g_assert(endptr == str + 5); +} + +static void test_qemu_strtoui_null(void) +{ + char f = 'X'; + const char *endptr = &f; + unsigned int res = 999; + int err; + + err = qemu_strtoui(NULL, &endptr, 0, &res); + + g_assert_cmpint(err, ==, -EINVAL); + g_assert(endptr == NULL); +} + +static void test_qemu_strtoui_empty(void) +{ + const char *str = ""; + char f = 'X'; + const char *endptr = &f; + unsigned int res = 999; + int err; + + err = qemu_strtoui(str, &endptr, 0, &res); + + g_assert_cmpint(err, ==, -EINVAL); + g_assert(endptr == str); +} + +static void test_qemu_strtoui_whitespace(void) +{ + const char *str = " \t "; + char f = 'X'; + const char *endptr = &f; + unsigned int res = 999; + int err; + + err = qemu_strtoui(str, &endptr, 0, &res); + + g_assert_cmpint(err, ==, -EINVAL); + g_assert(endptr == str); +} + +static void test_qemu_strtoui_invalid(void) +{ + const char *str = " xxxx \t abc"; + char f = 'X'; + const char *endptr = &f; + unsigned int res = 999; + int err; + + err = qemu_strtoui(str, &endptr, 0, &res); + + g_assert_cmpint(err, ==, -EINVAL); + g_assert(endptr == str); +} + +static void test_qemu_strtoui_trailing(void) +{ + const char *str = "123xxx"; + char f = 'X'; + const char *endptr = &f; + unsigned int res = 999; + int err; + + err = qemu_strtoui(str, &endptr, 0, &res); + + g_assert_cmpint(err, ==, 0); + g_assert_cmpuint(res, ==, 123); + g_assert(endptr == str + 3); +} + +static void test_qemu_strtoui_octal(void) +{ + const char *str = "0123"; + char f = 'X'; + const char *endptr = &f; + unsigned int res = 999; + int err; + + err = qemu_strtoui(str, &endptr, 8, &res); + + g_assert_cmpint(err, ==, 0); + g_assert_cmpuint(res, ==, 0123); + g_assert(endptr == str + strlen(str)); + + res = 999; + endptr = &f; + err = qemu_strtoui(str, &endptr, 0, &res); + + g_assert_cmpint(err, ==, 0); + g_assert_cmpuint(res, ==, 0123); + g_assert(endptr == str + strlen(str)); +} + +static void test_qemu_strtoui_decimal(void) +{ + const char *str = "0123"; + char f = 'X'; + const char *endptr = &f; + unsigned int res = 999; + int err; + + err = qemu_strtoui(str, &endptr, 10, &res); + + g_assert_cmpint(err, ==, 0); + g_assert_cmpuint(res, ==, 123); + g_assert(endptr == str + strlen(str)); + + str = "123"; + res = 999; + endptr = &f; + err = qemu_strtoui(str, &endptr, 0, &res); + + g_assert_cmpint(err, ==, 0); + g_assert_cmpuint(res, ==, 123); + g_assert(endptr == str + strlen(str)); +} + +static void test_qemu_strtoui_hex(void) +{ + const char *str = "0123"; + char f = 'X'; + const char *endptr = &f; + unsigned int res = 999; + int err; + + err = qemu_strtoui(str, &endptr, 16, &res); + + g_assert_cmpint(err, ==, 0); + g_assert_cmphex(res, ==, 0x123); + g_assert(endptr == str + strlen(str)); + + str = "0x123"; + res = 999; + endptr = &f; + err = qemu_strtoui(str, &endptr, 0, &res); + + g_assert_cmpint(err, ==, 0); + g_assert_cmphex(res, ==, 0x123); + g_assert(endptr == str + strlen(str)); +} + +static void test_qemu_strtoui_max(void) +{ + char *str = g_strdup_printf("%u", UINT_MAX); + char f = 'X'; + const char *endptr = &f; + unsigned int res = 999; + int err; + + err = qemu_strtoui(str, &endptr, 0, &res); + + g_assert_cmpint(err, ==, 0); + g_assert_cmphex(res, ==, UINT_MAX); + g_assert(endptr == str + strlen(str)); + g_free(str); +} + +static void test_qemu_strtoui_overflow(void) +{ + char *str = g_strdup_printf("%lld", (long long)UINT_MAX + 1ll); + char f = 'X'; + const char *endptr = &f; + unsigned int res = 999; + int err; + + err = qemu_strtoui(str, &endptr, 0, &res); + + g_assert_cmpint(err, ==, -ERANGE); + g_assert_cmphex(res, ==, UINT_MAX); + g_assert(endptr == str + strlen(str)); + g_free(str); +} + +static void test_qemu_strtoui_underflow(void) +{ + char *str = g_strdup_printf("%lld", (long long)INT_MIN - 1ll); + char f = 'X'; + const char *endptr = &f; + unsigned int res = 999; + int err; + + err = qemu_strtoui(str, &endptr, 0, &res); + + g_assert_cmpint(err, ==, -ERANGE); + g_assert_cmpuint(res, ==, (unsigned int)-1); + g_assert(endptr == str + strlen(str)); + g_free(str); +} + +static void test_qemu_strtoui_negative(void) +{ + const char *str = " \t -321"; + char f = 'X'; + const char *endptr = &f; + unsigned int res = 999; + int err; + + err = qemu_strtoui(str, &endptr, 0, &res); + + g_assert_cmpint(err, ==, 0); + g_assert_cmpuint(res, ==, (unsigned int)-321); + g_assert(endptr == str + strlen(str)); +} + +static void test_qemu_strtoui_full_correct(void) +{ + const char *str = "123"; + unsigned int res = 999; + int err; + + err = qemu_strtoui(str, NULL, 0, &res); + + g_assert_cmpint(err, ==, 0); + g_assert_cmpuint(res, ==, 123); +} + +static void test_qemu_strtoui_full_null(void) +{ + unsigned int res = 999; + int err; + + err = qemu_strtoui(NULL, NULL, 0, &res); + + g_assert_cmpint(err, ==, -EINVAL); +} + +static void test_qemu_strtoui_full_empty(void) +{ + const char *str = ""; + unsigned int res = 999; + int err; + + err = qemu_strtoui(str, NULL, 0, &res); + + g_assert_cmpint(err, ==, -EINVAL); +} +static void test_qemu_strtoui_full_negative(void) +{ + const char *str = " \t -321"; + unsigned int res = 999; + int err; + + err = qemu_strtoui(str, NULL, 0, &res); + g_assert_cmpint(err, ==, 0); + g_assert_cmpuint(res, ==, (unsigned int)-321); +} + +static void test_qemu_strtoui_full_trailing(void) +{ + const char *str = "123xxx"; + unsigned int res; + int err; + + err = qemu_strtoui(str, NULL, 0, &res); + + g_assert_cmpint(err, ==, -EINVAL); +} + +static void test_qemu_strtoui_full_max(void) +{ + char *str = g_strdup_printf("%u", UINT_MAX); + unsigned int res = 999; + int err; + + err = qemu_strtoui(str, NULL, 0, &res); + + g_assert_cmpint(err, ==, 0); + g_assert_cmphex(res, ==, UINT_MAX); + g_free(str); +} + static void test_qemu_strtol_correct(void) { const char *str = "12345 foo"; @@ -1612,6 +2189,86 @@ int main(int argc, char **argv) g_test_add_func("/cutils/parse_uint_full/correct", test_parse_uint_full_correct); + /* qemu_strtoi() tests */ + g_test_add_func("/cutils/qemu_strtoi/correct", + test_qemu_strtoi_correct); + g_test_add_func("/cutils/qemu_strtoi/null", + test_qemu_strtoi_null); + g_test_add_func("/cutils/qemu_strtoi/empty", + test_qemu_strtoi_empty); + g_test_add_func("/cutils/qemu_strtoi/whitespace", + test_qemu_strtoi_whitespace); + g_test_add_func("/cutils/qemu_strtoi/invalid", + test_qemu_strtoi_invalid); + g_test_add_func("/cutils/qemu_strtoi/trailing", + test_qemu_strtoi_trailing); + g_test_add_func("/cutils/qemu_strtoi/octal", + test_qemu_strtoi_octal); + g_test_add_func("/cutils/qemu_strtoi/decimal", + test_qemu_strtoi_decimal); + g_test_add_func("/cutils/qemu_strtoi/hex", + test_qemu_strtoi_hex); + g_test_add_func("/cutils/qemu_strtoi/max", + test_qemu_strtoi_max); + g_test_add_func("/cutils/qemu_strtoi/overflow", + test_qemu_strtoi_overflow); + g_test_add_func("/cutils/qemu_strtoi/underflow", + test_qemu_strtoi_underflow); + g_test_add_func("/cutils/qemu_strtoi/negative", + test_qemu_strtoi_negative); + g_test_add_func("/cutils/qemu_strtoi_full/correct", + test_qemu_strtoi_full_correct); + g_test_add_func("/cutils/qemu_strtoi_full/null", + test_qemu_strtoi_full_null); + g_test_add_func("/cutils/qemu_strtoi_full/empty", + test_qemu_strtoi_full_empty); + g_test_add_func("/cutils/qemu_strtoi_full/negative", + test_qemu_strtoi_full_negative); + g_test_add_func("/cutils/qemu_strtoi_full/trailing", + test_qemu_strtoi_full_trailing); + g_test_add_func("/cutils/qemu_strtoi_full/max", + test_qemu_strtoi_full_max); + + /* qemu_strtoui() tests */ + g_test_add_func("/cutils/qemu_strtoui/correct", + test_qemu_strtoui_correct); + g_test_add_func("/cutils/qemu_strtoui/null", + test_qemu_strtoui_null); + g_test_add_func("/cutils/qemu_strtoui/empty", + test_qemu_strtoui_empty); + g_test_add_func("/cutils/qemu_strtoui/whitespace", + test_qemu_strtoui_whitespace); + g_test_add_func("/cutils/qemu_strtoui/invalid", + test_qemu_strtoui_invalid); + g_test_add_func("/cutils/qemu_strtoui/trailing", + test_qemu_strtoui_trailing); + g_test_add_func("/cutils/qemu_strtoui/octal", + test_qemu_strtoui_octal); + g_test_add_func("/cutils/qemu_strtoui/decimal", + test_qemu_strtoui_decimal); + g_test_add_func("/cutils/qemu_strtoui/hex", + test_qemu_strtoui_hex); + g_test_add_func("/cutils/qemu_strtoui/max", + test_qemu_strtoui_max); + g_test_add_func("/cutils/qemu_strtoui/overflow", + test_qemu_strtoui_overflow); + g_test_add_func("/cutils/qemu_strtoui/underflow", + test_qemu_strtoui_underflow); + g_test_add_func("/cutils/qemu_strtoui/negative", + test_qemu_strtoui_negative); + g_test_add_func("/cutils/qemu_strtoui_full/correct", + test_qemu_strtoui_full_correct); + g_test_add_func("/cutils/qemu_strtoui_full/null", + test_qemu_strtoui_full_null); + g_test_add_func("/cutils/qemu_strtoui_full/empty", + test_qemu_strtoui_full_empty); + g_test_add_func("/cutils/qemu_strtoui_full/negative", + test_qemu_strtoui_full_negative); + g_test_add_func("/cutils/qemu_strtoui_full/trailing", + test_qemu_strtoui_full_trailing); + g_test_add_func("/cutils/qemu_strtoui_full/max", + test_qemu_strtoui_full_max); + /* qemu_strtol() tests */ g_test_add_func("/cutils/qemu_strtol/correct", test_qemu_strtol_correct); diff --git a/tests/test-io-channel-socket.c b/tests/test-io-channel-socket.c index b273fd3ba2..0597213f93 100644 --- a/tests/test-io-channel-socket.c +++ b/tests/test-io-channel-socket.c @@ -22,77 +22,9 @@ #include "io/channel-socket.h" #include "io/channel-util.h" #include "io-channel-helpers.h" +#include "socket-helpers.h" #include "qapi/error.h" -#ifndef AI_ADDRCONFIG -# define AI_ADDRCONFIG 0 -#endif -#ifndef EAI_ADDRFAMILY -# define EAI_ADDRFAMILY 0 -#endif - -static int check_bind(const char *hostname, bool *has_proto) -{ - int fd = -1; - struct addrinfo ai, *res = NULL; - int rc; - int ret = -1; - - memset(&ai, 0, sizeof(ai)); - ai.ai_flags = AI_CANONNAME | AI_ADDRCONFIG; - ai.ai_family = AF_UNSPEC; - ai.ai_socktype = SOCK_STREAM; - - /* lookup */ - rc = getaddrinfo(hostname, NULL, &ai, &res); - if (rc != 0) { - if (rc == EAI_ADDRFAMILY || - rc == EAI_FAMILY) { - *has_proto = false; - goto done; - } - goto cleanup; - } - - fd = qemu_socket(res->ai_family, res->ai_socktype, res->ai_protocol); - if (fd < 0) { - goto cleanup; - } - - if (bind(fd, res->ai_addr, res->ai_addrlen) < 0) { - if (errno == EADDRNOTAVAIL) { - *has_proto = false; - goto done; - } - goto cleanup; - } - - *has_proto = true; - done: - ret = 0; - - cleanup: - if (fd != -1) { - close(fd); - } - if (res) { - freeaddrinfo(res); - } - return ret; -} - -static int check_protocol_support(bool *has_ipv4, bool *has_ipv6) -{ - if (check_bind("127.0.0.1", has_ipv4) < 0) { - return -1; - } - if (check_bind("::1", has_ipv6) < 0) { - return -1; - } - - return 0; -} - static void test_io_channel_set_socket_bufs(QIOChannel *src, QIOChannel *dst) @@ -566,7 +498,7 @@ int main(int argc, char **argv) * each protocol to avoid breaking tests on machines * with either IPv4 or IPv6 disabled. */ - if (check_protocol_support(&has_ipv4, &has_ipv6) < 0) { + if (socket_check_protocol_support(&has_ipv4, &has_ipv6) < 0) { return 1; } diff --git a/tests/test-util-sockets.c b/tests/test-util-sockets.c new file mode 100644 index 0000000000..acadd85e8f --- /dev/null +++ b/tests/test-util-sockets.c @@ -0,0 +1,266 @@ +/* + * Tests for util/qemu-sockets.c + * + * Copyright 2018 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 library; if not, see <http://www.gnu.org/licenses/>. + * + */ + +#include "qemu/osdep.h" +#include "qemu-common.h" +#include "qemu/sockets.h" +#include "qapi/error.h" +#include "socket-helpers.h" +#include "monitor/monitor.h" + +static void test_fd_is_socket_bad(void) +{ + char *tmp = g_strdup("qemu-test-util-sockets-XXXXXX"); + int fd = mkstemp(tmp); + if (fd != 0) { + unlink(tmp); + } + g_free(tmp); + + g_assert(fd >= 0); + + g_assert(!fd_is_socket(fd)); + close(fd); +} + +static void test_fd_is_socket_good(void) +{ + int fd = qemu_socket(PF_INET, SOCK_STREAM, 0); + + g_assert(fd >= 0); + + g_assert(fd_is_socket(fd)); + close(fd); +} + +static int mon_fd = -1; +static const char *mon_fdname; + +int monitor_get_fd(Monitor *mon, const char *fdname, Error **errp) +{ + g_assert(cur_mon); + g_assert(mon == cur_mon); + if (mon_fd == -1 || !g_str_equal(mon_fdname, fdname)) { + error_setg(errp, "No fd named %s", fdname); + return -1; + } + return dup(mon_fd); +} + +/* Syms in libqemustub.a are discarded at .o file granularity. + * To replace monitor_get_fd() we must ensure everything in + * stubs/monitor.c is defined, to make sure monitor.o is discarded + * otherwise we get duplicate syms at link time. + */ +Monitor *cur_mon; +void monitor_init(Chardev *chr, int flags) {} + + +static void test_socket_fd_pass_name_good(void) +{ + SocketAddress addr; + int fd; + + cur_mon = g_malloc(1); /* Fake a monitor */ + mon_fdname = "myfd"; + mon_fd = qemu_socket(AF_INET, SOCK_STREAM, 0); + g_assert_cmpint(mon_fd, >, STDERR_FILENO); + + addr.type = SOCKET_ADDRESS_TYPE_FD; + addr.u.fd.str = g_strdup(mon_fdname); + + fd = socket_connect(&addr, &error_abort); + g_assert_cmpint(fd, !=, -1); + g_assert_cmpint(fd, !=, mon_fd); + close(fd); + + fd = socket_listen(&addr, &error_abort); + g_assert_cmpint(fd, !=, -1); + g_assert_cmpint(fd, !=, mon_fd); + close(fd); + + g_free(addr.u.fd.str); + mon_fdname = NULL; + close(mon_fd); + mon_fd = -1; + g_free(cur_mon); + cur_mon = NULL; +} + +static void test_socket_fd_pass_name_bad(void) +{ + SocketAddress addr; + Error *err = NULL; + int fd; + + cur_mon = g_malloc(1); /* Fake a monitor */ + mon_fdname = "myfd"; + mon_fd = dup(STDOUT_FILENO); + g_assert_cmpint(mon_fd, >, STDERR_FILENO); + + addr.type = SOCKET_ADDRESS_TYPE_FD; + addr.u.fd.str = g_strdup(mon_fdname); + + fd = socket_connect(&addr, &err); + g_assert_cmpint(fd, ==, -1); + error_free_or_abort(&err); + + fd = socket_listen(&addr, &err); + g_assert_cmpint(fd, ==, -1); + error_free_or_abort(&err); + + g_free(addr.u.fd.str); + mon_fdname = NULL; + close(mon_fd); + mon_fd = -1; + g_free(cur_mon); + cur_mon = NULL; +} + +static void test_socket_fd_pass_name_nomon(void) +{ + SocketAddress addr; + Error *err = NULL; + int fd; + + g_assert(cur_mon == NULL); + + addr.type = SOCKET_ADDRESS_TYPE_FD; + addr.u.fd.str = g_strdup("myfd"); + + fd = socket_connect(&addr, &err); + g_assert_cmpint(fd, ==, -1); + error_free_or_abort(&err); + + fd = socket_listen(&addr, &err); + g_assert_cmpint(fd, ==, -1); + error_free_or_abort(&err); + + g_free(addr.u.fd.str); +} + + +static void test_socket_fd_pass_num_good(void) +{ + SocketAddress addr; + int fd, sfd; + + g_assert(cur_mon == NULL); + sfd = qemu_socket(AF_INET, SOCK_STREAM, 0); + g_assert_cmpint(sfd, >, STDERR_FILENO); + + addr.type = SOCKET_ADDRESS_TYPE_FD; + addr.u.fd.str = g_strdup_printf("%d", sfd); + + fd = socket_connect(&addr, &error_abort); + g_assert_cmpint(fd, ==, sfd); + + fd = socket_listen(&addr, &error_abort); + g_assert_cmpint(fd, ==, sfd); + + g_free(addr.u.fd.str); + close(sfd); +} + +static void test_socket_fd_pass_num_bad(void) +{ + SocketAddress addr; + Error *err = NULL; + int fd, sfd; + + g_assert(cur_mon == NULL); + sfd = dup(STDOUT_FILENO); + + addr.type = SOCKET_ADDRESS_TYPE_FD; + addr.u.fd.str = g_strdup_printf("%d", sfd); + + fd = socket_connect(&addr, &err); + g_assert_cmpint(fd, ==, -1); + error_free_or_abort(&err); + + fd = socket_listen(&addr, &err); + g_assert_cmpint(fd, ==, -1); + error_free_or_abort(&err); + + g_free(addr.u.fd.str); + close(sfd); +} + +static void test_socket_fd_pass_num_nocli(void) +{ + SocketAddress addr; + Error *err = NULL; + int fd; + + cur_mon = g_malloc(1); /* Fake a monitor */ + + addr.type = SOCKET_ADDRESS_TYPE_FD; + addr.u.fd.str = g_strdup_printf("%d", STDOUT_FILENO); + + fd = socket_connect(&addr, &err); + g_assert_cmpint(fd, ==, -1); + error_free_or_abort(&err); + + fd = socket_listen(&addr, &err); + g_assert_cmpint(fd, ==, -1); + error_free_or_abort(&err); + + g_free(addr.u.fd.str); +} + + +int main(int argc, char **argv) +{ + bool has_ipv4, has_ipv6; + + socket_init(); + + g_test_init(&argc, &argv, NULL); + + /* We're creating actual IPv4/6 sockets, so we should + * check if the host running tests actually supports + * each protocol to avoid breaking tests on machines + * with either IPv4 or IPv6 disabled. + */ + if (socket_check_protocol_support(&has_ipv4, &has_ipv6) < 0) { + return 1; + } + + if (has_ipv4) { + g_test_add_func("/util/socket/is-socket/bad", + test_fd_is_socket_bad); + g_test_add_func("/util/socket/is-socket/good", + test_fd_is_socket_good); + g_test_add_func("/socket/fd-pass/name/good", + test_socket_fd_pass_name_good); + g_test_add_func("/socket/fd-pass/name/bad", + test_socket_fd_pass_name_bad); + g_test_add_func("/socket/fd-pass/name/nomon", + test_socket_fd_pass_name_nomon); + g_test_add_func("/socket/fd-pass/num/good", + test_socket_fd_pass_num_good); + g_test_add_func("/socket/fd-pass/num/bad", + test_socket_fd_pass_num_bad); + g_test_add_func("/socket/fd-pass/num/nocli", + test_socket_fd_pass_num_nocli); + } + + return g_test_run(); +} diff --git a/tests/virtio-scsi-test.c b/tests/virtio-scsi-test.c index 7393d69bb2..037872bb98 100644 --- a/tests/virtio-scsi-test.c +++ b/tests/virtio-scsi-test.c @@ -216,6 +216,9 @@ static void test_unaligned_write_same(void) const uint8_t write_same_cdb_2[VIRTIO_SCSI_CDB_SIZE] = { 0x41, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x33, 0x00, 0x00 }; + const uint8_t write_same_cdb_ndob[VIRTIO_SCSI_CDB_SIZE] = { + 0x41, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x33, 0x00, 0x00 + }; vs = qvirtio_scsi_pci_init(PCI_SLOT); @@ -225,6 +228,9 @@ static void test_unaligned_write_same(void) g_assert_cmphex(0, ==, virtio_scsi_do_command(vs, write_same_cdb_2, NULL, 0, buf2, 512, NULL)); + g_assert_cmphex(0, ==, + virtio_scsi_do_command(vs, write_same_cdb_ndob, NULL, 0, NULL, 0, NULL)); + qvirtio_scsi_pci_free(vs); } diff --git a/ui/cocoa.m b/ui/cocoa.m index 18de0bb3ea..110b393e4e 100644 --- a/ui/cocoa.m +++ b/ui/cocoa.m @@ -1330,7 +1330,7 @@ QemuCocoaView *cocoaView; /* Create the version string*/ NSString *version_string; version_string = [[NSString alloc] initWithFormat: - @"QEMU emulator version %s%s", QEMU_VERSION, QEMU_PKGVERSION]; + @"QEMU emulator version %s", QEMU_FULL_VERSION]; [version_label setStringValue: version_string]; [superView addSubview: version_label]; diff --git a/ui/console.c b/ui/console.c index a8868fc04f..530a491987 100644 --- a/ui/console.c +++ b/ui/console.c @@ -1282,11 +1282,16 @@ static QemuConsole *new_console(DisplayState *ds, console_type_t console_type, s->console_type = console_type; consoles = g_realloc(consoles, sizeof(*consoles) * (nb_consoles+1)); - if (console_type != GRAPHIC_CONSOLE) { + if (console_type != GRAPHIC_CONSOLE || qdev_hotplug) { s->index = nb_consoles; consoles[nb_consoles++] = s; } else { - /* HACK: Put graphical consoles before text consoles. */ + /* + * HACK: Put graphical consoles before text consoles. + * + * Only do that for coldplugged devices. After initial device + * initialization we will not renumber the consoles any more. + */ for (i = nb_consoles; i > 0; i--) { if (consoles[i - 1]->console_type == GRAPHIC_CONSOLE) break; @@ -1874,21 +1879,61 @@ QemuConsole *graphic_console_init(DeviceState *dev, uint32_t head, int height = 480; QemuConsole *s; DisplayState *ds; + DisplaySurface *surface; ds = get_alloc_displaystate(); - trace_console_gfx_new(); - s = new_console(ds, GRAPHIC_CONSOLE, head); - s->ui_timer = timer_new_ms(QEMU_CLOCK_REALTIME, dpy_set_ui_info_timer, s); + s = qemu_console_lookup_unused(); + if (s) { + trace_console_gfx_reuse(s->index); + if (s->surface) { + width = surface_width(s->surface); + height = surface_height(s->surface); + } + } else { + trace_console_gfx_new(); + s = new_console(ds, GRAPHIC_CONSOLE, head); + s->ui_timer = timer_new_ms(QEMU_CLOCK_REALTIME, + dpy_set_ui_info_timer, s); + } graphic_console_set_hwops(s, hw_ops, opaque); if (dev) { object_property_set_link(OBJECT(s), OBJECT(dev), "device", &error_abort); } - s->surface = qemu_create_message_surface(width, height, noinit); + surface = qemu_create_message_surface(width, height, noinit); + dpy_gfx_replace_surface(s, surface); return s; } +static const GraphicHwOps unused_ops = { + /* no callbacks */ +}; + +void graphic_console_close(QemuConsole *con) +{ + static const char unplugged[] = + "Guest display has been unplugged"; + DisplaySurface *surface; + int width = 640; + int height = 480; + + if (con->surface) { + width = surface_width(con->surface); + height = surface_height(con->surface); + } + + trace_console_gfx_close(con->index); + object_property_set_link(OBJECT(con), NULL, "device", &error_abort); + graphic_console_set_hwops(con, &unused_ops, NULL); + + if (con->gl) { + dpy_gl_scanout_disable(con); + } + surface = qemu_create_message_surface(width, height, unplugged); + dpy_gfx_replace_surface(con, surface); +} + QemuConsole *qemu_console_lookup_by_index(unsigned int index) { if (index >= nb_consoles) { @@ -1945,6 +1990,28 @@ QemuConsole *qemu_console_lookup_by_device_name(const char *device_id, return con; } +QemuConsole *qemu_console_lookup_unused(void) +{ + Object *obj; + int i; + + for (i = 0; i < nb_consoles; i++) { + if (!consoles[i]) { + continue; + } + if (consoles[i]->hw_ops != &unused_ops) { + continue; + } + obj = object_property_get_link(OBJECT(consoles[i]), + "device", &error_abort); + if (obj != NULL) { + continue; + } + return consoles[i]; + } + return NULL; +} + bool qemu_console_is_visible(QemuConsole *con) { return (con == active_console) || (con->dcls > 0); diff --git a/ui/qemu-pixman.c b/ui/qemu-pixman.c index 6e591ab821..3e52abd92d 100644 --- a/ui/qemu-pixman.c +++ b/ui/qemu-pixman.c @@ -6,6 +6,7 @@ #include "qemu/osdep.h" #include "qemu-common.h" #include "ui/console.h" +#include "standard-headers/drm/drm_fourcc.h" PixelFormat qemu_pixelformat_from_pixman(pixman_format_code_t format) { @@ -88,6 +89,27 @@ pixman_format_code_t qemu_default_pixman_format(int bpp, bool native_endian) return 0; } +/* Note: drm is little endian, pixman is native endian */ +pixman_format_code_t qemu_drm_format_to_pixman(uint32_t drm_format) +{ + static const struct { + uint32_t drm_format; + pixman_format_code_t pixman; + } map[] = { + { DRM_FORMAT_RGB888, PIXMAN_LE_r8g8b8 }, + { DRM_FORMAT_ARGB8888, PIXMAN_LE_a8r8g8b8 }, + { DRM_FORMAT_XRGB8888, PIXMAN_LE_x8r8g8b8 } + }; + int i; + + for (i = 0; i < ARRAY_SIZE(map); i++) { + if (drm_format == map[i].drm_format) { + return map[i].pixman; + } + } + return 0; +} + int qemu_pixman_get_type(int rshift, int gshift, int bshift) { int type = PIXMAN_TYPE_OTHER; diff --git a/ui/trace-events b/ui/trace-events index a957f363f1..eb4bf7f00d 100644 --- a/ui/trace-events +++ b/ui/trace-events @@ -2,6 +2,8 @@ # ui/console.c console_gfx_new(void) "" +console_gfx_reuse(int index) "%d" +console_gfx_close(int index) "%d" console_putchar_csi(int esc_param0, int esc_param1, int ch, int nb_esc_params) "escape sequence CSI%d;%d%c, %d parameters" console_putchar_unhandled(int ch) "unhandled escape character '%c'" console_txt_new(int w, int h) "%dx%d" diff --git a/util/Makefile.objs b/util/Makefile.objs index ae90b9963d..728c3541db 100644 --- a/util/Makefile.objs +++ b/util/Makefile.objs @@ -33,6 +33,7 @@ util-obj-y += throttle.o util-obj-y += getauxval.o util-obj-y += readline.o util-obj-y += rcu.o +util-obj-$(CONFIG_MEMBARRIER) += sys_membarrier.o util-obj-y += qemu-coroutine.o qemu-coroutine-lock.o qemu-coroutine-io.o util-obj-y += qemu-coroutine-sleep.o util-obj-y += coroutine-$(CONFIG_COROUTINE_BACKEND).o diff --git a/util/cutils.c b/util/cutils.c index b33ede83d1..0de69e6db4 100644 --- a/util/cutils.c +++ b/util/cutils.c @@ -298,6 +298,115 @@ static int check_strtox_error(const char *nptr, char *ep, } /** + * Convert string @nptr to an integer, and store it in @result. + * + * This is a wrapper around strtol() that is harder to misuse. + * Semantics of @nptr, @endptr, @base match strtol() with differences + * noted below. + * + * @nptr may be null, and no conversion is performed then. + * + * If no conversion is performed, store @nptr in *@endptr and return + * -EINVAL. + * + * If @endptr is null, and the string isn't fully converted, return + * -EINVAL. This is the case when the pointer that would be stored in + * a non-null @endptr points to a character other than '\0'. + * + * If the conversion overflows @result, store INT_MAX in @result, + * and return -ERANGE. + * + * If the conversion underflows @result, store INT_MIN in @result, + * and return -ERANGE. + * + * Else store the converted value in @result, and return zero. + */ +int qemu_strtoi(const char *nptr, const char **endptr, int base, + int *result) +{ + char *ep; + long long lresult; + + if (!nptr) { + if (endptr) { + *endptr = nptr; + } + return -EINVAL; + } + + errno = 0; + lresult = strtoll(nptr, &ep, base); + if (lresult < INT_MIN) { + *result = INT_MIN; + errno = ERANGE; + } else if (lresult > INT_MAX) { + *result = INT_MAX; + errno = ERANGE; + } else { + *result = lresult; + } + return check_strtox_error(nptr, ep, endptr, errno); +} + +/** + * Convert string @nptr to an unsigned integer, and store it in @result. + * + * This is a wrapper around strtoul() that is harder to misuse. + * Semantics of @nptr, @endptr, @base match strtoul() with differences + * noted below. + * + * @nptr may be null, and no conversion is performed then. + * + * If no conversion is performed, store @nptr in *@endptr and return + * -EINVAL. + * + * If @endptr is null, and the string isn't fully converted, return + * -EINVAL. This is the case when the pointer that would be stored in + * a non-null @endptr points to a character other than '\0'. + * + * If the conversion overflows @result, store UINT_MAX in @result, + * and return -ERANGE. + * + * Else store the converted value in @result, and return zero. + * + * Note that a number with a leading minus sign gets converted without + * the minus sign, checked for overflow (see above), then negated (in + * @result's type). This is exactly how strtoul() works. + */ +int qemu_strtoui(const char *nptr, const char **endptr, int base, + unsigned int *result) +{ + char *ep; + long long lresult; + + if (!nptr) { + if (endptr) { + *endptr = nptr; + } + return -EINVAL; + } + + errno = 0; + lresult = strtoull(nptr, &ep, base); + + /* Windows returns 1 for negative out-of-range values. */ + if (errno == ERANGE) { + *result = -1; + } else { + if (lresult > UINT_MAX) { + *result = UINT_MAX; + errno = ERANGE; + } else if (lresult < INT_MIN) { + *result = UINT_MAX; + errno = ERANGE; + } else { + *result = lresult; + } + } + return check_strtox_error(nptr, ep, endptr, errno); +} + +/** * Convert string @nptr to a long integer, and store it in @result. * * This is a wrapper around strtol() that is harder to misuse. diff --git a/util/main-loop.c b/util/main-loop.c index 7558eb5f53..992f9b0f34 100644 --- a/util/main-loop.c +++ b/util/main-loop.c @@ -29,6 +29,7 @@ #include "qemu/sockets.h" // struct in_addr needed for libslirp.h #include "sysemu/qtest.h" #include "sysemu/cpus.h" +#include "sysemu/replay.h" #include "slirp/libslirp.h" #include "qemu/main-loop.h" #include "block/aio.h" @@ -245,18 +246,19 @@ static int os_host_main_loop_wait(int64_t timeout) timeout = SCALE_MS; } + if (timeout) { spin_counter = 0; - qemu_mutex_unlock_iothread(); } else { spin_counter++; } + qemu_mutex_unlock_iothread(); + replay_mutex_unlock(); ret = qemu_poll_ns((GPollFD *)gpollfds->data, gpollfds->len, timeout); - if (timeout) { - qemu_mutex_lock_iothread(); - } + replay_mutex_lock(); + qemu_mutex_lock_iothread(); glib_pollfds_poll(); @@ -463,8 +465,13 @@ static int os_host_main_loop_wait(int64_t timeout) poll_timeout_ns = qemu_soonest_timeout(poll_timeout_ns, timeout); qemu_mutex_unlock_iothread(); + + replay_mutex_unlock(); + g_poll_ret = qemu_poll_ns(poll_fds, n_poll_fds + w->num, poll_timeout_ns); + replay_mutex_lock(); + qemu_mutex_lock_iothread(); if (g_poll_ret > 0) { for (i = 0; i < w->num; i++) { diff --git a/util/qemu-sockets.c b/util/qemu-sockets.c index 7f13e8a338..8bd8bb64eb 100644 --- a/util/qemu-sockets.c +++ b/util/qemu-sockets.c @@ -91,6 +91,14 @@ NetworkAddressFamily inet_netfamily(int family) return NETWORK_ADDRESS_FAMILY_UNKNOWN; } +bool fd_is_socket(int fd) +{ + int optval; + socklen_t optlen = sizeof(optval); + return !qemu_getsockopt(fd, SOL_SOCKET, SO_TYPE, &optval, &optlen); +} + + /* * Matrix we're trying to apply * @@ -1034,6 +1042,30 @@ fail: return NULL; } +static int socket_get_fd(const char *fdstr, Error **errp) +{ + int fd; + if (cur_mon) { + fd = monitor_get_fd(cur_mon, fdstr, errp); + if (fd < 0) { + return -1; + } + } else { + if (qemu_strtoi(fdstr, NULL, 10, &fd) < 0) { + error_setg_errno(errp, errno, + "Unable to parse FD number %s", + fdstr); + return -1; + } + } + if (!fd_is_socket(fd)) { + error_setg(errp, "File descriptor '%s' is not a socket", fdstr); + close(fd); + return -1; + } + return fd; +} + int socket_connect(SocketAddress *addr, Error **errp) { int fd; @@ -1048,7 +1080,7 @@ int socket_connect(SocketAddress *addr, Error **errp) break; case SOCKET_ADDRESS_TYPE_FD: - fd = monitor_get_fd(cur_mon, addr->u.fd.str, errp); + fd = socket_get_fd(addr->u.fd.str, errp); break; case SOCKET_ADDRESS_TYPE_VSOCK: @@ -1075,7 +1107,7 @@ int socket_listen(SocketAddress *addr, Error **errp) break; case SOCKET_ADDRESS_TYPE_FD: - fd = monitor_get_fd(cur_mon, addr->u.fd.str, errp); + fd = socket_get_fd(addr->u.fd.str, errp); break; case SOCKET_ADDRESS_TYPE_VSOCK: diff --git a/util/qemu-timer.c b/util/qemu-timer.c index 82d56507a2..2ed1bf2778 100644 --- a/util/qemu-timer.c +++ b/util/qemu-timer.c @@ -622,6 +622,18 @@ int64_t qemu_clock_get_ns(QEMUClockType type) } } +uint64_t qemu_clock_get_last(QEMUClockType type) +{ + QEMUClock *clock = qemu_clock_ptr(type); + return clock->last; +} + +void qemu_clock_set_last(QEMUClockType type, uint64_t last) +{ + QEMUClock *clock = qemu_clock_ptr(type); + clock->last = last; +} + void qemu_clock_register_reset_notifier(QEMUClockType type, Notifier *notifier) { diff --git a/util/rcu.c b/util/rcu.c index f4d09c8304..5676c22bd1 100644 --- a/util/rcu.c +++ b/util/rcu.c @@ -92,10 +92,11 @@ static void wait_for_readers(void) atomic_set(&index->waiting, true); } - /* Here, order the stores to index->waiting before the - * loads of index->ctr. + /* Here, order the stores to index->waiting before the loads of + * index->ctr. Pairs with smp_mb_placeholder() in rcu_read_unlock(), + * ensuring that the loads of index->ctr are sequentially consistent. */ - smp_mb(); + smp_mb_global(); QLIST_FOREACH_SAFE(index, ®istry, node, tmp) { if (!rcu_gp_ongoing(&index->ctr)) { @@ -142,8 +143,13 @@ static void wait_for_readers(void) void synchronize_rcu(void) { qemu_mutex_lock(&rcu_sync_lock); - qemu_mutex_lock(&rcu_registry_lock); + /* Write RCU-protected pointers before reading p_rcu_reader->ctr. + * Pairs with smp_mb_placeholder() in rcu_read_lock(). + */ + smp_mb_global(); + + qemu_mutex_lock(&rcu_registry_lock); if (!QLIST_EMPTY(®istry)) { /* In either case, the atomic_mb_set below blocks stores that free * old RCU-protected pointers. @@ -370,6 +376,7 @@ static void rcu_init_child(void) static void __attribute__((__constructor__)) rcu_init(void) { + smp_mb_global_init(); #ifdef CONFIG_POSIX pthread_atfork(rcu_init_lock, rcu_init_unlock, rcu_init_child); #endif diff --git a/util/sys_membarrier.c b/util/sys_membarrier.c new file mode 100644 index 0000000000..8dcb53e63e --- /dev/null +++ b/util/sys_membarrier.c @@ -0,0 +1,50 @@ +/* + * Process-global memory barriers + * + * Copyright (c) 2018 Red Hat, Inc. + * + * Author: Paolo Bonzini <pbonzini@redhat.com> + */ + +#include <qemu/osdep.h> +#include <qemu/sys_membarrier.h> +#include <qemu/error-report.h> + +#ifdef CONFIG_LINUX +#include <linux/membarrier.h> +#include <sys/syscall.h> + +static int +membarrier(int cmd, int flags) +{ + return syscall(__NR_membarrier, cmd, flags); +} +#endif + +void smp_mb_global(void) +{ +#if defined CONFIG_WIN32 + FlushProcessWriteBuffers(); +#elif defined CONFIG_LINUX + membarrier(MEMBARRIER_CMD_SHARED, 0); +#else +#error --enable-membarrier is not supported on this operating system. +#endif +} + +void smp_mb_global_init(void) +{ +#ifdef CONFIG_LINUX + int ret = membarrier(MEMBARRIER_CMD_QUERY, 0); + if (ret < 0) { + error_report("This QEMU binary requires the membarrier system call."); + error_report("Please upgrade your system to a newer version of Linux"); + exit(1); + } + if (!(ret & MEMBARRIER_CMD_SHARED)) { + error_report("This QEMU binary requires MEMBARRIER_CMD_SHARED support."); + error_report("Please upgrade your system to a newer version of Linux"); + exit(1); + } +#endif +} @@ -1948,7 +1948,7 @@ static void main_loop(void) static void version(void) { - printf("QEMU emulator version " QEMU_VERSION QEMU_PKGVERSION "\n" + printf("QEMU emulator version " QEMU_FULL_VERSION "\n" QEMU_COPYRIGHT "\n"); } @@ -2696,7 +2696,7 @@ static void qemu_run_exit_notifiers(void) notifier_list_notify(&exit_notifiers, NULL); } -static bool machine_init_done; +bool machine_init_done; void qemu_add_machine_init_done_notifier(Notifier *notify) { @@ -2713,8 +2713,8 @@ void qemu_remove_machine_init_done_notifier(Notifier *notify) static void qemu_run_machine_init_done_notifiers(void) { - notifier_list_notify(&machine_init_done_notifiers, NULL); machine_init_done = true; + notifier_list_notify(&machine_init_done_notifiers, NULL); } static const QEMUOption *lookup_opt(int argc, char **argv, @@ -3058,6 +3058,7 @@ int main(int argc, char **argv, char **envp) qemu_init_cpu_list(); qemu_init_cpu_loop(); + qemu_mutex_lock_iothread(); atexit(qemu_run_exit_notifiers); @@ -4502,6 +4503,7 @@ int main(int argc, char **argv, char **envp) blk_mig_init(); ram_mig_init(); + dirty_bitmap_mig_init(); /* If the currently selected machine wishes to override the units-per-bus * property of its default HBA interface type, do so now. */ |